0.前言
注释代码已公开,欢迎交流~~
注释代码已公开,欢迎交流
其他系列文章地址
1. 综述
ORB系统总共有3个线程分别为Tracking,LocalMapping,LoopClosing。
其中Tracking运行在主线程,由Tracking::GrabImageMonocular()
进入。
LocalMapping
线程由LocalMapping::Run()
进入;
LoopClosing
线程由LoopClosing::Run()
进入;
我将由这个3个线程讲述ORBslam2的单目模式是如何运作的。
2.Tracking跟踪
系统将图片交给Tracking::GrabImageMonocular()
后,先将图片转化为灰度图,然后使用图片构建了一个Frame
。
注意系统在初始化的时候使用了不同的ORBextractor
来构建Frame
,是因为在初始化阶段的帧需要跟多的特征点。
然后将进入了Track()
,Tracking的大部分功能将在这里实现:
Tracking
执行了4个任务:
- 单目初始化;
- 通过上一帧获得初始位姿估计或者重定位。也就是求出当前帧在世界坐标系下的位姿 T 2 T_2 T2;
- 跟踪局部地图(
TrackLocalMap()
) 求得位姿估计 T 3 T_3 T3。这个步骤回答当前帧的特征点和map中的哪些mappoint匹配。 - 判断是否需要给localmapping插入关键帧;
2.1. 单目初始化
- 单目初始化需要两帧。具体如何选择这两帧不在这里详述。
- 得到可以用于初始化的两帧后,我们先匹配他们两之间的特征点。
- 根据得到的两帧图像的特征匹配,我们就可以计算单应矩阵H,和基础矩阵F。这里就是高翔的slam十四讲第七章的2D-2D对级约束问题。
- 计算单应矩阵H,和基础矩阵F的过程采用了RANSAC算法来减少误匹配对计算结果的影响。
- 最后计算出的单应矩阵H,和基础矩阵F会有对应的得分,我们根据得分情况来选择使用单应矩阵H,还是基础矩阵F来进行初始化。
- 确定好使用单应矩阵H,还是基础矩阵F之后,会将H或者F分解为R,t。
- 在通过H或者F分解为R,t时,会有多个R,t的组合,我们需要将匹配的特征点通过三角化重投影后,更具投影结果来选择具体是哪个R,t的组合。
- 所以算出R,t后,我们自然也通过三角化得到了通过三角化重投影成功的匹配特征点的相对第一帧的3d坐标。至于剩下那些不能通过R,t重投影成功的匹配点,我们在后面将它剔除,
- 将初始化第一帧的光心作为世界坐标系原点,那么初始化第二帧在世界坐标系中的坐标就可以根据R,t计算出来了。
- 最后将初始化第一帧和第二帧转化为关键帧keyframe,将三角化重投影成功的匹配特征点的3d坐标包裹为mappoint,全部添加到map中。
- 在创建keyframe过程中,我们会记录它的特征点和哪些mappoint匹配。
- 在创建mappoint过程中,我们会记录它能被哪些keyframe的哪些特征点匹配。
2.2. 通过上一帧获得初始位姿估计或者重定位
初始化成功后,后面的帧所面对的情况是一样的。所以我们不从第3帧开始,我们从第i
开始。
假设系统给tracking
传过来了第i
个Frame,我先来阐述下我们现在有什么。我们在map中有许多关键帧keyframe,以及mappoint。
那么来自灵魂的拷问来了,1. 请问你如何快速求得当前帧Frame的相对世界坐标系的位姿呢?
好的,陈独秀同学已经举手示意了,他说可以像初始化那样,通过2D-2D的方式计算当前帧和上一个帧的F和H,进而得到它们之间的位姿关系。好的陈独秀同学,您可以坐下了。想法虽好,但是不可行,因为这样太耗时间了。
其实还有一种方式是通过将当前帧的特征点与map中的mappoint进行匹配,进而得到当前帧在世界坐标系中的位姿。这样这个问题就变成了高翔的slam十四讲第七章的3D-2D的PnP问题。
2.那如何将手中已经当前帧的特征点和map中的mappoint进行匹配呢?
一个一个暴力匹配?不可能的,这辈子都是不可能滴,信不信我tracking分分钟卡死给你看!