第一个导航地图:使用SLAM Toolbox构建静态地图,地图格式(YAML + PGM/PNG)详解

各位同学,欢迎来到第三章。

上一章我们把Nav2的环境搭好了,也跑通了第一个demo。但说实话,那个demo用的是官方自带的仿真地图,跟咱们实际要用的场景差得远。做机器人导航,第一步就是得有一张靠谱的地图。地图不对,后面所有路径规划、避障都是白搭。

这一章,我就带你亲手建一张静态地图。我们用到的工具是SLAM Toolbox,它是ROS2里最主流的建图工具之一。建完之后,我会详细拆解地图文件里那些YAML和PGM/PNG到底在说什么。

核心知识点:SLAM Toolbox建图流程 + 地图文件格式(YAML + PGM/PNG)深度解析

为什么选SLAM Toolbox?

市面上建图工具不少,比如gmapping、cartographer。我个人习惯用SLAM Toolbox,原因有三:

  • 原生支持ROS2:不用折腾各种依赖,装好就能用。
  • 支持在线/离线建图:你可以一边遥控机器人一边建,也可以录好bag包回来慢慢优化。
  • 自带地图保存功能:建完图直接调用服务就能保存,不用自己写脚本。

我在项目里遇到过好几次,用gmapping建图到一半程序崩了,地图没保存,白干。换了SLAM Toolbox之后,这种糟心事少了很多。

建图前的准备工作

别急着跑命令,先检查三件事:

  1. 机器人能正常发布/odom和/scan话题。没有里程计和激光数据,SLAM就是无米之炊。
  2. 激光雷达的安装高度和角度正确。我见过有人把雷达装歪了,建出来的地图全是斜的。
  3. 控制方式就绪。你可以用键盘、手柄或者写好的导航节点来控制机器人移动。

小技巧:建图前最好让机器人原地转几圈,让SLAM初始化好位姿。不然一上来就猛冲,地图容易飘。

启动SLAM Toolbox建图

假设你的机器人已经准备好了,打开终端,启动SLAM Toolbox:

ros2 launch slam_toolbox online_async_launch.py

这条命令会启动异步SLAM模式。说白了,就是一边接收激光数据一边建图,不会阻塞你的控制指令。

然后,你需要控制机器人把整个环境走一遍。我个人习惯先走外围,再走内部,最后补一些死角。你想想看,如果只走中间,墙角的信息就丢了。

注意:建图过程中不要急转弯,也不要走太快。SLAM算法需要连续的数据来匹配特征点。我曾经为了省时间,推着机器人快跑,结果地图直接扭曲了,重来一遍更费时间。

保存地图

走完一圈,感觉地图差不多了,就可以保存了。SLAM Toolbox提供了一个服务接口:

ros2 service call /slam_toolbox/save_map slam_toolbox/srv/SaveMap "{name: 'my_map'}"

执行后,你会在当前目录下看到两个文件:

  • my_map.yaml:地图的元数据文件
  • my_map.pgm:地图的像素图像文件

有些版本也会生成PNG格式,但PGM更常见。PGM是灰度图,每个像素的值代表该位置被占用的概率。

地图格式深度解析

好,地图建好了,咱们来拆开看看里面到底有什么。

1. YAML文件

用文本编辑器打开my_map.yaml,你会看到类似这样的内容:

image: my_map.pgm
resolution: 0.05
origin: [-10.0, -10.0, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196

我来逐行解释:

字段 含义 我的经验
image 指向像素地图文件的路径 建议用相对路径,方便移植
resolution 每个像素代表的实际距离(米/像素) 0.05米是常用值,精度够用,文件也不太大
origin 地图左下角在世界坐标系中的位置 [x, y, yaw] 这个值决定了地图的偏移量,建图时如果机器人起点不在原点,这里会变
negate 是否反转像素值(0或1) 一般保持0,除非你的地图颜色是反的
occupied_thresh 像素值大于此阈值视为障碍物 默认0.65,我一般不动
free_thresh 像素值小于此阈值视为自由空间 默认0.196,介于两者之间的视为未知区域

重点:occupied_thresh和free_thresh决定了地图的“软硬”程度。如果你发现机器人老往墙上撞,可以适当调高occupied_thresh;如果地图上很多噪点被当成障碍,可以调低free_thresh。

2. PGM/PNG文件

PGM文件是灰度图,每个像素的值范围是0~255。在Nav2里:

  • 0:完全自由(白色)
  • 100:未知区域(灰色)
  • 255:完全占用(黑色)

嗯,这里要注意:PGM文件本身不直接存概率,而是存灰度值。Nav2加载时会根据YAML里的阈值来判定每个格子是障碍、自由还是未知。

说白了,地图文件就是一张图片,YAML文件就是这张图片的说明书。两者缺一不可。

地图坐标系与像素坐标的转换

这个知识点容易让人迷糊,我简单说一下。

假设地图的origin是[-10, -10, 0],resolution是0.05。那么地图中第i行第j列的像素,对应的世界坐标是:

world_x = origin_x + j * resolution
world_y = origin_y + (height - i) * resolution

注意:图像的行是从上往下数的,而世界坐标的y是朝上的,所以要用height - i来翻转。

我在调试定位时经常手动算这个,确保地图原点跟机器人实际位置对得上。有一次怎么都对不上,最后发现是origin写错了,折腾了我一下午。

避坑指南

最后,分享几个我踩过的坑:

  • 地图文件路径不对:YAML里的image路径是相对于YAML文件本身的。如果你把地图挪了位置,记得改路径。
  • 分辨率不匹配:建图时用0.05,导航时如果用了0.1的地图,定位会不准。保持一致性。
  • 地图边界有黑边:建图时如果机器人没走到边界,地图边缘会有大片的未知区域(灰色)。Nav2会把未知区域当成障碍,导致路径规划失败。解决办法是建图时尽量走遍每个角落。

我的习惯:建完图后,我会用图像查看器打开PGM文件,肉眼检查一遍。如果看到不该有的黑点或白点,说明建图过程中有干扰,需要重来。

本章知识体系

为了让你更直观地理解本章内容,我画了一张流程图:

SLAM Toolbox 建图流程 启动SLAM Toolbox 控制机器人移动 保存地图服务 生成文件 my_map.yaml(元数据) my_map.pgm(像素图) 两个文件必须放在同一目录下,YAML中的image路径指向PGM文件 核心:地图 = 像素图(PGM)+ 说明书(YAML)

这张图把整个流程串起来了。从启动工具到控制移动,再到保存文件,最后生成两个核心文件。你只要按这个流程走,基本不会出错。

可可精品资料库