6. 源码走读:一帧激光的旅程
6. 源码走读:一帧激光数据的完整旅程
上一篇:5. gmapping的两大改进 | 下一篇:7. 实战:跑起来与调参
原理懂了,现在把 第 5 篇 的算法图对号入座到真实代码。gmapping 分两层:外层 ROS 壳(
slam_gmapping包)+ 内核算法库(openslam_gmapping)。
一、代码地图
1 | slam_gmapping/ ← ROS 壳: 订阅话题、格式转换、发 TF 和地图 |
二、外层:ROS 壳的接力赛
2.1 startLiveSlam():起跑线
1 | void SlamGMapping::startLiveSlam() { |
三个注意点:
/map用 latched 发布(地图变化慢,新订阅者自动收最后一版,见第 2 篇);tf::MessageFilter:激光帧攒着,直到能查到它时间戳对应的里程计位姿才放行进回调(时间同步);throttle_scans_:每 N 帧处理一帧(降频)。
2.2 laserCallback():每帧入口
1 | void SlamGMapping::laserCallback(scan) { |
map_to_odom_ 那行就是第 2 篇 TF 树的落点:最优粒子位姿(map 系)与里程计位姿(odom 系)的差。
2.3 addScan():格式桥
getOdomPose() 查 TF 拿该时刻里程计位姿 -> ROS 激光消息转成 GMapping::RangeReading(含位姿+距离数组)-> 调 gsp_->processScan(reading) 进入内核。
三、内核:processScan() – 第 5 篇算法图逐行对号 ⭐⭐
GridSlamProcessor::processScan() 是整个 gmapping 的心脏,流程与 第 5 篇 的 mermaid 图一一对应:
1 | processScan(read) { |
逐段对照:
| 代码段 | 对应原理 | 所在篇 |
|---|---|---|
drawFromMotion |
里程计运动模型采样 | 3 |
m_matcher.optimize |
扫描匹配”对齐” | 5 §1.3 |
sampleGaussian + 采样 |
改进提议分布(高斯近似) | 5 §1.3 |
weight *= ... |
重要性权重校正 | 5 §1.4 |
neff < threshold -> resample |
自适应重采样 | 5 §2 |
registerScan |
逐格子贝叶斯建图 | 4 §2.3 |
四、彩蛋:地图树(HierarchicalArray2D)– 内存救命术
每个粒子一张地图,重采样时”复制地图”岂不内存爆炸?openslam 的解法:
- 栅格不是平铺一个大数组,而是树状结构:叶子=格子块,粒子的地图 = 指向共享树的引用;
- 重采样复制粒子时只复制指针(写时复制 copy-on-write);
- 只有当两个粒子后续观测不同、要改写同一块格子时,才真正分裂出私有副本;
- 配合”活跃区域 active area”(只更新激光扫到的区域),进一步把更新量压到最小。
这是 gmapping 能在 2007 年的电脑上实时跑的工程支柱,也是面试常问点。
五、一帧激光的旅程(总览图)
flowchart TD
A["🔊 /scan 激光帧"] -->|tf::MessageFilter 时间同步| B["laserCallback"]
B -->|首帧| C["initMapper 初始化"]
B -->|后续帧| D["addScan: 转格式+查TF"]
D --> E["⭐ processScan (内核)"]
E --> F["运动模型采样"]
F --> G["扫描匹配+高斯提议分布"]
G --> H["权重更新 + Neff"]
H -->|Neff低| I["重采样(地图树共享)"]
H -->|Neff高| J["跳过重采样"]
I & J --> K["更新各粒子地图"]
K --> L["最优粒子 -> map->odom TF"]
K --> M["周期发布 /map 栅格图"]
style E fill:#fef7e0,stroke:#b06000
style G fill:#e8f0fe,stroke:#1a73e8
style I fill:#fce8e6,stroke:#d93025
📚 参考:源代码解析(本文主线参考,含 startLiveSlam/laserCallback/addScan 源码)、ROS1系列(订阅/发布话题全表)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 TeachCraft Blog!
评论