5.2ORB-SLAM3之回环矫正

news2024/10/5 20:25:16

1.简介

在上一章《回环检测之检测是否存共视区域》已经介绍了检测共视区域的部分,接下来就是对共视区域进行回环矫正或者地图融合。

回环矫正和之前的ORBSLAM系列一致,就是消除因为长时间运动产生的位姿累计误差和尺度漂移。在ORBSLAM3中新增了多地图系统,因此出现共视区域的地方有可能出现在不同的子地图中,需要对两个子地图进行融合。本篇主要对回环矫正部分进行介绍。
在这里插入图片描述

2.回环有效性验证

在寻找共视区域步骤中初步得到了当前关键帧与候选关键帧之间的相似变换,通过相似变换可进一步初步得到当前关键帧矫正后的位姿mg2oLoopScw。在IMU模式下会通过比较矫正前和矫正后当前关键帧位姿的差异判断回环的好坏,如果矫正误差的rpy三个方向上的角度低于一定阈值,认为当前回环是有效回环:

if(mpCurrentKF->GetMap()->IsInertial()){
    // 拿到当前关键帧相对于世界坐标系的位姿
    Sophus::SE3d Twc = mpCurrentKF->GetPoseInverse().cast<double>();
    g2o::Sim3 g2oTwc(Twc.unit_quaternion(),Twc.translation(),1.0);
    
    // mg2oLoopScw是通过回环检测的Sim3计算出的回环矫正后的当前关键帧的初始位姿, Twc(g2oTwc)是当前关键帧回环矫正前的位姿.
    // g2oSww_new 可以理解为correction,即初次矫正前后当前关键帧之间的位姿差异
    g2o::Sim3 g2oSww_new = g2oTwc*mg2oLoopScw;
    // 拿到 roll ,pitch ,yaw
    Eigen::Vector3d phi = LogSO3(g2oSww_new.rotation().toRotationMatrix());
    cout << "phi = " << phi.transpose() << endl; 
    // 这里算是通过imu重力方向验证回环结果, 如果pitch或roll角度偏差稍微有一点大,则回环失败. 对yaw容忍比较大(20度)
    if (fabs(phi(0))<0.008f && fabs(phi(1))<0.008f && fabs(phi(2))<0.349f){
        // 如果是imu模式
        if(mpCurrentKF->GetMap()->IsInertial()){
            // If inertial, force only yaw
            // 如果是imu模式,强制将焊接变换的的 roll 和 pitch 设为0
            if ((mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO || mpTracker->mSensor==System::IMU_RGBD) &&
                    mpCurrentKF->GetMap()->GetIniertialBA2()){
                phi(0)=0;
                phi(1)=0;
                g2oSww_new = g2o::Sim3(ExpSO3(phi),g2oSww_new.translation(),1.0);
                mg2oLoopScw = g2oTwc.inverse()*g2oSww_new;
            }
        }
    }
}

3.回环矫正

该函数的作用是在检测到回环后,根据回环关系对当前帧及其共视关键帧进行位姿矫正。

void LoopClosing::CorrectLoop();

在回环矫正前需要暂时停止局部建图线程(暂时不需要新的关键帧)和全局BA(回环矫正后会统一再进行全局BA),为闭环矫正做准备。

3.1 根据共视关系更新当前关键帧与其它关键帧之间的连接关系

void KeyFrame::UpdateConnections(bool upParent)
  1. 首先获得该关键帧的所有MapPoint点,统计有多少关键帧与当前关键帧存在共视关系(是否观测到了同一个3D点),统计结果放在KFcounter(可以通过地图点的observations属性获得)。
  2. 只要共视点数量大于阈值th=15,就为关键帧与当前帧互相添加连接关系,边的权重为共视点的数量。
  3. 如果遍历完所有的共视关键帧,没有连接到关键帧(权重超过阈值),则对权重最大的关键帧建立连接关系

前面三步都是为了寻找满足要求的关键帧,找到有资格和当前KF建立连接关系的共视关键帧,添加关键帧的核心函数如下:

void KeyFrame::AddConnection(KeyFrame *pKF, const int &weight)
{
    {
        unique_lock<mutex> lock(mMutexConnections);
        if(!mConnectedKeyFrameWeights.count(pKF))// 如果没建立共视关系则直接建立连接关系,本质上是一个map类型的变量
            mConnectedKeyFrameWeights[pKF]=weight;
        else if(mConnectedKeyFrameWeights[pKF]!=weight)// 如果已经建立共视关系了更新权重(共视点的数量)
            mConnectedKeyFrameWeights[pKF]=weight;
        else
            return;
    }

   // 按照权重对当前关键帧的共视关键帧进行排序
   UpdateBestCovisibles();
}
  1. 更新生成树的连接,初始化该关键帧的父关键帧为共视程度最高的那个关键帧,将当前关键帧作为其子关键帧

3.2 通过位姿传播,得到Sim3优化后与当前帧相连的关键帧的位姿以及它们的地图点

  • 调整当前关键帧的共视帧位姿
  • 调整共视帧的地图点坐标
  • 对调整后的当前帧地图点进行融合和替换
  • 对调整后的当前帧共视关键帧的地图点进行融合和替换
  • 更新当前关键帧的共视关键帧的连接关系
  • 对闭环共视关键帧进行本质图优化
  • 进行全局BA优化

3.2.1 通过mg2oLoopScw(认为是准的)来进行位姿传播,得到当前关键帧的共视关键帧的世界坐标系下Sim3 位姿(还没有修正)

已知在检测共视区域时已经得到了当前关键帧调整之后的sim3位姿,则当前关键帧的共视关键帧位姿可以通过其与关键帧之间的相对运动变换得到。

共视帧sim3坐标 = sim3相对变换 * 当前帧sim3坐标

g2o::Sim3 g2oCorrectedSiw = g2oSic*mg2oLoopScw;

3.2.2 得到矫正的当前关键帧的共视关键帧位姿后,修正这些关键帧的地图点

矫正前世界系坐标矫正前位姿 -> 矫正前相机系坐标---->g2oSiw.map(P3Dw)
矫正前相机系坐标
矫正后位姿 -> 矫正后世界系坐标---->g2oCorrectedSwi.map(g2oSiw.map(P3Dw))

// 将该未校正的eigP3Dw先从世界坐标系映射到未校正的pKFi相机坐标系,然后再反映射到校正后的世界坐标系下                
Eigen::Vector3d P3Dw = pMPi->GetWorldPos().cast<double>();
Eigen::Vector3d eigCorrectedP3Dw = g2oCorrectedSwi.map(g2oSiw.map(P3Dw));

地图点矫正完成后更新地图点id,平均观测方向和平均观测距离,关键帧对应的恒速模型(因为位姿出现了变化)和关键帧链接关系(主要是权重,因为地图点出现了变化)

3.3 检查当前帧的地图点与经过闭环匹配后该帧的地图点是否存在冲突,对冲突的进行替换或填补

对于存在闭环关系的当前关键帧和回环关键帧,原则上讲对于相互匹配的特征点会对应到同一个地图点(如下图),由于累计误差和漂移的存在相互匹配的一对特征点对应的地图点可能不一致,因此需要替换。考虑到匹配的地图点是经过一系列操作后比较精确的,现有的地图点很可能有累计误差,因此对于当前关键帧如果有重复的MapPoint,则用匹配的地图点代替现有的
在这里插入图片描述

// 取出同一个索引对应的两种地图点,决定是否要替换
MapPoint* pLoopMP = mvpLoopMatchedMPs[i];// 匹配投影得到的地图点
MapPoint* pCurMP = mpCurrentKF->GetMapPoint(i);// 原来的地图点

if(pCurMP)
    // 对于重复的Mappoint使用匹配的地图点替换
    pCurMP->Replace(pLoopMP);
else
{
    // 如果当前帧没有该MapPoint,则直接添加
    mpCurrentKF->AddMapPoint(pLoopMP,i);
    pLoopMP->AddObservation(mpCurrentKF,i);
    pLoopMP->ComputeDistinctiveDescriptors();
}

3.4 将闭环相连关键帧组mvpLoopMapPoints 投影到当前关键帧组中进行匹配,融合、新增或替换当前关键帧组中KF的地图点

上一步是对当前关键帧中的地图点进行检查,这一步是对当前关键帧的共视关键帧地图点进行处理。将回环处的共视关键帧组中的Mappoint投影到当前帧组中的每一个共视关键帧上,对于匹配上的特征点对应的地图点进行类似的替换、填补和融合。

SearchAndFuse(vCorrectedSim3, vpCheckFuseMapPoint);

3.5 更新当前关键帧之间的共视相连关系,得到因闭环时MapPoints融合而新得到的连接关系

同样的,因为当前关键帧的共视关键帧地图点坐标发生了变化,因此对于关键帧之间的连接权重可能会发生变化,需要进行更新。

pKFi->UpdateConnections();

然后,从所有当前关键帧的一级共视关键帧和二级共视关键帧中删除闭环前的一级共视关键帧和二级共视关键帧,得到由闭环产生的一二级共视关键帧,为本质图优化作准备。

3.6 对闭环共视关键帧进行本质图优化

参与本质图优化的是当前关键帧和因为回环而产生的共视关键帧。

/**
    * @brief Construct a new Optimizer:: Optimize Essential Graph object
    * @param pLoopMap 当前活跃子地图
    * @param mpLoopMatchedKF 几何验证时成功匹配的候选关键帧
    * @param mpCurrent 当前关键帧
    * @param NonCorrectedSim3 当前关键帧共视关键帧矫正前的位姿
    * @param CorrectedSim3 当前关键帧共视关键帧矫正后的位姿
    * @param LoopConnections 因为回环产生的当前关键帧的共视关键帧
    * @param bFixedScale 是否固定尺度
    */
Optimizer::OptimizeEssentialGraph(pLoopMap, mpLoopMatchedKF, mpCurrentKF, NonCorrectedSim3, CorrectedSim3, LoopConnections, bFixedScale);

3.7 新建一个线程用于全局BA优化

OptimizeEssentialGraph只是优化了一些主要关键帧的位姿,这里进行全局BA可以全局优化所有位姿和MapPoints

mpThreadGBA = new thread(&LoopClosing::RunGlobalBundleAdjustment, this, pLoopMap, mpCurrentKF->mnId);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea集成maven-mvnd

maven-mvnd是什么&#xff1f; 参考文档&#xff1a; Maven加强版 — mvnd的使用测试 - 知乎 1.下载mvnd安装包 Releases apache/maven-mvnd GitHub 2.修改配置文件&#xff1a;安装包中的conf目录下的mvnd.properties文件 配置maven settings的地址&#xff1a; 注意&am…

MySQL配置主从备份

文章目录 1.什么是主从备份2. 原理3.配置主服务器4.配置从服务器4.1进入数据库&#xff0c;准备建立连接4.2开启 slave 连接&#xff0c;主备机连接成功&#xff0c;数据开始同步4.3查看有关从属服务器线程的关键参数的信息 1.什么是主从备份 主从复制简单来说就是主库把增删改…

环境搭载vscode

Windows 10 下 VS Code 配置 C 开发环境&#xff08;MinGW&#xff09; 读书读傻了哟 配置 C/C 环境   主要是配置launch.json、tasks.json这两个文件&#xff08;当然&#xff0c;还有别的.json文件&#xff0c;可有可无&#xff09;。这两个文件位于.vscode文件夹下&#…

mysql--第一天基础操作

1.创建数据库 2.查询创建数据的语句 3.使用数据库&#xff0c;查询当前默认的数据库以及使用的编码方式校验规则 4.删除数据库 5.在一张表中定义多个字段&#xff0c;要使用今天提到的所有的数据类型&#xff08;数字&#xff0c;文本&#xff0c;日期&#xff09; 查看表结构

产品方案设计高效的4大注意事项

做产品方案时&#xff0c;我们容易遭遇&#xff1a;未澄清需求、未梳理业务方案、缺少思考过程以及缺少对比方案等误区&#xff0c;往往会造成产品方案并不能完全解决用户问题&#xff0c;项目后期容易遇到需求变更等风险。 因此如何如何高效设计产品方案&#xff1f;就显得尤为…

SpringCloud入门实战(十二)-Sleuth+Zipkin分布式请求链路跟踪详解

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

Django的数据库配置、生成(创建)过程、写入数据、查看数据的学习过程记录

目录 01-配置数据库信息02-安装Python的MySQL数据库驱动程序 mysqlclient03-安装Mysql&#xff0c;并启动Mysql04-定义Django的数据库模型(定义数据表-编写models.py文件)05-按照数据的配置生成数据库(执行迁移命令)05-01-生成迁移执行文件05-02-执行数据库模型迁移 06-查看数据…

Vue.js Js引入相关

Vue.js vue.js 新增了一些语法,有一些旧的模组并没有使用"先进"的export和import语法 即 es语法进行模块化。 <script></script>但 editor.md 真的很好用. 但很抱歉,它在vue中无法使用 es6 进行导入。 所以需要使用传统的方式进行导入。 很多人会把js…

【ARM Coresight 系列文章 2.1 - ARM Coresight 组件介绍】

文章目录 1.1 Coresight 组件介绍1.1.1 Trace sources1.1.2 Trace Sinks1.1.2 Trace links 1.1 Coresight 组件介绍 图 1-1 1.1.1 Trace sources 什么是 Trace source? 在ARM Coresight技术中&#xff0c;Trace Source是指处理器中的一个组件&#xff0c;用于产生和发送跟踪数…

全球十大看黄金走势免费app软件最新名单推荐(综合版)

选择黄金走势免费app软件时&#xff0c;有几个关键因素需要考虑。首先&#xff0c;要选择可靠的软件平台&#xff0c;确保其在金融市场上拥有良好的声誉和高度的信任度。此外&#xff0c;软件应提供及时准确的市场数据&#xff0c;包括实时行情、交易量和技术指标等&#xff0c…

高速PCB布局布线规范

目录 一、容抗/感抗 1.容抗 2.感抗 二、寄生电容/分布电容/杂散电容 1.寄生电容 2.分布电容 3.杂散电容 4.寄生电容/分布电容/杂散电容对信号的影响 5.怎么减小分布电容&#xff1f; 三.寄生电感 1.什么是寄生电感&#xff1f; 2.怎么减小寄生电感&#xff1f; 四.…

ROS:节点名称重名

目录 一、前言二、rosrun设置命名空间与重映射2.1设置命名空间2.2rosrun名称重映射2.3rosrun命名空间与名称重映射叠加 三、launch文件设置命名空间与重映射四、编码设置命名空间与重映射4.1C 实现:重映射4.2C 实现:命名空间4.3Python 实现:重映射 一、前言 ROS 中创建的节点是…

Python3在Windows上设置环境变量方法

Python3在Windows上设置环境变量方法&#xff0c;在环境变量中添加Python目录&#xff1a; 在命令提示框中(cmd) : 输入 path%path%;C:\Python 按下"Enter"。 注意: C:\Python 是Python的安装目录。 也可以通过以下方式设置&#xff1a; 右键点击"计算机&q…

《深入理解计算机系统》(9)内存管理

1、物理和虚拟寻址 物理寻址 主存被组织成一个由 M 个连续的字节大小的单元组成的数组。每字节都有一个唯一的物理地址。CPU 访问内存最自然的方式就是使用物理地址&#xff0c;称为物理寻址。下图是一个物理寻址的示例&#xff0c;该示例的上下文是一个加载指令&#xff0c;它…

群载波应急广播主机的应用

一、 概述 群载波主机是专为山洪灾害预警、气象预警、地质灾害预警设计的一款智能IP群载波主机。该群载波主机可通过网络实现与控制中心通讯&#xff0c;用户可实时远程控制功放的开关机状态以及检测设备的主要信息。群载波主机主要用于接收网络信号&#xff0c;与控制中心通讯…

如何在Ubuntu系统中添加硬盘

这里写自定义目录标题 一. 安装磁盘二. 查看和新建硬盘分区2.1 查看硬盘分区2.2 创建硬盘分区 三. 分区格式化四. 分区挂载到目录五. 配置启动挂载 众所周知&#xff0c;在Linux系统中有一个著名的说法&#xff0c;即”一切皆文件“。包括磁盘在内的各种连接到系统的设备都用文…

视频怎么实现倒放?分享这3个方法给大家!

如果你曾经想过将自己喜欢的视频倒放播放&#xff0c;你可能会发现这是一项相当具有挑战性的任务。尽管许多视频播放器提供了倒放功能&#xff0c;但有时候这些功能可能不够灵活&#xff0c;甚至根本不支持倒放。在本文中&#xff0c;我们将介绍几种可帮助你倒放视频的方法。 …

减噪 低振纹|拓尔微TMI8421打印机马达驱动解决方案

打印机作为现代办公不可或缺的设备为我们的工作带来了便利&#xff0c;但也会遇到一些”鸡肋“问题&#xff0c;如产生噪音或机器发热等问题&#xff0c;不仅无法高效的打印文件资料还会给安静的办公室环境带来噪音干扰&#xff0c;打乱工作思绪...... 一台高效稳定且减噪的打…

langchain源码阅读系列(三)之Chain模块

原文首发于博客文章langchain源码阅读 本节是langchian源码阅读系列第三篇&#xff0c;下面进入Chain模块&#x1f447;&#xff1a; LLM 应用构建实践笔记 Chain链定义 链定义为对组件的一系列调用&#xff0c;也可以包括其他链&#xff0c;这种在链中将组件组合在一起的想…

Python中读取与写入文件时的编码方式

在《Python中文件的读取》与《Python中文件的写入》中提到通过文件对象调用read()函数和write()函数实现文件的读写。以上方法能够正确地取或写入英文时&#xff0c;当要读写的内容是中文时&#xff0c;则需要考虑编码方式。 1 读取已经存在的数据 1.1 创建文件 创建一个txt…