第3章 机器人建模实战:构建两轮差速底盘

这一章咱们直接动手。不绕弯子,从零开始搭一个两轮差速底盘机器人。你想想看,差速底盘几乎是移动机器人的"Hello World",但真正把它在URDF里建好、轮子转起来、支撑轮不卡地,还是有不少门道的。我个人习惯先把整体结构想清楚再写代码,不然容易改来改去。

两轮差速底盘 URDF 底盘本体 (base_link) 几何:box / 质量 / 惯性 驱动轮 ×2 连续旋转关节 支撑轮 (万向轮) 固定关节 / 浮动关节 可视化检查:rviz / check_urdf

3.1 整体设计思路

差速底盘的核心就三个部分:一个车身、两个主动轮、一个或多个支撑轮。主动轮负责驱动和转向——说白了,两边轮子转速不一样就能转弯。支撑轮呢,就是防止底盘前后翘起来。我在项目里见过有人把支撑轮做成固定球,结果转弯时摩擦力太大,直接把底盘拽偏了。所以这里我建议用万向轮模型,或者至少给个浮动关节。

💡 建模原则: 每个轮子一个独立连杆,主动轮用连续旋转关节(continuous),支撑轮用固定关节(fixed)但几何上做成球状,或者用浮动关节模拟万向。

3.2 编写底盘 URDF

我们直接上代码。先定义底盘本体,再挂轮子。注意坐标系:base_link 是底盘中心,轮子关节的 origin 要算好偏移量。

<robot name="diff_drive_base">
    <!-- 底盘本体 -->
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.4 0.3 0.12"/>
            </geometry>
            <origin xyz="0 0 0.06" 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.12"/>
            </geometry>
        </collision>
        <inertial>
            <mass value="2.0"/>
            <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.015" iyz="0" izz="0.01"/>
        </inertial>
    </link>

    <!-- 左驱动轮 -->
    <link name="left_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.05" length="0.04"/>
            </geometry>
            <origin xyz="0 0 0" rpy="1.5708 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.0002" ixy="0" ixz="0" iyy="0.0002" iyz="0" izz="0.0003"/>
        </inertial>
    </link>

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

    <!-- 右驱动轮 (对称) -->
    <link name="right_wheel">
        <!-- 与左轮相同,略 -->
    </link>
    <joint name="right_wheel_joint" type="continuous">
        <parent link="base_link"/>
        <child link="right_wheel"/>
        <origin xyz="-0.15 -0.2 -0.06" rpy="0 0 0"/>
        <axis xyz="0 1 0"/>
    </joint>

    <!-- 支撑轮 (万向球) -->
    <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>
    </link>
    <joint name="caster_joint" type="fixed">
        <parent link="base_link"/>
        <child link="caster_wheel"/>
        <origin xyz="0.15 0 -0.09" rpy="0 0 0"/>
    </joint>
</robot>
🔧 我的小习惯: 写 URDF 时先把 visual 和 collision 分开写,不要偷懒只写一个。collision 几何可以比 visual 略简单,能大幅提升仿真性能。我曾经因为 collision 用太精细的 mesh,导致 Gazebo 卡成幻灯片……

3.3 轮子关节的细节

注意看左轮 joint 的 origin:xyz="-0.15 0.2 -0.06"。为什么 z 是负的?因为 base_link 的原点在底盘几何中心,轮子要往下偏移到底盘下表面。你想想看,如果 z 设成 0,轮子就嵌在车身里了。另外 axis xyz="0 1 0" 表示绕 Y 轴旋转——这样轮子才能向前滚动。

关节名称类型位置 (xyz)旋转轴说明
left_wheel_jointcontinuous-0.15, 0.2, -0.060 1 0左驱动轮,绕Y轴
right_wheel_jointcontinuous-0.15, -0.2, -0.060 1 0右驱动轮,对称
caster_jointfixed0.15, 0, -0.09—后部支撑球

3.4 模型可视化检查

写完了别急着高兴。先跑两个命令,确保模型没毛病。

# 1. 语法与结构检查
check_urdf diff_drive.urdf

# 2. 在 rviz 里看三维效果
ros2 launch urdf_tutorial display.launch.py model:=`pwd`/diff_drive.urdf
# 或者用 ROS1:
roslaunch urdf_tutorial display.launch model:=`pwd`/diff_drive.urdf

check_urdf 会输出关节树,确保每个关节都有 parent 和 child,没有悬空的连杆。我曾经有一次忘了给支撑轮加 link,结果 check_urdf 直接报错,找了好半天才发现。

⚠️ 常见翻车现场: 轮子 origin 的 z 值算错,导致轮子悬空或陷入地面。还有,支撑轮如果也用 continuous 关节,仿真里它会一直转个不停——除非你给它加摩擦。固定关节加球体是最稳妥的。

3.5 在 rviz 里看什么?

  • 颜色和材质: 底盘是不是蓝色?轮子是不是黑色?支撑轮是不是银色?
  • 关节原点: 轮子是否在正确的位置?左右对称吗?
  • 碰撞体: 打开 "RobotModel" 的 "Show Collision" 选项,看看碰撞体是否包裹住了视觉体。
  • TF 树: 打开 "TF" 插件,看看坐标系之间的变换是否连续。

我个人习惯在 rviz 里把轮子关节的 continuous 类型手动拖动一下,看看轮子能不能顺畅旋转。如果卡顿或者扭曲,说明惯性参数或者几何原点有问题。

✅ 检查清单:
  1. check_urdf 无报错
  2. rviz 中模型显示完整,颜色正确
  3. 轮子位置对称,支撑轮触地
  4. TF 树无断链
  5. 关节可正常旋转(continuous类型)

3.6 支撑轮的两种设计思路

支撑轮其实有两种常见做法。一种像我上面写的——固定关节 + 球体,简单可靠。另一种是用浮动关节(floating),让支撑轮可以自由倾斜,更接近真实万向轮。但浮动关节在 URDF 里需要额外指定 type="floating",而且仿真里容易抖动。我个人更推荐固定球体,除非你做的项目对地面适应性要求极高。

💡 避坑指南: 我曾经在一个 AGV 项目里用了浮动关节做支撑轮,结果 Gazebo 里底盘一直低频振荡。后来换成固定球体 + 微小摩擦系数,稳得很。所以,别盲目追求"真实",仿真里稳定比真实更重要。

3.7 本章小结

我们完成了一个完整的差速底盘 URDF 模型。从底盘本体到两个驱动轮,再到支撑轮,每一步都经过了检查和可视化。你想想看,这个模型虽然简单,但已经包含了 URDF 最核心的要素:link、joint、geometry、inertial、collision。下一章我们会往上面加传感器,但那是后话了。

嗯,到这里,你的第一个移动机器人模型就跑起来了。去 rviz 里转一转,成就感还是有的吧?