2018-10-09

/**轉載自https://blog.csdn.net/xiaoxiaowenqiang/article/details/79281122

* This file is part of ORB-SLAM2.

*

* LocalMapping作用是將Tracking中送來的關鍵幀放在mlNewKeyFrame列表中;

* 處理新關鍵幀,地圖點檢查剔除,生成新地圖點,Local BA,關鍵幀剔除。

* 主要工作在于維護局部地圖,也就是SLAM中的Mapping。

*

*

* Tracking線程 只是判斷當前幀是否需要加入關鍵幀,并沒有真的加入地圖,

* 因為Tracking線程的主要功能是局部定位,

*

* 而處理地圖中的關鍵幀,地圖點,包括如何加入,

* 如何刪除的工作是在LocalMapping線程完成的

*

* 建圖

* 處理新的關鍵幀 KeyFrame 完成局部地圖構建

* 插入關鍵幀 ------>? 處理地圖點(篩選生成的地圖點 生成地圖點)? -------->? 局部 BA最小化重投影誤差? -調整-------->? 篩選 新插入的 關鍵幀

*

* mlNewKeyFrames? ? list 列表隊列存儲關鍵幀

* 1】檢查隊列

*? ? ? CheckNewKeyFrames();

*

* 2】處理新關鍵幀 Proces New Key Frames

* ProcessNewKeyFrame(); 更新地圖點MapPoints 和 關鍵幀 KepFrame 的關聯關系? UpdateConnections() 更新關聯關系

*

* 3】剔除 地圖點 MapPoints

*? ? ? 刪除地圖中新添加的但 質量不好的 地圖點

*? ? ? a)? IncreaseFound 共視點? / IncreaseVisible? 投影在圖像上 < 25%

*? ? ? b) 觀測到該 點的關鍵幀太少

*

* 4】生成 地圖點 MapPoints

* 運動過程中和共視程度比較高的 關鍵幀 通過三角化 恢復出的一些地圖點

*

* 5】地圖點融合 MapPoints

*? ? ? 檢測當前關鍵幀和相鄰 關鍵幀(兩級相鄰) 中 重復的 地圖點 留下觀測幀高的 地圖點

*

* 6】局部 BA 最小化重投影誤差

*? ? ? 和當前關鍵幀相鄰的關鍵幀 中相匹配的 地圖點對 局部 BA最小化重投影誤差優化點坐標 和 位姿

*

* 7】關鍵幀剔除

*? ? ? 其90%以上的 地圖點 能夠被其他 共視 關鍵幀(至少3個) 觀測到,認為該關鍵幀多余,可以刪除

*

*/

#include "LocalMapping.h"

#include "LoopClosing.h"

#include "ORBmatcher.h"

#include "Optimizer.h"

#include<mutex>

namespace ORB_SLAM2

{

LocalMapping::LocalMapping(Map *pMap, const float bMonocular):

? ? mbMonocular(bMonocular), mbResetRequested(false), mbFinishRequested(false), mbFinished(true), mpMap(pMap),

? ? mbAbortBA(false), mbStopped(false), mbStopRequested(false), mbNotStop(false), mbAcceptKeyFrames(true)

{

}

void LocalMapping::SetLoopCloser(LoopClosing* pLoopCloser)

{

? ? mpLoopCloser = pLoopCloser;

}

void LocalMapping::SetTracker(Tracking *pTracker)

{

? ? mpTracker=pTracker;

}

void LocalMapping::Run()

{

? ? mbFinished = false;

? ? while(1)

? ? {

// Tracking will see that Local Mapping is busy

// 步驟1:設置進程間的訪問標志 告訴Tracking線程,LocalMapping線程正在處理新的關鍵幀,處于繁忙狀態

? ? ? ? ? ? ? // LocalMapping線程處理的關鍵幀都是Tracking線程發過的

? ? ? ? ? ? ? // 在LocalMapping線程還沒有處理完關鍵幀之前Tracking線程最好不要發送太快

SetAcceptKeyFrames(false);

// Check if there are keyframes in the queue

// 等待處理的關鍵幀列表不為空

if(CheckNewKeyFrames())

{

? ? // BoW conversion and insertion in Map

// 步驟2:計算關鍵幀特征點的詞典單詞向量BoW映射,將關鍵幀插入地圖

? ? ProcessNewKeyFrame();

? ? // Check recent MapPoints

? // 剔除ProcessNewKeyFrame函數中引入的不合格MapPoints

// 步驟3:對新添加的地圖點融合 對于 ProcessNewKeyFrame 和 CreateNewMapPoints 中 最近添加的MapPoints進行檢查剔除 ? ?

? //? MapPointCulling();

? ? // Triangulate new MapPoints


// 步驟4: 創建新的地圖點 相機運動過程中與相鄰關鍵幀通過三角化恢復出一些新的地圖點MapPoints ? ?

? ? CreateNewMapPoints();


? ? MapPointCulling();// 從上面 移到下面

? ? ? // 已經處理完隊列中的最后的一個關鍵幀

? ? if(!CheckNewKeyFrames())

? ? {

// Find more matches in neighbor keyframes and fuse point duplications

// 步驟5:相鄰幀地圖點融合 檢查并融合當前關鍵幀與相鄰幀(兩級相鄰)重復的MapPoints

SearchInNeighbors();

? ? }

? ? mbAbortBA = false;

// 已經處理完隊列中的最后的一個關鍵幀,并且閉環檢測沒有請求停止LocalMapping

? ? if(!CheckNewKeyFrames() && !stopRequested())

? ? {

// 步驟6:局部地圖優化 Local BA

if(mpMap->KeyFramesInMap() > 2)

? ? Optimizer::LocalBundleAdjustment(mpCurrentKeyFrame,&mbAbortBA, mpMap);

// 步驟7: 關鍵幀融合 檢測并剔除當前幀相鄰的關鍵幀中冗余的關鍵幀 Check redundant local Keyframes

? ? ? ? ? ? ? ? // 剔除的標準是:該關鍵幀的90%的MapPoints可以被其它關鍵幀觀測到

// Tracking中先把關鍵幀交給LocalMapping線程

// 并且在Tracking中InsertKeyFrame函數的條件比較松,交給LocalMapping線程的關鍵幀會比較密

? ? ? ? ? ? ? ? ? ? ? ? // 在這里再刪除冗余的關鍵幀

KeyFrameCulling();

? ? }

// 步驟8:將當前幀加入到閉環檢測隊列中

? ? mpLoopCloser->InsertKeyFrame(mpCurrentKeyFrame);

}

// 步驟9:等待線程空閑 完成一幀關鍵幀的插入融合工作

else if(Stop())

{

? ? // Safe area to stop

? ? while(isStopped() && !CheckFinish())

? ? {

usleep(3000);

? ? }

? ? if(CheckFinish())//檢查 是否完成

break;

}

? ? ? ? ? ? ? // 檢查重置

ResetIfRequested();

// Tracking will see that Local Mapping is not busy

// 步驟10:告訴 Tracking 線程? Local Mapping 線程 空閑 可一處理接收 下一個 關鍵幀

SetAcceptKeyFrames(true);

if(CheckFinish())

? ? break;

usleep(3000);

? ? }

? ? SetFinish();

}

/**

* @brief 插入關鍵幀

*

* 將關鍵幀插入到地圖中,以便將來進行局部地圖優化

* 這里僅僅是將關鍵幀插入到列表中進行等待

* @param pKF KeyFrame

*/

void LocalMapping::InsertKeyFrame(KeyFrame *pKF)

{

? ? unique_lock<mutex> lock(mMutexNewKFs);

? ? // 將關鍵幀插入到 等待處理的關鍵幀列表中

? ? mlNewKeyFrames.push_back(pKF);

? ? mbAbortBA=true;// BA優化停止

}

/**

* @brief? ? 查看列表中是否有等待 被處理的關鍵幀

* @return 如果存在,返回true

*/

bool LocalMapping::CheckNewKeyFrames()

{

? ? unique_lock<mutex> lock(mMutexNewKFs);

? ? return(!mlNewKeyFrames.empty());// 等待處理的關鍵幀列表是否為空

}

? /*

? a. 根據詞典 計算當前關鍵幀Bow,便于后面三角化恢復新地圖點;

? b. 將TrackLocalMap中跟蹤局部地圖匹配上的地圖點綁定到當前關鍵幀

? ? ? (在Tracking線程中只是通過匹配進行局部地圖跟蹤,優化當前關鍵幀姿態),

? ? ? 也就是在graph 中加入當前關鍵幀作為node,并更新edge。


? ? ? 而CreateNewMapPoint()中則通過當前關鍵幀,在局部地圖中添加與新的地圖點;

? c. 更新加入當前關鍵幀之后關鍵幀之間的連接關系,包括更新Covisibility圖和Essential圖

? (最小生成樹spanning tree,共視關系好的邊subset of edges from covisibility graph

? ? with high covisibility (θ=100), 閉環邊)。

? ? */


? /**

* @brief 處理列表中的關鍵幀

*

* - 計算Bow,加速三角化新的MapPoints

* - 關聯當前關鍵幀至MapPoints,并更新MapPoints的平均觀測方向和觀測距離范圍

* - 插入關鍵幀,更新Covisibility圖和Essential圖

* @see VI-A keyframe insertion

*/

void LocalMapping::ProcessNewKeyFrame()

{ ?

// 步驟1:從緩沖隊列中取出一幀待處理的關鍵幀

? ? ? ? ? ? // Tracking線程向LocalMapping中插入關鍵幀存在該隊列中

? ? {

unique_lock<mutex> lock(mMutexNewKFs);

// 從列表中獲得一個等待被插入的關鍵幀

mpCurrentKeyFrame = mlNewKeyFrames.front();

mlNewKeyFrames.pop_front();// 出隊

? ? }

? ? // Compute Bags of Words structures

// 步驟2:計算該關鍵幀特征點的Bow映射關系? ?

? ? //? 根據詞典 計算當前關鍵幀Bow,便于后面三角化恢復新地圖點? ?

? ? mpCurrentKeyFrame->ComputeBoW();// 幀描述子 用字典單詞線性表示的 向量

? ? // Associate MapPoints to the new keyframe and update normal and descriptor

? ? // 當前關鍵幀? TrackLocalMap中跟蹤局部地圖 匹配上的 地圖點

// 步驟3:跟蹤局部地圖過程中新匹配上的MapPoints和當前關鍵幀綁定

? ? ? // 在TrackLocalMap函數中將局部地圖中的MapPoints與當前幀進行了匹配,

? ? ? // 但沒有對這些匹配上的MapPoints與當前幀進行關聯? ?

? ? const vector<MapPoint*> vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches();

? ? for(size_t i=0; i<vpMapPointMatches.size(); i++)

? ? {

MapPoint* pMP = vpMapPointMatches[i];// 每一個與當前關鍵幀匹配好的地圖點

if(pMP)//地圖點存在

{

? ? if(!pMP->isBad())

? ? {

? ? ? // 為當前幀在tracking過重跟蹤到的MapPoints更新屬性

if(!pMP->IsInKeyFrame(mpCurrentKeyFrame))//下視野內

{

? ? pMP->AddObservation(mpCurrentKeyFrame, i);// 地圖點添加關鍵幀

? ? pMP->UpdateNormalAndDepth();// 地圖點更新 平均觀測方向 和 觀測距離深度

? ? pMP->ComputeDistinctiveDescriptors();// 加入關鍵幀后,更新地圖點的最佳描述子

}

else // 雙目追蹤時插入的點 可能不在 幀上this can only happen for new stereo points inserted by the Tracking

{

? // 將雙目或RGBD跟蹤過程中新插入的MapPoints放入mlpRecentAddedMapPoints,等待檢查

? ? ? ? ? ? ? ? ? ? ? ? ? // CreateNewMapPoints函數中通過三角化也會生成MapPoints

? ? ? ? ? ? ? ? ? ? ? ? ? // 這些MapPoints都會經過MapPointCulling函數的檢驗

? ? mlpRecentAddedMapPoints.push_back(pMP);

? ? // 候選待檢查地圖點存放在mlpRecentAddedMapPoints

}

? ? }

}

? ? }? ?

? ? // Update links in the Covisibility Graph

// 步驟4:更新關鍵幀間的連接關系,Covisibility圖和Essential圖(tree)

? ? mpCurrentKeyFrame->UpdateConnections();

? ? // Insert Keyframe in Map

// 步驟5:將該關鍵幀插入到地圖中

? ? mpMap->AddKeyFrame(mpCurrentKeyFrame);

}

/**

* @brief 剔除ProcessNewKeyFrame(不在幀上的地圖點 進入待查list)和

*? ? ? ? ? ? CreateNewMapPoints(兩幀三角變換產生的新地圖點進入 待查list)

*? ? ? ? ? ? 函數中引入的質量不好的MapPoints

* @see VI-B recent map points culling

*/

// 對于ProcessNewKeyFrame和CreateNewMapPoints中最近添加的MapPoints進行檢查剔除

void LocalMapping::MapPointCulling()

{

? ? // Check Recent Added MapPoints

? ? list<MapPoint*>::iterator lit = mlpRecentAddedMapPoints.begin();//待檢測的地圖點 迭代器

? ? const unsigned long int nCurrentKFid = mpCurrentKeyFrame->mnId;//當前關鍵幀id

? ? //? 從添加該地圖點的關鍵幀算起的 初始三個關鍵幀,

? ? // 第一幀不算,后面兩幀看到該地圖點的幀數,對于單目<=2,對于雙目和RGBD<=3;

? ? int nThObs;

? ? if(mbMonocular)

nThObs = 2;

? ? else

nThObs = 3;

? ? const int cnThObs = nThObs;

// 遍歷等待檢查的地圖點MapPoints

? ? while(lit !=mlpRecentAddedMapPoints.end())

? ? {

MapPoint* pMP = *lit;//? 新添加的地圖點

if(pMP->isBad())

{

// 步驟1:已經是壞點的MapPoints直接從檢查鏈表中刪除 ?

? ? lit = mlpRecentAddedMapPoints.erase(lit);

}

//? 跟蹤(匹配上)到該地圖點的普通幀幀數(IncreaseFound)< 應該觀測到該地圖點的普通幀數量(25%*IncreaseVisible):

// 該地圖點雖在視野范圍內,但很少被普通幀檢測到。 剔除

else if(pMP->GetFoundRatio()<0.25f )

{

// 步驟2:將不滿足VI-B條件的MapPoint剔除

// VI-B 條件1:

// 跟蹤到該MapPoint的Frame數相比預計可觀測到該MapPoint的Frame數的比例需大于25%

// IncreaseFound() / IncreaseVisible(該地圖點在視野范圍內) < 25%,注意不一定是關鍵幀。 ?

? ? pMP->SetBadFlag();

? ? lit = mlpRecentAddedMapPoints.erase(lit);//從待查? list中刪除

}

//

// 初始三個關鍵幀 地圖點觀測次數 不能太少

// 而且單目的要求更嚴格,需要三幀都看到

else if(( (int)nCurrentKFid - (int)pMP->mnFirstKFid) >=2 && pMP->Observations() <= cnThObs)

{

? // 步驟3:將不滿足VI-B條件的MapPoint剔除

? ? ? ? ? ? // VI-B 條件2:從該點建立開始,到現在已經過了不小于2幀,

? ? ? ? ? ? // 但是觀測到該點的關鍵幀數卻不超過cnThObs幀,那么該點檢驗不合格

? ? pMP->SetBadFlag();

? ? lit = mlpRecentAddedMapPoints.erase(lit);//從待查? list中刪除

}

else if(((int)nCurrentKFid - (int)pMP->mnFirstKFid ) >= 3)

// 步驟4:從建立該點開始,已經過了3幀(前三幀地圖點比較寶貴需要特別檢查),放棄對該MapPoint的檢測 ?

? ? lit = mlpRecentAddedMapPoints.erase(lit);//從待查? list中刪除

else

? ? lit++;

? ? }

}

/**

* @brief 相機運動過程中和共視程度比較高的關鍵幀通過三角化恢復出一些MapPoints

*? 根據當前關鍵幀恢復出一些新的地圖點,不包括和當前關鍵幀匹配的局部地圖點(已經在ProcessNewKeyFrame中處理)

*? 先處理新關鍵幀與局部地圖點之間的關系,然后對局部地圖點進行檢查,

*? 最后再通過新關鍵幀恢復 新的局部地圖點:CreateNewMapPoints()

*

* 步驟1:在當前關鍵幀的 共視關鍵幀 中找到 共視程度 最高的前nn幀 相鄰幀vpNeighKFs

* 步驟2:遍歷和當前關鍵幀 相鄰的 每一個關鍵幀vpNeighKFs

* 步驟3:判斷相機運動的基線在(兩針間的相機相對坐標)是不是足夠長

* 步驟4:根據兩個關鍵幀的位姿計算它們之間的基本矩陣 F =? inv(K1 轉置) * t12 叉乘 R12 * inv(K2)

* 步驟5:通過幀間詞典向量加速匹配,極線約束限制匹配時的搜索范圍,進行特征點匹配

* 步驟6:對每對匹配點 2d-2d 通過三角化生成3D點,和 Triangulate函數差不多

*? 步驟6.1:取出匹配特征點

*? 步驟6.2:利用匹配點反投影得到視差角? 用來決定使用三角化恢復(視差角較大) 還是 直接2-d點反投影(視差角較小)

*? 步驟6.3:對于雙目,利用雙目基線 深度 得到視差角

*? 步驟6.4:視差角較大時 使用 三角化恢復3D點

*? 步驟6.4:對于雙目 視差角較小時 二維點 利用深度值 反投影 成 三維點? ? 單目的話直接跳過

*? 步驟6.5:檢測生成的3D點是否在相機前方

*? 步驟6.6:計算3D點在當前關鍵幀下的重投影誤差? 誤差較大跳過

*? 步驟6.7:計算3D點在 鄰接關鍵幀 下的重投影誤差 誤差較大跳過

*? 步驟6.9:三角化生成3D點成功,構造成地圖點 MapPoint

*? 步驟6.9:為該MapPoint添加屬性

*? 步驟6.10:將新產生的點放入檢測隊列 mlpRecentAddedMapPoints? 交給 MapPointCulling() 檢查生成的點是否合適

* @see?

*/

void LocalMapping::CreateNewMapPoints()

{

? ? // Retrieve neighbor keyframes in covisibility graph

? ? int nn = 10;// 雙目/深度 共視幀 數量

? ? if(mbMonocular)

nn=20;//單目

// 步驟1:在當前關鍵幀的 共視關鍵幀 中找到 共視程度 最高的nn幀 相鄰幀vpNeighKFs

? ? const vector<KeyFrame*> vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn);

? ? ORBmatcher matcher(0.6,false);// 描述子匹配器

? ? ? ? ? ? // 當前關鍵幀 旋轉平移矩陣向量

? ? cv::Mat Rcw1 = mpCurrentKeyFrame->GetRotation();// 世界---> 當前關鍵幀

? ? cv::Mat Rwc1 = Rcw1.t();// 當前關鍵幀---> 世界

? ? cv::Mat tcw1 = mpCurrentKeyFrame->GetTranslation();

? ? cv::Mat Tcw1(3,4,CV_32F);// 世界---> 當前關鍵幀 變換矩陣

? ? Rcw1.copyTo(Tcw1.colRange(0,3));

? ? tcw1.copyTo(Tcw1.col(3));

? ? // 得到當前當前關鍵幀在世界坐標系中的坐標

? ? cv::Mat Ow1 = mpCurrentKeyFrame->GetCameraCenter();

? ? ? ? ? // 相機內參數

? ? const float &fx1 = mpCurrentKeyFrame->fx;

? ? const float &fy1 = mpCurrentKeyFrame->fy;

? ? const float &cx1 = mpCurrentKeyFrame->cx;

? ? const float &cy1 = mpCurrentKeyFrame->cy;

? ? const float &invfx1 = mpCurrentKeyFrame->invfx;

? ? const float &invfy1 = mpCurrentKeyFrame->invfy;

? ? const float ratioFactor = 1.5f*mpCurrentKeyFrame->mfScaleFactor;//匹配點 查找范圍

? ? int nnew=0;

? ? // Search matches with epipolar restriction and triangulate

// 步驟2:遍歷和當前關鍵幀 相鄰的 每一個關鍵幀vpNeighKFs ? ?

? ? for(size_t i=0; i<vpNeighKFs.size(); i++)

? ? {

if(i>0 && CheckNewKeyFrames())

? ? return;

KeyFrame* pKF2 = vpNeighKFs[i];//關鍵幀

// Check first that baseline is not too short

? // 鄰接的關鍵幀在世界坐標系中的坐標

cv::Mat Ow2 = pKF2->GetCameraCenter();

? // 基線向量,兩個關鍵幀間的相機相對坐標

cv::Mat vBaseline = Ow2-Ow1;

? // 基線長度

const float baseline = cv::norm(vBaseline);

// 步驟3:判斷相機運動的基線是不是足夠長

if(!mbMonocular)

{

? ? if(baseline < pKF2->mb)

? ? continue;// 如果是立體相機,關鍵幀間距太小時不生成3D點

}

else// 單目相機

{

? // 鄰接關鍵幀的場景深度中值

? ? const float medianDepthKF2 = pKF2->ComputeSceneMedianDepth(2);//中值深度

? ? // baseline與景深的比例

? ? const float ratioBaselineDepth = baseline/medianDepthKF2;

? ? ? ? ? ? ? ? ? // 如果特別遠(比例特別小),那么不考慮當前鄰接的關鍵幀,不生成3D點

? ? if(ratioBaselineDepth < 0.01)

continue;

}

// Compute Fundamental Matrix

// 步驟4:根據兩個關鍵幀的位姿計算它們之間的基本矩陣

? ? ? // 根據兩關鍵幀的姿態計算兩個關鍵幀之間的基本矩陣

? ? ? // F =? inv(K1 轉置)*E*inv(K2) = inv(K1 轉置) * t12 叉乘 R12 * inv(K2)

cv::Mat F12 = ComputeF12(mpCurrentKeyFrame,pKF2);

// Search matches that fullfil epipolar constraint

// 步驟5:通過幀間詞典向量加速匹配,極線約束限制匹配時的搜索范圍,進行特征點匹配

vector<pair<size_t,size_t> > vMatchedIndices;// 特征匹配候選點

matcher.SearchForTriangulation(mpCurrentKeyFrame,pKF2,F12,vMatchedIndices,false);

? ? ? ? // 相鄰關鍵幀 旋轉平移矩陣向量

cv::Mat Rcw2 = pKF2->GetRotation();

cv::Mat Rwc2 = Rcw2.t();

cv::Mat tcw2 = pKF2->GetTranslation();

cv::Mat Tcw2(3,4,CV_32F);// 轉換矩陣

Rcw2.copyTo(Tcw2.colRange(0,3));

tcw2.copyTo(Tcw2.col(3));

// 相機內參

const float &fx2 = pKF2->fx;

const float &fy2 = pKF2->fy;

const float &cx2 = pKF2->cx;

const float &cy2 = pKF2->cy;

const float &invfx2 = pKF2->invfx;

const float &invfy2 = pKF2->invfy;

// Triangulate each match

// 三角化每一個匹配點對

// 步驟6:對每對匹配點 2d-2d 通過三角化生成3D點,和 Triangulate函數差不多

const int nmatches = vMatchedIndices.size();

for(int ikp=0; ikp<nmatches; ikp++)

{

// 步驟6.1:取出匹配特征點 ?

? ? const int &idx1 = vMatchedIndices[ikp].first; // 當前匹配對在當前關鍵幀中的索引

? ? const int &idx2 = vMatchedIndices[ikp].second;// 當前匹配對在鄰接關鍵幀中的索引

? ? //當前關鍵幀 特征點 和 右圖像匹配點橫坐標

? ? const cv::KeyPoint &kp1 = mpCurrentKeyFrame->mvKeysUn[idx1];

? ? const float kp1_ur=mpCurrentKeyFrame->mvuRight[idx1];

? ? bool bStereo1 = kp1_ur >= 0;//右圖像匹配點橫坐標>=0是雙目/深度相機

? ? // 鄰接關鍵幀 特征點 和 右圖像匹配點橫坐標

? ? const cv::KeyPoint &kp2 = pKF2->mvKeysUn[idx2];

? ? const float kp2_ur = pKF2->mvuRight[idx2];

? ? bool bStereo2 = kp2_ur >= 0;

// 步驟6.2:利用匹配點反投影得到視差角? ?

? ? // Check parallax between rays

? ? // 相機歸一化平面上的點坐標

? ? cv::Mat xn1 = (cv::Mat_<float>(3,1) << (kp1.pt.x - cx1)*invfx1, (kp1.pt.y - cy1)*invfy1, 1.0);

? ? cv::Mat xn2 = (cv::Mat_<float>(3,1) << (kp2.pt.x - cx2)*invfx2, (kp2.pt.y - cy2)*invfy2, 1.0);


? ? // 由相機坐標系轉到世界坐標系,得到視差角余弦值

? ? cv::Mat ray1 = Rwc1*xn1;// 相機坐標系 ------> 世界坐標系

? ? cv::Mat ray2 = Rwc2*xn2;

? ? // 向量a × 向量b / (向量a模 × 向量吧模) = 夾角余弦值

? ? const float cosParallaxRays = ray1.dot(ray2) / (cv::norm(ray1)*cv::norm(ray2));


? ? // 加1是為了讓cosParallaxStereo隨便初始化為一個很大的值

? ? float cosParallaxStereo = cosParallaxRays+1;

? ? float cosParallaxStereo1 = cosParallaxStereo;

? ? float cosParallaxStereo2 = cosParallaxStereo;

// 步驟6.3:對于雙目,利用雙目基線 深度 得到視差角

? ? if(bStereo1)

cosParallaxStereo1 = cos(2*atan2(mpCurrentKeyFrame->mb/2,mpCurrentKeyFrame->mvDepth[idx1]));

? ? else if(bStereo2)

cosParallaxStereo2 = cos(2*atan2(pKF2->mb/2,pKF2->mvDepth[idx2]));

? ? // 得到雙目觀測的視差角

? ? cosParallaxStereo = min(cosParallaxStereo1,cosParallaxStereo2);


// 步驟6.4:三角化恢復3D點

? ? cv::Mat x3D;

? // cosParallaxRays>0 && (bStereo1 || bStereo2 || cosParallaxRays<0.9998)表明視差角正常

? ? ? ? ? ? ? ? ? // cosParallaxRays < cosParallaxStereo表明視差角很小

? ? ? ? ? ? ? ? ? // 視差角度小時用三角法恢復3D點,視差角大時用雙目恢復3D點(雙目以及深度有效)

? ? if(cosParallaxRays < cosParallaxStereo && cosParallaxRays>0 && (bStereo1 || bStereo2 || cosParallaxRays<0.9998))

? ? {

// Linear Triangulation Method

? ? ? // p1 = k × [R1 t1] × D? ? ? k逆 × p1 =? [R1 t1] × D? ? x1 = T1 × D? ? x1叉乘x1 = x1叉乘T1 × D = 0

? ? ? // p2 = k × [ R2 t2]? × D? ? k逆 × p2 =? [R2 t2] × D? ? x2 = T2 × D? ? x2叉乘x2 = x2叉乘T2 × D = 0

? ? ? //

? ? ? //p = ( x,y,1)

? ? ? //其叉乘矩陣為

? ? ? //? 叉乘矩陣 = [0? -1? y;? ? ? ? ? ? ? ? T0

? ? ? //? ? ? ? ? ? ? ? ? ? ? 1? 0? -x;? ? ? ? ? *? T1? *D? ===>( y * T2 - T1 ) *D = 0

? ? ? //? ? ? ? ? ? ? ? ? ? ? -y? x? 0 ] ? ? T2? ? ? ? ? ? ? ? ? ( x * T2 - T0 ) *D = 0

? ? ? //一個點兩個方程? 兩個點 四個方程? A × D =0? 求三維點 D? 對 A奇異值分解

cv::Mat A(4,4,CV_32F);

A.row(0) = xn1.at<float>(0)*Tcw1.row(2) - Tcw1.row(0);

A.row(1) = xn1.at<float>(1)*Tcw1.row(2) - Tcw1.row(1);

A.row(2) = xn2.at<float>(0)*Tcw2.row(2) - Tcw2.row(0);

A.row(3) = xn2.at<float>(1)*Tcw2.row(2) - Tcw2.row(1);

cv::Mat w,u,vt;

cv::SVD::compute(A,w,u,vt,cv::SVD::MODIFY_A| cv::SVD::FULL_UV);

x3D = vt.row(3).t();

if(x3D.at<float>(3)==0)

? ? continue;

// Euclidean coordinates

x3D = x3D.rowRange(0,3) / x3D.at<float>(3);//其次點坐標 除去尺度

? ? }

? //? 步驟6.4:對于雙目 視差角較小時 二維點 利用深度值 反投影 成 三維點

? ? else if(bStereo1 && cosParallaxStereo1<cosParallaxStereo2)// 雙目 視差角 小

? ? {

x3D = mpCurrentKeyFrame->UnprojectStereo(idx1);// 二維點 反投影 成 三維點? ? ? ? ? ? ?

? ? }

? ? else if(bStereo2 && cosParallaxStereo2 < cosParallaxStereo1)

? ? {

x3D = pKF2->UnprojectStereo(idx2);

? ? }

? ? ? // 單目 視差角 較小時 生成不了三維點

? ? else

continue; //沒有雙目/深度 且兩針視角差太小? 三角測量也不合適 得不到三維點 No stereo and very low parallax

? ? cv::Mat x3Dt = x3D.t();


// 步驟6.5:檢測生成的3D點是否在相機前方

? ? //Check triangulation in front of cameras

? ? float z1 = Rcw1.row(2).dot(x3Dt) + tcw1.at<float>(2);// 只算z坐標值

? ? if(z1<= 0)

continue;

? ? float z2 = Rcw2.row(2).dot(x3Dt)+tcw2.at<float>(2);

? ? if(z2 <= 0)

continue;


? ? ? ? // 步驟6.6:計算3D點在當前關鍵幀下的重投影誤差

? ? //Check reprojection error in first keyframe

? ? const float &sigmaSquare1 = mpCurrentKeyFrame->mvLevelSigma2[kp1.octave];//誤差 分布參數

? ? const float x1 = Rcw1.row(0).dot(x3Dt) + tcw1.at<float>(0);//相機歸一化坐標

? ? const float y1 = Rcw1.row(1).dot(x3Dt) + tcw1.at<float>(1);

? ? const float invz1 = 1.0/z1;

? ? if(!bStereo1)

? ? {// 單目

float u1 = fx1*x1*invz1 + cx1;//像素坐標

float v1 = fy1*y1*invz1 + cy1;

float errX1 = u1 - kp1.pt.x;

float errY1 = v1 - kp1.pt.y;

if((errX1*errX1+errY1*errY1) > 5.991*sigmaSquare1)

? ? continue;//投影誤差過大 跳過

? ? }

? ? else

? ? {// 雙目 / 深度 相機? 有右圖像匹配點橫坐標差值

float u1 = fx1*x1*invz1+cx1;

float u1_r = u1 - mpCurrentKeyFrame->mbf * invz1;//左圖像坐標值 - 視差 =? 右圖像匹配點橫坐標

float v1 = fy1*y1*invz1+cy1;

float errX1 = u1 - kp1.pt.x;

float errY1 = v1 - kp1.pt.y;

float errX1_r = u1_r - kp1_ur;

// 基于卡方檢驗計算出的閾值(假設測量有一個一個像素的偏差)

if((errX1*errX1 + errY1*errY1 + errX1_r*errX1_r) > 7.8*sigmaSquare1)

? ? continue;//投影誤差過大 跳過

? ? }

? ? ? ? // 步驟6.7:計算3D點在 鄰接關鍵幀 下的重投影誤差

? ? //Check reprojection error in second keyframe

? ? const float sigmaSquare2 = pKF2->mvLevelSigma2[kp2.octave];

? ? const float x2 = Rcw2.row(0).dot(x3Dt)+tcw2.at<float>(0);

? ? const float y2 = Rcw2.row(1).dot(x3Dt)+tcw2.at<float>(1);

? ? const float invz2 = 1.0/z2;

? ? if(!bStereo2)

? ? {// 單目

float u2 = fx2*x2*invz2+cx2;

float v2 = fy2*y2*invz2+cy2;

float errX2 = u2 - kp2.pt.x;

float errY2 = v2 - kp2.pt.y;

if((errX2*errX2+errY2*errY2)>5.991*sigmaSquare2)

? ? continue;//投影誤差過大 跳過

? ? }

? ? else

? ? {// 雙目 / 深度 相機? 有右圖像匹配點橫坐標差值

float u2 = fx2*x2*invz2+cx2;

float u2_r = u2 - mpCurrentKeyFrame->mbf*invz2;//左圖像坐標值 - 視差 =? 右圖像匹配點橫坐標

float v2 = fy2*y2*invz2+cy2;

float errX2 = u2 - kp2.pt.x;

float errY2 = v2 - kp2.pt.y;

float errX2_r = u2_r - kp2_ur;

if((errX2*errX2+errY2*errY2+errX2_r*errX2_r)>7.8*sigmaSquare2)

? ? continue;//投影誤差過大 跳過

? ? }


? ? ? ? ? // 步驟6.8:檢查尺度連續性

? ? //Check scale consistency

? ? cv::Mat normal1 = x3D-Ow1;//? 世界坐標系下,3D點與相機間的向量,方向由相機指向3D點

? ? float dist1 = cv::norm(normal1);// 模長

? ? cv::Mat normal2 = x3D-Ow2;

? ? float dist2 = cv::norm(normal2);

? ? if(dist1==0 || dist2==0)

continue;// 模長為0 跳過

? ? ? ? ? ? ? ? ? // ratioDist是不考慮金字塔尺度下的距離比例

? ? const float ratioDist = dist2/dist1;

? // 金字塔尺度因子的比例

? ? const float ratioOctave = mpCurrentKeyFrame->mvScaleFactors[kp1.octave] / pKF2->mvScaleFactors[kp2.octave];

? ? /*if(fabs(ratioDist-ratioOctave)>ratioFactor)

continue;*/

? ? // 深度比值和 兩幅圖像下的金字塔層級比值應該相差不大

? ? // |ratioDist/ratioOctave |<ratioFactor

? ? if(ratioDist * ratioFactor<ratioOctave || ratioDist > ratioOctave*ratioFactor)

continue;


? // 步驟6.9:三角化生成3D點成功,構造成地圖點 MapPoint

? ? // Triangulation is succesfull

? ? MapPoint* pMP = new MapPoint(x3D,mpCurrentKeyFrame,mpMap);


? ? ? ? ? ? // 步驟6.9:為該MapPoint添加屬性:

? ? ? // a.觀測到該MapPoint的關鍵幀

? ? pMP->AddObservation(mpCurrentKeyFrame,idx1); // 地圖點 添加觀測幀?

? ? pMP->AddObservation(pKF2,idx2);//

? ? mpCurrentKeyFrame->AddMapPoint(pMP,idx1);// 關鍵幀 添加地圖點

? ? pKF2->AddMapPoint(pMP,idx2);

? ? ? ? ? ? ? // b.該MapPoint的描述子

? ? pMP->ComputeDistinctiveDescriptors();

? ? ? ? ? ? ? // c.該MapPoint的平均觀測方向和深度范圍

? ? pMP->UpdateNormalAndDepth();

? ? ? ? ? ? ? // d.地圖添加地圖點

? ? mpMap->AddMapPoint(pMP);

? ? ? ? ? ? // 步驟6.10:將新產生的點放入檢測隊列 mlpRecentAddedMapPoints

? ? ? ? ? ? ? ? ? // 這些MapPoints都會經過MapPointCulling函數的檢驗

? ? mlpRecentAddedMapPoints.push_back(pMP);

? ? nnew++;

}

? ? }

}

/**

* @brief? ? 檢查并融合 當前關鍵幀 與 相鄰幀(一級二級相鄰幀)重復的地圖點 MapPoints

* 步驟1:獲得當前關鍵幀在covisibility幀連接圖中權重排名前nn的一級鄰接關鍵幀(按觀測到當前幀地圖點次數選取)

* 步驟2:獲得當前關鍵幀在 其一級相鄰幀 在 covisibility圖中權重排名前5 的二級鄰接關鍵幀

* 步驟3:將當前幀的 地圖點MapPoints 分別與 其一級二級相鄰幀的 地圖點 MapPoints 進行融合(保留觀測次數最高的)

* 步驟4:找到一級二級相鄰幀所有的地圖點MapPoints 與當前幀 的? 地圖點MapPoints 進行融合

* 步驟5:更新當前幀 地圖點 MapPoints 的描述子,深度,觀測主方向等屬性

* 步驟5:更新當前 與其它幀的連接關系 (? 觀測到互相的地圖點的次數等信息 )

* @return? 無

*/

void LocalMapping::SearchInNeighbors()

{

? ? // Retrieve neighbor keyframes

// 步驟1:獲得當前關鍵幀在covisibility圖中權重排名前nn的一級鄰接關鍵幀

? ? ? ? ? // 找到當前幀一級相鄰與二級相鄰關鍵幀

? ? int nn = 10;

? ? if(mbMonocular)

nn=20;//單目 多找一些

? // 一級相鄰

? ? const vector<KeyFrame*> vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn);

? ? vector<KeyFrame*> vpTargetKFs;// 最后合格的一級二級相鄰關鍵幀

? ? // 遍歷每一個 一級相鄰幀

? ? for(vector<KeyFrame*>::const_iterator vit=vpNeighKFs.begin(), vend=vpNeighKFs.end(); vit!=vend; vit++)

? ? {

KeyFrame* pKFi = *vit;// 一級相鄰關鍵幀

if(pKFi->isBad() || pKFi->mnFuseTargetForKF == mpCurrentKeyFrame->mnId)//壞幀? 或者 已經加入過

? ? continue;// 跳過

vpTargetKFs.push_back(pKFi);// 加入 最后合格的相鄰關鍵幀

pKFi->mnFuseTargetForKF = mpCurrentKeyFrame->mnId;// 已經做過相鄰匹配? 標記已經加入

// 步驟2:獲得當前關鍵幀在 其一級相鄰幀的? covisibility圖中權重排名前5的二級鄰接關鍵幀

? ? ? ? // 二級相鄰

// Extend to some second neighbors

const vector<KeyFrame*> vpSecondNeighKFs = pKFi->GetBestCovisibilityKeyFrames(5);

// 遍歷每一個 二級相鄰幀

for(vector<KeyFrame*>::const_iterator vit2=vpSecondNeighKFs.begin(), vend2=vpSecondNeighKFs.end(); vit2!=vend2; vit2++)

{

? ? KeyFrame* pKFi2 = *vit2;// 二級相鄰關鍵幀

? ? if(pKFi2->isBad() || pKFi2->mnFuseTargetForKF==mpCurrentKeyFrame->mnId || pKFi2->mnId==mpCurrentKeyFrame->mnId)

continue;// 二級相鄰關鍵幀是壞幀 在一級時已經加入 或者 又找回來了找到當前幀了 跳過

? ? vpTargetKFs.push_back(pKFi2);

}

? ? }

// 步驟3:將當前幀的 地圖點MapPoints 分別與 其一級二級相鄰幀的 地圖點 MapPoints 進行融合

? ? // Search matches by projection from current KF in target KFs

? ? ORBmatcher matcher;

? ? vector<MapPoint*> vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches();//與當前幀 匹配的地圖點

? ? // vector<KeyFrame*>::iterator

? ? for(auto? vit=vpTargetKFs.begin(), vend=vpTargetKFs.end(); vit!=vend; vit++)

? ? {

KeyFrame* pKFi = *vit;//其一級二級相鄰幀

// 投影當前幀的MapPoints到相鄰關鍵幀pKFi中,在附加區域搜索匹配關鍵點,并判斷是否有重復的MapPoints

// 1.如果MapPoint能匹配關鍵幀的特征點,并且該點有對應的MapPoint,那么將兩個MapPoint合并(選擇觀測數多的)

// 2.如果MapPoint能匹配關鍵幀的特征點,并且該點沒有對應的MapPoint,那么為該點添加MapPoint

matcher.Fuse(pKFi,vpMapPointMatches);

? ? }



// 步驟4:將一級二級相鄰幀所有的地圖點MapPoints 與當前幀(的MapPoints)進行融合

? ? ? ? ? ? // 遍歷每一個一級鄰接和二級鄰接關鍵幀 找到所有的地圖點

? ? // Search matches by projection from target KFs in current KF

? ? // 用于存儲一級鄰接和二級鄰接關鍵幀所有MapPoints的集合

? ? vector<MapPoint*> vpFuseCandidates;// 一級二級相鄰幀所有地圖點

? ? vpFuseCandidates.reserve(vpTargetKFs.size() * vpMapPointMatches.size());// 幀數量 × 每一幀地圖點數量

? ? ? ? ? ? // vector<KeyFrame*>::iterator

? ? for(auto vitKF=vpTargetKFs.begin(), vendKF=vpTargetKFs.end(); vitKF!=vendKF; vitKF++)

? ? {

KeyFrame* pKFi = *vitKF;//其一級二級相鄰幀

vector<MapPoint*> vpMapPointsKFi = pKFi->GetMapPointMatches();//地圖點

// vector<MapPoint*>::iterator

for(auto vitMP=vpMapPointsKFi.begin(), vendMP=vpMapPointsKFi.end(); vitMP!=vendMP; vitMP++)

{

? ? MapPoint* pMP = *vitMP;//? 一級二級相鄰幀 的每一個地圖點

? ? if(!pMP)

continue;

? ? if(pMP->isBad() || pMP->mnFuseCandidateForKF == mpCurrentKeyFrame->mnId)

continue;

? ? // 加入集合,并標記 已經加入

? ? pMP->mnFuseCandidateForKF = mpCurrentKeyFrame->mnId; //標記 已經加

? ? vpFuseCandidates.push_back(pMP); // 加入 一級二級相鄰幀 地圖點 集合

}

? ? }


? ? ? ? ? ? //一級二級相鄰幀 所有的 地圖點 與當前幀 融合

? ? ? ? ? ? // 投影 地圖點MapPoints到當前幀上,在附加區域搜索匹配關鍵點,并判斷是否有重復的地圖點

? ? ? ? // 1.如果MapPoint能匹配當前幀的特征點,并且該點有對應的MapPoint,那么將兩個MapPoint合并(選擇觀測數多的)

? ? ? ? // 2.如果MapPoint能匹配當前幀的特征點,并且該點沒有對應的MapPoint,那么為該點添加MapPoint

? ? matcher.Fuse(mpCurrentKeyFrame,vpFuseCandidates);

// 步驟5:更新當前幀MapPoints的描述子,深度,觀測主方向等屬性

? ? // Update points

? ? vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches();//當前幀 所有的 匹配地圖點

? ? for(size_t i=0, iend=vpMapPointMatches.size(); i<iend; i++)

? ? {

MapPoint* pMP=vpMapPointMatches[i];//當前幀 每個關鍵點匹配的地圖點

if(pMP)//存在

{

? ? if(!pMP->isBad())//非 壞點

? ? {

pMP->ComputeDistinctiveDescriptors();// 更新 地圖點的描述子(在所有觀測在的描述子中選出最好的描述子)

pMP->UpdateNormalAndDepth();? ? ? ? ? // 更新平均觀測方向和觀測距離

? ? }

}

? ? }

// 步驟5:更新當前幀的MapPoints后 更新與其它幀的連接關系 觀測到互相的地圖點的次數等信息

? ? ? ? ? ? // 更新covisibility圖

? ? // Update connections in covisibility graph

? ? mpCurrentKeyFrame->UpdateConnections();

}

/**

* @brief? ? 關鍵幀剔除

*? 在Covisibility Graph 關鍵幀連接圖 中的關鍵幀,

*? 其90%以上的地圖點MapPoints能被其他關鍵幀(至少3個)觀測到,

*? 則認為該關鍵幀為冗余關鍵幀。

* @param? pKF1 關鍵幀1

* @param? pKF2 關鍵幀2

* @return 兩個關鍵幀之間的基本矩陣 F

*/

void LocalMapping::KeyFrameCulling()

{

? ? // Check redundant keyframes (only local keyframes)

? ? // A keyframe is considered redundant if the 90% of the MapPoints it sees, are seen

? ? // in at least other 3 keyframes (in the same or finer scale)

? ? // We only consider close stereo points


// 步驟1:根據Covisibility Graph 關鍵幀連接 圖提取當前幀的 所有共視關鍵幀(關聯幀) ?

? ? vector<KeyFrame*> vpLocalKeyFrames = mpCurrentKeyFrame->GetVectorCovisibleKeyFrames();


? ? ? ? ? ? // vector<KeyFrame*>::iterator

? ? ? ? ? // 對所有的局部關鍵幀進行遍歷 ? ?

? ? for(auto? vit=vpLocalKeyFrames.begin(), vend=vpLocalKeyFrames.end(); vit!=vend; vit++)

? ? {

KeyFrame* pKF = *vit;// 當前幀的每一個 局部關聯幀

if(pKF->mnId == 0)//第一幀關鍵幀為 初始化世界關鍵幀 跳過

? ? continue;

// 步驟2:提取每個共視關鍵幀的 地圖點 MapPoints

const vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches();// 局部關聯幀 匹配的 地圖點

int nObs = 3;

const int thObs=nObs; //3

int nRedundantObservations=0;

int nMPs=0;

// 步驟3:遍歷該局部關鍵幀的MapPoints,判斷是否90%以上的MapPoints能被其它關鍵幀(至少3個)觀測到

for(size_t i=0, iend=vpMapPoints.size(); i<iend; i++)

{

? ? MapPoint* pMP = vpMapPoints[i];// 該局部關鍵幀的 地圖點 MapPoints

? ? if(pMP)

? ? {

if(!pMP->isBad())

{

? ? if(!mbMonocular)// 雙目/深度

? ? {? // 對于雙目,僅考慮近處的MapPoints,不超過mbf * 35 / fx

if(pKF->mvDepth[i] > pKF->mThDepth || pKF->mvDepth[i] < 0)

? ? continue;

? ? }

? ? nMPs++;

? ? // 地圖點 MapPoints 至少被三個關鍵幀觀測到

? ? if(pMP->Observations() > thObs)// 觀測幀個數 > 3

? ? {

const int &scaleLevel = pKF->mvKeysUn[i].octave;// 金字塔層數

const map<KeyFrame*, size_t> observations = pMP->GetObservations();// 局部 觀測關鍵幀地圖

int nObs=0;

for(map<KeyFrame*, size_t>::const_iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)

{

? ? KeyFrame* pKFi = mit->first;

? ? if(pKFi==pKF)// 跳過 原地圖點的幀

continue;

? ? const int &scaleLeveli = pKFi->mvKeysUn[mit->second].octave;// 金字塔層數


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 尺度約束,要求MapPoint在該局部關鍵幀的特征尺度大于(或近似于)其它關鍵幀的特征尺度

? ? if(scaleLeveli <= scaleLevel+1)

? ? {

nObs++;

if(nObs >= thObs)

? ? break;

? ? }

}

if(nObs >= thObs)

{// 該MapPoint至少被三個關鍵幀觀測到

? ? nRedundantObservations++;

}

? ? }

}

? ? }

}?

// 步驟4:該局部關鍵幀90%以上的MapPoints能被其它關鍵幀(至少3個)觀測到,則認為是冗余關鍵幀

if(nRedundantObservations > 0.9*nMPs)

? ? pKF->SetBadFlag();

? ? }

}

/**

* @brief? ? 根據兩關鍵幀的姿態計算兩個關鍵幀之間的基本矩陣

*? ? ? ? ? ? ? ? F =? inv(K1 轉置)*E*inv(K2) = inv(K1 轉置)*t叉乘R*inv(K2)

* @param? pKF1 關鍵幀1

* @param? pKF2 關鍵幀2

* @return 兩個關鍵幀之間的基本矩陣 F

*/

cv::Mat LocalMapping::ComputeF12(KeyFrame *&pKF1, KeyFrame *&pKF2)

{

? ? // Essential Matrix: t12叉乘R12

? ? // Fundamental Matrix: inv(K1轉置)*E*inv(K2) ?

? ? cv::Mat R1w = pKF1->GetRotation();? // Rc1w

? ? cv::Mat t1w = pKF1->GetTranslation();

? ? cv::Mat R2w = pKF2->GetRotation();? // Rc2w

? ? cv::Mat t2w = pKF2->GetTranslation();// t c2 w

? ? cv::Mat R12 = R1w*R2w.t();// R12 =Rc1w *? Rwc2 // c2 -->w --->c1

? ? cv::Mat t12 = -R12*t2w + t1w; // tw2? + t1w? ? // c2 -->w --->c1

? ? // t12 的叉乘矩陣

? ? cv::Mat t12x = SkewSymmetricMatrix(t12);

? ? const cv::Mat &K1 = pKF1->mK;

? ? const cv::Mat &K2 = pKF2->mK;

? ? return K1.t().inv()*t12x*R12*K2.inv();

}

/**

* @brief? ? 請求停止局部建圖線程? 設置停止標志

* @return 無

*/

void LocalMapping::RequestStop()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? mbStopRequested = true;//局部建圖 請求停止

? ? unique_lock<mutex> lock2(mMutexNewKFs);

? ? mbAbortBA = true;//停止BA 優化

}

/**

* @brief? ? 停止局部建圖線程? 設置停止標志

* @return 無

*/

bool LocalMapping::Stop()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? if(mbStopRequested && !mbNotStop)

? ? {

mbStopped = true;

cout << "局部建圖停止 Local Mapping STOP" << endl;

return true;

? ? }

? ? return false;

}

/**

* @brief? ? 檢查局部建圖線程 是否停止

* @return 是否停止標志

*/

bool LocalMapping::isStopped()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? return mbStopped;

}

/**

* @brief? 返回 請求停止局部建圖線程? 標志

* @return 返回 請求停止局部建圖線程? 標志

*/

bool LocalMapping::stopRequested()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? return mbStopRequested;

}

/**

* @brief? ? 釋放局部建圖線程?

* @return? 無

*/

void LocalMapping::Release()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? unique_lock<mutex> lock2(mMutexFinish);

? ? if(mbFinished)

return;

? ? mbStopped = false;

? ? mbStopRequested = false;

? ? // list<KeyFrame*>::iterator

? ? for(auto lit = mlNewKeyFrames.begin(), lend=mlNewKeyFrames.end(); lit!=lend; lit++)

delete *lit;//刪除關鍵幀

? ? mlNewKeyFrames.clear();

? ? cout << "局部建圖釋放 Local Mapping RELEASE" << endl;

}

/**

* @brief? ? 返回 可以接收新的一個關鍵幀標志

* @return 是否 可以接收新的一個關鍵幀

*/

bool LocalMapping::AcceptKeyFrames()

{

? ? unique_lock<mutex> lock(mMutexAccept);

? ? return mbAcceptKeyFrames;

}

/**

* @brief? ? 設置 可以接收新的一個關鍵幀標志

* @return 無

*/

void LocalMapping::SetAcceptKeyFrames(bool flag)

{

? ? unique_lock<mutex> lock(mMutexAccept);

? ? mbAcceptKeyFrames=flag;

}

/**

* @brief? ? 設置不要停止標志

* @return? 成功與否

*/

bool LocalMapping::SetNotStop(bool flag)

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? if(flag && mbStopped)//? 在已經停止的情況下 設置不要停止? 錯誤

return false;

? ? mbNotStop = flag;

? ? return true;

}

/**

* @brief? ? 停止 全局優化 BA

* @return 是否 可以接收新的一個關鍵幀

*/

void LocalMapping::InterruptBA()

{

? ? mbAbortBA = true;

}

/**

* @brief? 計算向量的 叉乘矩陣? ? 變叉乘為 矩陣乘

* @return 該向量的叉乘矩陣

*/

cv::Mat LocalMapping::SkewSymmetricMatrix(const cv::Mat &v)

{

// 向量 t=(a1 a2 a3) t叉乘A

// 等于 向量t的叉乘矩陣 * A

//? t的叉乘矩陣

//|0? ? -a3? a2 |

//|a3? ? 0? -a1|

//|-a2? a1? ? 0 |

? ? return (cv::Mat_<float>(3,3) <<? ? ? ? ? ? 0, -v.at<float>(2), v.at<float>(1),

? ? v.at<float>(2),? ? ? ? ? ? ? 0,-v.at<float>(0),

? ? -v.at<float>(1),? v.at<float>(0),? ? ? ? ? ? ? 0);

}

/**

* @brief? ? 請求重置

* @return? 無

*/

void LocalMapping::RequestReset()

{

? ? {

unique_lock<mutex> lock(mMutexReset);

mbResetRequested = true;

? ? }

? ? while(1)

? ? {

{

? ? unique_lock<mutex> lock2(mMutexReset);

? ? if(!mbResetRequested)

break;

}

usleep(3000);

? ? }

}

/**

* @brief? ? 重置線程

* @return? 無

*/

void LocalMapping::ResetIfRequested()

{

? ? unique_lock<mutex> lock(mMutexReset);

? ? if(mbResetRequested)

? ? {

mlNewKeyFrames.clear();

mlpRecentAddedMapPoints.clear();

mbResetRequested=false;

? ? }

}

void LocalMapping::RequestFinish()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? mbFinishRequested = true;

}

bool LocalMapping::CheckFinish()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? return mbFinishRequested;

}

void LocalMapping::SetFinish()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? mbFinished = true;? ?

? ? unique_lock<mutex> lock2(mMutexStop);

? ? mbStopped = true;

}

bool LocalMapping::isFinished()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? return mbFinished;

}

} //namespace ORB_SLAM

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 2018-10-09 Day1 認識Excel,突破理論 第四期的訓練營如期而至,回顧點點滴滴,感慨頗多,感激那個...
    晨E戰到底閱讀 250評論 0 0
  • 文章速讀的三步驟的。 1.快速瀏覽:用兩三分鐘的時間快速瀏覽所有文字,大概知道在講什么...
    橋本奈奈末閱讀 156評論 0 0
  • 今天語文課的設想,又超出了學生的實際水平。原先的教學設計是整體把握《做客喀什》的主要內容,從中體會出作者的...
    曉小利麗閱讀 558評論 0 0
  • 來源:WebSocket通信過程與實現 什么是 WebSocket ? WebSocket 是一種標準協議,用于在...
    四火流年閱讀 378評論 0 1
  • 使用Delphi編個木馬玩 板塊禁止發布 “電子書資料” ,此類主題請發布至 剛學電腦時很喜歡網絡安全,看著高手們...
    netppp閱讀 200評論 0 0