构造函数

  1. LoopClosing(Map* pMap, KeyFrameDatabase* pDB, ORBVocabulary* pVoc,const bool bFixScale);

主要分两部分,回环检测,以及GlobalBundleAdjustment

LoopClosing中的关键帧都是LocalMapping中送过来的:送过来一帧,就检查一帧。

  1. // in LocalMapping::Run()
  2. mpLoopCloser->InsertKeyFrame(mpCurrentKeyFrame);
  3.  
  4. // in LoopClosing.cpp
  5. void LoopClosing::InsertKeyFrame(KeyFrame *pKF)
  6. {
  7. unique_lock<mutex> lock(mMutexLoopQueue);
  8. if(pKF->mnId!=)
  9. mlpLoopKeyFrameQueue.push_back(pKF);
  10. }

因此我们需要在KeyFrameDataBase中寻找与mlpLoopKeyFrameQueue相似的闭环候选帧。主要过程包括:

  1. if(CheckNewKeyFrames())
  2. {
  3. // Detect loop candidates and check covisibility consistency
  4. if(DetectLoop())
  5. {
  6. // Compute similarity transformation [sR|t]
  7. // In the stereo/RGBD case s=1
  8. if(ComputeSim3())
  9. {
  10. // Perform loop fusion and pose graph optimization
  11. CorrectLoop();
  12. }
  13. }
  14. }

寻找回环候选帧,检查候选帧连续性;计算Sim3;闭环(地图点融合,位姿图优化)。

一. DetectLoop()

在共视关系中找到与当前关键帧Bow匹配最低得分minScore,在除去当前帧共视关系的关键帧数据库中,检测闭环候选帧:

  1. vector<KeyFrame*> vpCandidateKFs = mpKeyFrameDB->DetectLoopCandidates(mpCurrentKF, minScore);

闭环候选帧筛选过程:

1. Bow得分>minScore;

2. 统计满足1的关键帧中有共同单词最多的单词数maxcommonwords;

3. 筛选出共同单词数大于mincommons(=0.8*maxcommons)的关键帧;

4. 相连的关键帧分为一组,计算组得分(总分),得到最大总分bestAccScore,筛选出总分大于minScoreToRetain(=0.75*bestAccScore)的组,用组中得分最高的候选帧lAccScoreAndMatch代表该组。计算组得分的目的是剔除单独一帧得分较高,但是没有共视关键帧,作为闭环来说不够鲁棒。

对于通过了闭环检测的关键帧,还需要通过连续性检测(连续三帧都通过上面的筛选),才能作为闭环候选帧。

  1. mvpEnoughConsistentCandidates

二. ComputeSim3()

计算当前关键帧和闭环候选帧之间的Sim3,这个Sim3变换就是闭环前积累的尺度和位姿误差,该误差也可以帮助检验该闭环在空间几何姿态上是否成立。

1. Bow

通过SearchByBow搜索当前关键帧中和闭环候选帧匹配的地图点。(Bow通过将单词聚类到树结构node的方法,加快搜索匹配速度)

  1. int nmatches = matcher.SearchByBoW(mpCurrentKF,pKF,vvpMapPointMatches[i]);

若nmatches<20,剔除该候选帧,将匹配好的地图点放入当前候选帧对应的vvpMapPointMatches[i]中。

注意这里使用Bow匹配速度较快,但是会有漏匹配。

2. RANSAC

利用上面匹配上的地图点(虽然匹配上了,但是空间位置相差一个Sim3),用RANSAC方法求解Sim3位姿。(这里有可能求解不出Sim3,也就是虽然匹配满足,但是空间几何姿态不满足)。

这里Sim3的求解需要参考论文 Horn B K P. Closed-form solution of absolute orientation using unit quaternions. JOSA A, 1987, 4(4): 629-642.

  1. Sim3Solver* pSolver = new Sim3Solver(mpCurrentKF,pKF,vvpMapPointMatches[i],mbFixScale);
  2. pSolver->SetRansacParameters(0.99,,);// 至少20个inliers 300次迭代

  3. cv::Mat Scm  = pSolver->iterate(5,bNoMore,vbInliers,nInliers);

3. SearchBySim3

根据计算出的Sim3(s,R,t),去找更多的匹配点(SearchBySim3),更新vpMapPointMatches

  1. matcher.SearchBySim3(mpCurrentKF,pKF,vpMapPointMatches,s,R,t,7.5);

4. Optimize

使用更新过的匹配,使用g2o优化Sim3位姿,这时内点数nInliers>20,才说明通过。一旦找到闭环帧mpMatchedKF,则break,跳过对其他候选闭环帧的判断。

  1. const int nInliers = Optimizer::OptimizeSim3(mpCurrentKF, pKF, vpMapPointMatches, gScm, , mbFixScale);

5. SearchByProjection

获取mpMatchedKF及其相连关键帧对应的地图点。将这些地图点通过上面优化得到的Sim3(gScm-->mScw)变换投影到当前关键帧进行匹配,若匹配点>=40个,则返回ture,进行闭环调整,否则,返回false,线程暂停5ms后继续检查Tracking发送来的关键帧队列。

  1. matcher.SearchByProjection(mpCurrentKF, mScw, mvpLoopMapPoints, mvpCurrentMatchedPoints,);

注意这里得到的当前关键帧中匹配上闭环关键帧共视地图点(mvpCurrentMatchedPoints),将用于后面CorrectLoop时当前关键帧地图点的冲突融合。

到这里,不仅确保了当前关键帧与闭环帧之间匹配度高,而且保证了闭环帧的共视图中的地图点和当前关键帧的特征点匹配度更高(20--->40),说明该闭环帧是正确的。

三. CorrectLoop()

闭环纠正时,LocalMapper和Global BA必须停止。注意Global BA使用的是单独的线程。

分为两步,第一步LoopFusion,第二步Essential Graph优化。

其中Essential Graph包含三部分:1. 共视关系很好的关键帧;2. spanning tree连接关系(父子关系);3. 闭环关键帧连接关系。

使用计算出的Sim3对当前位姿进行调整,并且传播到当前关键帧相连的关键帧(相连关键帧之间相对位姿是知道的,通过当前关键帧的Sim3计算相连关键帧的Sim3)。这样回环的两侧关键帧就对齐了,利用调整过的位姿更新这些相连关键帧对应的地图点。然后将闭环帧及其相连帧的地图点都投影到当前帧以及相连帧上,投影匹配上的和Sim3计算过的地图点进行融合(就是替换成质量高的),涉及融合的关键帧还需要更新其在共视地图中的观测边关系,这是为了剥离出因为闭环产生的新的连接关系LoopConnections,用于优化Essential Graph。添加当前帧与闭环匹配帧之间的边,该边不参与优化。

记住地图点是连接关键帧之间的枢纽,每次调整地图点位置后都需要更新关键帧的连接关系。

下面新开一个线程进行全局优化。OptimizeEssentialGraph只是优化一些主要关键帧,全局优化可以优化所有的位姿与MapPoints。

ORB-SLAM(十)LoopClosing的更多相关文章

  1. 高博-《视觉SLAM十四讲》

    0 讲座 (1)SLAM定义 对比雷达传感器和视觉传感器的优缺点(主要介绍视觉SLAM) 单目:不知道尺度信息 双目:知道尺度信息,但测量范围根据预定的基线相关 RGBD:知道深度信息,但是深度信息对 ...

  2. 《SLAM十四讲》个人学习知识点梳理

    0.引言 从六月末到八月初大概一个月时间一直在啃SLAM十四讲[1]这本书,这本书把SLAM中涉及的基本知识点都涵盖了,所以在这里做一个复习,对这本书自己学到的东西做一个梳理. 书本地址:http:/ ...

  3. 视觉slam十四讲第七章课后习题6

    版权声明:本文为博主原创文章,转载请注明出处: http://www.cnblogs.com/newneul/p/8545450.html 6.在PnP优化中,将第一个相机的观测也考虑进来,程序应如何 ...

  4. 视觉slam十四讲第七章课后习题7

    版权声明:本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/newneul/p/8544369.html  7.题目要求:在ICP程序中,将空间点也作为优化变量考虑进来 ...

  5. 浅读《视觉SLAM十四讲:从理论到实践》--操作1--初识SLAM

    下载<视觉SLAM十四讲:从理论到实践>源码:https://github.com/gaoxiang12/slambook 第二讲:初识SLAM 2.4.2 Hello SLAM(书本P2 ...

  6. SLAM十四讲中Sophus库安装

    Sophus截止目前有很多版本,其中大体分为两类,一种是用模板实现的方法,一种是用非模板类实现的,SLAM十四讲中使用的是非模板类库,clone Sophus: git clone http://gi ...

  7. 《视觉SLAM十四讲》第2讲

    目录 一 视觉SLAM中的传感器 二 经典视觉SLAM框架 三 SLAM问题的数学表述 注:原创不易,转载请务必注明原作者和出处,感谢支持! 本讲主要内容: (1) 视觉SLAM中的传感器 (2) 经 ...

  8. 《视觉SLAM十四讲》第1讲

    目录 一 视觉SLAM 注:原创不易,转载请务必注明原作者和出处,感谢支持! 一 视觉SLAM 什么是视觉SLAM? SLAM是Simultaneous Localization and Mappin ...

  9. 视觉SLAM十四讲:从理论到实践 两版 PDF和源码

    视觉SLAM十四讲:从理论到实践 第一版电子版PDF 链接:https://pan.baidu.com/s/1SuuSpavo_fj7xqTYtgHBfw提取码:lr4t 源码github链接:htt ...

  10. 高翔《视觉SLAM十四讲》从理论到实践

    目录 第1讲 前言:本书讲什么:如何使用本书: 第2讲 初始SLAM:引子-小萝卜的例子:经典视觉SLAM框架:SLAM问题的数学表述:实践-编程基础: 第3讲 三维空间刚体运动 旋转矩阵:实践-Ei ...

随机推荐

  1. (fields.E130) DecimalFields must define a 'decimal_places' attribute.

    DecimalField类型:固定精度的十进制数,一般用来存金额相关的数据.额外的参数包括DecimalField.max_digits(整个数字的长度)和DecimalField.decimal_p ...

  2. 【[HNOI2015]菜肴制作】

    \(SDSC\)讲过的题,复习一下 如果用一个小根堆来维护拓扑的话显然是会不行的,因为这样求出来的是字典序最小的拓扑序,并不一定是1尽可能在前 因为字典序是贪心的,如果前面的一位能小就尽可能的小,并不 ...

  3. 关闭layer当前弹窗

    一. layer关闭弹出层方法1-1) 先获取某个弹出层的 index   var index = layer.open();   var index = layer.alert();   var i ...

  4. 阻止按下backspace键造成页面回退相像

    在IE浏览器中,会出现当你使用鼠标选中input标签或者是textarea标签,或者啥也没选中的时候,按下backspace键会触发浏览器的回退. 针对以上问题的解决思路:     1. 当按下键盘时 ...

  5. android:windowSoftInputMode属性详解 软键盘

    android:windowSoftInputMode activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性. 这个属性能影响两件事情: [一] ...

  6. SecureCRT连接主机时(串口/网络),无法从键盘输入

    Session Option-Connection-Serial-Flow Control,里面的选项全部取消掉,再重启CRT就ok了...

  7. var let const的一些区别

    var let const 都是来定义变量的. var let 作用域有些区别. const 类似于java中的常量的概念.即:只能给一个变量赋值一次,即指定一个引用. 举例来说: function ...

  8. 【读书笔记 - Effective Java】02. 遇到多个构造器参数时要考虑用构建器

    类有多个可选参数的解决方案: 1. 重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难以阅读. 2. JavaBeans模式,调用一个无参构造器来创造对象,然后调用sett ...

  9. Ajax实现下载进度条

    可以通过设置一个XMLHttpRequest对象的 responseType 属性来改变一个从服务器上返回的响应的数据类型.可用的属性值为空字符串 (默认), "arraybuffer&qu ...

  10. TinyMCE插件:RESPONSIVE filemanager 9 文件名统一格式化

    上传图片方法(filemanager/UploadHandler.php) 在上传图片的函数中查看,发现$file->name是一个完整的[文件名.后缀名],所以使用explode(),文件名和 ...