3、双目相机建模:左右相机坐标系定义、基线距离设置、同步触发配置

双目相机,说白了就是给机器人装上一双「立体眼睛」。单目相机只能看到二维画面,而双目能通过左右眼的视差算出物体的深度。这个能力在视觉SLAM、避障、目标抓取中太关键了。

我最早接触双目是在一个移动机器人项目上,当时想把深度图直接喂给导航栈。结果URDF里左右相机坐标系没对齐,基线距离也写错了,点云出来全是扭曲的。嗯,从那以后我养成了一个习惯——先画坐标系拓扑图,再写URDF。

核心要点:双目建模的本质是定义两个独立相机传感器,通过固定关节(fixed joint)将它们连接到同一个基座,并精确设置左右相机坐标系之间的平移量(即基线距离)。

3.1 左右相机坐标系定义

双目相机通常有两种结构:

  • 平行光轴结构:左右相机光轴平行,基线沿X轴方向。这是最常见的工业双目设计。
  • 汇聚光轴结构:左右相机向内偏转一定角度,用于近距离高精度测量。URDF中需要额外添加旋转关节。

我个人习惯用平行光轴结构,因为后续标定和深度计算都简单。坐标系定义遵循ROS的REP-103规范:

坐标系 原点位置 Z轴方向 X轴方向
left_camera_optical_frame 左相机光心 沿光轴指向场景 图像右方向
right_camera_optical_frame 右相机光心 沿光轴指向场景 图像右方向
stereo_camera_frame 双目模组几何中心 与左右相机Z轴平行 指向右相机方向

你想想看,如果左右相机的Z轴不平行,深度计算时就会引入额外的旋转矩阵,处理起来非常麻烦。所以URDF里我建议用fixed关节,只做平移,不做旋转。

<!-- 左相机坐标系 -->
<joint name="left_camera_joint" type="fixed">
  <parent link="stereo_camera_link"/>
  <child link="left_camera_link"/>
  <origin xyz="-0.06 0 0" rpy="0 0 0"/>
</joint>

<link name="left_camera_link">
  <inertial>
    <mass value="0.01"/>
    <origin xyz="0 0 0"/>
    <inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6"/>
  </inertial>
</link>

<!-- 右相机坐标系 -->
<joint name="right_camera_joint" type="fixed">
  <parent link="stereo_camera_link"/>
  <child link="right_camera_link"/>
  <origin xyz="0.06 0 0" rpy="0 0 0"/>
</joint>

<link name="right_camera_link">
  <inertial>
    <mass value="0.01"/>
    <origin xyz="0 0 0"/>
    <inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6"/>
  </inertial>
</link>

小技巧:左右相机的惯性参数可以设得很小(比如1e-6),因为相机通常作为传感器不参与动力学计算。但mass不能为0,否则ROS的robot_state_publisher会报错。

3.2 基线距离设置

基线距离就是左右相机光心之间的物理距离。这个值直接影响深度测量精度:

  • 基线越大:深度分辨率越高,但近距离盲区增大,模具体积也变大。
  • 基线越小:模组紧凑,适合近距离,但远距离深度误差会放大。

我在项目中遇到过选型失误的情况。当时为了追求小体积,选了个20mm基线的双目模组,结果在5米外测距误差直接飙到10%。后来换成60mm基线,同样距离误差降到2%以内。

应用场景 推荐基线距离 有效深度范围
桌面机械臂抓取 30 - 50 mm 0.2 - 2 m
室内移动机器人 60 - 120 mm 0.5 - 8 m
室外无人车 150 - 300 mm 1 - 30 m
无人机避障 40 - 80 mm 0.3 - 5 m

URDF中基线距离体现在origin xyz的X轴偏移量上。比如基线120mm,左右相机各偏移60mm:

<!-- 左相机偏移 -0.06m,右相机偏移 +0.06m,基线 = 0.12m -->
<origin xyz="-0.06 0 0" rpy="0 0 0"/>  <!-- 左 -->
<origin xyz="0.06 0 0" rpy="0 0 0"/>   <!-- 右 -->

注意:基线距离必须与相机标定文件(calibration.yaml)中的baseline参数严格一致。如果URDF里写120mm,标定文件里写100mm,深度计算会直接出错。我曾经因为这个bug排查了整整两天。

3.3 同步触发配置

双目相机要正常工作,左右图像必须在同一时刻采集。否则运动物体在左右图中位置不一致,视差计算全乱套。

同步触发有两种主流方式:

  1. 硬件同步:通过GPIO或触发线,让左右相机同时曝光。延迟通常在微秒级。
  2. 软件同步:通过ROS的message_filters做时间戳对齐。延迟在毫秒级,适合静态场景。

我个人强烈推荐硬件同步,尤其是移动机器人场景。软件同步在机器人急停或快速转弯时,左右图时间差可能达到10-20ms,足够让深度图出现大量空洞。

在URDF中,同步触发不直接体现在模型描述里,而是通过<gazebo>标签或插件配置:

<!-- 使用gazebo插件模拟双目同步 -->
<gazebo>
  <plugin name="stereo_camera_plugin" filename="libgazebo_ros_multicamera.so">
    <baseline>0.12</baseline>
    <leftCamera>
      <cameraName>left_camera</cameraName>
      <imageTopicName>left/image_raw</imageTopicName>
      <cameraInfoTopicName>left/camera_info</cameraInfoTopicName>
      <frameName>left_camera_optical_frame</frameName>
    </leftCamera>
    <rightCamera>
      <cameraName>right_camera</cameraName>
      <imageTopicName>right/image_raw</imageTopicName>
      <cameraInfoTopicName>right/camera_info</cameraInfoTopicName>
      <frameName>right_camera_optical_frame</frameName>
    </rightCamera>
    <sync>true</sync>  <!-- 开启同步触发 -->
    <syncDelay>0.001</syncDelay>  <!-- 最大同步延迟1ms -->
  </plugin>
</gazebo>

经验之谈:如果你用真实硬件,可以在ROS驱动节点中设置sync_mode: hardware。我习惯在启动脚本里加一个检查项,打印左右图像时间戳差值,超过2ms就报警。这个小脚本帮我抓出过好几次接线松动的问题。

3.4 双目相机URDF完整结构

下面这张图展示了双目相机在URDF中的完整坐标系拓扑:

双目相机URDF坐标系拓扑 stereo_camera_link left_camera_link right_camera_link left_camera_optical_frame right_camera_optical_frame fixed joint xyz: -0.06 0 0 fixed joint xyz: 0.06 0 0 rpy: -1.57 0 -1.57 rpy: -1.57 0 -1.57 基线距离 = 0.12 m 说明:光学坐标系相对于相机坐标系需要旋转 -90° 绕X轴和 -90° 绕Z轴,以符合ROS图像坐标系约定

从图中可以看到,stereo_camera_link是双目模组的物理基座,左右相机通过fixed关节挂载在基座两侧。每个相机下面还有一个光学坐标系,用于图像数据处理。

这里有个细节容易忽略:相机坐标系和光学坐标系是不同的。相机坐标系Z轴朝前(指向场景),Y轴朝下;而光学坐标系Z轴朝前,Y轴朝下,但X轴指向图像右方向。URDF中需要通过rpy旋转来对齐:

<!-- 从相机坐标系到光学坐标系的旋转 -->
<origin xyz="0 0 0" rpy="-1.5708 0 -1.5708"/>

常见错误:很多人忘记添加光学坐标系,直接把图像话题挂载到相机坐标系下。这样会导致camera_info中的frame_id与TF树不一致,RViz中无法正确显示点云。我曾经在项目评审时被指出这个问题,当场改代码,场面一度很尴尬。

好了,双目相机的URDF建模就这些核心内容。坐标系定义要准,基线距离要实测,同步触发要硬连。做到这三点,你的双目模型就能稳定工作了。