4.1 SDF格式深度解析


4.1 SDF格式逐层拆解

本节摘要:SDF(Simulation Description Format)是 Gazebo 的声明式场景语言,核心层级为 world → model → link → joint/collision/visual/sensor。它比 URDF 多覆盖"整个世界"(光照、物理、地形、多模型),且带 schema 可机器校验。本节解析层级语义、给出与 URDF 的对照及工程转换命令。

从 URDF 的短板说起

ROS 生态的老牌描述格式 URDF 只描述"一台机器人":link 与 joint,完了。但一个仿真实验还需要机器人之外的一切——地形、光照、物理参数、其他机器人、传感器挂载方式。SDF 的设计目标正是描述整个可运行的世界,机器人只是世界里的一种 model。两者关系:

维度 URDF SDF
覆盖范围 单机器人 整个世界(含机器人)
传感器声明 不支持 一等公民(<sensor>
场景环境 地形、光照、大气、物理全局参数
校验工具 依赖解析器报错 官方 schema + gz sdf 工具链
典型来源 手写、xacro 生成 从 URDF 转换、手写、程序生成

生态实践通常是:机器人本体维护一份 URDF(ros2_control 需要它),仿真时转换成 SDF 使用;世界与环境直接写 SDF。

层级语义:一个文件就是一棵树

<?xml version="1.0" ?> <sdf version="1.9"> <world name="demo_world"> <!-- 世界级: 物理与光照(第3章的账在这里开总账) --> <physics type="ode"><max_step_size>0.001</max_step_size></physics> <light type="directional" name="sun"><cast_shadows>true</cast_shadows></light> <!-- model: 世界里的一切"东西", 机器人/桌子/墙 语法平等 --> <model name="my_robot"> <pose>0 0 0.1 0 0 0</pose> <link name="base"> <inertial><mass>5</mass>...</inertial> <collision name="col">...</collision> <!-- 物理引擎吃这个 --> <visual name="vis">...</visual> <!-- 渲染引擎吃这个 --> <sensor name="lidar" type="gpu_lidar">...</sensor> <!-- 第5章 --> </link> <joint name="wheel_joint" type="revolute">...</joint> <plugin filename="gz-sim-diff-drive-system" name="...DiffDrive"/> </model> </world> </sdf>

几个容易被低估的语义点:

  • collision 与 visual 分离。物理引擎看碰撞体(应该粗糙),渲染看视觉体(可以精细)。第 3 章"碰撞简单、视觉好看"原则在语法层就有落点。
  • model 是通用容器。机器人、桌子、传感器支架在 SDF 里没有语法区别——"机器人"只是挂了驱动插件的 model。
  • include 复用。世界文件里 <include><uri>model://my_robot</uri></include> 引用模型库里的现成模型,不必复制粘贴(4.3 讲解析规则)。

层级与职责一览

层级与职责一览

工具链:校验、转换、规范化

SDF 的"机器可校验"不是口号,命令行就能兑现:

gz sdf -p my_robot.sdf > pretty.sdf # 解析+规范化输出, 语法错误在这里现形 gz sdf -k my_world.sdf # 校验, 不修改 # 从 URDF 生成 SDF(机器人本体进入仿真世界的标准通道) gz sdf -p model.urdf > model.sdf

gz sdf -p 是每次交付前的例行检查:它做 schema 校验并输出规范化版本。团队协作里把"提交前必须过 gz sdf -p"写进 CI,能拦住九成的低级语法问题(标签拼错、单位制混乱、版本声明缺失)。

⚠️ 常见坑:URDF→SDF 转换会自动补全碰撞体、惯性等 URDF 缺失项,补全值是保守默认而非你的真实参数。转换后必须人工核对 <inertial>(用 3.2 的量级法)。"转换完直接用"是惯性账错的第二大来源,仅次于单位错。

声明式不等于手写:程序生成 SDF 的正当地盘

层级树有个常被忽略的红利:SDF 是规则嵌套的 XML,凡是"结构重复、参数变化"的场景——传感器阵列、货架森林、随机化测试的世界批量生成——都适合写生成器而不是手工维护几百个重复块。随机化测试尤其如此:把障碍密度、光照强度、摩擦系数参数化,一份脚本产出上百个世界文件,正好喂给第 1 章说的并行批量仿真:

# gen_worlds.py:随机化仓库世界,供批量统计测试 import random, math random.seed(42) def make_world(n_shelves, mu, lux): shelves = [] for i in range(n_shelves): x, y = random.uniform(-8, 8), random.uniform(-8, 8) yaw = random.uniform(0, math.pi) shelves.append( f'<include><uri>model://shelf</uri>' f'<pose>{x:.2f} {y:.2f} 0 0 0 {yaw:.2f}</pose></include>') return f"""<sdf version="1.9"><world name="warehouse"> <physics type="ode"><max_step_size>0.001</max_step_size></physics> <light type="point" name="lamp"><diffuse>{lux} {lux} {lux}</diffuse></light> <road><surface><friction><ode><mu>{mu:.2f}</mu></ode></friction></surface></road> {''.join(shelves)} </world></sdf>""" for k in range(100): open(f"worlds/wh_{k:03d}.sdf", "w").write( make_world(random.randint(5, 30), random.uniform(0.4, 1.0), random.uniform(0.3, 0.9)))

这份生成器把"环境分布"从口头的"各种场景"变成可版本化、可复现的参数区间——团队任何人拿到同一份脚本与同一个种子,都能生成同一批世界。它与 4.3 节的模型库正交:生成器管布局与参数,model:// 引用管复用。判断该手写还是该生成,只看一条:文件里是否存在"结构相同、仅数值不同"的重复块——出现第三处重复时就该换生成器了。

本节要点回顾

  • SDF 描述世界,URDF 只描述机器人:传感器、地形、光照、多模型都是 SDF 的一等公民。
  • 层级即职责:world 管全局,model 万物平权,link 装物理与感知,joint 管约束。
  • collision/visual 分离在语法层就支持"碰撞简单、视觉精细"。
  • 工具链是契约的一部分gz sdf -p 进 CI;URDF 转换后必核惯性。

语言会了,下一节用它建真东西——地形、建筑、大面积静态环境,以及建模粒度的取舍。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U