4、Gazebo仿真世界构建:创建简单障碍物世界、创建室内走廊世界、创建复杂工厂世界
各位同学,欢迎来到第四章。
上一章我们把Nav2的框架搭起来了,也跑通了第一个仿真。但说实话,那个平坦的空旷世界,连个柱子都没有,根本没法验证导航算法的鲁棒性。你想想看,一个机器人如果只能在空地上跑,那跟玩具车有什么区别?
所以这一章,我们亲手来造几个像样的仿真世界。我会带你从最简单的障碍物世界开始,逐步升级到室内走廊,最后挑战一个复杂的工厂环境。这三个世界,基本覆盖了90%的导航测试场景。
本章核心目标:掌握Gazebo世界文件的编写逻辑,学会用模型拼接的方式快速构建复杂环境。
4.1 简单障碍物世界:从零开始搭一个测试场
我个人习惯,每次测试新算法前,先跑一个「障碍物世界」。这个世界不需要大,但要有几个关键元素:墙壁、柱子和一些随机摆放的箱子。
我们先看一个最简单的.world文件长什么样:
<?xml version="1.0"?>
<sdf version="1.6">
<world name="simple_obstacle_world">
<include>
<uri>model://sun</uri>
</include>
<include>
<uri>model://ground_plane</uri>
</include>
<!-- 一个静态的墙壁 -->
<model name="wall_01">
<static>true</static>
<pose>2.0 0.0 0.5 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry>
<box>
<size>0.2 3.0 1.0</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>0.2 3.0 1.0</size>
</box>
</geometry>
<material>
<ambient>0.8 0.2 0.2 1.0</ambient>
</material>
</visual>
</link>
</model>
<!-- 一个圆柱体柱子 -->
<model name="pillar_01">
<static>true</static>
<pose>-1.5 -1.5 0.5 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry>
<cylinder>
<radius>0.3</radius>
<length>1.0</length>
</cylinder>
</geometry>
</collision>
<visual name="visual">
<geometry>
<cylinder>
<radius>0.3</radius>
<length>1.0</length>
</cylinder>
</geometry>
</visual>
</link>
</model>
</world>
</sdf>
这段代码干了什么呢?说白了就是在地面上放了一面墙和一个柱子。注意看<static>true</static>,这个标记告诉Gazebo:这玩意儿是固定的,别被机器人撞飞了。我在项目中遇到过有人忘了加这个,结果机器人一启动,墙壁满天飞……那场面,挺滑稽的。
小技巧:调试阶段,把墙壁颜色设成红色(ambient 0.8 0.2 0.2),这样一眼就能看清障碍物分布。正式用的时候再改成灰色。
4.2 室内走廊世界:模拟真实办公环境
走廊环境是室内导航的经典场景。窄、长、有拐角,这对机器人的路径规划和定位都是考验。
我建议用「模块化」的思路来构建走廊。什么意思呢?就是先定义一段标准走廊,然后通过旋转和拼接,组成L型、T型甚至回字形结构。
下面是一个L型走廊的核心片段:
<model name="corridor_segment_01">
<static>true</static>
<pose>0 0 0 0 0 0</pose>
<link name="wall_left">
<pose>-1.5 0 0.5 0 0 0</pose>
<collision name="collision">
<geometry><box><size>0.1 10.0 1.0</size></box></geometry>
</collision>
<visual name="visual">
<geometry><box><size>0.1 10.0 1.0</size></box></geometry>
</visual>
</link>
<link name="wall_right">
<pose>1.5 0 0.5 0 0 0</pose>
<collision name="collision">
<geometry><box><size>0.1 10.0 1.0</size></box></geometry>
</collision>
<visual name="visual">
<geometry><box><size>0.1 10.0 1.0</size></box></geometry>
</visual>
</link>
</model>
你看,一个走廊段就是左右两面墙,中间留出3米宽的通道。然后我在拐角处再加一段垂直的走廊,用<pose>调整位置和朝向就行。
嗯,这里要注意:走廊的宽度一定要大于机器人的直径。我的机器人是0.5米宽,走廊留3米,绰绰有余。但如果你做的是叉车AGV,那走廊至少得留2米以上。
避坑指南:我曾经在走廊拐角处忘记加墙壁封口,结果机器人直接从「墙壁」穿过去了。后来我养成了一个习惯:所有拐角处,必须用一个小方块把缺口堵上。别偷懒,否则定位会飘。
4.3 复杂工厂世界:多楼层与动态障碍物
工厂环境,才是真正考验Nav2功底的地方。这里有货架、传送带、叉车,甚至还有移动的人。
我们先看一个工厂世界的整体结构图:
构建工厂世界时,我推荐用model://引用现成的模型库,而不是自己手写几何体。Gazebo官方模型库里有货架、传送带、叉车等模型,直接拿来用就行。
<!-- 引用货架模型 -->
<include>
<uri>model://shelf_unit</uri>
<name>shelf_01</name>
<pose>2.0 3.0 0 0 0 0</pose>
</include>
<!-- 引用传送带模型 -->
<include>
<uri>model://conveyor_belt</uri>
<name>belt_01</name>
<pose>5.0 2.0 0 0 0 0</pose>
</include>
动态障碍物怎么做?我一般用libActorPlugin.so插件,或者直接写一个简单的Python脚本,让模型沿着预设路径来回移动。下面是一个用插件实现移动障碍物的例子:
<model name="moving_obstacle_01">
<static>false</static>
<pose>0 0 0.5 0 0 0</pose>
<link name="body">
<collision name="collision">
<geometry><box><size>0.5 0.5 1.0</size></box></geometry>
</collision>
<visual name="visual">
<geometry><box><size>0.5 0.5 1.0</size></box></geometry>
</visual>
</link>
<plugin name="move_plugin" filename="libActorPlugin.so">
<path>0 0 0; 5 0 0; 5 5 0; 0 5 0</path>
<speed>0.5</speed>
</plugin>
</model>
这个插件会让障碍物沿着四个点循环移动,速度0.5米/秒。嗯,速度别设太快,否则Nav2的全局规划器可能来不及重规划。
我的经验:测试动态障碍物时,先把速度调到0.2米/秒,确认导航能稳定避障后,再逐步加快。直接上高速,机器人容易「懵圈」。
4.4 世界文件的组织与管理
最后聊一下文件管理。一个复杂的工厂世界,可能有几十个模型。我习惯把模型文件放在~/.gazebo/models/目录下,然后.world文件里只写<include>引用。这样世界文件本身很干净,也方便复用。
| 文件/目录 | 说明 |
|---|---|
| ~/.gazebo/models/ | 存放自定义模型,每个模型一个文件夹 |
| worlds/simple.world | 简单障碍物世界 |
| worlds/corridor.world | 室内走廊世界 |
| worlds/factory.world | 复杂工厂世界 |
启动时,用gazebo worlds/factory.world即可。如果你用launch文件,可以这样写:
<node name="gazebo" pkg="gazebo_ros" exec="gazebo" args="$(find-pkg-share my_nav2_bringup)/worlds/factory.world" output="screen"/>
好了,这一章的内容就到这里。三个世界,三种难度,足够你测试Nav2的各种功能了。记住,仿真世界越真实,算法在真机上跑起来就越稳。别嫌麻烦,多花点时间把世界搭好,后面调试会省很多事。