C++

C++ 是 SLAM 的主力语言。你在后续层级中会研究的几乎每一个系统 —— ORB-SLAM、DSO、VINS-Mono、KinectFusion —— 都是用 C++ 编写的,因为 SLAM 必须在资源受限的硬件上实时处理相机帧、IMU 数据包和优化问题。在这一层级,“掌握 C++“意味着能够熟练运用 SLAM 代码库所使用的特定语言习惯和工具链,而不仅仅是掌握语法。

现代 C++(C++11/14/17/20)。 SLAM 代码的生死存亡都系于现代语言习惯:基于范围的 for 循环、auto、lambda 函数、std::thread、智能指针以及移动语义。移动语义尤其重要,因为 SLAM 会传递大型对象(图像、点云、描述子矩阵),在热点循环中承受不起意外的深拷贝。

面向对象编程与设计模式。 SLAM 系统由多个相互交互的模块构成 —— 跟踪器、局部建图器、回环检测器、地图数据库 —— 它们在多个线程之间共享状态。理解继承与组合的取舍、传感器抽象所用的接口,以及常见的设计模式(地图数据库用单例模式,传感器驱动用工厂模式,发布/订阅式回调用观察者模式),能让像 ORB-SLAM 这样的大型代码库变得可读。

数据结构与算法。 你会不断地在复杂度上进行权衡:用于最近邻搜索的 kd-树和哈希网格,用于关键帧筛选的优先队列,用于共视关系和位姿图结构的图。选择合适的容器(std::vector 还是 std::unordered_map)会对帧率产生明显的影响。

编译器与构建系统。 实际项目通常用 CMake 来驱动 Make 或 Ninja 构建。你需要能够阅读和编写 CMakeLists.txt,用于查找 Eigen/OpenCV、设置优化标志(-O3-march=native),以及管理第三方子模块。一个面向 SLAM 的最小 CMakeLists.txt 示例:

cmake_minimum_required(VERSION 3.16)
project(my_vo)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)          # forget this and everything is "slow"

find_package(OpenCV REQUIRED)
find_package(Eigen3 REQUIRED)

add_executable(vo main.cpp)
target_link_libraries(vo ${OpenCV_LIBS} Eigen3::Eigen)

大致了解编译器做了什么(内联、向量化、debug 与 release 构建的区别),可以解释为什么一个”运行很慢”的 SLAM 系统往往只是一个 debug 构建而已。

C++ 中的 OpenCV。 OpenCV 是默认的图像处理层:图像的读取与去畸变、特征检测(cv::ORBcv::SIFT)、匹配(cv::BFMatchercv::FlannBasedMatcher)、位姿估计(cv::solvePnP)以及标定(cv::calibrateCamera)。一个最小的特征处理流程大致如下:

cv::Ptr<cv::ORB> orb = cv::ORB::create(2000);
std::vector<cv::KeyPoint> kps;
cv::Mat desc;
orb->detectAndCompute(img, cv::noArray(), kps, desc);

cv::BFMatcher matcher(cv::NORM_HAMMING);
std::vector<cv::DMatch> matches;
matcher.match(desc_prev, desc, matches);

真实 SLAM 系统中的并发

实时 SLAM 系统会在多个粒度上大量利用并行性:

常见陷阱

对SLAM的意义

上面每一个层级都假定你能够阅读、构建并修改一个中等规模的 C++ 代码库。复现一篇论文通常意味着克隆一个 C++ 仓库,针对你本地的 Eigen/OpenCV 版本修复其 CMake 构建,并对耗时瓶颈进行性能剖析。C++ 的熟练程度也正是让你从使用 SLAM 系统迈向改造 SLAM 系统的关键 —— 无论是替换特征检测器、增加一个传感器,还是通过多线程或 SIMD 来优化性能瓶颈。

动手实践

相关条目