pgmXT

PGM 文件格式完全指南

从文件头到 ROS 占用约定,一篇讲透机器人地图背后的 Portable Graymap

几乎所有 SLAM 系统(gmapping、cartographer、hector……)最终都把地图存成一对文件:map.pgm + map.yaml。PGM 本身是一个比 PNG 还古老的极简图像格式——正因为简单,它成了机器人领域的事实标准。理解它的结构,你就能解释修图时遇到的大多数"玄学问题"。

一、Netpbm 家族与 PGM 定位

PGM 属于 Netpbm 图像家族:

编号为奇数(P1/P2/P3)是 ASCII 文本编码,体积大但肉眼可读;偶数(P4/P5/P6)是二进制编码,紧凑高效。机器人地图几乎都使用 P5(二进制灰度)。

二、文件结构逐字节拆解

一个 P5 文件 = 魔数 + 宽度 + 高度 + 最大值 + 一个空白字符 + 原始灰度数据。用十六进制工具看 ROS 导出的地图,开头通常长这样:

P5
400 400
255
<此后是 400×400 字节的原始灰度数据>

头部中 # 开头是注释行,可以出现多次;各字段之间用空白分隔,解析器需要容忍换行/空格混用。

三、ROS map_server 的占用约定

PGM 格式本身并不知道什么是"墙"。真正定义语义的是 ROS 的 map_server 与 nav2_map_server,其约定是:

PGM 灰度值占用概率语义
0(黑)≈1.0障碍物(occupied)
254~255(近白)≈0可自由通行(free)
205−1未知(no information)

map_server 读取 YAML 时还有三个关键参数会改变这套映射:

灰度 205 是"未知"的官方约定值。如果你的编辑工具把灰色噪点涂成任意灰色(比如 128),map_server 会把它解释为"有一定占据概率的地面",导航时表现为机器人绕着一个看不见的障碍走——这就是"随便拿画灰笔补了两笔机器人突然绕路"的标准答案。

四、YAML 元数据与坐标还原

PGM 只存像素,不存物理尺寸,必须靠 YAML 还原到米制坐标系:

image: map.pgm
mode: trinary
resolution: 0.05     # 每像素 5 厘米
origin: [-13.6, -5.4, 0]   # 图像左上角像素在地图系中的 (x, y, 弧度)
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196

地图系中任一点 (x, y) 对应的像素坐标为:col = x/res + ox_px,row = 高度 − y/res + oy_px(图像 y 轴向下、地图 y 轴向上)。origin 记录的是左上角像素中心的地图坐标——这也是为什么 origin 常常是负数。

五、日常操作工具箱

在命令行生态里处理 PGM 非常轻松,作为在线编辑器的补充:

# PGM → PNG(方便在聊天软件里传阅)
convert map.pgm map.png          # ImageMagick
pnmtopng map.pgm > map.png       # Netpbm

# PNG → P5 PGM
convert map.png -colorspace Gray -compress none map.pgm 2>/dev/null

# 快速查看头部
head -c 20 map.pgm | xxd | head -2

# 批量查看分辨率
identify map.pgm

注意 ImageMagick 转出 PGM 时可能写成 P2 且行间带换行,个别老解析器只认 P5,遇到"能显示但加载失败"可以先用 pnmtopgm 规范化一遍。pgmXT 编辑器对 P2 / P5 两种头部都能识别,导出时统一写为标准 P5。

六、常见坑速查