ORB-SLAM2的总体框架与安装试用
ORB-SLAM2是一个实时的视觉SLAM工具库,它提供了利用单目、双目以及RGB-D完成稀疏三维重建的功能和接口。它是一个完整的SLAM系统,能够实时的进行闭环检测和重定位。作者选择将ORB-SLAM2开源,一方面是为了给SLAM社区做一些贡献,另一方面也希望能够给其它领域的研究人员提供一个开箱即用的SLAM解决方案。
从2015年到2021年,作者团队先后发布了ORB-SLAM、ORB_SLAM2、ORB-SLAM3。我们选择分析ORB-SLAM2的源码,主要是因为它的源码比较轻量,组织的也比较规范,研究起来比较方便。而且是一个具有完整功能(包括闭环检测、重定位、地图重用等功能)的实时的开源视觉SLAM系统,同时支持单目、双目、RGB-D相机。
本文我们简单分析它的总体框架,介绍如何源码编译这个工具库,并在TUM数据集上运行官方的单目和RGB-D的demo。以后我们将从这两个demo出发深入研究ORB-SLAM算法的机理和实现。
1. 总体框架
下图是从论文中扣下来的系统框图,左图是ORB-SLAM,右图是ORB-SLAM2的。两者大体上是没有什么差别的,虽然在ORB-SLAM2中没有体现单目,但是它完全可以处理。可以说左图所描述的系统完全是右图的一个子集,ORB-SLAM2是在ORB-SLAM的基础上进行的扩展,并且增加了双目、RGB-D相机的接口。
(a). ORB-SLAM的系统框图 | (b). ORB-SLAM2系统框图 |
总体来说,ORB-SLAM2有三个并行的线程:
- TRACKING: 用于跟踪相机的运动轨迹。它提取输入的每帧图像的特征点,与局部地图(local map)进行匹配,采用纯运动BA(motion-only BA)算法最小化重投影误差(reprojection error),进而估计相机的位置。同时它还将生成关键帧(keyFrame)供线程LOCAL MAPPING维护局部地图。如果因为遮挡或者相机运动过快导致TRACKING跟丢了,ORB-SLAM2会触发场景识别(Place Recognition)进行重定位。
- LOCAL MAPPING:用于更新局部地图。它通过共视图(covisibility graph)处理新关键帧的特征点并将之三角化生成新的地图点,通过局部BA(local BA)在相机的位姿周围获得最优的重建地图。该线程也对特征点和关键帧进行筛选,去除冗余,在一定程度上降低因为特征点和关键帧的累积导致算力需求的增长。
- LOOP CLOSING:用于对每个新生成的关键帧进行闭环检测。一旦检测到闭环,就会计算一个相似变换来估计闭环的累积误差,接着根据闭环首尾的相对位姿融合重复的地图点。最后还会通过相似性约束对位姿图,即Essential Graph(基图),进行优化。在完成位姿图的优化之后,该线程会开启第四个线程FULL BA,进行完全BA(full BA)获得整个系统的最优结构和运动轨迹。
由于ORB-SLAM2是根据特征点进行建图的,并不太关心传感器采集的图像形式。所以它可以接入单目、双目、RGB-D多种不同形式的相机。从传感器采集到图像到开始建图之间,还需要先对图像进行预处理,提取ORB特征点。并根据传感器的形式和特征点的深度信息将之划分为单目、近双目和远双目三类特征点。之后整个SLAM过程都是围绕着这三类特征点的操作。
可以说ORB-SLAM2主要是在维护共视图。虽然还有一个基图,但它只是共视图的一个子图。共视图是一个无向加权图。它的节点是关键帧,连边的权重描述了两个关键帧能够同时观测到的地图点的数量。为了防止共视图过于稠密,两帧之间共视的地图点至少有15个才会在图中建立连接关系。地图点是从关键帧中筛选出的一些特征点,它们已经通过三角化确定了世界坐标。所有的地图点集合就构成了一个稀疏的特征点地图。
2. 安装试用
ORB-SLAM2的安装过程还是很简单的,我们根据Github上的说明进行源码编译。ORB-SLAM2依赖Pangolin进行可视化和图形界面的渲染,使用OpenCV进行图像处理,用g2o进行图优化,DBoW2实现词袋模型进行场景识别,其中g2o还会用到Eigen3进行数学运算。ROS对于ORB-SLAM2而言并不是必需的,所以我也就没有动力再搞个ROS在这里了。
# 安装 eigen
git clone https://gitlab.com/libeigen/eigen.git
cd eigen && git checkout 3.4.0
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=${HOME}/local ..
make && make install
# 安装 opencv
git clone https://github.com/opencv/opencv.git
cd opencv && git checkout 4.6.0
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX=${HOME}/local ..
make && make install
# 安装 Pangolin
sudo apt install libglew-dev
git clone https://github.com/gaoyichao/Pangolin.git
cd Pangolin
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=${HOME}/local ..
make && make install
ORB_SLAM2 的众多依赖中,DBoW2和g2o我们不需要太关心,因为这两个依赖已经作为Thirdparty存在于源码中了。下面我们来简单介绍一下Eigen3、OpenCV、和Pangolin三个依赖,以及它们的源码安装方法。
Eigen3是一个C++编写的线性代数库,可以方便的进行向量、矩阵的数值运算。这个库是C++模板库,只有头文件,并不需要库文件。作者准备了一套 cmake 的编译安装脚本,可以通过右侧的脚本进行安装。 在执行 cmake 生成 Makefile 的时候可以通过宏 CMAKE_INSTALL_PREFIX 指定安装目录,这里我用的是${HOME}/local,也就是"~/local"。如果不想改变默认的安装路径可以在指令中删去这个宏。
大名鼎鼎的OpenCV是Intel开发的图像处理库,集成了很多机器视觉的算法。它的安装方式同上。需要注意的是,ORB-SLAM2 用的是 OpenCV3。我比较习惯用 OpenCV4,所以为了保证编译通过, 还需要对 ORB-SLAM2 的源码进行一点调整。读者如果觉得麻烦可以自己编译一个3.x版本的OpenCV,只需要在右侧的第10行中切换到一个3.x的标签上。
Pangolin是一个用于OpenGL显示/交互以及处理输入视频的库。因为它要用现代OpenGL的接口进行可视化,所以需要安装 glew 。 因为我在安装 Eigen 的时候修改了安装路径,导致 Pangolin 查找 Eigen 的机制出了些问题。我对它的 CMake 文件做了一些细微的修改,放在了这里。 安装完上述依赖之后,就可以编译ORB_SLAM2了。为了能够使用OpenCV4,我们还需要对源码进行一些调整,大家可以从这里下载修改后的源码。
$ git clone https://github.com/gaoyichao/ORB_SLAM2.git
$ cd ORB_SLAM2
$ ./build.sh
如果一切顺利,我们就可以在ORB_SLAM2/lib的目录下看到动态连接库libORB_SLAM2.so,在ORB_SLAM2/Examples目录及其子目录下看到各个demo的可执行文件。根据Github上的教程,我们下载了一个TUM数据包,将之保存在"~/Dataset/TUM"目录下并解压。然后我们就可以在ORB_SLAM2的根目录下通过如下的指令运行单目demo。该指令有三个输入参数,其中ORBvoc.txt是官方提供的一个用于场景识别和重定位的特征字典,TUM1.yaml是数据集相关的配置文件,最后的那个目录则是数据集所在目录。
$ ./Examples/Monocular/mono_tum Vocabulary/ORBvoc.txt \
Examples/Monocular/TUM1.yaml \
~/Dataset/TUM/rgbd_dataset_freiburg1_xyz
由于我们下载的这个数据集实际上是一个RGB-D相机采集的数据,在单目demo中只使用了其RGB图像数据。采用相同的数据集,我们还可以运行RGB-D的建图demo,这个demo多了一个参数associations.txt是用TUM官方提供的python脚本生成的RGB图与深度图之间的关系文件。
$ ./Examples/RGB-D/rgbd_tum Vocabulary/ORBvoc.txt \
Examples/RGB-D/TUM1.yaml \
~/Dataset/TUM/rgbd_dataset_freiburg1_xyz \
~/Dataset/TUM/rgbd_dataset_freiburg1_xyz/associations.txt
我们可以通过如下的指令生成associations.txt。需要注意的是,这个脚本是 python2 版本的,如果要用 python3 执行,还需要将其中的86, 87行的的代码改成下面4, 5行的形式。
$ cd ~/Dataset/TUM
$ python associate.py rgbd_dataset_freiburg1_xyz/rgb.txt rgbd_dataset_freiburg1_xyz/depth.txt > associations.txt
$ mv associations.txt rgbd_dataset_freiburg1_xyz
first_keys = list(first_list) # 若使用 python3 运行上述指令
second_keys = list(second_list) # 需要修改 associate.py 文件86, 87行
如果一切顺利,我们应该可以看到类似下图的两个窗口,一个显示图像和特征点,另一个显示ORB建图过程。由于单目图像的数据缺乏尺度信息,所以需要有一个初始化的阶段。
3. 源码目录
$ tree ORB_SLAM2/ -d -L 2
ORB_SLAM2/
├── build
│ └── CMakeFiles
├── cmake_modules
├── Examples
│ ├── Monocular
│ ├── RGB-D
│ ├── ROS
│ └── Stereo
├── include
├── lib
├── src
├── Thirdparty
│ ├── DBoW2
│ └── g2o
└── Vocabulary
左侧是编译之后 ORB_SLAM2 的目录结构。源码根目录下的文件都是一些编译脚本、说明文档、License文件。系统的实现和demo都组织这几个目录中。
其中 build 目录,就是我们刚才运行脚本 build.sh 编译 ORB_SLAM2 的时候创建的目录。这是我们使用 cmake 编译安装源码的惯例,用一个 build 目录保存编译过程中生成的各种目标文件, 将它们与源码分开。这样在遇到一些莫名所以的问题的时候,想要彻底地重新编译一遍的话,只要将 build 目录删掉就好。
cmake_modules 目录下只有一个文件 FindEigen3.cmake 用于在系统中寻找 Eigen 库。实际上这个文件已经没什么用处了。 因为我们已经修改了 CMakeLists.txt 的代码通过 include_directories 指定了 Eigen 库所需头文件的目录。
Examples 目录下有四个子目录分别为单目相机(Monocular)、深度相机(RGB-D)、双目相机(Stereo)以及ROS环境提供了不同的 demo 和相关配置。 本系列文章中,我们只涉及 Monocular 和 RGB-D 两个环境。为了编译方便,我们已经在 CMakeLists.txt 中将关于双目相机和ROS的例程注释掉了,读者有兴趣可以自己修改研究。
include 和 src 中放置的就是本系列关注的 ORB-SLAM2 实现源码。lib 也是由编译过程中生成的目录,它里面只有一个 libORB_SLAM2.so 的库文件。 估计作者是想通过动态链接库的形式提供一个开箱即用的 SLAM 方案,想让用户根据需要链接到这个库文件上,而不必关心 SLAM 的实现细节。
Thirdparty下则是g2o和DBoW2两个第三方库的源码。Vocabulary 中保存了一个作者预制的特征字典。ORB-SLAM2 使用词袋模型进行快速闭环检测和重定位, 这个预制的特征字典则是解释词袋模型中各个特征的依据,直接影响了 ORB-SLAM2 的运行效率和定位成功率。针对不同的场景和应用,我们可能需要训练自己的特征字典。
4. 完
本文简单分析了 ORB-SLAM2 的总体框架和目录结构,并详细讲述了如何编译它及其相关依赖库。根据我个人习惯,将依赖库安装在"~/local"目录下,修改了 ORB-SLAM2 的源码适配 OpenCV4 的接口。 如果一切顺利,就可以在TUM的一个数据包上运行单目和RGB-D的 demo 了。
我们将在以后的文章中结合论文,详细分析作者的设计思想,介绍各个部分的具体实现。