ORB-SLAM学习(二):地图初始化

绪论

提取完ORB特征点后,需要对地图进行初始化操作。如果是单目相机,由于图像中不包含深度信息,需要通过连续的几帧图像计算出尺度信息,构建初始的三维点云。如果是双目或RGB-D相机,本身的图像就含有深度信息,因此第一帧的时候就可以完成初始化操作。下面主要对单目相机的初始化进行分析。

基础知识

基础矩阵,本质矩阵

单应矩阵

奇异值分解

卡方检验

地图初始化

地图初始化在Track()函数中,首先判断mstate有没有初始化,如果没被初始化就根据传感器类型进入不同的初始化函数中,单目初始化函数为MonocularInitialization(),双目或RGB-D相机共用一个初始化函数StereoInitialization()

StereoInitialization()

首先判断单目初始器mpInitializer有没有被创建,没有并且这一帧图像的特征点数目超过100个,就创建mpInitializer,并且记录参考帧数据。

如果mpInitializer被创建了,但是这一帧的特征点数目没超过100,就删除mpInitializer从头再来。如果一切条件满足,就开始进行特征点匹配,这一步通过SearchForInitialization()函数实现,函数运行完之后mvbPrevMatched存储匹配好的特征点,mvIniMatches是个vector容器,索引值表示参考帧的关键点索引值,索引值对应的值表示当前帧的关键点索引值,通过这个保存特征点的匹配关系。

特征匹配之后调用mpInitializer对象的Initialize()函数进行单目初始化。

成员变量初始化

遍历一遍匹配好的特征点,把两帧之间匹配好的特征点储存在mvMatches12变量中,把是否匹配成功标志存入mvbMatched1中,成功为true,失败为false。

RANSAC随机采样

  • 设置随机数种子
  • 进行两百次迭代,每次迭代找出随机的八个点,用于后续的八点法求单应矩阵和基础矩阵,把每次找出的特征点索引储存在mvSets中,之后从待搜索的范围内删除该特征点的索引,确保不会被重复采集到

计算基础矩阵F和单应矩阵H

这里为了加速开了两个线程,下面分开介绍

计算单应矩阵H

在函数FindHomography()

首先进行对特征点进行归一化操作,这样尺度就获得了统一,计算结果具有更高的准确性。有关归一化的操作见基础知识。接下来对之前RANSAC的两百次随机采样的结果进行循环计算。在两百次循环中,干了一下这些事情:

  • 通过ComputeH21()函数计算归一化后的单应矩阵,计算完成后再通过之前归一化的转换矩阵的逆矩阵缩放回真实的单应矩阵。
  • 利用重投影误差对当次的RABSAC结果进行评分,计算重投影并评分调用了CheckHomography()函数。
  • 保存评分最高的单应矩阵并退出循环返回。

关于CheckHomography()函数:

初始化操作中协方差系数sigma设置为1.0,因为只有一层图像

在单应矩阵中,重投影误差定义为从当前帧图像投影到参考帧图像的特征点匹配度和从参考帧图像投影到当前帧图像的特征点匹配度,具体表现为根据所求的单应矩阵求出从图1到图2的特征点,再计算投影到图2的点和图2对应的特征点之间的距离。

误差计算玩后和卡方检验的阈值进行对比,如果大于阈值就舍去,小于阈值就得分累加,就这样计算两遍(图1到图2和从图2到图1),最后返回得分。

计算基础矩阵F

在函数FindFundamental()

首先进行对特征点进行归一化操作,这样尺度就获得了统一,计算结果具有更高的准确性。有关归一化的操作见基础知识。接下来对之前RANSAC的两百次随机采样的结果进行循环计算。在两百次循环中,干了这些事情:

  • 通过ComputeF21()函数计算归一化后的单应矩阵,计算完成后再通过之前归一化的转换矩阵的逆矩阵缩放回真实的基础矩阵。
  • 利用重投影误差对当次的RABSAC结果进行评分,计算重投影并评分调用了CheckFundamental()函数。
  • 保存评分最高的基础矩阵并退出循环返回。

关于CheckFundamental()函数:

初始化操作中协方差系数sigma设置为1.0,因为只有一层图像

在基础矩阵中,重投影误差定义为从当前帧图像投影到参考帧图像的特征点到极线的距离和从参考帧图像投影到当前帧图像的特征点到极线的距离,误差期望为0,到极线距离越远误差越大。

误差计算玩后和卡方检验的阈值进行对比,如果大于阈值就舍去,小于阈值就得分累加,就这样计算两遍(图1到图2和从图2到图1),最后返回得分。

根据得分判断是用H还是F求解位姿

得分计算公式:

\[ RH=\frac{SH}{SH+SF} \]

SH是单应矩阵的评分,SF是基础矩阵的评分,如果RH>0.4,就从单应矩阵恢复\(R,t\),如果RH<0.4,就从基础矩阵恢复\(R,t\)。恢复\(R,t\)的函数分别为ReconstructH()ReconstructF()

ReconstructH()

通过单应矩阵恢复位姿矩阵,有公式

\[ H=K(R-t\frac{n}{d})K^{-1} \]

其中K表示相机内参矩阵,n表示平面法向量,令中间的部分为\(A\),则有

\[ A=K^{-1}HK \]

这样求出矩阵\(A\),对其进行奇异值分解,得到

\[ A=UwV^{T} \]

得到8组解, 对8组解进行验证,选择产生相机前方最多3d点的一组解作为最优解并计算好点的数目。关于好点数目的计算调用了CheckRT()函数,最终把最优解的相关变量更新

关于CheckRT()函数:

首先将参考帧的坐标系定为世界坐标系,然后对所有的特征点对进行遍历,遍历做了以下行为:

  • 通过Triangulate()函数恢复3D点,该函数是利用三角化对点的深度进行估计;
  • 检查通过三角化3D生成的三维点坐标是否无穷远,如果是无穷远点就跳过;
  • 检查三维点的深度,求三维点和相机光心\(O_1,O_2\)的距离,同时根据余弦定理计算视差,如果深度小于0并且视差角过小也跳过;
  • 计算三维点投影到两幅图像的重投影误差,这里直接通过针孔相机模型的公式计算,即

\[ \begin{bmatrix} u\\v\\1 \end{bmatrix}=\frac{1}{Z} \begin{bmatrix} f_x&0&c_x\\0&f_y&c_y\\0&0&1 \end{bmatrix} \begin{bmatrix} X\\Y\\Z \end{bmatrix} \]

如果重投影误差太大,就跳过;

  • 累加好点(经过重重筛选幸存下来)的个数。

如果好点数目大于0,就选一个较小的视差角并返回好点个数。如果没有好点就把视差角设0。

ReconstructF()

通过分解本质矩阵计算得到\(R,t\),该操作调用函数DecomposeE()。根据本质矩阵分解原理会得到四组不同的解,但是只有一组是对的,所以要通过筛选进行判断。

通过CheckRT()检验得到的四组解,并选出最多好点的一组解进行判断。

如果好点数目优势不够明显就返回失败,如果通过考验就把相关解保存下来并指示初始化成功。

创建初始化地图点

如果上一步初始化成功了,首先把没有三角化的匹配点删除,然后将初始化后的第一帧作为世界坐标系,最后进行下列操作:

  • 将初始关键帧的描述子转换为词袋;
  • 将关键帧插入地图;
  • 遍历初始化的三维点,为这些三维点添加属性(还没学到这),最后插入到地图中,初始化完成。