ORB-SLAM2从理论到代码实现(六):Tracking程序详解(上)

1. Tracking框架

Tracking线程流程框图:

各流程对应的主要函数

2. Tracking整体流程图

上面这张图把Tracking.cc讲的特别明白。

tracking线程在获取图像数据后,会传给函数GrabImageStereo、GrabImageRGBD或GrabImageMonocular进行预处理,这里以GrabImageMonocular为例。

GrabImageMonocular(const cv::Mat &im, const double &timestamp)

函数功能

1. 将图像转为mImGray并初始化mCurrentFrame;

2. 进行tracking过程,输出世界坐标系到该帧相机坐标系的变换矩阵

im输入图像
timestamp时间戳
cv::Mat Tracking::GrabImageMonocular(const cv::Mat &im, const double &timestamp){mImGray = im;//读取图像// 步骤1:将RGB或RGBA图像转为灰度图像if(mImGray.channels()==3){if(mbRGB)cvtColor(mImGray,mImGray,CV_RGB2GRAY);elsecvtColor(mImGray,mImGray,CV_BGR2GRAY);}else if(mImGray.channels()==4){if(mbRGB)cvtColor(mImGray,mImGray,CV_RGBA2GRAY);elsecvtColor(mImGray,mImGray,CV_BGRA2GRAY);}// 步骤2:构造Frameif(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET)// 没有成功初始化的前一个状态就是NO_IMAGES_YETmCurrentFrame = Frame(mImGray,timestamp,mpIniORBextractor,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);elsemCurrentFrame = Frame(mImGray,timestamp,mpORBextractorLeft,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);// 步骤3:跟踪Track();return mCurrentFrame.mTcw.clone();
}

数据,流到Track(),由于代码超长,分几段粘贴注释。 

void Tracking::Track()

步骤

1. 判断tracking状态:如果是未初始化(NOT_INITIALIZED),则对单目和非单目分别执行MonocularInitialization()、StereoInitialization()进行初始化,并更新地图视图。

2.对于初始化成功的,接下来进行跟踪ORB-SLAM中关于跟踪状态有两种选择(由mbOnlyTracking判断)

(1)只进行跟踪不建图

(2)同时跟踪和建图:

初始化之后ORB-SLAM有三种跟踪模型可供选择

a.TrackWithMotionModel(); 运动模型:根据运动模型估计当前帧位姿——根据匀速运动模型对上一帧的地图点进行跟踪——优化位姿。

b.TrackReferenceKeyFrame(); 关键帧模型:BoW搜索当前帧与参考帧的匹配点——将上一帧的位姿作为当前帧的初始值——通过优化3D-2D的重投影误差来获得位姿。

c.Relocalization();重定位模型:计算当前帧的BoW——检测满足重定位条件的候选帧——通过BoW搜索当前帧与候选帧的匹配点——大于15个点就进行PnP位姿估计——优化。

这三个模型的选择方法:

首先假设相机恒速(即Rt和上一帧相同),然后计算匹配点数(如果匹配足够多则认为跟踪成功),如果匹配点数目较少,说明恒速模型失效,则选择参考帧模型(即特征匹配,PnP求解),如果参考帧模型同样不能进行跟踪,说明两帧键没有相关性,这时需要进行重定位,即和已经产生的关键帧中进行匹配(看看是否到了之前已经到过的地方)确定相机位姿,如果重定位仍然不能成功,则说明跟踪彻底丢失,要么等待相机回转,要不进行重置。

2.1. 初始化部分

void Tracking::Track()
{// track包含两部分:估计运动、跟踪局部地图// mState为tracking的状态// SYSTME_NOT_READY, NO_IMAGE_YET, NOT_INITIALIZED, OK, LOST// 如果图像复位过、或者第一次运行,则为NO_IMAGE_YET状态if(mState==NO_IMAGES_YET){mState = NOT_INITIALIZED;}// mLastProcessedState存储了Tracking最新的状态,用于FrameDrawer中的绘制mLastProcessedState=mState;// Get Map Mutex -> Map cannot be changedunique_lock<mutex> lock(mpMap->mMutexMapUpdate);// 步骤1:初始化if(mState==NOT_INITIALIZED)//判断是否初始化{if(mSensor==System::STEREO || mSensor==System::RGBD)//双目或深度相机StereoInitialization();//双目初始化elseMonocularInitialization();//单目初始化mpFrameDrawer->Update(this);if(mState!=OK)return;}
}

2.3. 跟踪

2.3.1. 跟踪上一帧或者参考帧或者重定位

else// 步骤2:跟踪{// System is initialized. Track Frame.系统完成初始化,跟踪帧// bOK为临时变量,用于表示每个函数是否执行成功bool bOK;// Initial camera pose estimation using motion model or relocalization (if tracking is lost)运用运动模型或重定位初始化相机位姿估计// 在viewer中有个开关menuLocalizationMode,有它控制是否ActivateLocalizationMode,并最终管控mbOnlyTracking// mbOnlyTracking等于false表示正常VO模式(有地图更新),mbOnlyTracking等于true表示用户手动选择定位模式if(!mbOnlyTracking){// Local Mapping is activated. This is the normal behaviour, unless// you explicitly activate the "only tracking" mode.// 正常初始化成功if(mState==OK){// Local Mapping might have changed some MapPoints tracked in last frame// 检查并更新上一帧被替换的MapPoints// 更新Fuse函数和SearchAndFuse函数替换的MapPointsCheckReplacedInLastFrame();// 步骤2.1:跟踪上一帧或者参考帧或者重定位// 运动模型是空的或刚完成重定位// mCurrentFrame.mnId<mnLastRelocFrameId+2表示刚重定位少于两帧// 应该只要mVelocity不为空,就优先选择TrackWithMotionModel// mnLastRelocFrameId上一次重定位的那一帧if(mVelocity.empty() || mCurrentFrame.mnId<mnLastRelocFrameId+2){// 将上一帧的位姿作为当前帧的初始位姿// 通过BoW的方式在参考帧中找当前帧特征点的匹配点// 优化每个特征点都对应3D点重投影误差即可得到位姿bOK = TrackReferenceKeyFrame();//跟踪参考帧}else{// 根据恒速模型设定当前帧的初始位姿// 通过投影的方式在参考帧中找当前帧特征点的匹配点// 优化每个特征点所对应3D点的投影误差即可得到位姿bOK = TrackWithMotionModel();//根据固定运动速度模型预测当前帧的位姿if(!bOK)// TrackReferenceKeyFrame是跟踪参考帧,不能根据固定运动速度模型预测当前帧的位姿态,通过bow加速匹配(SearchByBow)// 最后通过优化得到优化后的位姿bOK = TrackReferenceKeyFrame();}}else{// BOW搜索,PnP求解位姿bOK = Relocalization()//重定位成功与否}}else{// Localization Mode: Local Mapping is deactivated// 只进行跟踪tracking,局部地图不工作// 步骤2.1:跟踪上一帧或者参考帧或者重定位// tracking跟丢了if(mState==LOST){                bOK = Relocalization();//判断重定位成功与否标志}else{// mbVO是mbOnlyTracking为true时的才有的一个变量// mbVO为false表示此帧匹配了很多的MapPoints,跟踪很正常,// mbVO为true表明此帧匹配了很少的MapPoints,少于10个,要跪的节奏if(!mbVO){// In last frame we tracked enough MapPoints in the map// mbVO为0则表明此帧匹配了很多的3D map点,非常好if(!mVelocity.empty()){bOK = TrackWithMotionModel();}else{bOK = TrackReferenceKeyFrame();}}else{// In last frame we tracked mainly "visual odometry" points.在上一帧我们主要跟踪视觉里程计点// We compute two camera poses, one from motion model and one doing relocalization.我们由运动模型和重定位计算相机位姿// If relocalization is sucessfull we choose that solution, otherwise we retain// the "visual odometry" solution.// mbVO为1,则表明此帧匹配了很少的3D map点,少于10个,要跪的节奏,既做跟踪又做定位bool bOKMM = false;//运动模型是否成功判断标志bool bOKReloc = false;//重定位是否成功判断标志vector<MapPoint*> vpMPsMM;//记录地图点vector<bool> vbOutMM;//记录外点cv::Mat TcwMM;//变换矩阵if(!mVelocity.empty())//有速度{bOKMM = TrackWithMotionModel();//用运动模型追踪vpMPsMM = mCurrentFrame.mvpMapPoints;//记录地图点vbOutMM = mCurrentFrame.mvbOutlier;//记录外点TcwMM = mCurrentFrame.mTcw.clone();//当前帧的变换矩阵}bOKReloc = Relocalization();//用重定位// 重定位没有成功,但是运动模型跟踪成功if(bOKMM && !bOKReloc){mCurrentFrame.SetPose(TcwMM);mCurrentFrame.mvpMapPoints = vpMPsMM;mCurrentFrame.mvbOutlier = vbOutMM;if(mbVO){// 更新当前帧的MapPoints被观测程度for(int i =0; i<mCurrentFrame.N; i++){if(mCurrentFrame.mvpMapPoints[i] && !mCurrentFrame.mvbOutlier[i]){mCurrentFrame.mvpMapPoints[i]->IncreaseFound();}}}}else if(bOKReloc)// 只要重定位成功整个跟踪过程正常进行(定位与跟踪,更相信重定位){mbVO = false;}bOK = bOKReloc || bOKMM;}}}

2.3.2. local map跟踪

在帧间匹配得到初始的姿态后,现在对local map进行跟踪得到更多的匹配,并优化当前位姿,local map:当前帧、当前帧的MapPoints、当前关键帧与其它关键帧共视关系,在上一步中主要是两两跟踪(恒速模型跟踪上一帧、跟踪参考帧),这里搜索局部关键帧后搜集所有局部MapPoints,然后将局部MapPoints和当前帧进行投影匹配,得到更多匹配的MapPoints后进行Pose优化。

2.4. 速度模型

更新恒速运动模型TrackWithMotionModel中的mVelocity

  // 将最新的关键帧作为reference frame,接上面代码mCurrentFrame.mpReferenceKF = mpReferenceKF;if(!mbOnlyTracking){if(bOK)bOK = TrackLocalMap();}else{// mbVO true means that there are few matches to MapPoints in the map. We cannot retrieve// a local map and therefore we do not perform TrackLocalMap(). Once the system relocalizes// the camera we will use the local map again.// 重定位成功if(bOK && !mbVO)bOK = TrackLocalMap();}if(bOK)mState = OK;elsemState=LOST;// Update drawermpFrameDrawer->Update(this);// If tracking were good, check if we insert a keyframeif(bOK){// Update motion modelif(!mLastFrame.mTcw.empty()){// 步骤2.3:更新恒速运动模型TrackWithMotionModel中的mVelocitycv::Mat LastTwc = cv::Mat::eye(4,4,CV_32F);mLastFrame.GetRotationInverse().copyTo(LastTwc.rowRange(0,3).colRange(0,3));mLastFrame.GetCameraCenter().copyTo(LastTwc.rowRange(0,3).col(3));mVelocity = mCurrentFrame.mTcw*LastTwc; // 其实就是Tcl} elsemVelocity = cv::Mat();mpMapDrawer->SetCurrentCameraPose(mCurrentFrame.mTcw);

2.4.1. 清除UpdateLastFrame中为当前帧临时添加的MapPoints

清除临时的MapPoints,这些MapPoints在TrackWithMotionModel的UpdateLastFrame函数里生成(仅双目和rgbd)

检测并插入关键帧,对于双目会产生新的MapPoints

 // 步骤2.4:清除UpdateLastFrame中为当前帧临时添加的MapPointsfor(int i=0; i<mCurrentFrame.N; i++)//遍历当前帧所有MapPoint{MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];if(pMP)// 排除UpdateLastFrame函数中为了跟踪增加的MapPointsif(pMP->Observations()<1){mCurrentFrame.mvbOutlier[i] = false;mCurrentFrame.mvpMapPoints[i]=static_cast<MapPoint*>(NULL);}}// Delete temporal MapPoints// 步骤2.5:清除临时的MapPoints,这些MapPoints在TrackWithMotionModel的UpdateLastFrame函数里生成(仅双目和rgbd)// 步骤2.4中只是在当前帧中将这些MapPoints剔除,这里从MapPoints数据库中删除// 这里生成的仅仅是为了提高双目或rgbd摄像头的帧间跟踪效果,用完以后就扔了,没有添加到地图中for(list<MapPoint*>::iterator lit = mlpTemporalPoints.begin(), lend =  mlpTemporalPoints.end(); lit!=lend; lit++){MapPoint* pMP = *lit;delete pMP;}// 这里不仅仅是清除mlpTemporalPoints,通过delete pMP还删除了指针指向的MapPointmlpTemporalPoints.clear();// Check if we need to insert a new keyframe// 步骤2.6:检测并插入关键帧,对于双目会产生新的MapPointsif(NeedNewKeyFrame())CreateNewKeyFrame();// We allow points with high innovation (considererd outliers by the Huber Function)我们允许具有高创新点(考虑胡贝尔函数的异常值)// pass to the new keyframe, so that bundle adjustment will finally decide传递到新的关键帧,这样束调整将最终决定// if they are outliers or not. We don't want next frame to estimate its position如果它们是外点。我们不希望下一帧估计它的位置。// with those points so we discard them in the frame.// 删除那些在bundle adjustment中检测为outlier的3D map点for(int i=0; i<mCurrentFrame.N;i++){if(mCurrentFrame.mvpMapPoints[i] && mCurrentFrame.mvbOutlier[i])mCurrentFrame.mvpMapPoints[i]=static_cast<MapPoint*>(NULL);}}// Reset if the camera get lost soon after initialization// 跟踪失败,并且relocation也没有搞定,只能重新Resetif(mState==LOST){if(mpMap->KeyFramesInMap()<=5){cout << "Track lost soon after initialisation, reseting..." << endl;mpSystem->Reset();return;}}if(!mCurrentFrame.mpReferenceKF)mCurrentFrame.mpReferenceKF = mpReferenceKF;// 保存上一帧的数据mLastFrame = Frame(mCurrentFrame);}

2.5. 记录位姿信息,用于轨迹复现

if(!mCurrentFrame.mTcw.empty()){// 计算相对姿态T_currentFrame_referenceKeyFramecv::Mat Tcr = mCurrentFrame.mTcw*mCurrentFrame.mpReferenceKF->GetPoseInverse();mlRelativeFramePoses.push_back(Tcr);mlpReferences.push_back(mpReferenceKF);mlFrameTimes.push_back(mCurrentFrame.mTimeStamp);mlbLost.push_back(mState==LOST);}else{// This can happen if tracking is lost// 如果跟踪失败,则相对位姿使用上一次值mlRelativeFramePoses.push_back(mlRelativeFramePoses.back());mlpReferences.push_back(mlpReferences.back());mlFrameTimes.push_back(mlFrameTimes.back());mlbLost.push_back(mState==LOST);}
}

参考文献

主要内容来自下文,注释略有增改

ORB-SLAM2从理论到代码实现(六):Tracking.cc程序详解(上)_track lost soon after initialisation, reseting...-CSDN博客

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

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

相关文章

【php开发系统性学习】——thinkphp框架的控制器和视图的精简详细的使用

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

[Android]联系人-删除修改

界面显示 添加按钮点击&#xff0c;holder.imgDelete.setlog();具体代码 public MyViewHolder onCreateViewHolder(NonNull ViewGroup parent, int viewType) {//映射布局文件&#xff0c;生成相应的组件View v LayoutInflater.from(parent.getContext()).inflate(R.layout.d…

胶原蛋白三肽能否深入皮肤?一场关于美丽的科学之旅

在追求美丽的道路上&#xff0c;我们总是对各种护肤成分充满好奇。今天&#xff0c;就让我们一起探讨一个热门话题——胶原蛋白三肽&#xff0c;它究竟能否深入我们的皮肤&#xff0c;为我们带来期待中的美丽改变呢&#xff1f; 首先&#xff0c;我们需要了解胶原蛋白肽是什么…

开发心电疾病分类的深度学习模型并部署运行于ARM虚拟硬件平台(AVH)

目录 一、ARM虚拟硬件平台介绍 二、心电疾病分类模型介绍 三、部署流程 3.1 基于百度云平台订阅虚拟硬件镜像 3.2 安装编译相关组件 3.1 数据加载 3.2 模型转换 方式一&#xff1a; tensorflow模型转换为onnx模型&#xff0c;onnx模型转换为TVM模型 方式二&#xff1…

注册表Windows兼容性设置(AppCompatFlags)

属性 - 兼容性 EXE文件属性中有兼容性标签&#xff0c;当有些老版本软件不能正常运行时经常会调整这里的设置。 image.png 上面的所有选项都写在注册表中&#xff0c;其中“更改所有用户的设置”保存在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AppC…

html5各行各业官网模板源码下载(2)

文章目录 1.来源2.源码模板2.1 HTML5好看的旅行网站模板源码2.2 HTML5自适应医院叫号大屏模板源码2.3 HTML5好看的高科技登录页面模板源码2.4 HTML5宠物美容服务公司网站模板源码2.5 HTML5创意品牌广告设计公司网站模板源码2.6 HTML5实现室内设计模板源码2.7 HTML5黄金首饰网站…

Activiti7_使用

Activiti7_使用 一、Activiti7二、绘制工作流三、通过代码部署流程&#xff0c;再对流程进行实例化&#xff0c;完整运行一遍流程即可四、在springbooot中使用 一、Activiti7 为了实现后端的咨询流转功能&#xff0c;学习Activiti7&#xff0c;记录下使用的过程及遇到的问题 二…

openwrt 官方版 安装配置 AdGuard Home + smartdns 告别广告烦扰 教程 软路由实测 系列五

1 安装 adguard home opkg update opkg install adguardhome #启动 /etc/init.d/adguardhome start /etc/init.d/adguardhome enable #查看 rootOpenWrt:~# ps| grep AdGuardHome5101 root 1233m S /usr/bin/AdGuardHome -c /etc/adguardhome.yaml -w /var/adguardhom…

云界洞见——基于移动云云数据库MySQL应用实践

目录 简介1 新手入门1.1 创建MySQL实例1.2 公网连接MySQL实例 2 操作指南2.1 创建数据库2.2 数据备份设置2.3 日志管理2.4 监控告警2.5 代码审计 3 应用场景4 总结 如今&#xff0c;大型企业如金融企业和银行等&#xff0c;在下一代的微服务架构转型要求下&#xff0c;需要基础…

渗透测试的测试流程与注意事项

软件测试流程 渗透测试是一种重要的软件测试技术&#xff0c;通过对系统进行模拟攻击和漏洞评估&#xff0c;帮助组织发现和修复潜在的安全风险&#xff0c;提高系统的安全性和稳定性。在进行渗透测试时&#xff0c;需要注意合法授权、技术能力、安全意识和报告质量等方面的问…

科学提效|AI融入零售业,未来零售的创新之旅

零售业正经历着由人工智能&#xff08;AI&#xff09;引领的转型浪潮。AI在零售和消费品&#xff08;CPG&#xff09;行业的应用前景广阔&#xff0c;它正以多种创新方式重塑行业的运作模式。且随着技术的不断进步&#xff0c;AI在零售业的应用将变得更加广泛和深入。AI不仅能够…

即刻起飞——基于Amazon Bedrock快速构建生成式AI应用

即刻起飞 —— 基于 Amazon Bedrock 快速构建生成式 AI 应用 1. 前言 在百模大战中&#xff0c;AI行业的发展正在经历前所未有的变革。这场竞争不仅推动了AI技术的快速发展&#xff0c;也揭示了AI行业的新趋势。这些趋势不仅影响着我们如何看待和使用AI&#xff0c;也预示着AI…

免费且非常火的日程管理软件:飞项

一、简介 1、在日常繁忙的工签中&#xff0c;是否事情一大堆却记不住&#xff1f;系统自带的日历用着却是不方便&#xff0c;不顺手&#xff0c;提醒不及时&#xff1f;待办、打卡、记事乱七八糟的混在一起&#xff0c;关键时候找不到&#xff1f;市面上的日程管理软件那么多&a…

springboot+jwt+shiro+vue+elementUI+axios+redis+mysql完成一个前后端分离的博客项目

目录 简易博客项目(springbootjwtshirovueelementUIaxiosredismysql)第一章 整合新建springboot,整合mybatisplus第一步 创建项目(第八步骤就行)数据库:1、 修改pom.xml2、修改配置文件3、创建数据库vueblog然后执行下面命令生成表 第二步 配置分页MybatisPlusConfig生成代码(d…

Spring Boot 系统学习第三天:Spring依赖注入原理分析

1.概述 Spring中关于依赖注入的代码实现非常丰富&#xff0c;涉及大量类和组件之间的协作与交互。从原理上讲&#xff0c;任何一个框架都存在一条核心执行流程&#xff0c;只要抓住这条主流程&#xff0c;就能把握框架的整体代码结构&#xff0c;Spring也不例外。无论采用何种依…

YoloV8改进策略:注意力改进|HCANet全局与局部的注意力模块CAFM|二次创新|即插即用

文章目录 摘要用在自己的论文中,该如何描述原论文中的描述在自己论文中描述代码与详解官方结果改进方法测试结果总结摘要 在CAFM模型的基础上进行二次创新,我成功地开发了一个性能显著提升的改进版模型。这一创新不仅优化了特征提取和融合的方式,还极大地提高了模型的泛化能…

【C语言】自定义类型:联合与枚举的简明概述

&#x1f525;引言 关于自定义类型除了我们常用的结构体&#xff0c;还有联合与枚举也是属于自定义类型。本篇将简单介绍联合与枚举基本概念和使用方法 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&…

如何在.NET中集成SignalR

SignalR 简介 SignalR是一个开放源代码库&#xff0c;可用于简化向应用添加实时Web功能&#xff0c;实时Web功能使服务器端代码能够将内容推送到客户端。 SignalR开源库&#xff1a;https://github.com/SignalR/SignalR SignalR 应用场景 需要高频次从服务器获取信息的应用&am…

QML_Switch控件_3选2的控制算法

QML_Switch控件_3选2的控制算法 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.5Window {visible: truewidth: 400height: 400title: qsTr("Hello World")property int num: 0Row {spacing: 10Switch {id: switch1onCheckedChanged: {…

找出缺失的观测数据

代码实现&#xff1a; 在缺失的 n 个观测数据中&#xff0c;有 y 个观测数据是 x1&#xff0c;其余观测数据都是x int* missingRolls(int *rolls, int rollsSize, int mean, int n, int *returnSize) {int m rollsSize;int sum mean * (n m);int missingSum sum;for (int i…