4、多机器人模型创建:使用URDF创建差分驱动小车模型,配置多机器人命名空间与tf树

好,咱们进入正题。

这一章,我们要亲手打造一个差分驱动小车的URDF模型。然后,把它“复制”成多份,跑在同一个仿真环境里。说白了,就是让好几台小车各干各的活,互不干扰。

我刚开始做多机器人仿真时,踩过一个坑:两台小车一启动,tf树全乱套了。一个叫“base_link”,另一个也叫“base_link”,坐标系直接打架。后来我才明白——命名空间和tf前缀,是解决这个问题的关键。

4.1 差分驱动小车URDF模型

先从小车本身说起。差分驱动,就是左右两个轮子独立驱动。通过转速差实现转向。结构简单,控制也直观。

我习惯把模型拆成三部分:车体、左右驱动轮、万向轮(或从动轮)。

下面是一个精简版的URDF。注意,我故意省略了部分视觉和碰撞细节,只保留核心关节和连杆。

<robot name="diff_drive_robot">
  <!-- 车体 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.4 0.3 0.1"/>
      </geometry>
      <origin xyz="0 0 0.05" rpy="0 0 0"/>
    </visual>
    <collision>
      <geometry>
        <box size="0.4 0.3 0.1"/>
      </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>

  <!-- 左轮 -->
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.04"/>
      </geometry>
      <origin xyz="0 0 0" rpy="1.5708 0 0"/>
    </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>

  <!-- 左轮关节 -->
  <joint name="left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <origin xyz="-0.15 0.2 0.05" 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.05" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  <!-- 万向轮 -->
  <link name="caster_wheel">
    <visual>
      <geometry>
        <sphere radius="0.03"/>
      </geometry>
    </visual>
  </link>
  <joint name="caster_wheel_joint" type="fixed">
    <parent link="base_link"/>
    <child link="caster_wheel"/>
    <origin xyz="0.15 0 0.03" rpy="0 0 0"/>
  </joint>
</robot>

嗯,这里要注意:left_wheel_joint 的旋转轴是 0 1 0,也就是绕Y轴转。为什么?因为轮子朝前滚动时,旋转轴就是横向的。我见过有人写成绕X轴,结果轮子在地上“画圈”……

4.2 多机器人命名空间

单台小车跑起来没问题。但两台、三台呢?

ROS里,每个节点、话题、参数都有全局名称。如果两台小车都发布 /cmd_vel,那到底控制谁?

解决办法:给每台小车一个独立的命名空间。

比如,启动文件里这样写:

<group ns="robot1">
  <node name="spawn_model" pkg="gazebo_ros" type="spawn_model"
        args="-urdf -model robot1 -param robot_description -x 0.0 -y 0.0 -z 0.1"/>
</group>

<group ns="robot2">
  <node name="spawn_model" pkg="gazebo_ros" type="spawn_model"
        args="-urdf -model robot2 -param robot_description -x 1.0 -y 0.0 -z 0.1"/>
</group>

这样,robot1的话题就变成了 /robot1/cmd_vel,robot2的是 /robot2/cmd_vel。互不冲突。

我的小技巧: 我习惯在launch文件里用 $(arg robot_name) 动态生成命名空间。这样改一个参数就能换一台车,不用复制粘贴整个group。

4.3 tf树与多机器人

tf树是机器人坐标系的“家谱”。单台小车时,根节点通常是 base_link。但多台小车呢?

如果每台小车的根都叫 base_link,tf树就乱了。ROS不知道哪个 base_link 对应哪台车。

解决方案:给每台小车的tf加上前缀。

在URDF里,可以这样设置:

<robot name="diff_drive_robot">
  <!-- 所有link和joint的名称保持不变 -->
</robot>

然后在启动时,通过 robot_state_publisher 的 tf_prefix 参数来区分:

<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher">
  <param name="tf_prefix" value="robot1"/>
</node>

这样,robot1的tf树就变成了:

  • robot1/base_link
  • robot1/left_wheel
  • robot1/right_wheel

robot2的则是:

  • robot2/base_link
  • robot2/left_wheel
  • robot2/right_wheel

你看,这样tf树就清晰了。每个坐标系都带着“身份证”。

注意: 我曾经犯过一个错——只改了命名空间,忘了改tf前缀。结果话题是分开了,但tf树还是乱的。rviz里看到两台小车叠在一起,坐标系乱飞。排查了半天才发现是前缀没加。所以,命名空间和tf前缀要一起配。

4.4 整体架构图

下面这张图,展示了多机器人系统的核心逻辑。我画成了SVG,方便你理解。

多机器人仿真系统架构 机器人1 (robot1) 命名空间: /robot1/ tf前缀: robot1_ 话题: /robot1/cmd_vel tf树: robot1/base_link 机器人2 (robot2) 命名空间: /robot2/ tf前缀: robot2_ 话题: /robot2/cmd_vel tf树: robot2/base_link 独立运行 关键:命名空间隔离话题,tf前缀隔离坐标系

说白了,这张图就讲了一件事:命名空间管通信,tf前缀管坐标。两者缺一不可。

4.5 实战避坑指南

最后,分享几个我实际项目中踩过的坑:

  • tf前缀不要带斜杠。比如 robot1_ 可以,但 /robot1/ 不行。斜杠会让tf解析出错。
  • 检查所有节点是否都加了前缀。有时候你只改了 robot_state_publisher,但忘了改 joint_state_publisher,结果关节状态还是全局的。
  • 用 tf_echo 验证。启动后,跑一下 rosrun tf tf_echo /robot1/base_link /robot1/left_wheel,看看能不能正确输出变换。这是最直接的检查方法。
核心总结: 多机器人模型创建,本质上就是“复制+隔离”。复制URDF模型,用命名空间和tf前缀隔离每个实例。记住这两点,多机器人仿真就成功了一半。

嗯,这一章的内容就到这。你动手试试,把上面的URDF和launch文件跑起来。遇到问题,回头看看避坑指南。

可可精品资料库