3、创建第一个仿真世界:添加地面、添加光源、添加静态物体、保存世界文件。
好,咱们继续往下走。上一章我们把空荡荡的 Gazebo 界面跑起来了,但说实话,一个黑漆漆、啥也没有的仿真环境,看着就像个没装修的毛坯房。你想想看,机器人连个参照物都没有,怎么测试传感器?怎么验证算法?
所以这一章,我们就来亲手「装修」这个毛坯房。我会带你一步步添加地面、打上灯光、摆几个静态物体,最后把这个世界保存下来。嗯,做完这些,你的仿真世界才算真正有了「家」的样子。
核心目标:掌握 Gazebo 世界文件的构建逻辑,理解 .world 文件的 XML 结构。
3.1 世界文件的结构:.world 到底是个啥?
Gazebo 里的世界,说白了就是一个 .world 文件。它本质上是一个 SDF(Simulation Description Format)格式的 XML 文件。我个人习惯把它比作「仿真世界的剧本」——里面定义了场景里有什么、光线怎么打、物理引擎怎么跑。
一个最简的 .world 文件骨架长这样:
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="my_first_world">
<!-- 这里放各种元素 -->
</world>
</sdf>
注意看,最外层是 <sdf> 标签,里面包着一个 <world>。所有地面、光源、模型,都放在 <world> 里面。这个结构其实很直观,对吧?
3.2 添加地面:让机器人有个落脚的地方
没有地面的世界,机器人一出生就会「自由落体」。我在项目中遇到过这种情况,第一次加载模型时直接掉到虚空里去了,找了半天才发现是忘了加地面。
Gazebo 提供了一个内置的地面模型,叫 ground_plane。添加方式很简单,在 <world> 里加上这段:
<include>
<uri>model://ground_plane</uri>
</include>
就这么两行?对,就这么两行。Gazebo 会自动从模型数据库中加载一个标准地面。不过,如果你想自定义地面的颜色或纹理,可以再加一个 <pose> 标签调整位置,或者用 <material> 改材质。但刚开始,咱们先用默认的。
小提示:model:// 是 Gazebo 内置模型的引用方式。如果你自己下载了模型,也可以用 file:// 指定本地路径。
3.3 添加光源:别让世界一片漆黑
地面有了,但黑乎乎的也看不见。Gazebo 默认会带一个环境光,但很微弱。我建议你手动添加一个太阳光,让场景更真实。
光源的类型主要有三种:
| 光源类型 | 说明 | 适用场景 |
|---|---|---|
directional |
平行光,模拟太阳 | 室外场景,全局照明 |
point |
点光源,向四周发光 | 室内灯泡、手电筒 |
spot |
聚光灯,有方向锥角 | 舞台灯光、车灯 |
咱们先加一个平行光,模拟太阳。代码是这样的:
<light name="sun" type="directional">
<pose>0 0 10 0 0 0</pose>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<direction>-0.5 0.1 -1</direction>
<attenuation>
<range>1000</range>
<constant>0.9</constant>
<linear>0.01</linear>
<quadratic>0.001</quadratic>
</attenuation>
<cast_shadows>true</cast_shadows>
</light>
这里我解释几个关键参数:
diffuse:漫反射颜色,RGB 值,最后一个 1 是透明度。0.8 0.8 0.8 是偏白的暖光。direction:光线方向向量。我习惯让光从左上角打下来,这样阴影比较自然。cast_shadows:是否投射阴影。建议打开,否则物体看起来像飘着的。
为什么会把 pose 设成 0 0 10?因为平行光的位置其实不影响光照效果,但放在高处更符合直觉。你想想看,太阳不就在天上嘛。
3.4 添加静态物体:摆几个家具
光有地面和光源,世界还是太单调。我们来放几个静态物体——比如一个桌子、一个箱子。静态物体意味着它不会动,但机器人可以撞上去、传感器可以检测到它。
Gazebo 内置了一些基础模型,比如 cinder_block(煤渣砖块)、table(桌子)。我们用 cinder_block 来演示:
<include>
<uri>model://cinder_block</uri>
<pose>1.0 0.5 0.0 0 0 0</pose>
<static>true</static>
</include>
注意 <static>true</static> 这一行。它的意思是:这个物体是静态的,物理引擎不会对它施加重力或碰撞力。说白了,它就像钉在地上的柱子,雷打不动。
如果你想让物体可以被推动,就把 static 设为 false 或者直接删掉这行。不过,咱们这一章先做静态的,后面章节再讲动态物体。
避坑指南:我曾经犯过一个错误——给地面也加了 <static>true</static>,结果机器人掉到地面以下去了。因为地面本身默认就是静态的,你再加一层静态约束反而会出 bug。所以,地面模型不要手动加 static 标签。
3.5 保存世界文件:把你的劳动成果存下来
好了,地面、光源、物体都加完了。现在我们要把这个世界保存成一个 .world 文件。这样下次启动 Gazebo 时,直接加载这个文件,就能看到一模一样的场景。
完整的 my_first_world.world 文件内容如下:
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="my_first_world">
<!-- 地面 -->
<include>
<uri>model://ground_plane</uri>
</include>
<!-- 太阳光 -->
<light name="sun" type="directional">
<pose>0 0 10 0 0 0</pose>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<direction>-0.5 0.1 -1</direction>
<attenuation>
<range>1000</range>
<constant>0.9</constant>
<linear>0.01</linear>
<quadratic>0.001</quadratic>
</attenuation>
<cast_shadows>true</cast_shadows>
</light>
<!-- 静态物体:煤渣砖块 -->
<include>
<uri>model://cinder_block</uri>
<pose>1.0 0.5 0.0 0 0 0</pose>
<static>true</static>
</include>
<!-- 静态物体:再来一个,换个位置 -->
<include>
<uri>model://cinder_block</uri>
<pose>-1.0 -0.5 0.0 0 0 0.5</pose>
<static>true</static>
</include>
</world>
</sdf>
保存文件时,我建议你放在一个专门的目录里,比如 ~/gazebo_worlds/。命名要清晰,别用 test.world 这种名字,以后项目多了根本找不到。我个人习惯用 项目名_场景名.world 的格式。
启动这个世界的命令是:
gazebo ~/gazebo_worlds/my_first_world.world
看到没?Gazebo 加载后,地面、阳光、两个砖块,整整齐齐。你可以用鼠标拖拽视角,从各个角度观察。嗯,这才像个仿真世界的样子。
3.6 本章知识体系
为了让你更直观地理解这一章的内容结构,我画了一张图:
这张图把本章的核心逻辑串起来了:.world 文件是容器,里面装地面、光源、静态物体,最后保存成文件。你以后每次新建世界,都可以按这个模板来。
3.7 小结
这一章我们做了四件事:加了地面让机器人有落脚点,打了灯光让世界亮起来,摆了静态物体让场景有内容,最后保存了世界文件。每一步都不复杂,但组合起来,你就拥有了一个可复用的仿真环境。
嗯,这里要注意一点:.world 文件是纯文本,你可以用任何编辑器修改。我习惯用 VS Code,语法高亮看着舒服。如果你发现加载后物体位置不对,检查一下 <pose> 的坐标值,x、y、z 的顺序别搞反了。
好了,去动手试试吧。把代码敲进去,看看你的第一个仿真世界长什么样。
核心要点回顾:
- 世界文件以
<sdf>包裹<world>为骨架 - 地面用
model://ground_plane引入 - 光源分三种类型,平行光最常用
- 静态物体加
<static>true</static>防止物理干扰 - 保存文件后通过
gazebo 文件名.world启动
可可精品资料库