热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

openvslam(33)文件结构全局优化模块(globaloptimizationmodule):回环检测、posegraph优化、globalBA优化

博客地址https:blog.csdn.netu011341856articledetails109491058 这篇博客主要介绍OpenVSLAM的全局优化模块(global_o

博客地址

https://blog.csdn.net/u011341856/article/details/109491058

 

这篇博客主要介绍OpenVSLAM的全局优化模块(global_optimization_module),该模块是单独运行在一个线程中的。

它主要执行的工作是:SLAM的回环检测,以及回环成功之后的回环矫正,还包括紧随着回环检测成功之后的pose graph优化和全局的BA优化。

 


1.全局优化模块入口

 类的定义

 

 

 测试样例

 

说是全局优化模块,其实也就是一个普通的类。对于类,第一步肯定就是实例化构造对象,全局优化模块的实例化过程是在system.cc代码中的system的构造函数处,对应的代码的如下:

global_optimizer_ = new global_optimization_module(map_db_, bow_db_, bow_vocab_,
camera_->setup_type_ != camera::setup_type_t::Monocular);

可见全局优化模块和其它模块共用的数据包括

map_db_(地图的数据库)、

bow_db_(词袋数据库)、

bow_vocab_(词袋模型),

最后一个参数来控制优化时是否优化尺度(scale),

对于单目由于存在尺度漂移,所以需要优化尺度。

至此,全局优化模块构造完成。

另外由于它需要在一个独立的线程中运行,所以还需要新建一个线程,将该对象中的代码运行在这个线程中。

新建线程的代码在system.cc文件中的system::startup()函数中,对应的代码如下:

global_optimization_thread_ = std::unique_ptr(new std::thread(&openvslam::global_optimization_module::run, global_optimizer_));

 注意std::thread运行对象中函数的用法!


2.全局优化模块线程在干啥?

经过简单地配置,全局优化线程就欢脱的跑起来了,现在就让我们来看看它都具体干了啥?首先祭出一张流程图:

 

 

 

全局优化线程是一个死循环,只要程序正常运行,它将不停地在做同样的重复性工作,好无聊啊!

(1)首先,先检查一下SLAM的状态,确保没有收到SLAM要暂停、停止或者重启的命令!

这里命令的发出主要是通过显示模块中在pangolin的窗口上的那几个按钮!

(2)从关键帧队列中(keyfrms_queue_),取出最前面的一帧关键帧。如果关键帧队列中啥东西都没有,那怎么办呢?哎,这一趟循环了个屁,重新循环吧!心累

keyfrms_queue_这个成员变量主要用来存储关键帧,用于回环检测。其中关键帧数据主要是来自建图模块(mapping_module)的输入,只要建图模块成功的生成了一个关键帧,那么就将关键帧放入这个队列。

(3)将队列中取出来的这一个关键帧,与地图数据集中的所有关键帧进行比较,找到和自己的相似的关键帧,将其作为备选的回环关键帧。

如果找到了(闭环了),那就继续往下执行,全局优化

没有找到的话(没有闭环),又是一个无用的循环,那就回到开始,重新下一次循环吧!

这一步对应的函数是loop_detector::detect_loop_candidates(),这是一个重要的函数,下面的内容单独讲解这个函数。

(4)检测到了一些和自己相似的回环候选帧之后,需要验证这些候选关键帧是不是确实是一个真正的回环。

如果经过验证这些候选回环关键帧中没有真正的回环帧,那么这次循环就结束了,重新循环!

这一步对应的函数loop_detector::validate_candidates(),它也是一个重要的函数,后面会专门介绍。

(5)经过验证之后,确实是一个成功的回环,那么就需要执行回环矫正!

这一步对应的函数global_optimization_module::correct_loop(),这个函数后面介绍!

以上步骤中,一共出现了三个很重要的函数:



  • loop_detector::detect_loop_candidates()

  • loop_detector::validate_candidates()

  • global_optimization_module::correct_loop()

 

接下来我们一个一个去学习


2.1 检测回环候选帧

 

首先来看第一个函数loop_detector::detect_loop_candidates(),它主要是用来进行回环候选帧的检测,经过它的检测之后,可以获得很多的回环候选帧,这些候选帧中也许有真正的回环帧,也许没有!让我们来看看它都做了啥?首先祭出它的流程图:

 

 

 

让我向你具体介绍它都做了啥吧!

(1)首先,检测是否开启回环检测功能,另外如果距离上一次回环检测成功还没有超过10个关键帧,那就先不要进行回环检测,不然的话太频繁了,也不符合实际情况!如果不满足这两个条件,那就退出这个函数!

你可以想象一下,刚做完回环检测,你又回到原地的情况应该还是比较少见的吧!

 

(2)根据共视关系计算回环检测的最小评分。可以想象一下,如果拿着当前帧到地图数据库中去找和我比较相似的,那怎么判断拿个和我算比较相似,那个算比较不相似呢?如果设置的阈值过高可能错过了回环,如果阈值过低可能会检测到很多候选帧。于是作者这里的策略是把当前帧的共视关键帧取出来,以此作为判断依据,将共视关键帧中和自己bow评分最低的那个得分最为评判回环候选帧的阈值。

这一步操作对应的函数是loop_detector::compute_min_score_in_covisibilities()。这一点你是可以想象的,既然是回环帧,那至少和我们看起来还是比较相似的,至于取用共视关键帧中的最低评分,我估计作者是做了大量的实验之后,得出来的一个经验做法!
(3)根据上一步计算出来的评分,去地图数据库进行查找,那些只要和当前帧相似评分大于这个最低阈值的关键帧,都将作为回环候选帧取出来。

(4)如果没有得到任何的回环候选帧,那么结束当前函数。

(5)将每一个回环候选帧以及这个候选帧对应的所有共视关键帧打包组成一个集合(类似于下图虚线框起来的那样),以备后用。注意,这里其实有一个连续性检测条件,也就是算法认为,如果确实是一个真正的回环,那么应该是可以连续被检测到的,不可能上次检测到,这一次检测不到了。

这个理解起来还是比较难的,我通过一张向你解释一下这个过程吧!

 

 

 

上图中的三角形和圆形都是关键帧,为了以示区分我把它画成关键帧加共视关键帧组合的形式(图中虚线框起来的那样)。红色的三角形是当前的关键帧,它经过回环候选帧检测之后,获得了上图所示的四个虚线框组成的组合。连续性检测认为,如果下一次再进行回环候选帧检测,检测出来的组合和上一次检测出来的组合没有交集,那么它们就都不是可能的候选回环帧。例如,上图是本次的回环候选帧检测结果,下一次检测得到的回环候选帧组合与当前检测结果没有相交(所谓的相交,就是虚线框中这样的组合之间是否有公共的关键帧),则这两次结果里都不存在回环候选帧。
你品,你细品! 

 

(6)如果打包的关键帧集合连续被观测到三次,也就是说当前观测组成的回环帧集合,下一次又被检测到,总共被检测到三次,那么这个回环候选关键帧很可能是真正的回环帧,将其记录下来。

注意,如果连续性检测某一次失败了,那么之前累计的连续性结果也都归零,重新开始新的连续性检测。

通过以上算法之后,可能会获得很多回环候选帧,下一步就需要进行验证到底是不是真的回环!

 


2.2 验证回环候选帧

经过检测现在已经获得了很多回环候选帧,loop_detector::validate_candidates()函数主要就是用来对回环候选帧中的关键帧做检验,找到其中真正的回环帧。它的原理主要是做几何校验,验证其是否满足投影关系。

废话不多说,首先祭出流程图:

 

 

(1)首先,对候选关键帧与当前关键帧进行Sim3变换的几何校验。找到满足投影关系的回环候选关键帧。这一步对应的函数是:loop_detector::select_loop_candidate_via_Sim3()它的主要算法过程如下:



  • 通过bow加速搜索,获得候选关键帧与当前关键帧的匹配特征点;

  • 如果匹配的特征点少于20个,则说明当前候选关键帧不是一个好的候选关键帧,结束当前循环,进行下一次循环;

  • 根据搜索出来的匹配特征点对,求解当前关键帧到候选关键帧的Sim3变换,内点数量小于20,则认为不是一个好的候选帧;

  • 通过计算出来的Sim3变换,将两个关键帧对应的地图点相互向对方关键帧投影,获得更多的匹配关系;

  • 对获得的匹配关系进行优化,获得更加准确的结果;

  • 如果最终得到的内点数量大于20个,则认为当前候选关键帧是一个真正的回环帧,否则进行下一次循环,对下一个候选帧执行上述步骤。

 

(2)对上一步找到的回环帧,取出它的所有共视关键帧,以及共视关键帧赌赢的地图点。
(3)将上一步获得的地图点,全部向当前关键帧进行投影,获得更多的匹配关系,然后再重新计算一次Sim3变换,如果最终的匹配点对数量大于40个,则又进一步印证了当前检测出来的回环帧确实是一个回环帧。

经过上面的回环帧验证之后,我们获得的回环帧,也确实是一个真正的、可靠的回环帧了,下面该要执行的就是通过这个回环帧去消除掉累计误差了!

 

2.3 进行回环矫正
回环矫正对应的函数是:global_optimization_module::correct_loop()。它其中有一些细节还是比较难的,你可能需要关注一下它是怎么处理那些重复的关键帧,以及重复的地图点,怎么构建一个pose graph优化!

废话不多说,继续祭出我亲手画的流程图:

 

 

 

 

(1)首先,将我们辛辛苦苦取得的回环帧取出来,它可是咱们的宝贝啊,千呼万唤才弄出来的。
(2)如果目前系统中还正有一个回环全局优化在执行,就关闭它。毕竟又出现了新的回环帧之后,前面的累计误差可以消除,在此之前的别的回环优化都是浪费。
(3)确保mapp_module模块已经暂停。
(4)更新一下当前关键帧的连接关系,包括共视关系、权重等等。
(5)将当前关键帧以及它的共视关键帧,通过之前就算出来的Sim3变换进行位姿修正,并且也要将地图点也修正到它本该在的地方。
(6)替换当前关键帧以及共视关键帧和回环关键帧以及共视关键帧重复的地图点。

 你可以想象一下,既然是回环成功了,那肯定说明相机回到了以前出现过的地方,那么它们必然看到一样的地图点,但是由于存在累计误差,同样的地图点却出现了不同的坐标,所以是不是应该需要删除这些重复的地图点呢?如果当前帧和回环帧有共同的地图点,用回环帧中的地图点替换当前帧的地图点。而对于

 



推荐阅读
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
author-avatar
上海外国灬语大学-校友粉丝团
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有