orbslam2代码解读(4):loopclosing回环检测线程

书接上回,介绍完了局部建图线程,局部建图线程在进行局部BA之后,也会将新的关键帧mpLoopCloser放进回环线程的mlpLoopKeyFrameQueue容器中。所以这时候回环检测线程就根据这个新的关键帧来进行回环检测的操作。

回环检测的主要程序

// 线程主循环
    while(1)
    {
        // Loopclosing中的关键帧是LocalMapping发送过来的,LocalMapping是Tracking中发过来的
        // 在LocalMapping中通过 InsertKeyFrame 将关键帧插入闭环检测队列mlpLoopKeyFrameQueue
        // Step 1 查看闭环检测队列mlpLoopKeyFrameQueue中有没有关键帧进来
        if(CheckNewKeyFrames())
        {
            // Detect loop candidates and check covisibility consistency
            if(DetectLoop())
            {
               // Compute similarity transformation [sR|t]
               // In the stereo/RGBD case s=1
               if(ComputeSim3())
               {
                   // Perform loop fusion and pose graph optimization
                   CorrectLoop();
               }
            }
        }
        // 查看是否有外部线程请求复位当前线程
        ResetIfRequested();
        // 查看外部线程是否有终止当前线程的请求,如果有的话就跳出这个线程的主函数的主循环
        if(CheckFinish())
            break;
        //usleep(5000);
		std::this_thread::sleep_for(std::chrono::milliseconds(5));
	}
    // 运行到这里说明有外部线程请求终止当前线程,在这个函数中执行终止当前线程的一些操作
    SetFinish();

cvlife的注释版删掉一些冗余的解释后,主要就是这么一个流程,后续就逐个流程介绍。

step1,DetectLoop()

如果mlpLoopKeyFrameQueue容器中有新的关键帧,就会执行DetectLoop()函数。
这个函数的过程如下:

  1. 从队列中取出新的关键帧,作为当前检测闭环关键帧,且设置当前关键帧不要在优化的过程中被删除(mpCurrentKF->SetNotErase()
  2. 如果距离上次闭环没多久(小于10帧),或者map中关键帧总共还没有10帧,则不进行闭环检测
  3. 遍历当前回环关键帧所有连接关键帧(一级相邻关键帧),计算当前关键帧与每个共视关键帧的bow相似度得分,并得到最低得分minScore。意义:本质就是设置一个阈值,意思就是如果回环候选帧的相似度得分都比一级相邻关键帧的得分低,那么这些回环候选帧就不可信
  4. 在所有关键帧中找出相似度得分大于minScore的闭环候选帧(注意候选帧应该选不和当前帧相连的关键帧)。
    在这里插入图片描述
  5. 在候选帧中检测具有连续性的候选帧。其实就是当一个候选关键帧符合相似度要求的时候,先不急着确立回环关系,仅仅当候选关键帧连续与三个(代码上写的是3个,也可以改)新的关键帧都能连接上的时候,才确立回环关系。
    在这里插入图片描述
  6. 将当前新的关键帧放到关键帧数据库mpKeyFrameDB中,如果成功检测到回环就返回true,否则返回false。

step2,ComputeSim3()

如果上一个函数检测到回环了,那么就需要求出回环两个关键帧之间的匹配关系。如果是单目情况就需要考虑尺度漂移的问题,所以计算Sim3变换;如果是双目或者rgbd,这个尺度直接就设置成1。
这个函数的过程如下:

  1. 遍历闭环候选帧集,初步筛选出与当前关键帧的匹配特征点数大于20的候选帧集合(经典使用词袋找到两帧的匹配点对SearchByBoW),并为每一个候选帧构造一个Sim3Solver。
  2. 对每一个候选帧用Sim3Solver迭代匹配,直到有一个候选帧匹配成功,或者全部失败。匹配成功的过程:①先是通过Sim3Solver进行迭代求解,如果能够得到一个粗略的Sim3变换,那么就需要进一步优化这个粗略的Sim3变换。②通过matcher.SearchBySim3找到两帧更多的匹配点对(只有互相投影都成功匹配的才认为是可靠的匹配)。③用新的匹配点对通过g2o来优化Sim3(只优化Sim3,不优化地图点)。
    在这里插入图片描述在这里插入图片描述
           SearchBySim3             g2o的Sim3优化
  3. 如果Sim3优化成功,取出闭环候选的关键帧及其共视关键帧,以及这些共视关键帧的地图点。
  4. 根据Sim3变换,将闭环候选关键帧及其连接关键帧的所有地图点投影到当前关键帧进行投影匹配(matcher.SearchByProjection),如果匹配点对超过40个,则说明这个闭环是稳定且成功的,否则说明这个闭环是失败的,删除相关信息。
    在这里插入图片描述
          闭环候选帧往当前帧的投影过程

step3,CorrectLoop()

这个函数就是根据上一个函数计算出来的Sim3变换,调整与当前帧相连的关键帧位姿以及这些关键帧观测到的地图点位置。(简单来说就是位姿和尺度的联合矫正)
这个函数的过程如下:

  1. 结束局部地图线程、全局BA,为闭环矫正做准备
  2. 根据共视关系更新当前关键帧与其它关键帧之间的连接关系(因为之前闭环检测、计算Sim3中增加了该关键帧的地图点,所以需要更新)
  3. 得到Sim3优化后,当前帧与世界坐标系之间的Sim变换在ComputeSim3函数中已经确定并优化,通过相对位姿关系,可以确定这些相连的关键帧与世界坐标系之间的Sim3变换,进行后续的矫正。
    在这里插入图片描述
  4. 得到矫正的当前关键帧的共视关键帧位姿后,修正这些共视关键帧的地图点。修正的具体流程:①将当前kf及其共视kf的地图点进行遍历,先将地图点从世界坐标系根据初始的相机位姿(g2oSiw,这个相机位姿因为累计误差会有尺度漂移,所以实际上这个位姿是有尺度的)变换到相机坐标系。②再通过矫正后的相机位姿(g2oCorrectedSwi,这个Sim的尺度其实就是为了抵消尺度漂移而产生的尺度)将相机坐标系的地图点矫正回到世界坐标系当中。③至此这些地图点的尺度一致了。
    在这里插入图片描述
  5. 根据(当前关键帧和其相连的关键帧)与世界坐标系之间的Sim3变换,将Sim3转换成SE3,并且更新关键帧的位姿。这时(当前关键帧和其相连的关键帧)与产生时间较早的闭环候选关键帧的位姿也尺度一致了。具体Sim3转换SE3的代码:
    // 调用toRotationMatrix 可以自动归一化旋转矩阵
    Eigen::Matrix3d eigR = g2oCorrectedSiw.rotation().toRotationMatrix(); 
    Eigen::Vector3d eigt = g2oCorrectedSiw.translation();                  
    double s = g2oCorrectedSiw.scale();
    // 平移向量中包含有尺度信息,还需要用尺度归一化
    eigt *=(1./s); 

最终当前关键帧和其相连的关键帧直接设置为校正后的SE3,且更新关键帧之间的连接关系。

  1. 检查当前帧的地图点与经过闭环匹配后该帧的地图点是否存在冲突,对冲突的进行替换或填补。本质上更相信回环过程中得到的地图点(时间靠前,累计误差小),而当前关键帧原来的地图点时间靠后可能有累计误差。

  2. 闭环候选帧的相连关键帧组投影到当前关键帧的相连关键帧组中进行匹配,新增或替换当前关键帧的相连关键帧组中的地图点。跟第6步本质上是一样的,更相信闭环候选帧的相连关键帧组中的地图点,误差更小。第6、7步都是做关键帧之间地图点的更新融合,因为前面经过了尺度对齐,所以地图点肯定会有冲突。具体两个相连组也可以看下图:
    在这里插入图片描述

  3. 更新当前关键帧组之间的两级共视相连关系,得到因闭环时地图点融合而新得到的连接关系。这个步骤最大的作用就是得到因为闭环地图点调整而新生成的连接关系,后续本质图优化的时候需要加的边。

  4. 进行本质图优化。仅仅优化本质图中所有关键帧的位姿(Sim3位姿优化),不优化地图点。共视图比较稠密,本质图比共视图更稀疏,这是因为本质图的作用是用在闭环矫正时,用相似变换来矫正尺度漂移,把闭环误差均摊在本质图中。本质图中节点也是所有关键帧,但是连接边更少,只保留了联系紧密的边来使得结果更精确。本质图中的边包含:①生成树连接关系。②形成闭环的连接关系,闭环后地图点变动后新增加的连接关系。③当前帧与闭环匹配帧之间的连接关系(这里面也包括了当前遍历到的这个关键帧之前曾经存在过的回环边)。④共视程度超过100的关键帧也作为边进行优化。
    在这里插入图片描述在这里插入图片描述
             本质图                 生成树

最后就是将优化后的位姿更新到关键帧中,并且地图点根据参考帧优化前后的相对关系调整自己的位置,然后更新地图点的平均观测方向和深度。

  1. 本质图将位姿优化完后,新建一个线程用于全局BA优化。因为在全局BA之前,数据已经做了很多处理,通过本质图对相机位姿进行了优化,并且地图点也根据参考帧优化前后的相对关系调整自己的位置,所以这个全局BA的代码还是比较容易懂的。需要注意的点是:①这个全局BA除了初始关键帧是固定的,其他关键帧和地图点都是需要优化的量。②相机位姿是SE3优化,因为在本质图优化的时候是Sim3优化,并且做了尺度归一化了,所以全局BA用的是SE3优化。

总结

完结撒花!!!这一系列的记录文章,主要是把orbslam2中一些主要的函数实现过程弄清楚,并且明白整个orbslam2的运行流程。所以不会对函数中具体的代码做详细的讲解(这部分完全可以去看代码注释),而我们要做的是明白orbslam2的流程和处理思路。在学习orbslam2的时候真的学习到了很多,之前看完视觉slam14讲,里面的内容确实很多很详细可以作为一个教科书,但是更多的还是得学优秀的开源项目,这样才能对slam整个框架有更深的理解。感谢orbslam2的讲解课程和注解代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/699502.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

websocket php workerman 服务器nginx配置wss协议

首先 Nginx的版本要高,尽量用当前最新稳定版本。 其次 WSS协议,是在HTTPS协议的基础上,进行协议升级,进行通讯的,所以先要保证你有一个 HTTPS正常的WEB站点。 所以,通过Nginx -V 请保证 一定有 --with-ht…

Spring-Security(二)OAuth2认证详解(持续更新)

Spring Security & Oauth2系列: Spring Security(一) 源码分析及认证流程 Spring Security(二)OAuth2认证详解及自定义异常处理 文章目录 1、OAuth2.0 简介1.1 OAuth2.0 相关名词解释1.2 四种授权模式 1.3 、OAu…

2023 hnust 湖科大 嵌入式 实验报告+代码及复习资料等

2023 hnust 湖科大 嵌入式 实验报告代码及复习资料等 目录 流水灯 1 8位数码管动态扫描 3 按键输入 5 温度与关照 7 看门狗 9 内容 报告 代码 下载链接 https://pan.baidu.com/s/1LIN8rm42yrukXliI3XyZ1g?pwd1111

Java高阶数据结构-----并查集(详解)

目录 🧐一.并查集的基本概念&实例: 🤪二.并查集代码: 😂三:并查集的一些习题: A.省份数量 B.等式方程的可满足性 🧐一.并查集的基本概念&实例: 并查集概念&…

vue操作蓝牙教程

项目背景 想在VUE中使用蓝牙功能,百度了好久也尝试了好多都没法实现。 概念讲价 如果要在浏览器中使用蓝牙,去搜索关键字【navigator.bluetooth】,搜索后发现这根本不是想要的结果。 解决方法 去搜索关键字【uniappbluetoothvue】&#x…

Web前端三大主流框架简介与优缺点对比分析

随着互联网的快速发展,Web前端开发技术不断进步,各种前端框架应运而生,极大地提高了开发效率和用户体验。在众多框架中,React、Vue.js 和 Angular 是目前最受欢迎的三大主流框架。本文将对它们进行详细介绍,并对它们的…

110.网络游戏逆向分析与漏洞攻防-装备系统数据分析-装备与技能描述信息的处理

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了 内容…

网络数据库后端相关面试题(其三)

18, 传输控制协议tcp和用户数据报协议udp有哪些区别 第一,tcp是面向字节流的,基本的传输单位是tcp报文段;而udp是面向报文的,基本传输单位是用户数据报。 第二, tcp注重安全可靠性,连接双方在…

Linux网络 - HTTP协议

文章目录 前言一、HTTP协议1.urlurl特殊字符 requestrespond 总结 前言 上一章内容我们讲了在应用层制定了我们自己自定义的协议、序列化和反序列化。 协议的制定相对来讲还是比较麻烦的,不过既然应用层的协议制定是必要的,那么肯定已经有许多计算机大佬…

内存分配器性能优化

背景 在之前我们提到采用自定义的内存分配器来解决防止频繁 make 导致的 gc 问题。gc 问题本质上是 CPU 消耗,而内存分配器本身如果产生了大量的 CPU 消耗那就得不偿失。经过测试初代内存分配器实现过于简单,产生了很多 CPU 消耗,因此必须优…

果汁机锂电池充电,5V升压12.7V 升压恒压芯片SL1571B

在现代化的日常生活中,果汁机已经逐渐成为了许多家庭厨房的必备电器。随着科技的不断进步,果汁机的性能也在不断提升,其中锂电池的应用更是为果汁机带来了前所未有的便利。而5V升压12.7V升压恒压芯片SL1571B,作为果汁机锂电池充电…

skywalking9.4 链路追踪

下载,很慢很慢很慢!!!! jdk 使用jdk17 skywalking-apm 9.4 java-agent 9.0 idea 本地开发配置 第1行配置按实际来; 第2行自定义,一般和微服务名称相同; 第3行ip写安装的机器ip,端…

QQ音乐绿钻API接口:解锁更多音乐可能性

在我们日常生活中,音乐是不可或缺的一部分。无论是在上班途中,还是在健身房锻炼时,我们都可以通过听音乐来放松自己。然而,在现如今的音乐市场中,有时候我们会觉得收听的歌曲有限,想要尝试更多不同的音乐类…

量产导入 | DFT和ATE概述

什么是DFT DFT(Design for Test),即可测性设计。 一切为了芯片流片后测试所加入的逻辑设计,都叫DFT。 DFT只是为了测试芯片制造过程中有没有缺陷,而不是用来验证芯片功能的,芯片功能的完善应该应该是在芯片开发过程用先进验证方法学去做的。 芯片制造过程相当复杂,工艺缺陷…

降价潮背后:大模型落地门槛真的降了吗?

“比起价格门槛,AI大模型的应用门槛,更难跨越。” 大模型争相降价下,AI应用的门槛真的降低了吗? 答案还真不一定。因为除了价格门槛,AI大模型还有应用门槛。甚至,后者比前者更具挑战性。 B端业务场景向来…

3D感知视觉表示与模型分析:深入探究视觉基础模型的三维意识

在深度学习与大规模预训练的推动下,视觉基础模型展现出了令人印象深刻的泛化能力。这些模型不仅能够对任意图像进行分类、分割和生成,而且它们的中间表示对于其他视觉任务,如检测和分割,同样具有强大的零样本能力。然而&#xff0…

Java集合的组内平均值怎么计算

哈喽,大家好,我是木头左! 在Java中,经常需要对集合进行各种操作,其中之一就是计算集合的组内平均值。本文将介绍如何使用Java集合来计算组内平均值,并提供一些示例代码和实用技巧。 1. 使用Java 8 Stream A…

MMdeploy在cuda+tensorrt下的配置和编译

MMdeploy在cudatensorrt下的配置和编译 Python安装配置MMdeploy配置openmmlab系列从工程安装mmdeploy MMdeploy_runtime以及demo编译安装量化编译runtime和demo Python安装配置MMdeploy 配置openmmlab系列 pip install -U openmim如果mim命令遭遇故障,或者安装失败…

龙迅LT9211D MIPIDSI/CSI桥接到2 PORT LVDS,支持 3840x2160 30Hz分辨率

龙迅LT9211D描述: LT9211D是一款高性能的MIPI DSI/CSI-2到双端口LVDS转换器。LT9211D反序列化输入的MIPI视频数据,解码数据包,并将格式化的视频数据流转换为AP和移动显示面板或摄像机之间的LVDS发射机输出。LT9211D支持最大12.5 dB输入均衡和…

boost asio异步服务器(3)增加发送队列实现全双工通信

增加发送节点 构造发送节点,管理发送数据。发送节点的类如下。 这个发送节点用于保证发送和接收数据的有效性。 增加发送队列 前边实现的是一个简单的echo服务器,也就是服务器将收到的内容发送给对应的客户端。但是在实际的服务器设计中,服务…