2. 地图构建基础:SLAM原理、占用网格地图、地图数据结构
大家好,欢迎来到第二章。
上一章我们聊了Nav2的整体架构,算是搭了个大框架。这一章,咱们得把地基打牢——地图构建。说白了,机器人要是不知道自己在哪,周围长啥样,那避障、导航全是空谈。
我个人习惯把地图构建比作「给机器人配眼镜」。没地图,它就是瞎子。地图不准,它就是近视眼。嗯,咱们今天就来聊聊这副「眼镜」是怎么造出来的。
2.1 SLAM:边走路边画地图
SLAM,全称是 Simultaneous Localization and Mapping,中文叫「即时定位与地图构建」。名字挺长,其实核心就一句话:机器人一边移动,一边猜自己在哪里,同时把周围环境画下来。
你想想看,这其实是个「先有鸡还是先有蛋」的问题:
- 要知道自己位置,得先有地图
- 要画地图,得先知道位置
SLAM 就是同时解决这两个问题。我在项目中遇到过不少新手,上来就问:「能不能先建好地图再导航?」当然可以,但那是纯定位。真正的 SLAM 是让你在完全未知的环境里,也能跑起来。
核心要点:SLAM 不是一种算法,而是一类问题的统称。常见的实现方式有基于滤波的(如Gmapping)和基于图优化的(如Cartographer、KartoSLAM)。
2.2 占用网格地图:把世界切成小格子
地图怎么表示?最直观的方式就是「占用网格地图」(Occupancy Grid Map)。
说白了,就是把现实世界切成一个个小方格。每个格子有三种状态:
- 空闲(Free):这里能走,没东西
- 占用(Occupied):这里有障碍物,别撞
- 未知(Unknown):还没探测到,小心点
每个格子存一个概率值,0 表示完全空闲,100 表示完全占用,-1 表示未知。这就是 ROS 里 nav_msgs/OccupancyGrid 消息的玩法。
我的小技巧:实际项目中,我一般把阈值设在 65 以上算占用,35 以下算空闲。中间那一段,嗯,就当未知处理。太敏感容易把地面上的小石子当墙,太迟钝又容易撞到椅子腿。
2.3 地图数据结构:ROS2 里怎么玩
在 ROS2 里,地图数据通过 nav_msgs/msg/OccupancyGrid 来传递。咱们来看看它的结构:
# 这是 OccupancyGrid 消息的核心字段
std_msgs/Header header # 时间戳和坐标系
nav_msgs/MapMetaData info # 地图元数据
time map_load_time # 地图加载时间
float32 resolution # 分辨率,单位米/像素
uint32 width # 宽度,像素数
uint32 height # 高度,像素数
geometry_msgs/Pose origin # 地图原点在世界坐标系中的位姿
int8[] data # 一维数组,按行存储的占用值
这里有个坑,我踩过好几次:data 是一维数组。你要访问第 (x, y) 个格子,得自己算索引:
index = y * width + x
别搞反了,x 是列,y 是行。我曾经在调试时把 x 和 y 写反了,结果地图显示出来是镜像的,找了半天 bug……
注意:地图坐标系通常遵循 REP 103 规范,x 轴向前,y 轴向左,z 轴向上。但地图原点(origin)不一定在左下角,它可以是任意位置。读取地图时一定要结合 origin 字段做坐标变换。
2.4 SLAM 建图流程:一张图说清楚
下面我用一张 SVG 流程图,把整个 SLAM 建图的闭环画出来。这是我自己总结的,项目里一直这么用:
2.5 地图分辨率:选多大合适?
地图分辨率直接影响建图质量和计算开销。我整理了一个表格,方便大家参考:
| 分辨率 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 0.05 m(5 cm) | 室内精细建图 | 细节丰富,能识别小物体 | 内存大,计算慢 |
| 0.10 m(10 cm) | 通用室内导航 | 平衡精度与性能 | 小物体可能被忽略 |
| 0.20 m(20 cm) | 室外/大场景 | 内存小,速度快 | 细节丢失严重 |
我的建议:如果你用的是 2D 激光雷达,室内场景 0.05 米就够用了。别贪心,分辨率太高反而容易引入噪声。我记得有次项目里用了 0.025 米,结果地图文件 200MB,加载都要半天……后来老老实实改回 0.05。
2.6 避坑指南:建图时常见的几个坑
做 SLAM 建图,有些坑是绕不开的。我把自己踩过的列出来,大家少走弯路:
- 里程计不准:轮子打滑、地面不平,都会导致里程计漂移。解决办法是加 IMU 做融合,或者用激光雷达做 scan-to-scan 匹配。
- 闭环检测失败:环境太相似(比如长走廊),闭环检测容易误报。我一般会调低闭环阈值,宁可漏检也别误检。
- 地图原点偏移:建图时机器人初始位置就是地图原点。如果中途重启了建图节点,原点可能会变。建议每次建图前先固定起点。
- 动态物体干扰:建图时有人走来走去,地图上会留下「鬼影」。最好在无人环境下建图,或者用动态物体滤除算法。
重要提醒:建图完成后,记得保存地图。ROS2 里用 nav2_map_server 的 map_saver_cli 工具。命令很简单:
ros2 run nav2_map_server map_saver_cli -f my_map
这会生成 my_map.yaml 和 my_map.pgm 两个文件。别问我为什么强调这个——我曾经建了一下午的图,结果忘了保存……
2.7 小结
这一章我们聊了 SLAM 的基本原理、占用网格地图的概念,以及 ROS2 里的地图数据结构。说白了,地图构建就是「用传感器数据,把环境量化成格子,再存成标准格式」的过程。
下一章我们会深入 Nav2 的全局规划器,看看地图建好后,机器人怎么规划出一条好路。嗯,到时候再聊。