LoopClosing.h
/**
* This file is part of ORB-SLAM2.
*
* Copyright (C) 2014-2016 Raúl Mur-Artal <raulmur at unizar dot es> (University of Zaragoza)
* For more information see <https://github.com/raulmur/ORB_SLAM2>
*
* ORB-SLAM2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ORB-SLAM2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ORB-SLAM2. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOOPCLOSING_H
#define LOOPCLOSING_H
#include "KeyFrame.h"
#include "LocalMapping.h"
#include "Map.h"
#include "ORBVocabulary.h"
#include "Tracking.h"
#include "KeyFrameDatabase.h"
#include <thread>
#include <mutex>
#include "Thirdparty/g2o/g2o/types/types_seven_dof_expmap.h"
namespace ORB_SLAM2
{
class Tracking;
class LocalMapping;
class KeyFrameDatabase;
/**用来做回环检测的类 */
class LoopClosing
{
public:
typedef pair<set<KeyFrame*>,int> ConsistentGroup;
typedef map<KeyFrame*,g2o::Sim3,std::less<KeyFrame*>,
Eigen::aligned_allocator<std::pair<const KeyFrame*, g2o::Sim3> > > KeyFrameAndPose;
public:
/**
* @brief:构造函数
* @param pMap:地图
* @param pDB:关键帧数据库
* @param pVoc :词袋模型
* @param bFixScale:是否固定缩放比例
*/
LoopClosing(Map* pMap, KeyFrameDatabase* pDB, ORBVocabulary* pVoc,const bool bFixScale);
void SetTracker(Tracking* pTracker);
void SetLocalMapper(LocalMapping* pLocalMapper);
// Main function
void Run();
/**插入关键帧 */
void InsertKeyFrame(KeyFrame *pKF);
void RequestReset();
// This function will run in a separate thread
void RunGlobalBundleAdjustment(unsigned long nLoopKF);
bool isRunningGBA(){
unique_lock<std::mutex> lock(mMutexGBA);
return mbRunningGBA;
}
bool isFinishedGBA(){
unique_lock<std::mutex> lock(mMutexGBA);
return mbFinishedGBA;
}
void RequestFinish();
bool isFinished();
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
protected:
bool CheckNewKeyFrames();
bool DetectLoop();
bool ComputeSim3();
void SearchAndFuse(const KeyFrameAndPose &CorrectedPosesMap);
void CorrectLoop();
void ResetIfRequested();
bool mbResetRequested;
std::mutex mMutexReset;
bool CheckFinish();
void SetFinish();
bool mbFinishRequested;
bool mbFinished;
std::mutex mMutexFinish;
Map* mpMap;
Tracking* mpTracker;
KeyFrameDatabase* mpKeyFrameDB;
ORBVocabulary* mpORBVocabulary;
LocalMapping *mpLocalMapper;
//储存所有的回环的关键帧
std::list<KeyFrame*> mlpLoopKeyFrameQueue;
std::mutex mMutexLoopQueue;
// Loop detector parameters
float mnCovisibilityConsistencyTh;
// Loop detector variables
KeyFrame* mpCurrentKF;
//回环匹配上的一帧
KeyFrame* mpMatchedKF;
std::vector<ConsistentGroup> mvConsistentGroups;
std::vector<KeyFrame*> mvpEnoughConsistentCandidates;
std::vector<KeyFrame*> mvpCurrentConnectedKFs;
std::vector<MapPoint*> mvpCurrentMatchedPoints;
std::vector<MapPoint*> mvpLoopMapPoints;
cv::Mat mScw;
g2o::Sim3 mg2oScw;
long unsigned int mLastLoopKFid;
// Variables related to Global Bundle Adjustment
bool mbRunningGBA;
bool mbFinishedGBA;
bool mbStopGBA;
std::mutex mMutexGBA;
std::thread* mpThreadGBA;
// Fix scale in the stereo/RGB-D case
bool mbFixScale;
bool mnFullBAIdx;
};
} //namespace ORB_SLAM
#endif // LOOPCLOSING_H
LoopClosing.cc
/**
* This file is part of ORB-SLAM2.
*
* Copyright (C) 2014-2016 Raúl Mur-Artal <raulmur at unizar dot es> (University of Zaragoza)
* For more information see <https://github.com/raulmur/ORB_SLAM2>
*
* ORB-SLAM2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ORB-SLAM2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ORB-SLAM2. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LoopClosing.h"
#include "Sim3Solver.h"
#include "Converter.h"
#include "Optimizer.h"
#include "ORBmatcher.h"
#include<mutex>
#include<thread>
namespace ORB_SLAM2
{
LoopClosing::LoopClosing(Map *pMap, KeyFrameDatabase *pDB, ORBVocabulary *pVoc, const bool bFixScale):
mbResetRequested(false), mbFinishRequested(false), mbFinished(true), mpMap(pMap),
mpKeyFrameDB(pDB), mpORBVocabulary(pVoc), mpMatchedKF(NULL), mLastLoopKFid(0), mbRunningGBA(false), mbFinishedGBA(true),
mbStopGBA(false), mpThreadGBA(NULL), mbFixScale(bFixScale), mnFullBAIdx(0)
{
mnCovisibilityConsistencyTh = 3;
}
void LoopClosing::SetTracker(Tracking *pTracker)
{
mpTracker=pTracker;
}
void LoopClosing::SetLocalMapper(LocalMapping *pLocalMapper)
{
mpLocalMapper=pLocalMapper;
}
/**
* 主要的函数,单独的线程中运行
*
* */
void LoopClosing::Run()
{
mbFinished =false;
while(1)
{
// Check if there are keyframes in the queue
//检测是否有新的关键帧
if(CheckNewKeyFrames())
{
// Detect loop candidates and check covisibility consistency
//检测循环
if(DetectLoop())
{
// Compute similarity transformation [sR|t]
// In the stereo/RGBD case s=1
//计算Sim3变换
if(ComputeSim3())
{
// Perform loop fusion and pose graph optimization
//执行回环融合和位姿优化
CorrectLoop();
}
}
}
ResetIfRequested();
if(CheckFinish())
break;
usleep(5000);
}
SetFinish();
}
/*插入新的关键帧到回环检测队列 */
void LoopClosing::InsertKeyFrame(KeyFrame *pKF)
{
unique_lock<mutex> lock(mMutexLoopQueue);
if(pKF->mnId!=0)
mlpLoopKeyFrameQueue.push_back(pKF);
}
bool LoopClosing::CheckNewKeyFrames()
{
unique_lock<mutex> lock(mMutexLoopQueue);
return(!mlpLoopKeyFrameQueue.empty());
}
/**
* 检测回环
* 1.首先根据最新的一个关键帧,求出相邻的关键帧,然后和当前帧进行匹配,得到最小的匹配的分数minScore;
* 2.根据最小分数,在关键帧数据库中寻找到所有大于minScore的n个候选关键帧;
* 3.分别得到这些候选关键帧的共视关键帧作为候选关键帧组,一共n个组;
* 4.将遍历这n候选关键帧组,并和上一次的候选关键帧组进行匹配,看有没有那个组由相同的关键帧,如果有,就是连续的,计数+1,否则,计数清0
* 5.连续计数>3的候选关键帧组就是由足够连续性的组,意味着连续三个关键帧检测都检测到了这个关键帧组是回环关键帧组.
* 为什么要检测连续性?我的理解是回环意味着回到以前的访问的一个地方,之前访问的地方现在重新访问肯定有许多相似的关键帧,
* 如果仅仅依靠一次关键帧的相似度去做回环会发现有很多相似的场景会被当做是回环.
*
*/
bool LoopClosing::DetectLoop()
{
//取出最近的关键帧
{
unique_lock<mutex> lock(mMutexLoopQueue);
mpCurrentKF = mlpLoopKeyFrameQueue.front();
mlpLoopKeyFrameQueue.pop_front();
// Avoid that a keyframe can be erased while it is being process by this thread
mpCurrentKF->SetNotErase();
}
//If the map contains less than 10 KF or less than 10 KF have passed from last loop detection
//如果距离上一个回环少于10个关键帧,就不再进行回环
if(mpCurrentKF->mnId<mLastLoopKFid+10)
{
mpKeyFrameDB->add(mpCurrentKF);
mpCurrentKF->SetErase();
return false;
}
// Compute reference BoW similarity score
// This is the lowest score to a connected keyframe in the covisibility graph
// We will impose loop candidates to have a higher similarity than this
//获取当前帧所有的共视帧
const vector<KeyFrame*> vpConnectedKeyFrames = mpCurrentKF->GetVectorCovisibleKeyFrames();
const DBoW2::BowVector &CurrentBowVec = mpCurrentKF->mBowVec;
float minScore = 1;
/* 遍历共视帧,计算和当前帧的ORB匹配分数,得到最低分数的共视帧
这里的miniScore作为一个标准,在数据库中找到大于这个最小分数的,
才有可能是之前观测到的回环的关键帧*/
for(size_t i=0; i<vpConnectedKeyFrames.size(); i++)
{
KeyFrame* pKF = vpConnectedKeyFrames[i];
if(pKF->isBad())
continue;
const DBoW2::BowVector &BowVec = pKF->mBowVec;
float score = mpORBVocabulary->score(CurrentBowVec, BowVec);
if(score<minScore)
minScore = score;
}
// Query the database imposing the minimum score
//查找数据库,找到和当前帧匹配分数大于最小分数的关键帧
vector<KeyFrame*> vpCandidateKFs = mpKeyFrameDB->DetectLoopCandidates(mpCurrentKF, minScore);
// If there are no loop candidates, just add new keyframe and return false
//如果没有候选回环的关键帧,就添加关键帧并返回
if(vpCandidateKFs.empty())
{
mpKeyFrameDB->add(mpCurrentKF);
mvConsistentGroups.clear();
mpCurrentKF->SetErase();
return false;
}
// For each loop candidate check consistency with previous loop candidates
// Each candidate expands a covisibility group (keyframes connected to the loop candidate in the covisibility graph)
// A group is consistent with a previous group if they share at least a keyframe
// We must detect a consistent loop in several consecutive keyframes to accept it
/*对每一个回环的候选关键帧,与之前的回环候选帧检测连续性,每个回环候选关键帧拓展了一个共视组(共视组里面的
关键帧都是回环候选帧的共视帧),一个组和另外的组是连续的如果它们共享至少一个关键帧,我们必须连续几帧都检测
到这个回环才算真的检测到了回环
*/
mvpEnoughConsistentCandidates.clear();
//当前的连续组(关键帧的集合,整数)
vector<ConsistentGroup> vCurrentConsistentGroups;
//vbConsistentGroup用来判断某个组是否添加到mvConsistentGroups里面过,true:表示添加过,false:表示没有
vector<bool> vbConsistentGroup(mvConsistentGroups.size(),false);
for(size_t i=0, iend=vpCandidateKFs.size(); i<iend; i++)
{
//取出一个候选关键帧
KeyFrame* pCandidateKF = vpCandidateKFs[i];
//取出该候选关键帧的共视帧,作为候选关键帧组
set<KeyFrame*> spCandidateGroup = pCandidateKF->GetConnectedKeyFrames();
//在这组中加入该关键帧自己
spCandidateGroup.insert(pCandidateKF);
bool bEnoughConsistent = false;
bool bConsistentForSomeGroup = false;
//遍历之前的关键帧连续组
for(size_t iG=0, iendG=mvConsistentGroups.size(); iG<iendG; iG++)
{
//取出一个组的关键帧
set<KeyFrame*> sPreviousGroup = mvConsistentGroups[iG].first;
//看sPreviousGroup和spCandidateGroup这两个组有没有相同的关键帧,如果有,就是连续的
bool bConsistent = false;
//遍历该候选关键帧组,判断之前的连续组里面有没有包含候选关键帧
for(set<KeyFrame*>::iterator sit=spCandidateGroup.begin(), send=spCandidateGroup.end(); sit!=send;sit++)
{
//之前的组里面是否有当前的候选关键帧,如果由一帧,那么就是连续的
if(sPreviousGroup.count(*sit))
{
bConsistent=true;
bConsistentForSomeGroup=true;
break;
}
}
//如果是连续的
if(bConsistent)
{
int nPreviousConsistency = mvConsistentGroups[iG].second;
int nCurrentConsistency = nPreviousConsistency + 1;
//如果没有添加过
if(!vbConsistentGroup[iG])
{
ConsistentGroup cg = make_pair(spCandidateGroup,nCurrentConsistency);
vCurrentConsistentGroups.push_back(cg);
//避免添加同样的组两次
vbConsistentGroup[iG]=true; //this avoid to include the same group more than once
}
//如果连续性的值大于一定的阈值
if(nCurrentConsistency>=mnCovisibilityConsistencyTh && !bEnoughConsistent)
{
mvpEnoughConsistentCandidates.push_back(pCandidateKF);
bEnoughConsistent=true; //this avoid to insert the same candidate more than once
}
}
}//for
// If the group is not consistent with any previous group insert with consistency counter set to zero
//如果候选的共视帧的组和之前的组没有任何的连续性,那么连续性计数设置为0
if(!bConsistentForSomeGroup)
{
ConsistentGroup cg = make_pair(spCandidateGroup,0);
vCurrentConsistentGroups.push_back(cg);
}
}//for
// Update Covisibility Consistent Groups
//更新共视帧连续组的数据
mvConsistentGroups = vCurrentConsistentGroups;
// Add Current Keyframe to database
//将当前帧添加到数据库
mpKeyFrameDB->add(mpCurrentKF);
//如果连续帧候选组不为空,那么就是检测到回环了
if(mvpEnoughConsistentCandidates.empty())
{
mpCurrentKF->SetErase();
return false;
}
else
{
return true;
}
mpCurrentKF->SetErase();
return false;
}
/**
* 计算Sim3
*
*/
bool LoopClosing::ComputeSim3()
{
// For each consistent loop candidate we try to compute a Sim3
const int nInitialCandidates = mvpEnoughConsistentCandidates.size();
// We compute first ORB matches for each candidate
// If enough matches are found, we setup a Sim3Solver
//首先计算每个候选帧的ORB匹配特征,如果找到足够的匹配特征点,就设置一个Sim3Solver
ORBmatcher matcher(0.75,true);
vector<Sim3Solver*> vpSim3Solvers;
vpSim3Solvers.resize(nInitialCandidates);
vector<vector<MapPoint*> > vvpMapPointMatches;
vvpMapPointMatches.resize(nInitialCandidates);
vector<bool> vbDiscarded;
vbDiscarded.resize(nInitialCandidates);
int nCandidates=0; //candidates with enough matches
for(int i=0; i<nInitialCandidates; i++)
{
KeyFrame* pKF = mvpEnoughConsistentCandidates[i];
// avoid that local mapping erase it while it is being processed in this thread
pKF->SetNotErase();
if(pKF->isBad())
{
vbDiscarded[i] = true;
continue;
}
//将候选帧和当前帧进行ORB匹配
int nmatches = matcher.SearchByBoW(mpCurrentKF,pKF,vvpMapPointMatches[i]);
if(nmatches<20)
{
vbDiscarded[i] = true;
continue;
}
//如果匹配特征点数目>=20就进行Sim3解析
else
{
Sim3Solver* pSolver = new Sim3Solver(mpCurrentKF,pKF,vvpMapPointMatches[i],mbFixScale);
pSolver->SetRansacParameters(0.99,20,300);
vpSim3Solvers[i] = pSolver;
}
nCandidates++;
}//for
bool bMatch = false;
// Perform alternatively RANSAC iterations for each candidate
// until one is succesful or all fail
//对每一个候选帧执行RANSAC迭代,直到找到一个候选帧成功找到Sim3解或者失败
while(nCandidates>0 && !bMatch)
{
//遍历每一个候选帧
for(int i=0; i<nInitialCandidates; i++)
{
if(vbDiscarded[i])
continue;
KeyFrame* pKF = mvpEnoughConsistentCandidates[i];
// Perform 5 Ransac Iterations
vector<bool> vbInliers;
int nInliers;
bool bNoMore;
//执行5次Ransac跌代
Sim3Solver* pSolver = vpSim3Solvers[i];
cv::Mat Scm = pSolver->iterate(5,bNoMore,vbInliers,nInliers);
// If Ransac reachs max. iterations discard keyframe
//如果达到最大迭代次数,但是没有收敛,那么放弃这个关键帧
if(bNoMore)
{
vbDiscarded[i]=true;
nCandidates--;
}
// If RANSAC returns a Sim3, perform a guided matching and optimize with all correspondences
//如果返回了Sim3解,那么优化所有的匹配特征点
if(!Scm.empty())
{
//将Sim3迭代后的inlier的MapPoint添加到vpMapPointMatches
vector<MapPoint*> vpMapPointMatches(vvpMapPointMatches[i].size(), static_cast<MapPoint*>(NULL));
for(size_t j=0, jend=vbInliers.size(); j<jend; j++)
{
if(vbInliers[j])
vpMapPointMatches[j]=vvpMapPointMatches[i][j];
}
//获取Sim3求解的R,t,s
cv::Mat R = pSolver->GetEstimatedRotation();
cv::Mat t = pSolver->GetEstimatedTranslation();
const float s = pSolver->GetEstimatedScale();
//根据R,t,s在mpCurrentKF和pKF之间寻找更多的匹配特征点,并更新vpMapPointMatches
matcher.SearchBySim3(mpCurrentKF,pKF,vpMapPointMatches,s,R,t,7.5);
//优化Sim3,gScm表示候选帧pKF到当前帧mpCurrentKF的Sim3变换
g2o::Sim3 gScm(Converter::toMatrix3d(R),Converter::toVector3d(t),s);
const int nInliers = Optimizer::OptimizeSim3(mpCurrentKF, pKF, vpMapPointMatches, gScm, 10, mbFixScale);
// If optimization is succesful stop ransacs and continue
if(nInliers>=20)
{
//找到了闭环帧了
bMatch = true;
mpMatchedKF = pKF;
//世界坐标系到候选帧的Sim3变换
g2o::Sim3 gSmw(Converter::toMatrix3d(pKF->GetRotation()),Converter::toVector3d(pKF->GetTranslation()),1.0);
//世界坐标系到当前帧的Sim3变换Tw-curr = Tw-candidate * Tcandidate-curr
mg2oScw = gScm*gSmw;
//世界坐标系到当前帧的变换
mScw = Converter::toCvMat(mg2oScw);
mvpCurrentMatchedPoints = vpMapPointMatches;
//找到一个闭环帧就退出循环
break;
}
}//if
}//for
}//while
if(!bMatch)
{
for(int i=0; i<nInitialCandidates; i++)
mvpEnoughConsistentCandidates[i]->SetErase();
mpCurrentKF->SetErase();
return false;
}
// Retrieve MapPoints seen in Loop Keyframe and neighbors
//将和mpMatchedKF共视的帧全部取出来,将这些帧的MapPoint全部放入mvpLoopMapPoints
vector<KeyFrame*> vpLoopConnectedKFs = mpMatchedKF->GetVectorCovisibleKeyFrames();
vpLoopConnectedKFs.push_back(mpMatchedKF);
mvpLoopMapPoints.clear();
for(vector<KeyFrame*>::iterator vit=vpLoopConnectedKFs.begin(); vit!=vpLoopConnectedKFs.end(); vit++)
{
KeyFrame* pKF = *vit;
vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches();
for(size_t i=0, iend=vpMapPoints.size(); i<iend; i++)
{
MapPoint* pMP = vpMapPoints[i];
if(pMP)
{
if(!pMP->isBad() && pMP->mnLoopPointForKF!=mpCurrentKF->mnId)
{
mvpLoopMapPoints.push_back(pMP);
//标记MapPoint已经添加,避免重复添加
pMP->mnLoopPointForKF=mpCurrentKF->mnId;
}
}
}
}
// Find more matches projecting with the computed Sim3
/**将mvpLoopMapPoints中的地图点重新投影到当前帧,寻找更多的匹配*/
matcher.SearchByProjection(mpCurrentKF, mScw, mvpLoopMapPoints, mvpCurrentMatchedPoints,10);
// If enough matches accept Loop
//统计合理的匹配点
int nTotalMatches = 0;
for(size_t i=0; i<mvpCurrentMatchedPoints.size(); i++)
{
if(mvpCurrentMatchedPoints[i])
nTotalMatches++;
}
//当匹配点数目大于40,除了mpMatchedKF,mvpEnoughConsistentCandidates其它都可以被删除
if(nTotalMatches>=40)
{
for(int i=0; i<nInitialCandidates; i++)
if(mvpEnoughConsistentCandidates[i]!=mpMatchedKF)
mvpEnoughConsistentCandidates[i]->SetErase();
return true;
}
//当匹配点数目<=40,mvpEnoughConsistentCandidates都可以被删除
else
{
for(int i=0; i<nInitialCandidates; i++)
mvpEnoughConsistentCandidates[i]->SetErase();
mpCurrentKF->SetErase();
return false;
}
}
/**
* 纠正回环关键帧的位姿
* */
void LoopClosing::CorrectLoop()
{
cout << "Loop detected!" << endl;
// Send a stop signal to Local Mapping
// Avoid new keyframes are inserted while correcting the loop
//发送信号给LocalMapping,避免在纠正loop的时候不能添加关键帧
mpLocalMapper->RequestStop();
// If a Global Bundle Adjustment is running, abort it
//如果正在执行全局优化,退出
if(isRunningGBA())
{
unique_lock<mutex> lock(mMutexGBA);
mbStopGBA = true;
mnFullBAIdx++;
if(mpThreadGBA)
{
mpThreadGBA->detach();
delete mpThreadGBA;
}
}
// Wait until Local Mapping has effectively stopped
//等待在LocalMapping停止
while(!mpLocalMapper->isStopped())
{
usleep(1000);
}
// Ensure current keyframe is updated
//保证当前关键帧的共视关系是最新的
mpCurrentKF->UpdateConnections();
// Retrive keyframes connected to the current keyframe and compute corrected Sim3 pose by propagation
//取出当前帧的共视帧
mvpCurrentConnectedKFs = mpCurrentKF->GetVectorCovisibleKeyFrames();
mvpCurrentConnectedKFs.push_back(mpCurrentKF);
KeyFrameAndPose CorrectedSim3, NonCorrectedSim3;
CorrectedSim3[mpCurrentKF]=mg2oScw;
//获取从相机坐标系到世界坐标系转换的变换
cv::Mat Twc = mpCurrentKF->GetPoseInverse();
{
// Get Map Mutex
unique_lock<mutex> lock(mpMap->mMutexMapUpdate);
for(vector<KeyFrame*>::iterator vit=mvpCurrentConnectedKFs.begin(), vend=mvpCurrentConnectedKFs.end(); vit!=vend; vit++)
{
KeyFrame* pKFi = *vit;
//第i个关键帧到世界坐标系中的坐标
cv::Mat Tiw = pKFi->GetPose();
if(pKFi!=mpCurrentKF)
{
//得到当前帧到第i个关键帧的变换
cv::Mat Tic = Tiw*Twc;
cv::Mat Ric = Tic.rowRange(0,3).colRange(0,3);
cv::Mat tic = Tic.rowRange(0,3).col(3);
//构造Sim3
g2o::Sim3 g2oSic(Converter::toMatrix3d(Ric),Converter::toVector3d(tic),1.0);
//得到世界坐标系到第i个关键帧的变换
g2o::Sim3 g2oCorrectedSiw = g2oSic*mg2oScw;
//Pose corrected with the Sim3 of the loop closure
//CorrectedSim3里面首先算出关键帧相对于当前帧的Pose,然后再相对于当前帧的优化后的SimPose,得到关键帧的Sim3Pose
CorrectedSim3[pKFi]=g2oCorrectedSiw;
}
cv::Mat Riw = Tiw.rowRange(0,3).colRange(0,3);
cv::Mat tiw = Tiw.rowRange(0,3).col(3);
//第i个关键帧在世界坐标系中的Sim3 Pose
g2o::Sim3 g2oSiw(Converter::toMatrix3d(Riw),Converter::toVector3d(tiw),1.0);
//Pose without correction
NonCorrectedSim3[pKFi]=g2oSiw;
}
// Correct all MapPoints obsrved by current keyframe and neighbors, so that they align with the other side of the loop
//纠正当前关键帧和相邻关键帧观测到的所有地图点,这样它们就可以和回环的另外一侧align
for(KeyFrameAndPose::iterator mit=CorrectedSim3.begin(), mend=CorrectedSim3.end(); mit!=mend; mit++)
{
KeyFrame* pKFi = mit->first;
g2o::Sim3 g2oCorrectedSiw = mit->second;
g2o::Sim3 g2oCorrectedSwi = g2oCorrectedSiw.inverse();
g2o::Sim3 g2oSiw =NonCorrectedSim3[pKFi];
vector<MapPoint*> vpMPsi = pKFi->GetMapPointMatches();
for(size_t iMP=0, endMPi = vpMPsi.size(); iMP<endMPi; iMP++)
{
MapPoint* pMPi = vpMPsi[iMP];
if(!pMPi)
continue;
if(pMPi->isBad())
continue;
if(pMPi->mnCorrectedByKF==mpCurrentKF->mnId)
continue;
// Project with non-corrected pose and project back with corrected pose
cv::Mat P3Dw = pMPi->GetWorldPos();
Eigen::Matrix<double,3,1> eigP3Dw = Converter::toVector3d(P3Dw);
//因为MapPoint相对于关键帧的Pose不会变换,所以首先用没有矫正的相机
//Pose将MapPoint从世界坐标系转换到i个关键帧的坐标系
//然后用矫正的相机Pose将MapPoint从i个关键帧坐标系转换到世界坐标系
Eigen::Matrix<double,3,1> eigCorrectedP3Dw = g2oCorrectedSwi.map(g2oSiw.map(eigP3Dw));
//更新地图点的Pose
cv::Mat cvCorrectedP3Dw = Converter::toCvMat(eigCorrectedP3Dw);
pMPi->SetWorldPos(cvCorrectedP3Dw);
pMPi->mnCorrectedByKF = mpCurrentKF->mnId;
pMPi->mnCorrectedReference = pKFi->mnId;
pMPi->UpdateNormalAndDepth();
}
// Update keyframe pose with corrected Sim3. First transform Sim3 to SE3 (scale translation)
//使用Sim3更新关键帧的Pose,首先将Sim3转换为SE3,
Eigen::Matrix3d eigR = g2oCorrectedSiw.rotation().toRotationMatrix();
Eigen::Vector3d eigt = g2oCorrectedSiw.translation();
double s = g2oCorrectedSiw.scale();
//缩放平移变换
eigt *=(1./s); //[R t/s;0 1]
//将Sim3转换为SE3
cv::Mat correctedTiw = Converter::toCvSE3(eigR,eigt);
pKFi->SetPose(correctedTiw);
// Make sure connections are updated
pKFi->UpdateConnections();
}
// Start Loop Fusion
// Update matched map points and replace if duplicated
//开始回环融合,更新匹配的地图点,如果重复了就替换,
//mvpCurrentMatchedPoints的大小就是当前帧的地图点的大小
for(size_t i=0; i<mvpCurrentMatchedPoints.size(); i++)
{
if(mvpCurrentMatchedPoints[i])
{
MapPoint* pLoopMP = mvpCurrentMatchedPoints[i];
MapPoint* pCurMP = mpCurrentKF->GetMapPoint(i);
//如果当前帧已经有这个地图点,那么用闭环帧的地图点进行替换
if(pCurMP)
pCurMP->Replace(pLoopMP);
else //如果当前帧没有这个地图点,那么添加这个闭环地图点
{
mpCurrentKF->AddMapPoint(pLoopMP,i);
pLoopMP->AddObservation(mpCurrentKF,i);
pLoopMP->ComputeDistinctiveDescriptors();
}
}
}
}
// Project MapPoints observed in the neighborhood of the loop keyframe
// into the current keyframe and neighbors using corrected poses.
// Fuse duplications.
SearchAndFuse(CorrectedSim3);
// After the MapPoint fusion, new links in the covisibility graph will appear attaching both sides of the loop
map<KeyFrame*, set<KeyFrame*> > LoopConnections;
//遍历当前帧的关联帧
for(vector<KeyFrame*>::iterator vit=mvpCurrentConnectedKFs.begin(), vend=mvpCurrentConnectedKFs.end(); vit!=vend; vit++)
{
//取一个关键帧
KeyFrame* pKFi = *vit;
//取更新之前的该关键帧的共视帧
vector<KeyFrame*> vpPreviousNeighbors = pKFi->GetVectorCovisibleKeyFrames();
// Update connections. Detect new links.
pKFi->UpdateConnections();
//取更新之后的共视帧
LoopConnections[pKFi]=pKFi->GetConnectedKeyFrames();
for(vector<KeyFrame*>::iterator vit_prev=vpPreviousNeighbors.begin(), vend_prev=vpPreviousNeighbors.end(); vit_prev!=vend_prev; vit_prev++)
{
//删除老的共视帧信息
LoopConnections[pKFi].erase(*vit_prev);
}
for(vector<KeyFrame*>::iterator vit2=mvpCurrentConnectedKFs.begin(), vend2=mvpCurrentConnectedKFs.end(); vit2!=vend2; vit2++)
{
//删除老的共视帧信息
LoopConnections[pKFi].erase(*vit2);
}
}
// Optimize graph
Optimizer::OptimizeEssentialGraph(mpMap, mpMatchedKF, mpCurrentKF, NonCorrectedSim3, CorrectedSim3, LoopConnections, mbFixScale);
mpMap->InformNewBigChange();
// Add loop edge
//添加新的回环边
mpMatchedKF->AddLoopEdge(mpCurrentKF);
mpCurrentKF->AddLoopEdge(mpMatchedKF);
// Launch a new thread to perform Global Bundle Adjustment
mbRunningGBA = true;
mbFinishedGBA = false;
mbStopGBA = false;
mpThreadGBA = new thread(&LoopClosing::RunGlobalBundleAdjustment,this,mpCurrentKF->mnId);
// Loop closed. Release Local Mapping.
mpLocalMapper->Release();
mLastLoopKFid = mpCurrentKF->mnId;
}
/**
* 将mvpLoopMapPoints投影到关键帧,然后融合重复的MapPoint
* CorrectedPosesMap:关键帧及其Sim3Pose
* */
void LoopClosing::SearchAndFuse(const KeyFrameAndPose &CorrectedPosesMap)
{
ORBmatcher matcher(0.8);
for(KeyFrameAndPose::const_iterator mit=CorrectedPosesMap.begin(), mend=CorrectedPosesMap.end(); mit!=mend;mit++)
{
KeyFrame* pKF = mit->first;
//Sim3 --> SE3
g2o::Sim3 g2oScw = mit->second;
cv::Mat cvScw = Converter::toCvMat(g2oScw);
// Project mvpLoopMapPoints into pKF using a given Sim3 and search for duplicated MapPoints.
vector<MapPoint*> vpReplacePoints(mvpLoopMapPoints.size(),static_cast<MapPoint*>(NULL));
matcher.Fuse(pKF,cvScw,mvpLoopMapPoints,4,vpReplacePoints);
// Get Map Mutex
//替换MapPoint
unique_lock<mutex> lock(mpMap->mMutexMapUpdate);
const int nLP = mvpLoopMapPoints.size();
for(int i=0; i<nLP;i++)
{
MapPoint* pRep = vpReplacePoints[i];
if(pRep)
{
pRep->Replace(mvpLoopMapPoints[i]);
}
}
}
}
void LoopClosing::RequestReset()
{
{
unique_lock<mutex> lock(mMutexReset);
mbResetRequested = true;
}
while(1)
{
{
unique_lock<mutex> lock2(mMutexReset);
if(!mbResetRequested)
break;
}
usleep(5000);
}
}
void LoopClosing::ResetIfRequested()
{
unique_lock<mutex> lock(mMutexReset);
if(mbResetRequested)
{
mlpLoopKeyFrameQueue.clear();
mLastLoopKFid=0;
mbResetRequested=false;
}
}
/**
*
*/
void LoopClosing::RunGlobalBundleAdjustment(unsigned long nLoopKF)
{
cout << "Starting Global Bundle Adjustment" << endl;
/**迭代10次 */
int idx = mnFullBAIdx;
Optimizer::GlobalBundleAdjustemnt(mpMap,10,&mbStopGBA,nLoopKF,false);
// Update all MapPoints and KeyFrames
// Local Mapping was active during BA, that means that there might be new keyframes
// not included in the Global BA and they are not consistent with the updated map.
// We need to propagate the correction through the spanning tree
/**更新MapPoint和关键帧
* 在BA的时候LocalMapping是工作的,这就意味着在Global BA之间会有新的关键帧插入进来,
* 但是这些新加入的帧和更新的Map是不一致的,因此我们需要通过Spanning tree传导这个纠正
*/
{
unique_lock<mutex> lock(mMutexGBA);
if(idx!=mnFullBAIdx)
return;
if(!mbStopGBA)
{
cout << "Global Bundle Adjustment finished" << endl;
cout << "Updating map ..." << endl;
mpLocalMapper->RequestStop();
// Wait until Local Mapping has effectively stopped
while(!mpLocalMapper->isStopped() && !mpLocalMapper->isFinished())
{
usleep(1000);
}
// Get Map Mutex
unique_lock<mutex> lock(mpMap->mMutexMapUpdate);
// Correct keyframes starting at map first keyframe
list<KeyFrame*> lpKFtoCheck(mpMap->mvpKeyFrameOrigins.begin(),mpMap->mvpKeyFrameOrigins.end());
//通过SpanningTree来遍历所有关键帧,纠正没有参与到BA中的关键帧的Pose
while(!lpKFtoCheck.empty())
{
//取KeyFrame
KeyFrame* pKF = lpKFtoCheck.front();
const set<KeyFrame*> sChilds = pKF->GetChilds();
cv::Mat Twc = pKF->GetPoseInverse();
//遍历childs
for(set<KeyFrame*>::const_iterator sit=sChilds.begin();sit!=sChilds.end();sit++)
{
KeyFrame* pChild = *sit;
//如果没有处理过
if(pChild->mnBAGlobalForKF!=nLoopKF)
{
//求出pKF到pChild的变换,然后再根据BA优化后的pKF的位姿,调整pChild的位姿
cv::Mat Tchildc = pChild->GetPose()*Twc;
pChild->mTcwGBA = Tchildc*pKF->mTcwGBA;//*Tcorc*pKF->mTcwGBA;
pChild->mnBAGlobalForKF=nLoopKF;//标记为已经处理过的
}
//将child 关键帧添加到list中继续处理
lpKFtoCheck.push_back(pChild);
}
//pKF在BA之前的Pose
pKF->mTcwBefGBA = pKF->GetPose();
//更新pKF的Pose为BA之后的Pose
pKF->SetPose(pKF->mTcwGBA);
lpKFtoCheck.pop_front();
}
// Correct MapPoints
const vector<MapPoint*> vpMPs = mpMap->GetAllMapPoints();
for(size_t i=0; i<vpMPs.size(); i++)
{
MapPoint* pMP = vpMPs[i];
if(pMP->isBad())
continue;
//如果已经参与过BA优化,那么更新Pose
if(pMP->mnBAGlobalForKF==nLoopKF)
{
// If optimized by Global BA, just update
pMP->SetWorldPos(pMP->mPosGBA);
}
else
{
// Update according to the correction of its reference keyframe
KeyFrame* pRefKF = pMP->GetReferenceKeyFrame();
//如果MapPoint的参考帧pRefKF没有纠正过位姿,那么无法进行也无法纠正MapPoint的位姿.
if(pRefKF->mnBAGlobalForKF!=nLoopKF)
continue;
// Map to non-corrected camera
//通过BA之前的Pose投影到camera坐标系
cv::Mat Rcw = pRefKF->mTcwBefGBA.rowRange(0,3).colRange(0,3);
cv::Mat tcw = pRefKF->mTcwBefGBA.rowRange(0,3).col(3);
cv::Mat Xc = Rcw*pMP->GetWorldPos()+tcw;
// Backproject using corrected camera
//用BA之后的Pose反投影到世界坐标系
cv::Mat Twc = pRefKF->GetPoseInverse();
cv::Mat Rwc = Twc.rowRange(0,3).colRange(0,3);
cv::Mat twc = Twc.rowRange(0,3).col(3);
pMP->SetWorldPos(Rwc*Xc+twc);
}
}
mpMap->InformNewBigChange();
mpLocalMapper->Release();
cout << "Map updated!" << endl;
}
mbFinishedGBA = true;
mbRunningGBA = false;
}
}
void LoopClosing::RequestFinish()
{
unique_lock<mutex> lock(mMutexFinish);
mbFinishRequested = true;
}
bool LoopClosing::CheckFinish()
{
unique_lock<mutex> lock(mMutexFinish);
return mbFinishRequested;
}
void LoopClosing::SetFinish()
{
unique_lock<mutex> lock(mMutexFinish);
mbFinished = true;
}
bool LoopClosing::isFinished()
{
unique_lock<mutex> lock(mMutexFinish);
return mbFinished;
}
} //namespace ORB_SLAM