4. 机器人模型URDF/SDF:编写差速驱动机器人URDF、添加激光雷达与IMU
好,我们正式开始动手写机器人模型了。这一章,说白了就是给机器人“画骨”和“装器官”。URDF 就是骨架,激光雷达和 IMU 就是眼睛和耳朵。我当年刚接触 ROS 时,觉得 URDF 就是个 XML 文件,随便写写就行。结果在仿真里机器人要么轮子悬空,要么雷达装反了方向……嗯,后来才明白,这里面的门道其实不少。
4.1 差速驱动底盘的核心结构
差速驱动,顾名思义,靠两个轮子的速度差来实现转向。结构上其实很简单:一个车身、两个驱动轮、一个或两个万向轮。我个人习惯把万向轮放在前后,这样机器人转弯时更稳。
先看一个典型的差速底盘参数:
| 部件 | 尺寸/参数 | 说明 |
|---|---|---|
| 车身 | 长 0.4m × 宽 0.3m × 高 0.15m | 内部放电池和主控 |
| 驱动轮 | 半径 0.05m,宽度 0.03m | 左右各一个 |
| 万向轮 | 半径 0.02m | 前后各一个,或只放后面 |
| 轮距 | 0.25m | 左右轮中心距离 |
为什么轮距这么重要?因为它直接影响你的转弯半径和里程计精度。我在项目里遇到过,轮距写错了 1 厘米,结果机器人原地转圈时,实际转了 360 度,里程计只报了 350 度。这种误差积累起来,导航根本没法用。
4.2 编写差速驱动机器人 URDF
URDF 的核心就两个东西:link 和 joint。link 是刚体,joint 是连接关系。我们一步步来。
4.2.1 车身 link
车身用 box 形状,颜色随便选,但建议用亮色,仿真里看得清楚。
<link name="base_footprint">
<visual>
<geometry>
<box size="0.4 0.3 0.15"/>
</geometry>
<material name="blue">
<color rgba="0 0 0.8 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.4 0.3 0.15"/>
</geometry>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
</inertial>
</link>
4.2.2 驱动轮 link 与 joint
轮子要能转,所以 joint 类型必须是 continuous。注意坐标轴方向:
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.03"/>
</geometry>
<material name="black"/>
</visual>
<collision>
<cylinder radius="0.05" length="0.03"/>
</collision>
</link>
<joint name="left_wheel_joint" type="continuous">
<parent link="base_footprint"/>
<child link="left_wheel"/>
<origin xyz="-0.1 0.125 -0.075" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
这里 axis xyz="0 1 0" 表示绕 Y 轴旋转。为什么?因为轮子前进方向是 X 轴,旋转轴自然就是 Y 轴。你想想看,如果写成绕 X 轴,轮子就变成侧着滚了——我见过有人犯过这个错,仿真里机器人直接“漂移”。
4.2.3 万向轮
万向轮用 fixed 关节就行,因为它不主动驱动。但注意,万向轮其实应该能自由旋转,不过 URDF 里模拟不了那么细,我们一般用球状轮子代替。
<link name="caster_wheel">
<visual>
<geometry>
<sphere radius="0.02"/>
</geometry>
</visual>
</link>
<joint name="caster_joint" type="fixed">
<parent link="base_footprint"/>
<child link="caster_wheel"/>
<origin xyz="0.15 0 -0.085"/>
</joint>
4.3 添加激光雷达
激光雷达一般装在车顶或车头。我个人习惯装在车顶中心,这样视野最开阔。但要注意,雷达不能和车身碰撞,所以高度要留够。
<link name="laser">
<visual>
<geometry>
<cylinder radius="0.03" length="0.05"/>
</geometry>
<material name="red"/>
</visual>
</link>
<joint name="laser_joint" type="fixed">
<parent link="base_footprint"/>
<child link="laser"/>
<origin xyz="0 0 0.12" rpy="0 0 0"/>
</joint>
4.4 添加 IMU
IMU 通常放在机器人重心附近,这样测量到的加速度和角速度最准确。URDF 里 IMU 其实不需要 visual,但为了调试方便,我还是会画一个小方块表示。
<link name="imu_link">
<visual>
<geometry>
<box size="0.02 0.02 0.02"/>
</geometry>
<material name="green"/>
</visual>
</link>
<joint name="imu_joint" type="fixed">
<parent link="base_footprint"/>
<child link="imu_link"/>
<origin xyz="0 0 0.02" rpy="0 0 0"/>
</joint>
IMU 的坐标系约定:X 朝前,Y 朝左,Z 朝上。这和 ROS 的标准一致。如果你用其他方向的 IMU,记得在 launch 文件里做旋转矩阵变换。
4.5 整体结构图
下面这张图展示了我们刚才搭建的机器人模型结构。你可以看到各个部件之间的层级关系:
4.6 验证 URDF
写完之后,一定要验证。ROS 提供了 check_urdf 工具:
check_urdf my_robot.urdf
如果输出类似这样,就说明语法没问题:
robot name is: my_robot
---------- Successfully Parsed XML ---------------
root link: base_footprint has 4 child(ren)
child(1): left_wheel
child(2): right_wheel
child(3): caster_wheel
child(4): laser
child(5): imu_link
- 每个 link 必须有 visual、collision、inertial 三部分
- 驱动轮用 continuous 关节,万向轮用 fixed
- 激光雷达和 IMU 的坐标系方向必须和 ROS 标准一致
- 写完一定要用 check_urdf 验证,别偷懒
嗯,到这里,一个完整的差速驱动机器人 URDF 就写好了。你可以在 Rviz 里加载看看,如果轮子能转、雷达能扫出点云,那就说明模型没问题。下一章我们会把这个模型用起来,配合 Gazebo 做仿真。