本卷建立源码地图。先知道对象是谁、数据向哪里走、谁在哪个执行上下文中修改数据,再进入几千行 Tracking 和 Optimizer 才不会迷路。
1.ORB-SLAM3 的四项关键扩展
- 视觉惯性 MAP 系统:把视觉重投影与 IMU 约束紧耦合,惯性初始化也采用概率一致的优化;
- 高召回地点识别:候选首先做几何验证,再检查局部共视一致性,减少等待连续三帧词袋一致性造成的漏检;
- Atlas 多地图:跟踪长时间失败时可创建新地图,以后发现公共区域再融合;
- 抽象相机模型:Tracking 和优化器通过投影/反投影/Jacobian 接口支持针孔和鱼眼。
论文还用短期、中期、长期数据关联解释精度来源。源码中的连续帧匹配、局部地图投影匹配、重定位/闭环/融合分别对应这三个层次。

*图:ORB-SLAM3 总体架构。Atlas 是多地图状态中心,三条算法通道围绕它协作。*
2.仓库与六种Sensor模式
主库重点目录:
| 目录 | 作用 |
|---|---|
| include/ | 类声明、公开接口、成员变量 |
| src/ | 主流程和算法实现 |
| include/CameraModels/ | GeometricCamera、Pinhole、KannalaBrandt8 |
| Examples/ | 数据集和相机入口、时间戳读取、输入同步 |
| Thirdparty/DBoW2 | 词袋地点识别 |
| Thirdparty/g2o | 图优化基础框架 |
| Thirdparty/Sophus | 李群表示和运算 |
System::eSensor 定义六种输入组合:
MONOCULAR, STEREO, RGBD,
IMU_MONOCULAR, IMU_STEREO, IMU_RGBD
Sensor 决定帧构造方式、是否接收 IMU、尺度是否固定、是否执行惯性初始化等。相机模型则决定像点投影方式;鱼眼不是第七种 Sensor。
3. 输入接口与配置
System 对外提供:
- TrackMonocular(image, timestamp, imuMeasurements, filename);
- TrackStereo(left, right, timestamp, imuMeasurements, filename);
- TrackRGBD(image, depth, timestamp, imuMeasurements, filename)。
调用者负责按时间顺序送入图像。视觉惯性示例还需要从 IMU 流中取出落在相邻图像时间区间内的测量,并传给相应 Track*()。双目图像必须同步;RGB-D 深度必须与彩色图注册。
配置读取涉及两条兼容路径:Settings 解析新格式;部分旧配置仍通过 cv::FileStorage 直接读取。配置不只是内参,还包括帧率、颜色顺序、ORB 参数、双目基线/深度阈值、IMU 噪声/频率、相机---IMU 外参、Viewer 参数和可选 loopClosing。
配置错误的影响链
- 焦距/主点错误 → 投影和重投影雅可比错误;
- 双目基线或深度尺度错误 → 地图整体尺度错误;
- Tbc 方向错误 → 视觉与惯性预测互相冲突;
- IMU 噪声单位错误 → 信息矩阵权重错误;
- 时间戳不同步 → 预积分覆盖错误时间段。
4. System 构造和模块连接
构造函数完整签名包含词典、配置、Sensor、Viewer 开关、初始帧编号和序列名。核心顺序是:
- 保存 Sensor,读取配置;
- 加载 ORB 词典;
- 创建 KeyFrameDatabase;
- 创建 Atlas,必要时加载已保存 Atlas;
- 向 Atlas 标记是否包含惯性传感器;
- 创建 FrameDrawer、MapDrawer;
- 在调用线程中创建 Tracking;
- 创建 LocalMapping 并启动其 Run() 线程;
- 创建 LoopClosing 并启动其 Run() 线程;
- 互相注入 Tracker、LocalMapper、LoopCloser 指针;
- 可选地创建 Viewer 线程。
重要事实:源码注释明确说明 Tracking "live in the main thread of execution"。所以"Tracking 线程"是算法语境中的执行通道,不是 System 内部新建的 std::thread。

*图:Tracking、Local Mapping、Loop Closing、Viewer 和 Global BA 的执行关系。*
应用/Example 调用线程
└─ System::Track*()
└─ Tracking::GrabImage*()
└─ Tracking::Track()
后台线程 A: LocalMapping::Run()
后台线程 B: LoopClosing::Run()
可选线程 C: Viewer::Run()
条件线程 D: LoopClosing::RunGlobalBundleAdjustment()
5. Track*() 到 Tracking::Track()
以 TrackMonocular() 为例,接口首先检查 Sensor 是否兼容,再响应以下控制请求:
- 激活/关闭纯定位模式;
- 重置当前活跃地图或全部系统。
随后若有 IMU 测量,调用 GrabImuData() 放入 Tracking 队列;再调用 GrabImageMonocular()。后者处理图像缩放/灰度、构造 Frame,最终进入 Track()。
Track() 是状态机中枢。主要状态包括:
- NO_IMAGES_YET:尚无图像;
- NOT_INITIALIZED:等待建立初始地图;
- OK:正常跟踪;
- RECENTLY_LOST:短时丢失,惯性模式可能继续预测并尝试恢复;
- LOST:恢复窗口耗尽,需要重定位、重置或创建新地图。
状态并非只由"匹配点数量"决定,还受地图规模、初始化阶段、IMU 是否完成初始化、距上次重定位帧数和时间戳连续性影响。

*图:输入经过 Frame 和 Tracking 形成关键帧/地图点,最终输出轨迹和稀疏地图。*
6. 为什么要并行
Tracking 必须尽快给出每帧位姿,预算接近相机帧间隔。Local Mapping 的三角化和 Local BA 更耗时;Loop Closing 的地点识别和图优化发生频率更低但可能更昂贵。拆分执行上下文可以让相机输入继续前进。
并行也带来一致性问题:
- Tracking 读局部地图时,Local Mapping 可能添加或删除点;
- Loop Closing 校正整张地图时,Local Mapping 必须停止;
- Global BA 运行期间若出现新闭环,旧优化结果可能失效;
- 系统退出时各线程要先收到 finish 请求,再等待其确认。
ORB-SLAM3 使用互斥锁、停止/完成标志和队列保护实现协作。不要把 mutex 当作算法细节;它决定一条调用链是否可以安全修改地图。
7. Frame、KeyFrame 与观测
Frame
表示一次实时输入,包含:
- 时间戳、图像特征、描述子和金字塔尺度信息;
- 相机模型、内参、畸变和双目/RGB-D 深度;
- 当前位姿、速度和 IMU bias;
- 与特征对应的 MapPoint*;
- BoW 向量;
- 前一帧与 IMU 预积分对象。
Frame 生命周期较短,但用于恢复完整轨迹的相对位姿、参考关键帧和丢失标记会由 Tracking 另存。
KeyFrame
关键帧由选中的 Frame 构造,是地图的长期节点。它保存特征和观测,还维护:
- 与其他关键帧的共视权重;
- 生成树的父子关系;
- 回环边/融合边;
- 前后惯性关键帧;
- 线程安全的位姿和连接更新。
不是"图像清晰就成为关键帧"。NeedNewKeyFrame() 同时考虑帧间隔、局部建图是否繁忙、跟踪内点、近点数量和 Sensor 模式。
8. MapPoint 与地图图结构
MapPoint 保存世界坐标、代表性描述子、法向/可见距离、观测集合,以及被看到/被匹配统计。一个地图点必须经历:
- 由双目深度或多视图三角化创建;
- 在邻域关键帧中投影搜索;
- 计算代表性描述子和法向;
- 根据找到率、观测数和年龄筛选;
- 若与已有点重复则融合或替换。
共视图的边权通常与共同观测的地图点数量相关。Tracking 通过共视邻居构造局部地图,Local Mapping 用它选择三角化和 BA 邻域。生成树提供始终连通的稀疏骨架;闭环边补充长期约束;Essential Graph 用较稀疏的图完成全局校正。
9. Map、Atlas 与地点数据库
Map 持有一组 KeyFrame 和 MapPoint,并记录初始关键帧、参考点、变化索引和 IMU 初始化状态。
Atlas 持有多张可能断开的 Map:
- CreateNewMap():创建并激活新地图;
- ChangeMap(map):切换活跃地图;
- GetCurrentMap():供 Tracking/Mapping 获取活跃地图;
- 保存相机模型,并支持序列化。
KeyFrameDatabase 跨 Atlas 使用统一 ORB 词袋数据库。查询结果可能用于重定位、同地图闭环或跨地图融合;最终类别要看候选关键帧所属 Map 及几何验证结果。
10. 输出、持久化和关闭
Track*() 返回当前相机位姿;System 还复制 Tracking 状态、当前跟踪地图点和关键点,供应用查询。轨迹保存函数根据 TUM、EuRoC 或 KITTI 格式输出位姿。因为普通 Frame 位姿常以参考 KeyFrame 的相对变换保存,导出时要沿关键帧关系恢复到统一坐标。
V1.0 支持 Atlas 保存/加载。Boost 序列化恢复对象后还需要重建部分运行时指针和数据库关系;序列化不是简单地把内存地址写入文件。
安全关闭顺序是:
- System::Shutdown() 向后台模块请求结束;
- 等待 Local Mapping、Loop Closing 和 Viewer 确认完成;
- 若 Global BA 仍在运行,等待或终止对应流程;
- 再保存轨迹/地图或销毁对象。
删除模块会怎样:
- 删除 Local Mapping:地图不再增长和局部优化,最终无法稳定跟踪新区域;
- 删除 Loop Closing:短/中期跟踪仍能运行,但长程漂移无法校正,地图也不能融合;
- 删除 Atlas:跟踪彻底失败后只能重置或结束,不能保留旧地图等待以后复用;
- 删除 KeyFrameDatabase:重定位、闭环和地图融合都会失去高效候选检索。