3、机器人模型与SDF/URDF:创建你的第一个机器人模型

做机器人导航,第一步是什么?

不是写算法,也不是调参数。你得先有个「机器人」。

在Gazebo里,这个「机器人」就是模型文件。说白了,就是告诉仿真器:我的机器人长什么样、有多重、轮子在哪、传感器装在哪。没有模型,导航就是空中楼阁。

我个人习惯,每次开始一个新项目,都会花半小时把模型文件从头写一遍。不是为了炫技,而是为了把机器人的每个关节、每个传感器都刻在脑子里。嗯,这招挺管用的。

核心要点:URDF和SDF是Gazebo里描述机器人的两种格式。URDF更老、更简单;SDF更现代、功能更强。但别急着站队——实际项目中,两者经常混用。

3.1 从零开始:写一个最简单的URDF

先别管那些复杂的。我们写一个「会动的方块」。

<robot name="my_first_robot">
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.4 0.3 0.2"/>
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <box size="0.4 0.3 0.2"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="1.0"/>
      <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
    </inertial>
  </link>
</robot>

这段代码干了什么?

  • link:机器人的一个刚体部件。这里只有一个方块。
  • visual:长得什么样。蓝色方块,0.4米长、0.3米宽、0.2米高。
  • collision:碰撞检测用的形状。我建议你让collision和visual保持一致,否则仿真时会出现「看着没撞上,实际撞上了」的诡异情况。我在项目里吃过这个亏。
  • inertial:质量、惯性张量。这个容易被忽略,但少了它,Gazebo会报错。

我的小技巧:惯性张量不会算?用这个公式:对于长方体,Ixx = (1/12)*m*(h² + d²)。懒得算的话,直接用在线计算器。别在这上面卡太久。

3.2 给机器人装上轮子:joint的使用

一个方块太无聊了。我们加两个轮子,让它能跑起来。

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

joint就是关节。它连接两个link,决定了它们之间的相对运动。

  • type="continuous":轮子嘛,得一直转。continuous就是无限旋转。
  • parent/child:谁是谁的爹。这里base_link是父,轮子是子。
  • origin:轮子装在底盘的什么位置。xyz是偏移,rpy是旋转。
  • axis:绕哪个轴转。轮子绕Y轴转,所以是"0 1 0"。

你想想看,如果axis写错了,轮子就会在地上「横着滚」。我刚开始学的时候,轮子在地上平移就是不转圈,查了半天才发现是轴写错了。嗯,这种错误犯一次就够了。

3.3 SDF vs URDF:到底该用哪个?

这个问题,几乎每个初学者都会问。我直接给你结论:

特性 URDF SDF
出身 ROS原生格式 Gazebo原生格式
支持世界环境 ❌ 只能描述单个机器人 ✅ 可以描述整个场景
物理属性 基础支持 更丰富(摩擦、阻尼、弹性)
传感器模型 需要插件扩展 原生支持
代码可读性 简单直观 稍复杂但更灵活

我的建议是:

  • 做ROS2导航:用URDF。因为Nav2和robot_state_publisher对URDF支持最好。
  • 做复杂仿真:用SDF。比如你要模拟一个带传送带的工厂,SDF是唯一选择。
  • 混合使用:URDF描述机器人,SDF描述环境。这是最常用的方案。

注意:千万不要在URDF里写<gazebo>标签来定义物理属性。我曾经这么干过,结果Gazebo和ROS的解析器打架,模型加载出来轮子陷进地里。正确的做法是用单独的SDF文件或world文件来覆盖物理参数。

3.4 在Gazebo中加载模型

模型写好了,怎么让它出现在Gazebo里?

方法一:直接启动时加载

gazebo --verbose my_robot.world

方法二:通过ROS2 launch文件

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='gazebo_ros',
            executable='spawn_entity.py',
            arguments=['-entity', 'my_robot', '-file', 'my_robot.urdf'],
            output='screen'
        )
    ])

方法三:手动插入(调试时最方便)

# 在Gazebo GUI中
Model -> Insert -> 选择你的模型文件

我个人调试时,喜欢先用方法三。看到模型出现在场景里了,再写launch文件。这样能快速定位是模型问题还是启动脚本问题。

3.5 避坑指南:模型加载失败的常见原因

模型加载失败,90%是这几个原因:

  • 路径问题:Gazebo找不到你的模型文件。解决办法:设置GAZEBO_MODEL_PATH环境变量。
  • 缺少依赖:URDF里引用了其他模型(比如轮子单独一个文件),但没把路径加进去。
  • 惯性参数错误:惯性张量不合法(比如对角线为0)。Gazebo会直接崩溃。
  • 编码问题:文件保存成了UTF-8带BOM,Gazebo解析到第一个字符就报错。

调试技巧:用check_urdf命令检查URDF语法。用gz sdf --check检查SDF语法。这两个命令能帮你省下至少一半的调试时间。

3.6 本章知识体系

下面这张图,是我梳理的本章核心逻辑。建议你保存下来,写模型时对照着看。

机器人模型知识体系 机器人模型 URDF 格式 link(刚体) joint(关节) SDF 格式 模型+世界描述 物理属性更丰富 加载方式:world文件 / launch / 手动插入 URDF SDF 加载

这张图把本章内容串起来了。左边是URDF路线,右边是SDF路线,底部是加载方式。你写模型时,沿着这条线走,基本不会漏掉关键环节。

好了,模型文件的基础就这些。下一节我们会把这个模型跑起来,让它在Gazebo里真正动起来。到时候你会发现——嗯,写模型只是开始,让模型「听话」才是真功夫。

可可精品资料库