
ORB-SLAM2:Tracking线程学习随笔【李哈哈:看看总有收获篇】
发布日期:2021-05-08 14:58:52
浏览次数:16
分类:精选文章
本文共 5045 字,大约阅读时间需要 16 分钟。
前言
继之前学习特征提取之后,终于要开始SLAM正经线程的学习了
因为感觉做不完毕设了,所以后期会看的快一些,几篇文章将以随笔形式展开,主要记录学习过程中的一些心得体会随笔
构造帧
从输入的图片及时间戳构造Frame,完成提取特征点及计算描述子等操作,已经在我的系列视频讲的很清楚啦,链接放在这里供大家临幸嘿嘿:
描述子是个矩阵是怎么回事
描述子是在该函数中提取的:
/** * @brief 用仿函数(重载括号运算符)方法来计算图像特征点!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * @param[in] _image 输入原始图的图像 * @param[in] _mask 掩膜mask * @param[in & out] _keypoints 存储特征点关键点的向量 * @param[in & out] _descriptors 存储特征点描述子的矩阵 */ void ORBextractor::operator()(InputArray _image, InputArray _mask, vector&_keypoints, OutputArray _descriptors)
这是构造帧时调用的仿函数
其中的_keypoints
是存储该帧特征点的向量,是一个保存KeyPoint
的向量,这里的KeyPoint
是关键点的类,好像是OpenCV定义的吧 而_descriptors
就是保存描述子的矩阵,对其的构造操作在函数内: _descriptors.create(nkeypoints, //矩阵的行数,对应为特征点的总个数 32, //矩阵的列数,对应为使用32*8=256位描述子 CV_8U); //矩阵元素的格式
由此可见,其之所以是个矩阵,就是因为它要保存很多个点的描述子信息,每个特征点对应一行元素
小tips: 之所以不用vector,应该是因为vector的元素没有32×8这么长的内存空间,所以用了一个矩阵强行每一行表示一个特征点恒速模型的地图点如何构造
对于帧间位姿估计,我重点看了一下恒速运动模型
其中在特征点匹配的时候,遍历的是参考帧的地图点pMapPoints
,以投影关系寻找附近的特征点,满足阈值就认为是当前帧的地图点了(我也不确定嗷) 看这一问题主要是在研究局部地图跟踪的时候,对于更新局部关键帧实现方法,涉及到了遍历当前帧所有MapPoints的操作,这里的地图点来源于上一段话 双目恒速模型做了什么:
使用函数UpdateLastFrame()
更新了上一帧从世界坐标系到上一帧的位姿,并且构造了临时地图点,临时地图点的生成是从有深度的特征点来的
mCurrentFrame.SetPose(mVelocity * mLastFrame.mTcw);
通过函数SearchByProjection()
将上一帧的地图点投影到当前帧,进行地图点匹配,其实是把上一帧构造的临时地图点与当前帧特征点匹配,匹配上则把这些地图点加到当前帧上 最终优化位姿、剔除外点 总结: 得到了当前帧的位姿,以及一些地图点 问题: 单目的时候,上一帧的地图点什么时候加进去的!!!!!! 局部地图跟踪小结
调用TrackLocalMap()来实现局部地图跟踪
一、更新局部关键帧和局部地图点UpdateLocalMap();
调用该函数,其中两个关键函数UpdateLocalKeyFrames(); UpdateLocalPoints();
分别完成了更新局部关键帧和局部地图点 ①更新局部关键帧: UpdateLocalKeyFrames(); Step 1: 遍历当前帧的MapPoints,记录所有能观测到当前帧MapPoints的关键帧 通过GetObservations()
得到能够观测到当前地图点的所有关键帧及该地图点在这些关键帧中的索引 然后将有共视关系的关键帧以及该关键帧观测到了当前帧地图点的数目存入keyframeCounter[it->first]++;
,前者是某个关键帧,后者是共视程度 注意这里就是暴力遍历,没有用到共视图什么的函数,也没有设置看到相同地图点的阈值 Step 2: 更新局部关键帧(mvpLocalKeyFrames),添加局部关键帧有三个步骤 注意,这里是更新局部关键帧的关键一步 - 第一步:将能观测到当前帧MapPoints的关键帧作为局部关键帧,具体操作为:
mvpLocalKeyFrames.push_back(it->first);
- 第二步:遍历第一步得到的局部关键帧里共视程度很高的(前10个)关键帧,将他们作为局部关键帧
- 第三步:将第一步的的子关键帧和父关键帧作为局部关键帧
Step 3: 更新当前帧的参考关键帧,与自己共视程度最高的关键帧作为参考关键帧
这里的共视程度最高,指的是具有最大观测数目的关键帧 这里重新选择了参考关键帧,因为他们之间观察到了最多相同的地图点哦 ②更新局部地图点: UpdateLocalPoints() Step 1: 清空局部地图点:mvpLocalMapPoints.clear();
Step 2: 遍历刚刚生成的局部关键帧,将其中的地图点添加到变量mvpLocalMapPoints
中 到这里就完成了局部关键帧和局部地图点的更新 二、在局部地图中查找与当前帧匹配的MapPoints,其实也就是用局部地图点进行跟踪:SearchLocalPoints()
mCurrentFrame.isInFrustum(pMP, 0.5)
来判断LocalMapPoints
中的点是否在视野内 Step 3: 如果需要进行投影匹配的点的数目大于0,就进行投影匹配 这里还是使用了函数SearchByProjection()
: // 对视野范围内的MapPoints通过投影进行特征点匹配matcher.SearchByProjection(mCurrentFrame, mvpLocalMapPoints, th);
Projection:投影
这里其实是利用投影关系进行匹配,该函数有多个重载,这里使用的是局部地图点 三、更新局部所有的MapPoints后对位姿再次优化:Optimizer::PoseOptimization(&mCurrentFrame)
四、更新地图点状态 这一步不知道是干啥呢 五、如果最近刚刚发生了重定位,那么至少成功匹配50个点才认为是成功跟踪,正常状态只要跟踪地图点大于30就认为成功了 到这里就完成了局部地图跟踪 总结一下,就是更新了局部的关键帧和地图点,并在这个局部地图里进行了投影匹配,进而进行优化,最后判断是否成功跟踪 此时回到Track函数,若跟踪成功,mState就是OK,否则就是LOST了,这也是我们跟踪线程最大的状态表示 关键帧生成问题
这里涉及两个问题:关键帧生成条件判断&创建新的关键帧
一、关键帧生成条件判断:bool Tracking::NeedNewKeyFrame() 最终返回true/false告知后边的函数是否创建新的关键帧 判断条件主要如下:// Condition 1a: More than "MaxFrames" have passed from last keyframe insertion// Step 7.2:很长时间没有插入关键帧,可以插入const bool c1a = mCurrentFrame.mnId >= mnLastKeyFrameId + mMaxFrames;// Condition 1b: More than "MinFrames" have passed and Local Mapping is idle// Step 7.3:满足插入关键帧的最小间隔并且localMapper处于空闲状态,可以插入const bool c1b = (mCurrentFrame.mnId >= mnLastKeyFrameId + mMinFrames && bLocalMappingIdle);// Condition 1c: tracking is weak// Step 7.4:在双目,RGB-D的情况下当前帧跟踪到的点比参考关键帧的0.25倍还少,或者满足bNeedToInsertCloseconst bool c1c = mSensor != System::MONOCULAR && //只考虑在双目,RGB-D的情况 (mnMatchesInliers < nRefMatches * 0.25 || //当前帧和地图点匹配的数目非常少 bNeedToInsertClose); //需要插入// Condition 2: Few tracked points compared to reference keyframe. Lots of visual odometry compared to map matches.// Step 7.5:和参考帧相比当前跟踪到的点太少 或者满足bNeedToInsertClose;同时跟踪到的内点还不能太少const bool c2 = ((mnMatchesInliers < nRefMatches * thRefRatio || bNeedToInsertClose) && mnMatchesInliers > 15);if ((c1a || c1b || c1c) && c2){ }
几个变量分别表示如下含义
c1a:很长时间(1s)没有插入关键帧
c1b:查询局部地图管理器是否繁忙,localMapper处于空闲状态,才能生成关键帧 c1c:当前跟踪到点的数量较少 c2:上述条件成立之前,当前帧与参考关键帧重复度不能太高
Ps:一定注意该函数只是判断是否需要插入关键帧哦
二、创建新的关键帧:void Tracking::CreateNewKeyFrame()
该函数内部完成以下几步操作: Step 1: 将当前帧构造成关键帧 Step 2: 将当前关键帧设置为当前帧的参考关键帧 Step 3: 对于双目或rgbd摄像头,为当前帧生成新的地图点;单目无操作 Step 4: 插入关键帧 这里实现方式如下:mpLocalMapper->InsertKeyFrame(pKF);
这里实际上执行了如下函数:
mlNewKeyFrames.push_back(pKF);
将关键帧插入待处理队列mlNewKeyFrames
中,供局部建图线程处理
后记(流程梳理)
该线程依次完成了帧构造(特征提取)、单目初始化、帧间相对位姿估计(恒速模型、跟踪参考关键帧模型)、重定位、局部地图跟踪、关键帧生成
可以认为这是SLAM前端里程计部分,但该线程也存在一些优化(例如局部地图跟踪中就有小的回环优化PoseOptimization)小问题
没有看重定位和跟踪参考关键帧模型哦
基本上细节都很粗略的略过了 帧间的参考帧,当前帧,邻近的关键帧等概念还不是很清晰,尤其是在不断变换的时候发表评论
最新留言
逛到本站,mark一下
[***.202.152.39]2025年04月19日 11时38分18秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
MySQL-时区导致的时间前后端不一致
2021-05-08
2021-04-05阅读小笔记:局部性原理
2021-05-08
go语言简单介绍,增强了解
2021-05-08
架构师入门:搭建基本的Eureka架构(从项目里抽取)
2021-05-08
MongoDB 快速扫盲贴
2021-05-08
one + two = 3
2021-05-08
sctf_2019_easy_heap
2021-05-09
PyQt5之音乐播放器
2021-05-09
Redis进阶实践之十八 使用管道模式提高Redis查询的速度
2021-05-09
SQL注入
2021-05-09
MPI Maelstrom POJ - 1502 ⭐⭐ 【Dijkstra裸题】
2021-05-09
Problem 330A - Cakeminator (思维)
2021-05-09
LeetCode75 颜色分类 (三路快排C++实现与应用)
2021-05-09
C语言+easyX图形库的推箱子实现
2021-05-09
调试vs2019代码的流程
2021-05-09
脱壳与加壳-加壳-6-代码实现加密导入表
2021-05-09
Typora配置PicGo时,提示Failed to fetch
2021-05-09
bcolz的新操作
2021-05-09
zmq的send
2021-05-09
delete对象时会自动调用类的析构函数
2021-05-09