3. 模型文件与SDFormat:从零搭建你的第一张桌子
好,我们正式开始接触仿真世界的「建筑材料」——模型文件。
你可能已经听说过URDF,它是ROS里描述机器人的标准格式。但Gazebo用的却是SDF。这两个东西到底什么关系?我该用哪个?别急,我们一个一个说清楚。
3.1 什么是SDF?
SDF,全称是Simulation Description Format,翻译过来就是「仿真描述格式」。说白了,它就是一套用来描述仿真场景中所有物体的「说明书」。
你想想看,仿真世界里有什么?有地面、有桌子、有机器人、有灯光、有传感器……这些东西的位置、形状、颜色、物理属性,总得有个统一的方式告诉Gazebo吧?SDF就是干这个的。
我个人习惯把SDF理解成「仿真世界的HTML」。HTML描述网页的结构,SDF描述仿真场景的结构。它用标签化的方式,把每个物体的信息组织得清清楚楚。
核心要点:SDF是Gazebo的原生格式,它比URDF更强大,支持更丰富的仿真特性。
3.2 SDF与URDF的区别
很多初学者会问:既然ROS用URDF,为什么Gazebo不用?
嗯,这里有个历史原因。URDF诞生得早,当时ROS团队设计它的时候,主要考虑的是机器人运动学描述——关节、连杆、传动这些。但后来发现,做仿真还需要摩擦力、惯性矩阵、传感器参数、甚至灯光和地形……URDF就有点力不从心了。
我在项目中遇到过这样的情况:用URDF描述一个简单的四轮小车,结果想加个摄像头,还得额外写一堆插件配置,非常别扭。后来换成SDF,一个文件搞定所有。
| 对比项 | SDF | URDF |
|---|---|---|
| 设计目标 | 完整的仿真场景描述 | 机器人运动学描述 |
| 支持多机器人 | ✅ 原生支持 | ❌ 需要额外处理 |
| 支持传感器 | ✅ 内置支持 | ❌ 需要插件 |
| 支持物理属性 | ✅ 丰富且精确 | ✅ 基础支持 |
| 支持场景元素 | ✅ 灯光、地形、重力等 | ❌ 不支持 |
| 版本迭代 | 持续更新(SDFormat 1.10+) | 基本稳定 |
简单总结:做仿真,优先用SDF。如果你是从ROS项目迁移过来的URDF模型,Gazebo也提供了转换工具,但我建议你直接学SDF,一步到位。
3.3 编写第一个SDF文件:一张静态桌子
光说不练假把式。我们来写一个最简单的SDF文件——一张桌子。它不会动,没有关节,就是一个静态物体。
先创建文件:my_table.sdf
<?xml version="1.0" ?>
<sdf version="1.7">
<model name="my_table">
<static>true</static>
<link name="table_top">
<collision name="top_collision">
<geometry>
<box>
<size>1.2 0.8 0.05</size>
</box>
</geometry>
</collision>
<visual name="top_visual">
<geometry>
<box>
<size>1.2 0.8 0.05</size>
</box>
</geometry>
<material>
<ambient>0.8 0.6 0.4 1</ambient>
<diffuse>0.8 0.6 0.4 1</diffuse>
</material>
</visual>
<pose>0 0 0.8 0 0 0</pose>
</link>
<link name="leg_1">
<collision name="leg_1_collision">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
</collision>
<visual name="leg_1_visual">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.3 0.3 0.3 1</diffuse>
</material>
</visual>
<pose>-0.55 -0.35 0.375 0 0 0</pose>
</link>
<link name="leg_2">
<collision name="leg_2_collision">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
</collision>
<visual name="leg_2_visual">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.3 0.3 0.3 1</diffuse>
</material>
</visual>
<pose>0.55 -0.35 0.375 0 0 0</pose>
</link>
<link name="leg_3">
<collision name="leg_3_collision">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
</collision>
<visual name="leg_3_visual">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.3 0.3 0.3 1</diffuse>
</material>
</visual>
<pose>-0.55 0.35 0.375 0 0 0</pose>
</link>
<link name="leg_4">
<collision name="leg_4_collision">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
</collision>
<visual name="leg_4_visual">
<geometry>
<box>
<size>0.05 0.05 0.75</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.3 0.3 0.3 1</diffuse>
</material>
</visual>
<pose>0.55 0.35 0.375 0 0 0</pose>
</link>
</model>
</sdf>
这个文件描述了一张木色桌面、四条黑色桌腿的桌子。桌面尺寸1.2米×0.8米,厚度5厘米,高度0.8米。每个部件都同时定义了collision(碰撞体)和visual(可视化体),这是SDF的标准做法。
小技巧:碰撞体可以比可视化体简单一些,节省计算资源。比如桌腿的碰撞体用长方体就够了,没必要用圆柱体。
3.4 使用 gz model 命令加载模型
文件写好了,怎么把它放进Gazebo里?
Gazebo提供了一个命令行工具:gz model。用法很简单:
# 启动Gazebo(先开一个空场景)
gz sim empty.sdf
# 在另一个终端中,加载我们的桌子模型
gz model --load -m my_table.sdf
如果你用的是Gazebo Classic(旧版本),命令稍有不同:
# 启动Gazebo
gazebo
# 插入模型
gz model --spawn-file my_table.sdf --model-name my_table
加载成功后,你应该能在仿真场景中看到一张桌子。如果看不到,检查一下文件路径是否正确,或者Gazebo是否在运行状态。
注意:我曾经犯过一个低级错误——在Gazebo还没完全启动时就执行了加载命令,结果模型没加载进去。后来我养成了习惯:等Gazebo界面稳定显示后再执行gz model命令。
3.5 本章知识体系
为了让你更直观地理解SDF的结构,我画了一张图:
从这张图你可以看到,SDF的结构是树状的。最外层是<sdf>,里面可以包含多个<model>,每个模型由若干<link>组成,每个link又包含碰撞体、可视化体和位置信息。
嗯,到这里你应该对SDF有了基本的认识。它不复杂,就是一套标签语言,把仿真世界里的东西描述清楚。下一节我们会继续深入,看看如何给桌子加上物理属性,让它变得「真实」起来。
课后练习:试着修改桌子的颜色,或者把桌面改成圆形。用gz model重新加载,看看效果。
可可精品资料库