3、创建第一个仿真世界:SDF文件格式入门、构建简单地面与光照、保存与加载自定义世界

好,咱们正式开始动手了。

上一章我们把Gazebo装好、跑通了那个空荡荡的默认世界。说实话,我第一次看到那个灰色网格地面时,心里还挺激动的——但很快就不满足了。你想想看,一个仿真环境连个像样的地面都没有,怎么跑机器人?

这一章,我们就来亲手搭建第一个仿真世界。核心武器就是——SDF文件。

3.1 SDF到底是什么?

SDF,全称是Simulation Description Format。说白了,它就是Gazebo用来描述仿真世界的“剧本”。

你告诉它:这里放一块地面,那里打一束光,墙角放个箱子……SDF文件把这些信息全部写下来。Gazebo启动时,照着剧本把世界搭出来。

我个人习惯把SDF类比成HTML——HTML描述网页长什么样,SDF描述仿真世界长什么样。结构清晰,层级分明,一看就懂。

核心要点:

  • SDF是XML格式,标签成对出现
  • 根标签是 <sdf>,里面包 <world>
  • 一个world里可以有多个model、light、plugin等

嗯,这里要注意:SDF有两个版本,1.6和1.7。Gazebo 9以上默认用1.7。我建议你直接用1.7,省得后面踩坑。

3.2 从零写一个SDF世界文件

别怕,咱们不搞复杂的。先写一个最简单的世界:一块地面,一束光。

<?xml version="1.0" ?>
<sdf version="1.7">
  <world name="my_first_world">

    <!-- 地面 -->
    <model name="ground_plane">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>100 100</size>
            </plane>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>100 100</size>
            </plane>
          </geometry>
          <material>
            <ambient>0.8 0.8 0.8 1</ambient>
            <diffuse>0.8 0.8 0.8 1</diffuse>
          </material>
        </visual>
      </link>
    </model>

    <!-- 光照 -->
    <light name="sun" type="directional">
      <cast_shadows>true</cast_shadows>
      <pose>0 0 10 0 0 0</pose>
      <diffuse>1 1 1 1</diffuse>
      <specular>0.5 0.5 0.5 1</specular>
      <direction>-0.5 0.1 -1</direction>
    </light>

  </world>
</sdf>

这段代码干了三件事:

  • 定义了一个名为 my_first_world 的世界
  • 放了一个100x100的灰色地面,带碰撞和视觉属性
  • 加了一束方向光,模拟太阳,带阴影

我在项目中遇到过有人把 <static> 写成 false,结果地面一碰就飞了……记住,地面必须是静态的,true 别写错。

3.3 保存与加载自定义世界

写好的文件怎么用?很简单。

第一步:保存

把上面的代码复制到一个文本文件里,命名为 my_first_world.sdf。我习惯放在 ~/.gazebo/worlds/ 目录下,方便管理。

mkdir -p ~/.gazebo/worlds
# 把文件保存到 ~/.gazebo/worlds/my_first_world.sdf

第二步:加载

启动Gazebo时,用 --world 参数指定你的SDF文件:

gazebo --world ~/.gazebo/worlds/my_first_world.sdf

或者,如果你已经打开了Gazebo,也可以通过菜单 File → Open World 来加载。

小技巧:

我曾经在调试时反复修改SDF文件,每次都要手动重启Gazebo,很烦。后来发现可以用 gzserver 和 gzclient 分开启动,改完SDF后只重启 gzserver 就行,快很多。

3.4 理解地面与光照的细节

咱们再深入一点,看看刚才代码里那些参数到底什么意思。

地面参数解析

参数 含义 我的建议
<normal> 地面的法线方向 默认0 0 1(朝上),别改
<size> 地面的长宽 100x100够用,太大影响性能
<ambient> 环境光颜色 灰色0.8 0.8 0.8比较自然
<diffuse> 漫反射颜色 和ambient保持一致即可

你可能会问:为什么地面要同时定义 collision 和 visual?

这是Gazebo的设计哲学——碰撞几何和视觉几何可以不一样。比如你做一个精细的机器人模型,视觉部分可以很复杂,但碰撞部分用简单的长方体就行,这样仿真跑得快。地面简单,所以两者保持一致。

光照参数解析

参数 含义 注意事项
type 光源类型 directional(方向光)、point(点光源)、spot(聚光灯)
<cast_shadows> 是否投射阴影 true才有真实感,但耗性能
<direction> 光线方向 负值表示从斜上方照下来

我记得有一次做室内仿真,用了点光源但忘了调 attenuation(衰减),结果整个房间亮得像白天……后来加了衰减参数才正常。如果你做室内场景,记得加上 <attenuation><range>20</range></attenuation>。

3.5 本章知识体系

下面这张图帮你理清SDF世界的核心结构:

sdf (version="1.7") world (name="...") model (地面/物体) light (光照) plugin (插件) static / link collision visual inertial pose / diffuse / direction 根标签 世界容器 模型 光照 插件

从这张图可以看得很清楚:一个SDF世界文件,根节点是 <sdf>,里面包一个 <world>,world下面可以放任意多个model、light、plugin。每个model又包含static、link等子标签,link下面再细分collision、visual、inertial。

说白了,这就是一个树形结构。你只要记住这个骨架,写任何SDF文件都不会迷路。

3.6 动手试试

光看不练假把式。我建议你现在就做三件事:

  1. 把上面的SDF代码复制下来,保存成 my_first_world.sdf
  2. 用Gazebo加载它,看看地面和光照效果
  3. 试着改改 <diffuse> 的颜色值,比如改成 0.2 0.6 0.2 1(绿色地面),重新加载看看变化

注意:

如果你改了SDF文件但Gazebo里没变化,别慌。先检查文件有没有保存,再确认Gazebo加载的是不是同一个文件。我曾经在这上面浪费了半小时——改完忘了保存,还以为是Gazebo出bug了。

好了,这一章的内容就到这儿。你亲手创建了第一个仿真世界,虽然简单,但这是万里长征的第一步。后面我们会往这个世界里加机器人、加传感器、让它动起来。

可可精品资料库