4. 环境感知与建图(上):占据栅格地图原理、Gmapping SLAM算法原理与实战
各位同学,大家好。今天我们进入一个非常核心的话题——环境感知与建图。说白了,就是让机器人知道“我在哪”以及“周围长什么样”。
我个人习惯把建图比作“给机器人配一副眼镜”。没有地图,路径规划就是瞎指挥。今天我们先聊上半部分:占据栅格地图的原理,以及经典的Gmapping SLAM算法。
4.1 占据栅格地图:机器人的“视力表”
先问大家一个问题:机器人怎么表示一个房间?用照片?用CAD图纸?都不对。机器人用的是占据栅格地图(Occupancy Grid Map)。
简单来说,就是把环境切成一个个小格子。每个格子存一个概率值:0表示绝对空,1表示绝对有障碍物,0.5表示未知。
核心公式(贝叶斯更新)
每个栅格的状态更新,其实是一个贝叶斯滤波过程:
P(occ | z) = P(z | occ) * P(occ) / P(z)
实际工程中,我们通常用对数几率(log-odds)来避免数值下溢:
l_i = l_i + log( P(z|occ) / P(z|free) )
我在项目中遇到过一个问题:直接用概率相乘,很快所有格子都变成0或1了,失去了不确定性。后来改用对数形式,才稳定下来。嗯,这里要注意,初始值一般设为0(对应概率0.5)。
4.2 占据栅格地图的更新流程
机器人每走一步,激光雷达扫一圈,我们就更新一次地图。流程如下:
- 射线投射:从机器人位置出发,沿激光方向打一条射线。
- 命中点:射线终点对应的栅格,标记为“占据”。
- 穿过的栅格:射线路径上的其他栅格,标记为“空闲”。
- 更新概率:用贝叶斯公式更新每个栅格的log-odds值。
避坑指南
我曾经在更新时忘了处理“未知区域”。结果机器人一开机,地图全是黑的。后来加了一个判断:如果栅格从未被观测过,跳过更新。这才正常。
4.3 Gmapping SLAM:经典中的经典
Gmapping 是ROS里最常用的2D SLAM算法。它基于粒子滤波(RBPF),说白了就是:用一堆粒子来猜机器人的位姿,每个粒子带一张地图。
为什么它这么流行?因为它在小场景(比如家庭、办公室)里又快又准。我当年做扫地机器人项目,第一版用的就是Gmapping,效果相当不错。
4.4 Gmapping的核心原理
Gmapping 的核心思想是:先估计位姿,再更新地图。具体分三步:
- 提议分布:用里程计+激光雷达的观测,生成粒子的候选位姿。
- 权重计算:每个粒子根据观测与地图的匹配程度,计算权重。
- 重采样:扔掉权重低的粒子,复制权重高的粒子。
你想想看,如果只用里程计,粒子会越散越开。但Gmapping用激光雷达的观测来“拉回”粒子,这就是它精度高的原因。
注意
Gmapping 对激光雷达的帧率有要求。我试过用5Hz的雷达,结果粒子发散得一塌糊涂。建议至少10Hz以上。
4.5 实战:在ROS中运行Gmapping
好了,理论讲完,我们直接上手。以下是我在ROS Noetic上跑Gmapping的步骤:
4.5.1 安装与启动
# 安装Gmapping
sudo apt install ros-noetic-gmapping
# 启动Gmapping节点
rosrun gmapping slam_gmapping scan:=/scan
这里要注意,scan:=/scan 是把激光雷达的话题重映射到Gmapping默认的输入话题。如果你的雷达话题是/laser/scan,就改成scan:=/laser/scan。
4.5.2 参数调优
Gmapping 的参数很多,但核心就几个:
| 参数名 | 默认值 | 说明 |
|---|---|---|
| particles | 30 | 粒子数。越大越准,但越慢。我一般设50~80。 |
| delta | 0.05 | 地图分辨率(米/像素)。0.05就是5cm一格。 |
| linearUpdate | 1.0 | 机器人移动多少米触发一次更新。 |
| angularUpdate | 0.5 | 机器人旋转多少弧度触发一次更新。 |
我的经验
如果地图出现“重影”,通常是linearUpdate设得太大了。我一般设0.5米,效果会好很多。
4.6 知识体系总览
为了让大家更直观地理解本章的知识结构,我画了一张图:
4.7 实战中的常见问题
最后,分享几个我踩过的坑:
- 地图漂移:通常是里程计不准。可以试试增加粒子数,或者用IMU辅助。
- 建图速度慢:把
particles从30降到20,或者增大linearUpdate。 - 地图有空洞:激光雷达的盲区。可以调整雷达安装高度,或者用多传感器融合。
一句话总结
占据栅格地图是机器人的“视力表”,Gmapping是“配眼镜的师傅”。两者结合,才能让机器人看清世界。