ORB-SLAM2 ---- Tracking::Relocalization函数

news2024/11/16 7:31:35

目录

1.函数作用

2.步骤

3.code 

4.函数解释 

4.1 将当前帧的描述子转化为BoW向量

4.2 用词袋找到与当前帧相似的候选关键帧

4.3  遍历所有的候选关键帧,通过词袋进行快速匹配,用匹配结果初始化PnP Solver

4.4 通过一系列操作,直到找到能够匹配上的关键帧


1.函数作用

        我们先用最近的关键帧来跟踪当前的普通帧,失败!

        根据恒速模型失败了,我们再根据参考关键帧来跟踪,失败!

        如果跟踪状态不成功,那么就只能重定位了!如果这再失败,那就重置slam系统了!

2.步骤

 * Step 1:计算当前帧特征点的词袋向量
 * Step 2:找到与当前帧相似的候选关键帧
 * Step 3:通过BoW进行匹配
 * Step 4:通过EPnP算法估计姿态
 * Step 5:通过PoseOptimization对姿态进行优化求解
 * Step 6:如果内点较少,则通过投影的方式对之前未匹配的点进行匹配,再进行优化求解

函数返回值是true或者false表示是否跟踪成功!

3.code 

bool Tracking::Relocalization()
{
    // Compute Bag of Words Vector
    // Step 1:计算当前帧特征点的词袋向量
    mCurrentFrame.ComputeBoW();

    // Relocalization is performed when tracking is lost
    // Track Lost: Query KeyFrame Database for keyframe candidates for relocalisation
    // Step 2:用词袋找到与当前帧相似的候选关键帧
    vector<KeyFrame*> vpCandidateKFs = mpKeyFrameDB->DetectRelocalizationCandidates(&mCurrentFrame);
    
    // 如果没有候选关键帧,则退出
    if(vpCandidateKFs.empty())
        return false;

    const int nKFs = vpCandidateKFs.size();

    // We perform first an ORB matching with each candidate
    // If enough matches are found we setup a PnP solver
    ORBmatcher matcher(0.75,true);
    //每个关键帧的解算器
    vector<PnPsolver*> vpPnPsolvers;
    vpPnPsolvers.resize(nKFs);

    //每个关键帧和当前帧中特征点的匹配关系
    vector<vector<MapPoint*> > vvpMapPointMatches;
    vvpMapPointMatches.resize(nKFs);
    
    //放弃某个关键帧的标记
    vector<bool> vbDiscarded;
    vbDiscarded.resize(nKFs);

    //有效的候选关键帧数目
    int nCandidates=0;

    // Step 3:遍历所有的候选关键帧,通过词袋进行快速匹配,用匹配结果初始化PnP Solver
    for(int i=0; i<nKFs; i++)
    {
        KeyFrame* pKF = vpCandidateKFs[i];
        if(pKF->isBad())
            vbDiscarded[i] = true;
        else
        {
            // 当前帧和候选关键帧用BoW进行快速匹配,匹配结果记录在vvpMapPointMatches,nmatches表示匹配的数目
            int nmatches = matcher.SearchByBoW(pKF,mCurrentFrame,vvpMapPointMatches[i]);
            // 如果和当前帧的匹配数小于15,那么只能放弃这个关键帧
            if(nmatches<15)
            {
                vbDiscarded[i] = true;
                continue;
            }
            else
            {
                // 如果匹配数目够用,用匹配结果初始化EPnPsolver
                // 为什么用EPnP? 因为计算复杂度低,精度高
                PnPsolver* pSolver = new PnPsolver(mCurrentFrame,vvpMapPointMatches[i]);
                pSolver->SetRansacParameters(
                    0.99,   //用于计算RANSAC迭代次数理论值的概率
                    10,     //最小内点数, 但是要注意在程序中实际上是min(给定最小内点数,最小集,内点数理论值),不一定使用这个
                    300,    //最大迭代次数
                    4,      //最小集(求解这个问题在一次采样中所需要采样的最少的点的个数,对于Sim3是3,EPnP是4),参与到最小内点数的确定过程中
                    0.5,    //这个是表示(最小内点数/样本总数);实际上的RANSAC正常退出的时候所需要的最小内点数其实是根据这个量来计算得到的
                    5.991); // 自由度为2的卡方检验的阈值,程序中还会根据特征点所在的图层对这个阈值进行缩放
                vpPnPsolvers[i] = pSolver;
                nCandidates++;
            }
        }
    }

    // Alternatively perform some iterations of P4P RANSAC
    // Until we found a camera pose supported by enough inliers
    // 这里的 P4P RANSAC是Epnp,每次迭代需要4个点
    // 是否已经找到相匹配的关键帧的标志
    bool bMatch = false;
    ORBmatcher matcher2(0.9,true);

    // Step 4: 通过一系列操作,直到找到能够匹配上的关键帧
    // 为什么搞这么复杂?答:是担心误闭环
    while(nCandidates>0 && !bMatch)
    {
        //遍历当前所有的候选关键帧
        for(int i=0; i<nKFs; i++)
        {
            // 忽略放弃的
            if(vbDiscarded[i])
                continue;
    
            //内点标记
            vector<bool> vbInliers;     
            
            //内点数
            int nInliers;
            
            // 表示RANSAC已经没有更多的迭代次数可用 -- 也就是说数据不够好,RANSAC也已经尽力了。。。
            bool bNoMore;

            // Step 4.1:通过EPnP算法估计姿态,迭代5次
            PnPsolver* pSolver = vpPnPsolvers[i];
            cv::Mat Tcw = pSolver->iterate(5,bNoMore,vbInliers,nInliers);

            // If Ransac reachs max. iterations discard keyframe
            // bNoMore 为true 表示已经超过了RANSAC最大迭代次数,就放弃当前关键帧
            if(bNoMore)
            {
                vbDiscarded[i]=true;
                nCandidates--;
            }

            // If a Camera Pose is computed, optimize
            if(!Tcw.empty())
            {
                //  Step 4.2:如果EPnP 计算出了位姿,对内点进行BA优化
                Tcw.copyTo(mCurrentFrame.mTcw);
                
                // EPnP 里RANSAC后的内点的集合
                set<MapPoint*> sFound;

                const int np = vbInliers.size();
                //遍历所有内点
                for(int j=0; j<np; j++)
                {
                    if(vbInliers[j])
                    {
                        mCurrentFrame.mvpMapPoints[j]=vvpMapPointMatches[i][j];
                        sFound.insert(vvpMapPointMatches[i][j]);
                    }
                    else
                        mCurrentFrame.mvpMapPoints[j]=NULL;
                }

                // 只优化位姿,不优化地图点的坐标,返回的是内点的数量
                int nGood = Optimizer::PoseOptimization(&mCurrentFrame);

                // 如果优化之后的内点数目不多,跳过了当前候选关键帧,但是却没有放弃当前帧的重定位
                if(nGood<10)
                    continue;

                // 删除外点对应的地图点
                for(int io =0; io<mCurrentFrame.N; io++)
                    if(mCurrentFrame.mvbOutlier[io])
                        mCurrentFrame.mvpMapPoints[io]=static_cast<MapPoint*>(NULL);

                // If few inliers, search by projection in a coarse window and optimize again
                // Step 4.3:如果内点较少,则通过投影的方式对之前未匹配的点进行匹配,再进行优化求解
                // 前面的匹配关系是用词袋匹配过程得到的
                if(nGood<50)
                {
                    // 通过投影的方式将关键帧中未匹配的地图点投影到当前帧中, 生成新的匹配
                    int nadditional = matcher2.SearchByProjection(
                        mCurrentFrame,          //当前帧
                        vpCandidateKFs[i],      //关键帧
                        sFound,                 //已经找到的地图点集合,不会用于PNP
                        10,                     //窗口阈值,会乘以金字塔尺度
                        100);                   //匹配的ORB描述子距离应该小于这个阈值

                    // 如果通过投影过程新增了比较多的匹配特征点对
                    if(nadditional+nGood>=50)
                    {
                        // 根据投影匹配的结果,再次采用3D-2D pnp BA优化位姿
                        nGood = Optimizer::PoseOptimization(&mCurrentFrame);

                        // If many inliers but still not enough, search by projection again in a narrower window
                        // the camera has been already optimized with many points
                        // Step 4.4:如果BA后内点数还是比较少(<50)但是还不至于太少(>30),可以挽救一下, 最后垂死挣扎 
                        // 重新执行上一步 4.3的过程,只不过使用更小的搜索窗口
                        // 这里的位姿已经使用了更多的点进行了优化,应该更准,所以使用更小的窗口搜索
                        if(nGood>30 && nGood<50)
                        {
                            // 用更小窗口、更严格的描述子阈值,重新进行投影搜索匹配
                            sFound.clear();
                            for(int ip =0; ip<mCurrentFrame.N; ip++)
                                if(mCurrentFrame.mvpMapPoints[ip])
                                    sFound.insert(mCurrentFrame.mvpMapPoints[ip]);
                            nadditional =matcher2.SearchByProjection(
                                mCurrentFrame,          //当前帧
                                vpCandidateKFs[i],      //候选的关键帧
                                sFound,                 //已经找到的地图点,不会用于PNP
                                3,                      //新的窗口阈值,会乘以金字塔尺度
                                64);                    //匹配的ORB描述子距离应该小于这个阈值

                            // Final optimization
                            // 如果成功挽救回来,匹配数目达到要求,最后BA优化一下
                            if(nGood+nadditional>=50)
                            {
                                nGood = Optimizer::PoseOptimization(&mCurrentFrame);
                                //更新地图点
                                for(int io =0; io<mCurrentFrame.N; io++)
                                    if(mCurrentFrame.mvbOutlier[io])
                                        mCurrentFrame.mvpMapPoints[io]=NULL;
                            }
                            //如果还是不能够满足就放弃了
                        }
                    }
                }

                // If the pose is supported by enough inliers stop ransacs and continue
                // 如果对于当前的候选关键帧已经有足够的内点(50个)了,那么就认为重定位成功
                if(nGood>=50)
                {
                    bMatch = true;
                    // 只要有一个候选关键帧重定位成功,就退出循环,不考虑其他候选关键帧了
                    break;
                }
            }
        }//一直运行,知道已经没有足够的关键帧,或者是已经有成功匹配上的关键帧
    }

    // 折腾了这么久还是没有匹配上,重定位失败
    if(!bMatch)
    {
        return false;
    }
    else
    {
        // 如果匹配上了,说明当前帧重定位成功了(当前帧已经有了自己的位姿)
        // 记录成功重定位帧的id,防止短时间多次重定位
        mnLastRelocFrameId = mCurrentFrame.mnId;
        return true;
    }
}

4.函数解释 

4.1 将当前帧的描述子转化为BoW向量

ORB-SLAM2 ---- Frame::ComputeBoW函数(TrackReferenceKeyFrame调用版)https://blog.csdn.net/qq_41694024/article/details/128007040?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22128007040%22%2C%22source%22%3A%22qq_41694024%22%7D        这是笔者自己写的博客!里面介绍的很清楚。

4.2 用词袋找到与当前帧相似的候选关键帧

        我们从SLAM系统中所有的关键帧找到和当前帧相似的关键帧作为候选重定位关键帧。

ORB-SLAM2 ---- KeyFrameDatabase::DetectRelocalizationCandidates函数解析https://blog.csdn.net/qq_41694024/article/details/128086208        这是笔者自己写的博客!里面介绍的很清楚。

        如果没有和当前帧相似的关键帧,则退出。

4.3  遍历所有的候选关键帧,通过词袋进行快速匹配,用匹配结果初始化PnP Solver

        我们在进行这步之前先了解一下EPnP算法:它是一个成熟的算法,具体原理不用我们知道,我们只要知道怎么调用它就可以了。

        它的输入为:

        ①n个世界坐标系下的3D点,论文中称为3D参考点

        ②这个3D点投影在图像上的2D坐标

        ③相机内参矩阵 ,包括焦距和主点

        它的输出为:

        ①相机的位姿R,t
        我们看看代码中它的临时变量:

        ①vpPnPsolvers 每个关键帧的解算器

        ②vector<vector<MapPoint*> > vvpMapPointMatches 每个关键帧和当前帧中特征点的匹配关系

        ③vbDiscarded 放弃某个关键帧的标记

        进入正题了:

        我们首先遍历所有待匹配的关键帧。对于一帧来说:

        pKF获取当前候选关键帧,我们用当前待重跟踪关键帧mCurrentFramepKF利用BoW进行粗略的特征点匹配,匹配结果存放在vvpMapPointMatches[i]中。如果匹配结果小于15个特征点,则我们放弃此帧,置vbDiscarded为true。我们用mCurrentFramevvpMapPointMatches[i]初始化PnPsolver,将PnP求解器放在vpPnPsolvers中,并将有效候选关键帧个数nCandidates+1。

        结束这个循环后,我们记录了候选追踪关键帧的有效个数nCandidates,并有了nCandidates个PnPSovler。

4.4 通过一系列操作,直到找到能够匹配上的关键帧

        我们先了解这段代码中的临时变量:

        ①bMatch 是否已经找到相匹配的关键帧的标志

        ②vbInliers 内点标记

        ③nInliers 内点数

        ④bNoMore 表示RANSAC已经没有更多的迭代次数可用 -- 也就是说数据不够好,RANSAC也已经尽力了。。。

        ⑤sFound  EPnP里RANSAC后的内点的集合

        ⑥np 内点的数量

        让我们看看这段代码的流程吧!

        遍历当前所有的候选关键帧nKFs,针对于nKFs[i]:

        ①若在上步标记为vbDiscarded则放弃此帧。

        ②通过EPnP算法估计姿态,迭代5次,得到Tcw(相机位姿)。若RANSAC已经没有更多的迭代次数可用,置此帧为vbDiscarded[i]=true,候选追踪关键帧的有效个数nCandidates--

        ③若相机位姿Tcw计算出来了,对内点进行BA优化。遍历所有内点,设置该关键帧mCurrentFrame可以观测到的地图点,并将此地图点放到sFound集合中。只优化位姿,不优化地图点的坐标,返回的是内点的数量nGood,如果优化后的内点数目不多(小于10),则跳过当前候选关键帧,但是却没有放弃当前帧的重定位。

        ④如果优化后的内点数目足够多,则删除外点对应的地图点。

        ⑤如果内点较少(10<x<50),则通过投影的方式对之前未匹配的点进行匹配,再进行优化求解,由于前面的匹配关系是用词袋匹配BoW过程得到的,我们通过投影的方式将关键帧中未匹配的地图点投影到当前帧中,生成新的匹配。如果通过投影过程新增了比较多的匹配特征点对(大于50),根据投影匹配的结果,再次采用3D-2D pnp BA优化位姿,如果BA后内点数还是比较少(<50)但是还不至于太少(>30),可以挽救一下,最后垂死挣扎。如果对于当前的候选关键帧已经有足够的内点(50个)了,那么就认为重定位成功。

        若失败重新执行上一步的过程,只不过使用更小的搜索窗口。如果成功挽救回来,匹配数目达到要求,最后BA优化一下。

        折腾了这么久还是没有匹配上,重定位失败。如果匹配上了,说明当前帧重定位成功了(当前帧已经有了自己的位姿),记录成功重定位帧的idmnLastRelocFrameId,防止短时间多次重定位。

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

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

相关文章

项目成本管理软件能为你做什么?

成本管理与企业在当前以及未来项目中取得成功的能力密切相关。投资可靠的项目成本管理软件可以带来巨大的节约。一个好的成本管理解决方案不会把它当作一个孤立的功能&#xff0c;而是把它作为项目和投资组合绩效的关键因素加以利用&#xff0c;并在各个项目之间进行数据关联。…

金山表单结果如何自动通知企业微信

金山表单内置了丰富的模版&#xff0c;从表单、接龙、问卷、投票&#xff0c;可以满足你各种表单数据数据收集的需求。但是很多用户经常也会有一个痛点&#xff0c;通过金山表单收集的信息&#xff0c;如何才能实时通知企业微信/钉钉/飞书呢&#xff1f; 比如防疫登记、安全复工…

1.2 异步相关概念:深入了解

1.同步(Synchronous) VS 异步(Asynchronous) 所谓同步&#xff0c;可以理解为每当系统执行完一段代码或者函数后&#xff0c;系统将一直等待该段代码或函数返回的值或消息&#xff0c;直到系统接收到返回的值或消息后才继续往下执行下一段代码或者函数&#xff0c;在等待返回值…

汽车行业大趋势——软件定义汽车

文章目录 前言一、软件定义汽车的驱动力二、SOA架构在软件定义汽车中的作用三、车载软件架构&#xff08;内核、中间件、应用层&#xff09;长期趋势总结前言 最早在2007年4月份的IEEE会议中提出“软件定义汽车”&#xff08;SDV&#xff0c;Software Define Vehicle&#xff…

flask-admin菜鸟学习笔记

近期在工作中需要维护若干个信息表&#xff0c;在这个过程中需要经常对表格进行操作、交叉操作、各个表格同步&#xff0c;和某平台信息同步。。。在此过程中需要建立一个“隐性”的流程&#xff0c;要第一步同步A和B&#xff0c;再同步B和C&#xff0c;。。。而检索更是痛苦&a…

Python 中 4 个高效的技巧!

今天我想和大家分享 4 个省时的 Python 技巧&#xff0c;可以节省 10~20% 的 Python 执行时间。 反转列表 Python 中通常有两种反转列表的方法&#xff1a;切片或 reverse() 函数调用。这两种方法都可以反转列表&#xff0c;但需要注意的是内置函数 reverse() 会更改原始列表…

最近面了12个人,发现这个测试基础题都答不上来...

一般面试我都会问一两道很基础的题目&#xff0c;来考察候选人的“地基”是否扎实&#xff0c;有些是操作系统层面的&#xff0c;有些是 python语言方面的&#xff0c;还有些… 深耕IT行业多年&#xff0c;我们发现&#xff0c;对于一个程序员而言&#xff0c;能去到一线互联网…

托管海外服务器有哪些要求?

对于成长中的企业来说&#xff0c;需要使用自己的Web 服务器来有效地控制网站运营安全以及关键数据管理。但与此同时&#xff0c;创建一个高效的数据中心需要巨大的基础设施成本&#xff0c;24小时现场经验丰富的技术和管理支持团队来管理所有数据中心需求、服务器和数据等等。…

详细介绍NLP关键词提取算法

PageRank 算法 基于词图模型的关键词提取算法主要有 PageRank 和 TextRank。 PageRank 是 TextRank 算法的思想基础&#xff0c;TextRank 是 PageRank 在文本上的应用。 来源&#xff1a; Google 创始人拉里佩奇和谢尔盖布林于 1997 年构建早期的搜索系统原型时提出的链接分析…

uniCloud云开发及一键生成代码

uniapp云开发&#xff08;云数据库&#xff09; 准备工作 一、新建项目选择云开发 关联云函数 在cloudfouctions右键点击创建云函数 在base下的index.js中写入 use strict; exports.main async (event, context) > {//event为客户端上传的参数console.log(event : , ev…

PyG (PyTorch Geometric) 异质图神经网络HGNN

诸神缄默不语-个人CSDN博文目录 PyTorch Geometric (PyG) 包文档与官方代码示例学习笔记&#xff08;持续更新ing…&#xff09; 本文介绍使用PyG实现异质图神经网络&#xff08;HGNN&#xff09;的相关操作。 本文主要参考PyG文档异质图部分&#xff1a;Heterogeneous Graph…

Matplotlib可视化50图:气泡图(2)

导读 本文[1]将学习如何使用 Python 的 Matplotlib 库通过示例绘制气泡图。 简介 气泡图是散点图的改进版本。在散点图中&#xff0c;有两个维度 x 和 y。在气泡图中&#xff0c;存在三个维度 x、y 和 z。其中第三维 z 表示权重。这样&#xff0c;气泡图比二维散点图在视觉上提…

C语言 编译和链接

C语言 编译和链接引言翻译环境运行环境声明一、预定义符号二、#define 符号1. #define 定义标识符2. #define 定义宏宏带来的陷阱宏的两个特殊的使用场景① 使用 #&#xff0c;把一个宏参数变成对应的字符串② 使用 ##&#xff0c;将两个宏参数合并成一个符号宏参数的使用3. #d…

某度旋转验证码

案例地址:aHR0cHM6Ly96aXl1YW4uYmFpZHUuY29tL2xpbmtzdWJtaXQvdXJs 运行结果截图: 抓包分析, 整个流程如下 第一个包,提交参数是ak和时间戳(ak是定值) 返回的参数中,as和tk后面都会用到 点击提交,会弹出验证码,第二个包,请求参数的tk是第一个包返回的, ak同第一…

总算给女盆友讲明白了,如何使用stream流的filter()操作

一、引言 在上一篇文章中《这么简单&#xff0c;还不会使用java8 stream流的map()方法吗&#xff1f;》分享了使用stream的map()方法&#xff0c;不知道小伙伴还有印象吗&#xff0c;先来回顾下要点&#xff0c;map()方法是把一个流中的元素T转换为另外一个新流中的元素R&…

身边的那些信审人员都去哪了?

最近几天看到朋友圈很多信用卡审核中心的老同事&#xff08;老同学&#xff09;在秀到深圳9周年&#xff0c;在2013年的时候&#xff0c;大家都是一起通过校招来到了XX银行信用卡中心的信贷审批部&#xff0c;成为了信用卡人工审核员&#xff0c;那时候入职信贷审批部近百人&am…

这个算法不一般,控制拥塞有一手!

数字时代下&#xff0c;远程办公、线上协同成为刚需&#xff0c;直播带货等业务模式盛行&#xff0c;数据流量爆炸式增长&#xff0c;低时延、高流畅的网络传输诉求给数据中心的处理能力带来了极大挑战。RDMA作为一种新型网络传输技术&#xff0c;可大幅提升网络传输实效&#…

HTML期末学生大作业-节日网页作业html+css+javascript

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

详细总结快慢指针的在链表中的常见题型

常见快慢指针题型1.找出链表中间结点2.找到倒数第K个结点3.判断环形链表4.找到环形链表的入口&#xff08;进阶&#xff09;5.相交链表1.找出链表中间结点 双指针进阶解法 1.定义两个指针&#xff0c;一个快指针&#xff0c;一个慢指针。 2.快指着一次走两步&#xff0c;慢指针…

基于冲突搜索的多机器人路径规划(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 随着自动化物流系统的发展,移动机器人作为运输系统的关键工具,各方面的技术得到了快速的发展。多移动机器人路径规划是机器人导…