如基础概念的Atlas 所述,每次插入的关键帧都与完整地图的DboW数据库进行匹配。如果发现了相同的场景,且两个关键帧同时位于活动地图,则意味着发生了回环,便按照回环的方式进行融合处理;如果匹配上的关键帧位于非活动地图,则需要将两个子地图进行拼接, LoopClosing
就是完成上述的相似场景发现、回环/拼接任务的线程。
线程启动
LoopClosing
线程在System
构造函数中初始化与创建,其核心函数为LoopClosing::Run
.
在LoopClosing::Run
中,线程每个5毫秒执行一次回环检测与地图融合检测操作。主体主体逻辑非常简单,即为上述所述的发现相似场景、进行回环/拼接,主要包含:
CheckNewKeyFrames
:检测线程中待检测的关键帧序列mlpLoopKeyFrameQueue
是否为空NewDetectCommonRegions
:检测新的关键帧和关键帧数据库中的帧是否存在相似区域,并更新mbMergeDetected
和mbLoopDetected
两个变量mbMergeDetected
和mbLoopDetected
变量进行相应的回环与地图融合操作。NewDetectCommonRegions
NewDetectCommonRegions
是LoopClosing
的关键环节,其任务为确认当前待检测关键帧pKF
是否和数据库中的某个关键帧pKFi
有相似性,若pKF
和pKFi
有相似性,且两者位于同一地图,则回环检测标志mbLoopDetected
将被置为true,若pKF
和pKFi
有相似性,且两者位于不同地图,则融合标志位mbMergeDetected
将被置为true。
其具体思路为:
KeyFrameDatabase::DetectNBestCandidates
,在关键数据库里寻找和当前帧相似的候选回环关键帧和候选融合关键帧;LoopClosing::DetectCommonRegionsFromBoW
,基于词袋模型判断是否从候选帧中成功检测到融合或者回环。mnLoopNumCoincidences
为回环最相似帧和当前帧共视组相似的帧数;mnMergeNumCoincidences
为融合最相似帧和当前帧共视组相似的帧数;mnLoopNumCoincidences
、 mnMergeNumCoincidences
大于3才说明检测到相似/融合。若0<mnLoopNumCoincidences
< 3,说明当前帧虽没有满足相似/融合条件,但也和数据库存在相似性,在进行下一关键帧的检测时,会调用DetectAndReffineSim3FromLastKF
直接检测下一关键帧和当前帧的最相似帧的相似性,如果也满足相似条件,则NumCoincidences++
,但当连续的下两帧相似性均不满足,则清除相关变量,后续待检测关键帧从1.重新开始。void KeyFrameDatabase::DetectNBestCandidates(KeyFrame *pKF, vector &vpLoopCand, vector &vpMergeCand, int nNumCandidates)
函数名 | 函数参数 | 描述 |
---|---|---|
KeyFrameDatabase::DetectNBestCandidates|在数据库中找寻和当前帧最相似的n帧候选帧 | *pKF | 输入的关键帧,函数将找寻和pKF最为相似的若干候选帧 |
修改的变量 | 描述 | |
vpLoopCand | 若候选帧和pKF在同一地图,则放入vpLoopCand | |
vpMergeCand | 若候选帧和pKF在不同地图,放入vpMergeCand | |
nNumCandidates | 候选帧数目 |
具体思路:
list lKFsSharingWords
:在地图中搜索所有和pKF有相同单词的关键帧,并剔除掉其中落在pKF局部地图的关键帧,然后计算剩余每一关键帧和pKF的相同单词个数;lKFsSharingWords
中进一步筛选满足条件的关键帧(相同单词数>0.8*最大相同单词数),并借助词袋模型计算每帧图像和当前相似度,结果保存在list > lScoreAndMatch
中; lScoreAndMatch
中的每一帧it,并遍历it的共视图中前十权重的关键帧vpNeighs,抽取出vpNeighs落在 lScoreAndMatch
中的关键帧并和it组乘一个新的集合,计算该集合的相似度总和以及相似度最高对应的关键帧;将每一个集合的累计得分以及分支最高的关键帧存储在list > lAccScoreAndMatch
; lScoreAndMatch
按照分支进行排序; lAccScoreAndMatch
,并返回前n个关键帧,其中若关键帧和pKF在同一地图,则将关键帧放入vpLoopCand
,否则放入vpMergeCand
;bool LoopClosing::DetectCommonRegionsFromBoW(std::vector &vpBowCand, KeyFrame* &pMatchedKF2, KeyFrame* &pLastCurrentKF, g2o::Sim3 &g2oScw,int &nNumCoincidences, std::vector &vpMPs, std::vector &vpMatchedMPs)
函数名 | 函数参数 | 描述 |
---|---|---|
DetectCommonRegionsFromBoW | 基于词袋模型判断是否从候选帧中成功检测到相似帧 | vpBowCand | 候选帧 |
修改的变量 | 描述 | |
pMatchedKF2 | 候选帧中和当前帧最相似的关键帧 | |
pLastCurrentKF | 上一当前帧 | |
g2oScw | 回环或者重定位后计算得到的世界坐标系到当前帧的sim3变换 | |
nNumCoincidences | 用计算的sim3变换检验当前帧的共视帧组,检验通过的帧数 | |
vpMPs | 候选帧及其共视帧的所有地图点 | |
vpMatchedMPs | 候选帧及其共视帧的所有地图点中满足g2oScw投影变换的点 |
具体思路:
spConnectedKeyFrames
vpBowCand
获取每一帧pKFi
,提取pKFi
的前10共视帧,并和pKFi
组成候选帧的共视帧组vpCovKFi
;vpCovKFi
,若其中有某一帧出现在spConnectedKeyFrames
中,说明检测到的候选帧是距离较近的关键帧,则直接进行下一个pKFi
的处理;vpCovKFi
中的每一关键帧和当前帧对比,进行特征点关联,进而找到特征点关联的地图点,将地图点保存在vvpMatchedMPs[j]
变量中,vvpMatchedMPs[j]
是一个数组,数组序号是当前帧特征点序号,存储内容是vpCovKFi[j]
对应的地图点vpMatchedPoints
和vpKeyFrameMatchedMP
中,其中,vpMatchedPoints
代表候选帧的共视帧组和当前帧对应的地图点,vpKeyFrameMatchedMP
代表vpMatchedPoints
中地图点对应的关键帧vpMatchedMP
与vpMatchedKF
中,vpMatchedMP
保存满足重投影的地图点,vpMatchedKF
保存对应的共视帧。bool LoopClosing::DetectAndReffineSim3FromLastKF(KeyFrame* pCurrentKF, KeyFrame* pMatchedKF, g2o::Sim3 &gScw, int &nNumProjMatches,std::vector &vpMPs, std::vector &vpMatchedMPs)
函数名 | 函数参数 | 描述 |
---|---|---|
DetectAndReffineSim3FromLastKF | 判断当前帧pCurrentKF和pMatchedKF是否满足相似关系 | pCurrentKF | 当前帧 |
pMatchedKFw | 相似的关键帧 | |
修改的变量 | 描述 | |
gScw | 世界坐标系到当前帧的sim3粗略估计,该函数会对其进一步优化 | |
nNumProjMatches | 相似帧中和当前帧成功关联上的地图点数目 | |
vpMPs | 相似帧及其共视帧的所有地图点 | |
vpMatchedMPs | 相似帧及其共视帧的所有地图点中和当前帧成功关联的地图点 |
具体思路:
FindMatchesByProjection
,找出符合gScw
变换关系的地图点集合vpMatchedMPs
以及地图点数nNumProjMatches
,如果nNumProjMatches
大于阈值,则对相似帧到当前帧的变化矩阵gScm
进行优化,并返回满足gScm
变化的地图点数numOptMatches
。numOptMatches
大于阈值,再一次调用FindMatchesByProjection
检验满足优化后的gScw_estimation
的地图点个数,如果nNumProjMatches
大于阈值,函数返回true,认为当前帧pCurrentKF
和pMatchedKFw
具有相似关系。int LoopClosing::FindMatchesByProjection(KeyFrame* pCurrentKF, KeyFrame* pMatchedKFw, g2o::Sim3 &g2oScw,set pMatchedMPinOrigin, vector &vpMapPoints, vector &vpMatchedMapPoints)
函数名 | 函数参数 | 描述 |
---|---|---|
FindMatchesByProjection | 基于重投影将相似帧的地图点和当前帧的特征点进行关联 | pCurrentKF | 当前帧 |
pMatchedKFw | 相似的关键帧 | |
修改的变量 | 描述 | |
g2oScw | 回环或者重定位后计算得到的世界坐标系到当前帧的sim3粗略估计,该函数会对其进一步优化 | |
spMatchedMPinOrigin | 无效变量,没有使用到 | |
vpMapPoints | 相似帧及其共视帧的所有地图点 | |
vpMatchedMapPoints | 相似帧及其共视帧的所有地图点中和当前帧成功关联的地图点 |
具体思路:
vpCovKFm
vpCovKFm
中帧数小于10,则添加相似帧的所有二级共视帧。(这部分逻辑有误,但影响不大,可修改为,若集合vpCovKFm
中帧数小于10,则添加相似帧的二级共视帧直至vpCovKFm
中帧数大于10,要求二级共视帧不落在当前帧的共视帧集合中以及相似帧的一级共视帧集合中)vpCovKFm
,将其中所有的地图点添加入地图点集合spMapPoints
中,spMapPoints
中的地图点两两互斥;ORBmatcher::SearchByProjection
,将spMapPoints
中的地图点进行重投影,返回关联成功的地图点集合vpMatchedMapPoints,同时记录关联成功的地图点数num_matches
num_matches
int ORBmatcher::SearchByProjection(KeyFrame* pKF, Sophus::Sim3f &Scw, const vector &vpPoints,vector &vpMatched, int th, float ratioHamming)
函数名 | 函数参数 | 描述 |
---|---|---|
SearchByProjection | 基于重投影将地图点和关键帧特征点进行关联 | pKF | 关键帧 |
Scw | 世界坐标系到当前帧的转化矩阵(回环、融合后) | |
vpPoints | 待关联的地图点 | |
th | 搜索半径基值(还需乘以金字塔缩放系数得到真正的搜索半径) | |
ratioHamming | 汉明距离比例,最优点距离小于ratioHamming*TH_LOW标定关联成功 | |
修改的变量 | 描述 | |
vpMatchedMapPoints | 关联成功的地图点集合,其中vpMatchedMapPoints[i]表示和pKF第i个特征点关联的地图点,若无则为空指针 |
具体思路:
vpPoints
,若地图点是bad的,进行下一个地图点的关联Scw
转换到当前帧相机坐标系下,若相机坐标系下该地图点的深度小于0,则进行下一个地图点的关联pKF
的相机内参将地图点投影到图像上获取像素uv
,若投影的像素点超出图像边界,则进行下一个地图点的关联Pn
,计算地图点和当前帧的光心构成的向量PO
,若两者向量夹角超过60°,则进行下一个地图点的关联pKF
中的金字塔层级nPredictedLevel
,利用nPredictedLevel
与th
确定搜索半径r
,以uv
为圆心,r
为半径,搜索落在其中的图像特征点序号vIndices
。vIndices
,计算特征点描述子和地图点描述子的汉明距离,获取最小距离对应的特征点序号,如果最小距离小于阈值,则关联成功,nmatches++
,结果保存在vpMatched[bestIdx]
中。void KeyFrameDatabase::DetectBestCandidates(KeyFrame *pKF, vector &vpLoopCand, vector &vpMergeCand, int nMinWords)
函数名 | 函数参数 | 描述 |
---|---|---|
KeyFrameDatabase::DetectBestCandidates 获取满足条件的所有候选帧 | KeyFrame *pKF | 输入的关键帧,函数将找寻和pKF最为相似的若干候选帧 |
vector KeyFrame* &vpLoopCand | 若候选帧和pKF在同一地图,则放入vpLoopCand | |
vectorKeyFrame* &vpMergeCand | 若候选帧和pKF在不同地图,放入vpMergeCand | |
int nMinWords | 候选帧和pKF共视点最低阈值 |
具体思路:
list lKFsSharingWords
:在地图中搜索所有和pKF有相同单词的关键帧,并剔除掉其中落在pKF局部地图的关键帧,然后计算剩余每一关键帧和pKF的相同单词个数;lKFsSharingWords
中进一步筛选满足条件的关键帧(相同单词数>0.8*最大相同单词数),并借助词袋模型计算每帧图像和当前相似度,结果保存在list > lScoreAndMatch
中; lScoreAndMatch
中的每一帧it,并遍历it的共视图中前十权重的关键帧vpNeighs,抽取出vpNeighs落在 lScoreAndMatch
中的关键帧并和it组乘一个新的集合,计算该集合的相似度总和以及相似度最高对应的关键帧;将每一个集合的累计得分以及分支最高的关键帧存储在list > lAccScoreAndMatch
; lAccScoreAndMatch
,并返回满足条件(累计得分>0.75*最大累计得分)的关键帧,其中若关键帧和pKF在同一地图,则将关键帧放入vpLoopCand
,否则放入vpMergeCand
;函数名 | 函数参数 | 描述 |
---|---|---|
KeyFrame::UpdateConnections | 更新关键帧间的连接关系,包括更新共视图,父子关键帧 | bool upParent | 若候选帧和pKF在同一地图,则放入vpLoopCand |
修改的变量 | 描述 | |
std::map KeyFrame*,int mConnectedKeyFrameWeights | map (KeyFrame*,int) (有共视点的关键帧,共视点数) | |
std::vector KeyFrame* mvpOrderedConnectedKeyFrames | 将共视图中的关键帧按照共视点个数进行排序得到的关键帧集合 | |
std::vector int mvOrderedWeights | 共视图中的共视点个数排序 | |
KeyFrame* mpParent; | 父关键帧 |
当NewDetectCommonRegions()
将融合检测标志mbMergeDetected
置为true
后,线程开始进入地图融合部分。若传感器中包含IMU,在地图融合前还需满足
满足上述条件后,若系统含有IMU,则执行MergeLocal2
将融合帧所在地图融合到当前帧所在地图,若系统不含IMU则执行MergeLocal
将当前帧所在地图融合到融合帧所在地图。
MergeLocal2()
主要思路:
mpThreadGBA
,停止局部建图线程mpLocalMapper->RequestStop()
mSold_new
,将当前地图中的关键帧位姿、关键帧速度、地图点位置、IMU到在世界坐标系下的坐标mOwb
转换到融合帧的世界坐标系下。mSold_new
更新每个帧之间的相互变换矩阵mlRelativeFramePoses
,更新IMU预积分的结果,将IMU的预积分帧更新。NonCorrectedSim3
vpCurrentConnectedKFs
、融合帧及融合帧的共视图数组mvpMergeConnectedKFs
、mvpMergeConnectedKFs
数组中的地图点集合spMapPointMerge
,将spMapPointMerge
中的点复制进vpCheckFuseMapPoint
。SearchAndFuse
,搜寻当前帧及当前帧的共视图数组vpCurrentConnectedKFs
中可用vpCheckFuseMapPoint
中的地图点替代的地图点。vpCurrentConnectedKFs
、mvpMergeConnectedKFs
中关键帧的连接关系。Optimizer::MergeInertialBA
。void LoopClosing::SearchAndFuse(const vector &vConectedKFs, vector &vpMapPoints)
函数名 | 函数参数 | 描述 |
---|---|---|
SearchAndFuse | 搜索关键帧数组中能用vpMapPoints代替的地图点 | vConectedKFs | 关键帧数组 |
vpMapPoints | 地图点 |
主要思路:
vConectedKFs
中每一关键帧,获取关键帧的位姿Scw
ORBmatcher::Fuse
,获取vpReplacePoint
,即地图点vpReplacePoint[i]
可用vpMapPoints[i]代替。vpReplacePoints
,调用MapPoint::Replace
进行地图点替换。主要思路:
mpThreadGBA
,停止局部建图线程mpLocalMapper->RequestStop()
spLocalWindowKFs
、spLocalWindowKFs
的地图点spLocalWindowMPs
。融合帧及其共视帧spMergeConnectedKFs
,spMergeConnectedKFs
的地图点spMapPointMerge
。将spMapPointMerge
中的地图点复制到vpCheckFuseMapPoint
spLocalWindowKFs
,spLocalWindowMPs
将其位姿更新至融合帧坐标系下,并在融合帧所在地图增加关键帧及地图点。LoopClosing::SearchAndFuse
进行地图点融合 Optimizer::LocalBundleAdjustment
进行优化LoopClosing::RunGlobalBundleAdjustment
进行全局优化