周六看到了ORBSLAM3的源码,安装运行后看了一下其代码结构,因为加IMU的部分是针对之前的ORB-VI, 因此大家可以参考jinpang的LearnORBVI可以更纯粹地学习视觉+IMU的组合;
这篇文章主要是针对其在Tracking线程做出的改动,尤其是添加Atlas后对Tracking部分的影响,LoopClosing和MapMerging的部分会在后面的分析中讲到,有错误也欢迎各位指正。
各部分的流程图整理好之后,稍后发出。
ORBSLAM3相对于ORBSLAM2做出的主要改动:
1. Atlas: 用于
保存很多琐碎的地图
;主要的作用发挥在Tracking线程Lost时,之前Lost后需要回到原先的位置进行Relocalization(),但是现在,如果tracking线程丢失,ORBSLAM3会在之前的所有小地图中进行查询匹配,如果匹配成功,则tracking线程继续; 如果匹配不成功,则重新开辟一个小的地图;
2. 也是因为这种情况,ORBSLAM3中还有很多小的地图(类似于carto中的submap)
而
Localmapping 线程只发生在当下active的map中
;
3.新增最大后验概率,用于初始化并且refine IMU数据;
4. loop and map merging 线程: 因为有了很多琐碎的地图,因此新增了map merging的部分,如果
两个地图有重合部分
,且重合处属于当前Active的Map,则对其进行LoopCorrection,类似于之前的检测到回环。如果两个地图
重合的部分属于别的地图
,这就类似于找到了其他两个地图的重合点,因此就将他们merge到一起。
If the common area belongs to the active map, it performs loop correction; if it belongs to a different map, both maps are seamlessly merged into a single one, that becomes the active map.
1. 相机模型:
新增了基于KB鱼眼模型的追踪;这个模型与OpenCV的cv::fisheye标定的时候所使用的模型是一样的,因此大家可以使用opencv直接进行标定,建议使用size较大的标定板,这是注意很有可能你的标定板检测不需要subpixel即可很准确。
鱼眼模型带来的改变:
a.
重定位
:ORBSLAM2通过求解PnP问题进行重定位后的计算,但是因为新增了鱼眼模型,因此需要新建一个可以不基于相机模型的Pnp算法,因此也就有了最大似然PnP算法;
b. 鱼眼模型带来的另一个改变是
双目匹配
时,这里对比一下两个项目在这里的区别:
ORBSLAM2: 采用双目去畸变后的图像,去畸变的remap过程发生在进入system之前,ComputeStereoMatches() 所采用的双目匹配方法是在极线上搜索,是一个
很小的带状区域
;搜索到匹配点之后直接根据
视差d计算深度z
;
ORBSLAM3: 采用
暴力匹配
的方式,在全局
使用knn搜索
,只查找与当前左目描述子距离最近和次近的右目描述子,只要满足Lowe’s Law就可以送去三角化计算关键点所对应的地图点。
三角化的这部分在ORBSLAM2中只用于跟踪,根据两帧匹配好的图像进行计算地图点的坐标,这里把他用于双目匹配计算深度;我在EUCM双目鱼眼中计算深度的方法也是如此,但是因为
没有加入前面的最优与次优筛选的条件
,
outliner就会比较多
;
2. 新添加了一种状态RECENTLY_LOST,因此主要的状态分为以下:
NO_IMAGE_YET
NOT_INITIALIZED
OK
RECENTLY_LOST: 当前地图中的KF>10,且丢失时间<5秒
个人认为,这个状态其实就是相当于原先的LOST,在这里,如果激活了IMU则通过IMU数据进行预测,也就是 calculate current pose by IMU data,
但如果丢失时间,超过五秒,则由当前state切换为LOST;如果是非IMU,即纯视觉状态,则直接进行重定位Relocalization();
LOST: 这个状态就是ORBSLAM3的一个新加的状态(
虽然名字与之前的一样
),因为新增了atlas,如果当前地图中的关键帧数量<10个,可以认为当前地图中没有重要信息,直接ResetActiveMap(),这个思路相当于之前的Reset(), 即如果初始化刚刚成功就丢失,可以认为当前map并不重要,直接重新初始化,这里也是一样的思路,这个部分最终发挥作用是在函数Tracking::ResetActiveMap()中。但如果当前的地图已经有很多关键帧(大于10帧),则调用CreateNewMap() 暂存该地图,再新建一个空的地图。
接下来
对比一下Tracking::ResetActiveMap() 与ORBSLAM2中的Reset()
,方便更清楚这个函数的作用
ORBSLAM3 中 Tracking::ResetActiveMap() 部分功能:
Map* pMap = mpAtlas->GetCurrentMap();
mpLocalMapper->RequestResetActiveMap(pMap);
mpLoopClosing->RequestResetActiveMap(pMap);
mpKeyFrameDB->clearMap(pMap);
mpAtlas->clearMap();
ORBSLAM2 中Reset()函数:
mpLocalMapper->RequestReset();
// Reset Loop Closing
mpLoopClosing->RequestReset();
cout << "Reseting Database...";
mpKeyFrameDB->clear();
cout << " done" << endl;
// Clear Map (this erase MapPoints and KeyFrames)
mpMap->clear();
可以看到该函数的主要作用相对于之前的Reset() ,
不变的部分
是对一些Tracking所用到的类成员变量进行reset,如mCurrentFrame, mLastFrame等,同时,通知其他几个线程需要reset的消息。 但是
变化的部分
主
要是Atlas的加入,每一个线程的reset都是只针对currentMap,并不会影响其他的Map,新建的线程mpAltas也会将目前的地图清除,
CreateMapInAtlas(),用于新建一个submap,而atlas就是用于管理这些small map的。
接下来主要说一下ATLAS
, atlas是新建的线程,主要用于管理地图,既然有了地图管理者,就会有几个随之而来的类成员变量,
最主要的是mspMaps,用于存放小地图,同时一个重要的函数createNewMap()用于新建地图
:
强调一下 createNewMap(): 主要作用如下:
1. 保存当前地图 mpCurrentMap->SetStoredMap(); 主要通过设置变量IsInUse为False,方便后面进行判断;
2. 设定新地图的初始帧的KFid:mnLastInitKFidMap = mpCurrentMap->GetMaxKFid()+1;
3. 接下来一切准备就绪,
mpCurrentMap = new Map(mnLastInitKFidMap); //初始化一个地图
mpCurrentMap->SetCurrentMap(); //将其设置为正在使用,也就是active
mspMaps.insert(mpCurrentMap); //将此地图插入到mspMaps,也就是地图管理变量中
就此,创建新的小地图完成;
接下来最主要的一个问题就是
ORBSLAM3会在什么时候创建新的地图:
1. 初始化:
Map* pCurrentMap = mpAtlas->GetCurrentMap();
a. 如果是带有IMU的初始化,则在IMU初始化完成后就直接调用 CreateMapInAtlas();(结合IMU的第一点作用,即在视觉初始化成功之前进行位置追踪,这里不难理解)
b. 如果是不带有IMU的Tracking,初始化的过程和ORBSLAM2一样,在初始化时进行新建地图,以双目为例,在
StereoInitialization() 中,若有特征点足够,则
KeyFrame* pKFini = new KeyFrame(mCurrentFrame,mpAtlas->GetCurrentMap(),mpKeyFrameDB);
而这里
GetCurrentMap()也会调用createNewMap()来新建地图
;
2. 如果当前状态设定为LOST
,且当前地图中的关键帧数量已经多余10帧,则调用createNewMap()保存当前地图,创建新地图;
下面是Tracking部分的流程图,排除IMU的添加,蓝色的字体是纯视觉来讲,与之前ORBSLAM2的主要差别。
如有疑问,欢迎交流: wx:
baobaohaha_ 欢迎对SLAM有兴趣的小伙伴一起交流学习~~