一、整体主线:从"看见目标"到"控制机器人运动"
这一阶段的核心,是理解机器人系统中一条完整的视觉感知到运动控制闭环。
机器人首先通过摄像头获取图像,并把图像发布到 /camera/image_raw。随后,感知节点订阅图像话题,对图像进行 AprilTag 或 YOLO 目标检测,得到目标是否存在、目标中心点、目标偏移量、置信度等信息。
检测完成后,控制节点会根据目标位置判断机器人应该左转、右转、前进还是停止 ,最后发布 /cmd_vel 速度指令。
这条链路可以概括为:
图像输入 → 目标检测 → 感知结果组织 → 方向判断 / 坐标转换 → /cmd_vel 控制 → 安全停止
其中,/camera/image_raw 是视觉输入,/cmd_vel 是运动输出,中间的目标检测、目标位置判断和 TF 坐标变换,是机器人从"看见目标"到"朝目标运动"的关键过程。
二、图像输入与目标检测
视觉系统的起点是 camera_publisher_node.py。
它的作用是从真实摄像头 或 mock 图像 中读取画面,然后把 OpenCV 图像转换成 ROS2 的 sensor_msgs/msg/Image 消息,并发布到 /camera/image_raw。
OpenCV 图像和 ROS2 图像消息不是同一种格式。
OpenCV 更适合做图像读取、处理和显示;ROS2 topic 更适合在机器人系统中传递数据。因此中间需要 cv_bridge 进行转换。
也就是说:
OpenCV 负责图像处理,ROS2 topic 负责系统通信, cv_bridge 负责两者之间的格式转换。
图像消息里还需要关注 frame_id。
在当前项目中,图像的 frame_id 通常设置为 camera_link,表示这张图像来自相机坐标系。
这个信息很重要,因为后续如果要把目标点从相机坐标系转换到机器人本体坐标系,就必须知道目标最开始属于哪个坐标系。
三、AprilTag 与 YOLO 的区别
这一阶段主要使用了两类目标检测方式:AprilTag 和 YOLO。
AprilTag 是一种人为视觉标记,适合机器人实验中的稳定目标识别。它的优点是检测稳定、目标 ID 明确、结果容易验证,非常适合用来做目标跟随和闭环控制测试。
但是 AprilTag 的局限也很明显:
它只能识别人为放置的标签,不能识别真实环境中的普通物体。
YOLO 是通用物体检测模型,可以识别人、杯子、瓶子、椅子等常见类别。它更接近真实场景中的视觉感知任务。
不过需要注意:
YOLO 输出的是二维检测框,不等于真实三维位置。
也就是说,YOLO 可以告诉机器人"目标在图像中的哪里",但不能单独告诉机器人"目标距离自己多少米"。如果要得到真实三维位置,还需要结合深度相机、双目相机、固定距离假设,或者目标尺寸估计等方法。
所以这两者的工程定位不同:
AprilTag 更适合稳定验证闭环控制,YOLO 更适合通用物体识别。
四、感知结果如何组织
目标检测完成后,系统不能只知道"检测到了目标",还需要把检测结果整理成后续控制节点能够使用的数据。
对于 AprilTag,/perception/target 中比较重要的信息包括:
detected:是否检测到目标id:AprilTag 的编号center_x/center_y:目标中心点在图像中的像素坐标offset_x/offset_y:目标相对于图像中心的偏移area_ratio:目标面积占图像面积的比例,可粗略反映远近confidence:检测置信度frame_id:感知结果所属坐标系
其中,对控制最关键的是 detected 和 offset_x。
如果 detected 为 false,说明目标丢失,机器人应该停止或进入搜索状态。
如果 offset_x 小于 0,说明目标偏左;如果 offset_x 大于 0,说明目标偏右;如果 offset_x 接近 0,说明目标基本居中。
对于 YOLO,/perception/objects 通常是多目标检测结果,可以包含类别、置信度、检测框、中心点、偏移量和面积比例。
初期使用 JSON 字符串发布感知结果是合理的,因为它调试方便、结构直观、容易通过 topic echo 查看。但从更规范的工程角度看,后续可以升级成自定义 ROS2 msg,或者使用标准检测消息类型。
五、从目标检测到 /cmd_vel 控制
目标检测结果本身不能直接让机器人运动,还需要经过控制逻辑转换成速度指令。
最基础的方法是根据目标在图像中的左右偏移进行控制:
- 目标偏左:机器人左转
- 目标偏右:机器人右转
- 目标居中:机器人前进
- 目标丢失:机器人停止
在这个过程中,target_direction_node.py 负责把连续的 offset_x 转换成离散方向,比如 left、right、center 和 lost。
检测节点只负责感知,方向判断节点负责解释感知结果,控制节点只负责发布速度。
target_follower_node.py 负责根据方向发布 /cmd_vel。
/cmd_vel 使用的是 geometry_msgs/msg/Twist,其中最常用的是:
linear.x:控制前进速度angular.z:控制旋转速度
如果目标在左侧,可以让 angular.z 为正,使机器人左转。
如果目标在右侧,可以让 angular.z 为负,使机器人右转。
如果目标居中,可以给一个较小的 linear.x,让机器人前进。
如果目标丢失,则让 linear.x 和 angular.z 都为 0,使机器人停止。
这就是一个最小版的视觉伺服闭环。
所谓视觉伺服,即为:
机器人根据视觉反馈不断调整自己的运动,让目标逐渐回到期望位置。
在当前项目里,这个期望位置就是图像中心附近。
六、TF 坐标变换:从图像偏移到空间位置
只根据图像偏移控制机器人,是一个简化版本。
它可以完成基础目标跟随,但表达能力有限。
因为图像偏移只能说明:
目标在画面左边、右边,还是中间。
但真实机器人控制还需要知道:
目标在机器人前方多少?在机器人左侧还是右侧?距离是否已经足够近?
这就需要 TF 坐标变换。
本阶段建立的 TF 树是:
map → odom → base_link → camera_link
其中:
map:全局地图坐标系odom:里程计坐标系,短期连续,但长期可能漂移base_link:机器人本体坐标系camera_link:相机坐标系
对移动机器人来说,base_link 最重要。
通常可以理解为:
base_link的 x 轴:机器人前方base_link的 y 轴:机器人左侧base_link的 z 轴:机器人上方
所以,如果一个目标点在 base_link 下的 x 为正,说明目标在机器人前方;如果 y 为正,说明目标在机器人左侧;如果 y 为负,说明目标在机器人右侧。
TF 的核心作用就是:
把相机坐标系 camera_link 下的目标点,转换到机器人本体坐标系 base_link 下。
这样控制逻辑就从:目标在图像左边,所以左转
升级成:目标在机器人左前方,所以左转并前进
七、安全机制:视觉控制不能裸跑
视觉控制不能只考虑正常情况,还必须考虑异常情况。
如果摄像头断开、检测节点停止、目标丢失、置信度过低,机器人都不应该继续按照旧指令运动。
因此控制节点必须加入安全保护。
比较重要的安全机制包括:
- 目标丢失时停止
- 长时间没有收到感知消息时停止
- 置信度低于阈值时忽略检测结果
- 速度指令限幅
- 程序退出时发布零速度
- TF 查询失败时停止
其中,超时停止非常关键。
因为机器人系统是连续运行的,如果控制节点一直使用几秒前的旧检测结果,可能会导致机器人在目标已经消失的情况下继续移动。所以控制节点需要记录最近一次收到有效感知消息的时间。一旦超过设定阈值,就发布零速度。
这体现了机器人系统中的 fail-safe 思想:
当系统不确定时,默认进入更安全的状态。
对移动机器人来说,最安全的默认状态通常就是停止。