4、地图文件深度解析:PGM文件结构、YAML文件参数详解、地图坐标系与原点设置
做导航避不开地图文件。很多新手拿到一张地图,直接丢给Nav2就开始跑,结果机器人要么原地转圈,要么直接撞墙。我刚开始也踩过这个坑——地图加载成功了,但机器人就是找不到北。
说白了,地图文件就是机器人的「眼睛」。它告诉机器人:哪里能走,哪里是墙,你当前在什么位置。今天我们就把它彻底拆开看看。
4.1 PGM文件结构——地图的像素世界
PGM文件,全称Portable Gray Map。它是一种最简单的灰度图像格式。我习惯把它叫做「黑白灰地图」。
打开一个PGM文件,你会看到这样的内容:
P5
# 地图文件,由SLAM生成
800 800
255
...(后面是一大堆二进制数据)
结构其实很简单:
- 第一行:P5表示二进制格式,P2表示文本格式。我建议你用P5,文件小很多。
- 第二行:注释,以#开头。可有可无。
- 第三行:宽度和高度,单位是像素。800x800意味着地图有64万个像素点。
- 第四行:最大灰度值,通常是255。
- 第五行开始:真正的像素数据。
每个像素的值代表什么?
| 像素值 | 含义 | ROS2中的解释 |
|---|---|---|
| 0(黑色) | 障碍物 | 占据概率100% |
| 205(灰色) | 未知区域 | 未探索区域 |
| 254(白色) | 自由空间 | 可通行区域 |
4.2 YAML文件参数详解——地图的身份证
PGM文件只存了像素数据,但机器人还需要知道:这张地图对应现实世界的什么位置?每个像素代表多大?这就需要YAML文件了。
一个典型的YAML文件长这样:
image: map.pgm
resolution: 0.05
origin: [-10.0, -10.0, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196
我来逐个拆解:
image
就是PGM文件的路径。可以是相对路径,也可以是路径。我个人习惯用相对路径,方便移植。
resolution
每个像素对应现实世界的多少米。0.05表示一个像素是5厘米。这个值越小,地图越精细,但文件也越大。
origin
这是最容易被忽视的参数。它表示地图左下角像素在现实世界坐标系中的位置。
格式是 [x, y, yaw]:
- x, y:左下角像素对应的世界坐标(米)
- yaw:地图的旋转角度(弧度)
举个例子:origin: [-10.0, -10.0, 0.0] 表示地图左下角在世界坐标的(-10, -10)处,地图没有旋转。
negate
是否反转黑白。0表示不反转,1表示反转。一般保持默认0。
什么时候用1?如果你拿到的地图是白色障碍物、黑色可通行区域,那就需要反转。不过这种情况很少见。
occupied_thresh 和 free_thresh
这两个参数决定了像素值如何映射到占据概率。
- occupied_thresh:像素值大于此阈值,认为是障碍物。默认0.65。
- free_thresh:像素值小于此阈值,认为是自由空间。默认0.196。
- 介于两者之间的,算未知区域。
像素值到概率的转换公式:
概率 = (255 - 像素值) / 255
所以:
- 黑色像素(0) → 概率1.0 → 障碍物
- 白色像素(254) → 概率0.0039 → 自由空间
- 灰色像素(205) → 概率0.196 → 未知区域
你看,free_thresh默认0.196,刚好卡在灰色像素的阈值上。这不是巧合,是设计好的。
4.3 地图坐标系与原点设置
坐标系是导航的灵魂。ROS2里用的是右手坐标系:
- X轴:向前
- Y轴:向左
- Z轴:向上
地图坐标系(map frame)是全局固定的。原点通常设置在:
- 建图开始时机器人的位置
- 或者手动指定的某个参考点
我建议把原点设在房间的某个角落,或者走廊的交叉口。这样坐标值都是正数,调试时看着舒服。
世界坐标 = 像素坐标 × 分辨率 + 原点偏移
像素坐标 = (世界坐标 - 原点偏移) / 分辨率
举个例子:
假设地图分辨率0.05,原点[-10, -10, 0]。那么像素(200, 300)对应的世界坐标是:
x = 200 × 0.05 + (-10) = 0
y = 300 × 0.05 + (-10) = 5
也就是说,这个像素点在世界坐标的(0, 5)处。
4.4 知识体系总览
下面这张图帮你理清地图文件的核心逻辑:
4.5 避坑指南
最后分享几个我踩过的坑:
- PGM文件路径写错:YAML里image路径写的是相对路径,但启动节点的工作目录不对,导致地图加载失败。我建议用路径调试,稳定后再改相对路径。
- origin的yaw搞反了:地图旋转角度是弧度制,正数逆时针。我曾经填了90度(应该是1.57),结果地图转了90度,机器人定位全乱套。
- 分辨率不匹配:建图时用的0.05,导航时YAML里写成了0.1。地图看起来一样,但实际尺寸差了一倍。机器人走一步,实际走了两步。
- 像素值255的问题:有些工具生成的PGM文件,自由空间用255而不是254。这时候需要手动改一下,或者在YAML里调整阈值。
file map.pgm命令查看文件信息,再用python -c "from PIL import Image; img=Image.open('map.pgm'); print(img.size, img.getpixel((0,0)))"检查像素值。确认无误后再加载到Nav2里。
嗯,地图文件这块其实不难,但细节很多。你只要记住:PGM存像素,YAML存参数,坐标系定位置。三者配合好了,机器人就能看懂这个世界。