详谈ORB-SLAM2的跟踪线程Tracking

news2024/12/28 20:25:13

ORB-SLAM2的三大线程几乎都是每个部分对应一个函数,非常清晰。函数名起的都十分贴切,望文就能生义,我们更应该关注的是里面的关键帧地图点这些变量是怎么流转的。

Tracking线程的流程就是:首先输入一帧图像,然后实际上接着进行5步。初始化(实际上相机刚开始需要初始化过程)、图像预处理(提取ORB特征点)、初始位姿估计(根据前一张图片或者附近的一张图片估计当前帧的位姿)、基于当前帧的位姿构建当前帧的局部地图(当前帧附近的很小的一部分地图)、是否生成新的关键帧

PS:通过追踪局部帧的地图而建图,实际上后面有专门负责建设局部地图的线程,所以这里的局部地图并没有用来建图,只是用来优化当前帧的位姿

一、跟踪线程各成员函数/变量

1、跟踪状态

(1)枚举类型eTrackingState

Tracking类中定义枚举类型eTrackingState,表示跟踪状态,系统创建时设值SYSTEM_NOT_READY,加载好后设值NO_IMAGES_YET,对于单目视觉至少有两帧图像才能成功,对于双目视觉需要一帧图像就鞥初始化成功,如果一帧图像的特征点过少也不能初始化成功

意义
SYSTEM_NOT_READY系统没有准备好,一般就是在启动后加载配置文件和词典文件时候的状态
NO_IMAGES_YET还没有接收到输入图像
NOT_INITIALIZED接收到图像但未初始化成功
OK跟踪成功
LOST跟踪失败

(2)Tracking类的成员变量

Tracking类的成员变量mState和mLastProcessedState分别表示当前帧的跟踪状态和上一帧的跟踪状态

成员变量访问控制意义
eTrackingState mStatepublic当前帧mCurrentFrame的跟踪状态
eTrackingState mLastProcessedStatepublic前一帧mLastFrame的跟踪状态

(3)跟踪状态流程

在这里插入图片描述
此博客参考ncepu_Chen的《5小时让你假装大概看懂ORB-SLAM2源码》

void Tracking::Track() {
    
	// ...
    
    unique_lock<mutex> lock(mpMap->mMutexMapUpdate);

    // step1. 若还没初始化,则尝试初始化
    if (mState == NOT_INITIALIZED) {
        if (mSensor == System::STEREO || mSensor == System::RGBD)
            StereoInitialization();
        else
            MonocularInitialization();
		if (mState != OK)
            return;
    } 
    
    // ...
}
void Tracking::CreateInitialMapMonocular() {
    // mInitialFrame 和 mCurrentFrame 是最早的两个关键帧
    KeyFrame *pKFini = new KeyFrame(mInitialFrame, mpMap, mpKeyFrameDB);  
    KeyFrame *pKFcur = new KeyFrame(mCurrentFrame, mpMap, mpKeyFrameDB);  

    // step1. 计算两个关键帧的词袋
    pKFini->ComputeBoW();
    pKFcur->ComputeBoW();

    // step2. 将两个关键帧插入地图
    mpMap->AddKeyFrame(pKFini);
    mpMap->AddKeyFrame(pKFcur);

    // step3. 处理所有地图点
    for (size_t i = 0; i < mvIniMatches.size(); i++) {
        // 创建并添加地图点
        MapPoint *pMP = new MapPoint(mvIniP3D[i], pKFcur, mpMap);		
        mpMap->AddMapPoint(pMP);
        // 添加关键帧到地图点的观测
        pKFini->AddMapPoint(pMP, i);
        pKFcur->AddMapPoint(pMP, mvIniMatches[i]);
		// 添加地图点到关键帧的观测
        pMP->AddObservation(pKFini, i);
        pMP->AddObservation(pKFcur, mvIniMatches[i]);
        // 计算地图点描述子并更新平均观测数据
        pMP->ComputeDistinctiveDescriptors();
        pMP->UpdateNormalAndDepth();
    }

    // 基于观测到的地图点,更新关键帧共视图
    pKFini->UpdateConnections();
    pKFcur->UpdateConnections();

    // step4. 全局BA: 优化所有关键帧位姿和地图点
    Optimizer::GlobalBundleAdjustemnt(mpMap, 20);

	// step5. 将两帧间的平移尺度归一化(以场景的中值深度为参考)
    float medianDepth = pKFini->ComputeSceneMedianDepth(2);
    float invMedianDepth = 1.0f / medianDepth;
    cv::Mat Tc2w = pKFcur->GetPose();
    Tc2w.col(3).rowRange(0, 3) = Tc2w.col(3).rowRange(0, 3) * invMedianDepth;
    pKFcur->SetPose(Tc2w);

	// step6. 将坐标点尺度也归一化
    vector<MapPoint *> vpAllMapPoints = pKFini->GetMapPointMatches();
    for (size_t iMP = 0; iMP < vpAllMapPoints.size(); iMP++) {
        if (vpAllMapPoints[iMP]) {
            MapPoint *pMP = vpAllMapPoints[iMP];
            pMP->SetWorldPos(pMP->GetWorldPos() * invMedianDepth);
        }
    }

    // step7. 将关键帧插入局部地图,更新归一化后的位姿和地图点坐标  
    mpLocalMapper->InsertKeyFrame(pKFini);
    mpLocalMapper->InsertKeyFrame(pKFcur);
    mCurrentFrame.SetPose(pKFcur->GetPose());
    mnLastKeyFrameId = mCurrentFrame.mnId;
    mpLastKeyFrame = pKFcur;
    mvpLocalKeyFrames.push_back(pKFcur);
    mvpLocalKeyFrames.push_back(pKFini);
    mvpLocalMapPoints = mpMap->GetAllMapPoints();
    mpReferenceKF = pKFcur;
    mCurrentFrame.mpReferenceKF = pKFcur;
    mLastFrame = Frame(mCurrentFrame);
    mpMap->SetReferenceMapPoints(mvpLocalMapPoints);
    mpMapDrawer->SetCurrentCameraPose(pKFcur->GetPose());
    mpMap->mvpKeyFrameOrigins.push_back(pKFini);
    
   	// step8. 更新跟踪状态变量mState
    mState = OK;
}

void Tracking::StereoInitialization() {
    if (mCurrentFrame.N > 500) {
        
        // 基于当前帧构造初始关键帧
        mCurrentFrame.SetPose(cv::Mat::eye(4, 4, CV_32F));
        KeyFrame *pKFini = new KeyFrame(mCurrentFrame, mpMap, mpKeyFrameDB);
        mpMap->AddKeyFrame(pKFini);
        mpLocalMapper->InsertKeyFrame(pKFini);
        // 构造地图点
        for (int i = 0; i < mCurrentFrame.N; i++) {
            float z = mCurrentFrame.mvDepth[i];
            if (z > 0) {
                cv::Mat x3D = mCurrentFrame.UnprojectStereo(i);
                MapPoint *pNewMP = new MapPoint(x3D, pKFini, mpMap);
			    pNewMP->AddObservation(pKFini, i);
                pNewMP->ComputeDistinctiveDescriptors();
                pNewMP->UpdateNormalAndDepth();
				mpMap->AddMapPoint(pNewMP);
                pKFini->AddMapPoint(pNewMP, i);
				mCurrentFrame.mvpMapPoints[i] = pNewMP;
            }
        }

		// 构造局部地图
        mvpLocalKeyFrames.push_back(pKFini);
        mvpLocalMapPoints = mpMap->GetAllMapPoints();
        mpReferenceKF = pKFini;
        mCurrentFrame.mpReferenceKF = pKFini;
		
        // 更新跟踪状态变量mState
        mState = OK;
    }
}

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

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

相关文章

开启前端CSS学习之路-css003

&#x1f60a;博主页面&#xff1a;鱿年年 &#x1f449;博主推荐专栏&#xff1a;《WEB前端》&#x1f448; ​&#x1f493;博主格言&#xff1a;追风赶月莫停留&#xff0c;平芜尽处是春山❤️ 目录 CSS引入方式 一、CSS的三种样式表 1.内部样式表 2.行内样式表 3.外…

用java判断闰年

闰年的判断&#xff1a; 能被400整除的年份是闰年能被4整除的但是不能被100整除的年份是闰年 import java.util.Scanner; public class leapYear{public static void main(String[] args){int year;Scanner sc new Scanner(System.in);year sc.nextInt();if((year % 400 0)…

Java中对象的比较

从大根堆说到对象的比较大小根堆对象的比较equalsComparableComparator大小根堆 大小根堆是堆相关的知识,堆这种数据结构总结起来就是:堆顶元素是最大的就是大根堆,而每个堆顶元素以下的又可以看成一个堆: 要注意的是,堆底层是用数组实现的: 但是对这种数据结构具体化之后与…

JVM OOM和CPU问题排查

目录 1、JVM调优工具 1.1、jps 1.2、jstat 1.3、jstack 1.4、jinfo 1.5、jmap 2、OOM排查过程 2.1、OOM原因 2.2、OOM发生区域 2.2.1、Java堆溢出&#xff1a;heap 2.2.2、Java栈溢出&#xff1a;stack 2.2.3、运行时常量溢出&#xff1a;constant 2.2.4、方法区…

计算机毕业设计选题推荐nodejs+vue355的网上购物商城系统

网上购物商城&#xff0c;在系统首页可以查看首页、关于我们、商品信息、新闻信息、交流论坛、留言反馈、个人中心、后台管理、在线客服等内容 前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 1、 node_modules文件夹(有npn install产…

spring整合mybatis的核心思路(数据源切换)

文章目录1. 整合思路2. 最简单的整合步骤1. 导入依赖2. 准备基础类UserUserMapperuserMapper.xmlMybatisConfig3. 测试TestMybatisSpring3. 整合多个mybatis配置1. 修改MybatisConfig2. 测试TestMybatisSpring4. AbstractRoutingDataSource实现源切换1. 准备基础类PersonPerson…

JVM的栈内存

每当启动一个新线程时&#xff0c;Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。虚拟机只会直接对Java栈执行两种操作&#xff1a;以帧为单位的压栈和出栈。 某个线程正在执行的方法被称为该线程的当前方法&#xff0c;当前方法使用的栈帧称为当前帧…

车载以太网 - SomeIP测试 - 初识 - 01

SOA,Service-Oriented Architecture,即面向服务的架构 SOA是一种面向服务的架构,定义了“服务器”和“客户端”,前者是服务、数据的提供者,后者是订阅了所需要的服务或者数据。应用程序之间是公三耦合,并通过服务总线作为中间件进行通信。SOA更像是一种框架,需要将信息从…

【JavaGuide面试总结】Java集合篇·下

【JavaGuide面试总结】Java集合篇下1.HashMap 的长度为什么是 2 的幂次方2.HashMap 有哪几种常见的遍历方式?3.HashSet 如何检查重复?4.ConcurrentHashMap 和 Hashtable 的区别5.ConcurrentHashMap 线程安全的具体实现方式/底层具体实现JDK1.8 之前JDK1.8 之后6.JDK 1.7 和 J…

从零编写linux0.11 - 第九章 文件系统(一)

从零编写linux0.11 - 第九章 文件系统&#xff08;一&#xff09; 编程环境&#xff1a;Ubuntu 20.04、gcc-9.4.0 代码仓库&#xff1a;https://gitee.com/AprilSloan/linux0.11-project linux0.11源码下载&#xff08;不能直接编译&#xff0c;需进行修改&#xff09; 本章…

【青训营】Go的一些性能优化技巧

一、Slice切片的性能优化 对Slice进行内存预分配 尽可能在使用make()初始化函数的时候提供容量信息&#xff0c;因为切片本质是一个数组片段的描述&#xff0c;其源码如下&#xff1a; type slice struct{array unsafe.Pointerlen int// 长度cap int// 容量 }如果没有指定容…

22.1.28打卡 Codeforces Round #847 (Div. 3) A~E

https://codeforces.com/contest/1790A. Polycarp and the Day of Pi题意问你第一个和"314159265358979323846264338327"不同的字符串下标1是什么如果全部相同输出最后一个下标1/* ⣿⣿⣿⣿⣿⣿⡷⣯⢿⣿⣷⣻⢯⣿⡽⣻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠸⣿⣿⣆…

Python基于LSTM预测特斯拉股票

Python基于LSTM预测特斯拉股票 提示&#xff1a;前言 Python基于LSTM预测特斯拉股票 股票预测是指&#xff1a;对股市具有深刻了解的证券分析人员根据股票行情的发展进行的对未来股市发展方向以及涨跌程度的预测行为。这种预测行为只是基于假定的因素为既定的前提条件为基础的…

通用智能基础模型假说

🍿*★,*:.☆欢迎您/$:*.★* 🍿 https://dongfangyou.blog.csdn.net/article/details/128761358 通过上面说的 人本身自带一个 各种行为反馈的评价模型 拥有这个模型便可拥有通用智能 简单的分析一下该模型到底应该由什么组成 最基础的模型是什么 首先人类最基础的模型应该…

【小程序】类taro语法中小程序端使用f2

前言 最近在类taro框架中小程序端使用最新版f2。&#xff08;这里我使用rax&#xff09;并封装了库&#xff0c;特此记录一下。 使用 想直接用的同学直接在你的rax项目中安装rax-my-f2这个包&#xff0c;他依赖antv/f2与antv/f2-context这2个包。 import { MyCanvas } from…

Kubernetes 笔记(06)— 搭建多节点集群、kubeadm 安装 master/worker/console/flannel 网络插件

1. kubeadm 官网&#xff1a;https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/ 为了简化 Kubernetes 的部署工作&#xff0c;社区里就出现了一个专门用来在集群中安装 Kubernetes 的工具&#xff0c;名字就叫 kubeadm&#xff0c;意思就是 Kubernetes 管理员…

Java设计模式-状态模式State

介绍 状态模式&#xff08;State Pattern&#xff09;&#xff1a;它主要用来解决对象在多种状态转换时&#xff0c;需要对外输出不同的行为的问题。状态和行为是一一对应的&#xff0c;状态之间可以相互转换。当一个对象的内在状态改变时&#xff0c;允许改变其行为&#xff…

学习记录677@项目管理之配置管理案例

案例 Simple公司的质量管理体系中的配置管理程序文件中有如下规定: (1)由变更控制委员会(CCB)制定项目的配置管理计划; (2)由配置管理员(CMO)创建配置管理环境: (3)由CCB 审核变更计划; (4)项目中配置基线的变更经过变更申请、变更评估、变更实施后便可发布&#xff1b; (5)CC…

Java基础10:常用API(下)

Java基础10&#xff1a;常用API&#xff08;下&#xff09;一、Date二、SimpleDateFormat三、Calendar四、ZoneId五、Instant六、ZoneDateTime七、DateTimeFormatter八、LocalDate、LocalTime、LocalDateTime九、Duration、Period、ChronoUnit十、包装类一、Date Date类是一个…

基于PIL和Tesseract的数字计算验证码识别处理思路

如图,我们在使用python自动化的时候经常会遇到很多各式各样的验证码。这个是一个数字加法的验证码。 干扰项里包含完整的数字、字母信息,普通的OCR识别可能不是很准确。 但是不管怎们样,咱们先把必要的环境搭建起来,试一下Tesseract的识别结果吧。 1、安装Tesseract: 首…