4、SDF模型格式入门:SDF与URDF的区别、SDF文件结构、基本标签、构建一个简单盒子模型

好,咱们进入仿真工作流的第四站。前面几章我们聊了URDF,很多同学可能觉得URDF挺顺手。但到了Gazebo里,你会发现URDF有些力不从心。这时候,SDF就该登场了。

说白了,SDF是Gazebo的亲儿子。它专门为仿真而生,能描述URDF搞不定的东西——比如物理属性、传感器、甚至整个场景。我个人习惯是:URDF做机器人本体描述,SDF做仿真环境描述。两者配合,干活不累。

SDF 模型格式 与URDF的区别 SDF 文件结构 基本标签详解 URDF:机器人本体 SDF:仿真环境+场景 <model> → <link> <visual> + <collision> <inertial> 质量/惯量 <plugin> 传感器/控制器 构建一个简单盒子模型

4.1 SDF与URDF的区别

先说说为什么要有两套格式。URDF是ROS时代的产物,它擅长描述机器人的运动学树——关节、连杆、传动。但URDF有个硬伤:它描述不了场景。你想想看,仿真里除了机器人,还有地面、墙壁、障碍物、灯光……这些URDF统统管不了。

SDF就不一样了。它从一开始就是为仿真设计的。我记得第一次用SDF时,最直观的感受是:一个SDF文件就能搞定整个仿真世界。机器人、场景、物理参数、传感器,全写在一个文件里,清爽得很。

对比维度 URDF SDF
设计目标 机器人本体描述 完整仿真场景描述
物理属性 需手动添加<gazebo>扩展 原生支持摩擦、阻尼等
传感器 不支持 原生支持激光、相机、IMU等
场景元素 不支持 支持地面、灯光、重力等
多机器人 需多个文件组合 一个文件支持多个模型
版本迭代 ROS1时代产物,更新慢 Gazebo主导,持续演进
我的建议:如果你只是做机器人运动学验证,URDF够用。但一旦涉及物理仿真、传感器仿真、多机器人协同,直接上SDF。别走弯路。

4.2 SDF文件结构

SDF的文件结构其实很直观。它采用XML格式,根标签是<sdf>,里面包含<world>或<model>。我习惯把SDF文件想象成一棵倒挂的树——根在最上面,枝叶往下展开。

一个典型的SDF文件骨架长这样:

<?xml version="1.0" ?>
<sdf version="1.7">
  <world name="my_world">
    <include>
      <uri>model://ground_plane</uri>
    </include>
    
    <model name="my_box">
      <link name="box_link">
        <visual name="visual">
          <geometry>
            <box>
              <size>1.0 1.0 1.0</size>
            </box>
          </geometry>
        </visual>
      </link>
    </model>
  </world>
</sdf>

嗯,这里要注意:SDF的版本号很重要。我用的是1.7,这是Gazebo 11的默认版本。如果你用老版本,有些标签可能不支持。

4.3 基本标签详解

咱们把常用标签过一遍。这些是我在项目中反复用的,你记牢了,后面写模型会非常顺手。

<sdf> 根标签

所有SDF文件的根。必须指定version属性。我个人习惯用1.7,兼容性最好。

<world> 世界标签

定义整个仿真世界。包含场景、物理参数、模型、灯光等。一个SDF文件可以有多个world,但Gazebo一次只能加载一个。

<model> 模型标签

描述一个具体的物体或机器人。模型由多个<link>和<joint>组成。注意:SDF的模型可以没有关节——比如一个静态的盒子,一个link就够了。

<link> 连杆标签

代表刚体。每个link包含视觉、碰撞、惯性三大部分。我刚开始学的时候总搞混:<visual>是你看得见的,<collision>是物理引擎算碰撞用的,<inertial>是算动力学用的。三者可以不一样!

关键点:视觉模型可以比碰撞模型精细,但碰撞模型越简单,仿真跑得越快。我一般用立方体或球体做碰撞模型,哪怕视觉模型是个复杂的雕刻品。

<geometry> 几何标签

支持多种几何体:<box>、<sphere>、<cylinder>、<mesh>。其中<mesh>可以加载STL、DAE等格式的文件。

<pose> 位姿标签

定义模型或link的位置和朝向。格式是:x y z roll pitch yaw。注意单位:位置是米,角度是弧度。

我曾经踩过的坑:在SDF里,<pose>是相对于父坐标系的。如果你在<model>里写<pose>,它是相对于世界坐标系。如果在<link>里写,是相对于模型坐标系。千万别搞反了,否则你的模型会飞到天上去。

4.4 构建一个简单盒子模型

理论说完了,咱们动手。我带你写一个完整的盒子模型SDF文件。这个盒子有颜色、有质量、有碰撞属性。

<?xml version="1.0" ?>
<sdf version="1.7">
  <model name="red_box">
    <pose>0 0 0.5 0 0 0</pose>
    
    <link name="box_link">
      <!-- 视觉部分:红色立方体 -->
      <visual name="visual">
        <geometry>
          <box>
            <size>1.0 1.0 1.0</size>
          </box>
        </geometry>
        <material>
          <ambient>1 0 0 1</ambient>
          <diffuse>1 0 0 1</diffuse>
        </material>
      </visual>
      
      <!-- 碰撞部分:和视觉一样大 -->
      <collision name="collision">
        <geometry>
          <box>
            <size>1.0 1.0 1.0</size>
          </box>
        </geometry>
      </collision>
      
      <!-- 惯性部分:质量1kg -->
      <inertial>
        <mass>1.0</mass>
        <inertia>
          <ixx>0.1667</ixx>
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyy>0.1667</iyy>
          <iyz>0</iyz>
          <izz>0.1667</izz>
        </inertia>
      </inertial>
    </link>
  </model>
</sdf>

这个盒子边长1米,质量1公斤,红色。你可能会问:惯性矩的数值怎么来的?对于立方体,绕中心轴的惯性矩公式是 I = (1/12) * m * (a² + b²)。这里a=b=c=1,所以 I = 1/12 * 1 * (1+1) = 0.1667。嗯,数学没白学吧?

小技巧:如果你懒得算惯性矩,可以用Gazebo自带的工具gz sdf -k your_model.sdf来检查。它会告诉你惯性矩是否合理。我曾经有个模型因为惯性矩写错了,仿真时盒子像气球一样飘起来……排查了半天才发现是惯性矩少了个零。

保存这个文件为red_box.sdf,然后在终端运行:

gazebo --verbose red_box.sdf

你会看到一个红色的立方体稳稳地落在Gazebo的地面上。如果它掉下去了,检查一下<pose>的z值——我设的是0.5,因为盒子高1米,中心在0.5时底面刚好贴地。

好了,这就是SDF的入门。说白了,SDF就是把URDF做不到的事情补上,而且做得更彻底。你掌握了这些基础标签,后面写复杂的机器人模型、传感器配置、场景搭建,都会轻松很多。

核心要点回顾:
  • SDF是Gazebo的原生格式,比URDF更适合仿真
  • 文件结构:sdf → world/model → link → visual/collision/inertial
  • 视觉、碰撞、惯性三者可以不同,碰撞模型越简单仿真越快
  • 惯性矩要算对,否则物理行为会出问题
  • pose的坐标系层级要搞清楚,否则模型位置会乱

可可精品资料库