第二章:SDFormat模型语言精讲:从零手写一个差速轮机器人

好,咱们进入正题。上一章我们把Gazebo的生态摸了个大概,这一章,我带你亲手写一个能动的机器人模型。不是用URDF,而是用SDFormat——Gazebo的亲儿子语言。

说实话,我刚开始接触Gazebo时也纠结过:URDF不是挺好的吗?为什么还要学SDF?后来踩了几个坑才明白——URDF是ROS时代的产物,而SDFormat才是为仿真而生的。它更灵活,更贴近物理引擎的思维方式。

2.1 SDFormat vs URDF:为什么我选SDF

先别急着写代码,咱们理清一个概念。URDF和SDF都能描述机器人,但定位完全不同。

对比项 URDF SDFormat
设计初衷 ROS机器人描述 Gazebo仿真描述
物理属性 需要额外插件 原生支持
多机器人 不支持 原生支持
世界环境 不支持 原生支持
闭链机构 不支持 支持

你看,URDF更像一个「零件清单」,而SDF是「完整的世界说明书」。我个人习惯:只要用Gazebo,一律用SDF。省心。

核心观点:SDFormat是Gazebo的「母语」。你用URDF进Gazebo,中间还得过一层解析器,多一道转换就多一个坑。直接写SDF,一步到位。

2.2 从零开始:差速轮机器人的骨架

咱们要做的机器人很简单:一个底盘,两个驱动轮,一个万向轮。差速驱动,靠左右轮速度差实现转向。

先搭骨架。SDF文件最外层是<sdf>标签,里面包着<model>。每个模型由<link>和<joint>组成。

<?xml version="1.0" ?>
<sdf version="1.7">
  <model name="my_diff_drive">
    <!-- 这里放link和joint -->
  </model>
</sdf>

嗯,这里要注意:version我建议用1.7,这是目前最稳定的版本。1.8虽然出了,但有些插件还没跟上。

2.3 底盘:<link>标签深度解析

底盘是机器人的身体。我们用<link>定义它。一个link包含三部分:惯性、碰撞、视觉。

<link name="chassis">
  <pose>0 0 0.05 0 0 0</pose>
  <inertial>
    <mass>2.0</mass>
    <inertia>
      <ixx>0.02</ixx>
      <ixy>0</ixy>
      <ixz>0</ixz>
      <iyy>0.02</iyy>
      <iyz>0</iyz>
      <izz>0.04</izz>
    </inertia>
  </inertial>
  <collision name="collision">
    <geometry>
      <box>
        <size>0.4 0.3 0.1</size>
      </box>
    </geometry>
  </collision>
  <visual name="visual">
    <geometry>
      <box>
        <size>0.4 0.3 0.1</size>
      </box>
    </geometry>
    <material>
      <ambient>0.2 0.5 0.8 1</ambient>
    </material>
  </visual>
</link>

这里我多说一句惯性矩阵。很多新手直接复制粘贴,结果机器人飘得像气球。为什么?因为惯性参数不对。我有个经验公式:对于实心长方体,ixx = (1/12)*m*(h² + d²)。你算一下,心里就有数了。

避坑指南:我曾经在项目里直接用了默认惯性值,结果机器人一跑就翻。后来发现是izz设得太小,底盘绕Z轴转得太灵活。记住:惯性不是随便填的,它决定了机器人转动的「手感」。

2.4 轮子与关节:<joint>的奥秘

轮子不能焊死在底盘上,得用<joint>连接。差速轮用revolute关节,绕Y轴旋转。

<joint name="left_wheel_joint" type="revolute">
  <parent>chassis</parent>
  <child>left_wheel</child>
  <pose>-0.15 0.2 -0.05 0 0 0</pose>
  <axis>
    <xyz>0 1 0</xyz>
    <limit>
      <lower>-1e16</lower>
      <upper>1e16</upper>
    </limit>
  </axis>
</joint>

注意pose的写法。它是子link相对于父link的偏移。我刚开始总搞混——以为pose是子link在世界中的位置。其实不是,它是相对于父link的。

轮子本身也是一个link,但简单很多:

<link name="left_wheel">
  <pose>0 0 0 0 0 0</pose>
  <inertial>
    <mass>0.2</mass>
    <inertia>
      <ixx>0.0001</ixx>
      <iyy>0.0001</iyy>
      <izz>0.0002</izz>
    </inertia>
  </inertial>
  <collision name="collision">
    <geometry>
      <cylinder>
        <radius>0.05</radius>
        <length>0.04</length>
      </cylinder>
    </geometry>
  </collision>
  <visual name="visual">
    <geometry>
      <cylinder>
        <radius>0.05</radius>
        <length>0.04</length>
      </cylinder>
    </geometry>
  </visual>
</link>

右轮同理,只是pose的Y坐标取反。万向轮用ball关节,让它自由转动。

2.5 让机器人动起来:<sensor>与插件

光有模型还不够,得让它能动。差速轮需要两个东西:一个diff_drive插件,一个imu传感器。

<plugin name="diff_drive" filename="libDiffDrivePlugin.so">
  <left_joint>left_wheel_joint</left_joint>
  <right_joint>right_wheel_joint</right_joint>
  <wheel_separation>0.4</wheel_separation>
  <wheel_diameter>0.1</wheel_diameter>
  <torque>5.0</torque>
</plugin>

<link name="chassis">
  <sensor name="imu" type="imu">
    <always_on>1</always_on>
    <update_rate>100</update_rate>
  </sensor>
</link>

这里wheel_separation是左右轮间距,wheel_diameter是轮子直径。这两个参数决定了机器人的转弯半径。我见过有人把间距写错,结果机器人原地打转——说白了就是左右轮速度算反了。

注意:插件名libDiffDrivePlugin.so是Gazebo自带的,不需要额外安装。但如果你用的是Gazebo Fortress或更高版本,插件名可能不同。建议先gazebo --version确认版本。

2.6 完整模型与知识体系

好了,把上面所有零件拼起来,就是一个完整的差速轮机器人。我画了一张图,帮你理清SDFormat的核心结构:

SDFormat 模型结构图 <sdf> <model> <link> <joint> <plugin> inertial collision visual parent child axis diff_drive / imu 每个 link 必须包含 inertial、collision、visual 三要素 joint 连接 parent 和 child,定义运动关系 plugin 赋予模型「生命」——让它动起来、感知世界

这张图把SDFormat的核心逻辑讲透了。你写模型时,就照着这个结构填内容。先link,再joint,最后plugin。顺序别搞反。

2.7 测试你的模型

写完后,保存为my_robot.sdf。用Gazebo加载:

gazebo --verbose my_robot.sdf

如果一切正常,你会看到一个蓝色底盘的机器人。在Gazebo的「Topic」面板里,找到/my_diff_drive/cmd_vel,发布一个速度指令:

gz topic -t /my_diff_drive/cmd_vel -m gz.msgs.Twist -p "linear: {x: 0.5}, angular: {z: 0.2}"

机器人应该会向前走并转弯。如果不动,检查两点:一是关节名称是否和插件里一致,二是wheel_separation是否和实际间距匹配。

调试技巧:我习惯在Gazebo里打开「View -> Joints」,能看到每个关节的状态。如果关节显示红色,说明连接有问题。另外,--verbose参数会打印详细日志,90%的问题都能从日志里找到线索。

好了,你的第一个SDFormat机器人已经跑起来了。它虽然简单,但骨架完整。后面加激光雷达、加摄像头、加机械臂,都是在这个基础上「搭积木」。记住:SDFormat的核心就是link、joint、plugin三件套。吃透它们,Gazebo里没有你搭不出来的机器人。

可可精品资料库