4. 实战:搭建一个两轮差速底盘

好,咱们直接动手。这一章我会带你从零开始,搭一个两轮差速底盘的 URDF 模型。底盘 link 怎么设计?轮子的 joint 怎么配?最后怎么在 RViz 里检查?一条龙走完。

我个人习惯先把整体结构想清楚,再写代码。差速底盘说白了就是两个主动轮 + 一个万向轮(或者从动轮),靠左右轮的速度差来转向。你想想看,扫地机器人、AGV 小车,基本都是这个套路。

base_link (底盘) 左轮 右轮 万向轮 joint_L joint_R joint_caster X Z 差速底盘拓扑:底盘 + 左右驱动轮 + 万向轮

4.1 底盘 link 设计

底盘是机器人的身体,所有轮子、传感器都挂在它上面。我一般用 base_link 作为根 link。形状嘛,简单点就用 box,尺寸根据实际小车来。

下面是一个典型的底盘 link 定义。注意,我习惯把原点放在底盘几何中心,这样轮子坐标算起来方便。

<link name="base_link">
  <visual>
    <geometry>
      <box size="0.4 0.3 0.08"/>
    </geometry>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <material name="blue">
      <color rgba="0.2 0.4 0.9 1.0"/>
    </material>
  </visual>
  <collision>
    <geometry>
      <box size="0.4 0.3 0.08"/>
    </geometry>
  </collision>
  <inertial>
    <mass value="2.0"/>
    <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.02"/>
  </inertial>
</link>

这里我用了 box 几何,长 0.4m,宽 0.3m,高 0.08m。碰撞和视觉保持一致,省得后面仿真出幺蛾子。惯性参数我给了个粗略值,真要做实物你得用 CAD 算一下。

💡 我的习惯: 底盘厚度别太薄,否则轮子 joint 的 offset 算起来很别扭。0.08m 是个比较舒服的值。

4.2 车轮 joint 配置

差速底盘的两个主动轮,一般用 continuous 类型的 joint,可以绕 Y 轴无限旋转。万向轮用 fixed 或者 continuous 都行,这里我用 fixed 简化。

先看左轮 joint。我记得第一次配轮子时,坐标算反了,小车原地转圈……后来学乖了,画个草图再写。

<joint name="left_wheel_joint" type="continuous">
  <parent link="base_link"/>
  <child link="left_wheel"/>
  <origin xyz="-0.15 0.2 -0.04" rpy="0 0 0"/>
  <axis xyz="0 1 0"/>
</joint>

解释一下:xyz 里 -0.15 是沿 X 向后(我习惯把车头朝 X 正方向),0.2 是向左偏移(Y 正方向),-0.04 是让轮子中心比底盘底面低一点,刚好露出半个轮子。轴是 Y 轴,这样轮子才能转起来。

右轮对称,把 Y 改成负的:

<joint name="right_wheel_joint" type="continuous">
  <parent link="base_link"/>
  <child link="right_wheel"/>
  <origin xyz="-0.15 -0.2 -0.04" rpy="0 0 0"/>
  <axis xyz="0 1 0"/>
</joint>

万向轮我放在底盘后方中间:

<joint name="caster_wheel_joint" type="fixed">
  <parent link="base_link"/>
  <child link="caster_wheel"/>
  <origin xyz="0.15 0.0 -0.06" rpy="0 0 0"/>
</joint>

注意,万向轮一般比主动轮小一点,所以 z 方向更低,保证它着地。

4.3 轮子 link 定义

轮子用圆柱体。左轮和右轮除了名字和颜色,其他一样。我直接贴左轮:

<link name="left_wheel">
  <visual>
    <geometry>
      <cylinder radius="0.05" length="0.04"/>
    </geometry>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <material name="black">
      <color rgba="0.1 0.1 0.1 1.0"/>
    </material>
  </visual>
  <collision>
    <geometry>
      <cylinder radius="0.05" length="0.04"/>
    </geometry>
  </collision>
  <inertial>
    <mass value="0.2"/>
    <inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0002"/>
  </inertial>
</link>

右轮复制一份,把 link name 改成 right_wheel,颜色可以换成灰色以示区分。万向轮我用小球代替:

<link name="caster_wheel">
  <visual>
    <geometry>
      <sphere radius="0.03"/>
    </geometry>
    <material name="silver">
      <color rgba="0.7 0.7 0.7 1.0"/>
    </material>
  </visual>
  <collision>
    <geometry>
      <sphere radius="0.03"/>
    </geometry>
  </collision>
  <inertial>
    <mass value="0.05"/>
    <inertia ixx="0.00001" ixy="0" ixz="0" iyy="0.00001" iyz="0" izz="0.00001"/>
  </inertial>
</link>

4.4 可视化检查

写完了别急着高兴,一定要用 RViz 看一眼。我每次都会踩到一些低级错误,比如 joint 轴方向反了、轮子悬空。

启动检查三步走:

  1. 保存 URDF 文件,比如 diff_drive.urdf。
  2. 终端运行 check_urdf diff_drive.urdf,看有没有语法错误。
  3. 用 urdf_to_graphiz diff_drive.urdf 生成结构图,确认父子关系。

然后在 RViz 里加载:

ros2 launch urdf_tutorial display.launch.py model:=diff_drive.urdf

或者用 robot_state_publisher 自己发布。我个人偏好直接 launch,省事。

🔍 检查重点:
  • 轮子是否与底盘有重叠?调整 origin 的 z 值。
  • 左右轮是否对称?量一下 Y 方向距离。
  • 万向轮是否刚好触地?别让它悬空或陷进底盘。
⚠️ 我曾经踩过的坑: 把轮子的 joint 类型写成了 revolute,结果轮子只能转 ±90°,小车直接趴窝。差速轮一定要用 continuous。

4.5 完整模型一览

把上面所有片段拼起来,就是一个能用的差速底盘 URDF。下面我整理了一个简化的结构表格:

组件link 名称joint 名称类型父 link
底盘base_link—根 link—
左轮left_wheelleft_wheel_jointcontinuousbase_link
右轮right_wheelright_wheel_jointcontinuousbase_link
万向轮caster_wheelcaster_wheel_jointfixedbase_link

你想想看,这个结构其实很干净。底盘是中心,轮子挂在两边。后面加传感器、加外壳,都往 base_link 上堆就行。

嗯,到这里你已经亲手搭了一个差速底盘。虽然简单,但它是所有移动机器人的基础。下一章我们会在这个底盘上加更多轮子,变成四轮小车。不过那是后话了,先把今天的模型跑起来再说。