ORB_SLAM2中Tracking线程
??Tracking線程是ORB_SLAM2的主線程。在System.cc中,使用構造函數進行了初始化,開啟了三個線程。
可執行程序—>System構造函數(初始化三個線程)—>處理輸入的幀(TrackMonocular)—>調用Tracking線程中的GrabImageMonocular函數(追蹤線程的主要函數)。
?
可執行程序---->system構造函數初始化三個線程---->system中的單目初始化函數(TrackMonocular)----->
Tracking線程(GrabImageMonocular)
(1)灰度化處理
(2)構建當前幀(在這里會提取特征點,并將特征點分配到網格里)
(3)進入Track函數---->
Track函數(Track())
(1)單目初始化(MonocularInitialization):
1)特征點匹配,如果匹配數目不夠的話,等待后兩幀。(SearchForInitialization)
2)并行計算H、F矩陣。H矩陣采用(RANSAC+DLC),F矩陣采用(RANSAC+八點法)。
3)根據評分(重投影誤差)選擇F/H。
4)分解F/H,選擇最合適的R,t(通過統計合理的路標點的數量,如:視差、重投影誤差、Z>0)
5)三角化估計路標點深度信息。(2,3,4,5都在(Initialize)函數中實現)
6)全局BA優化,進行尺度歸一化。(CreateInitialMapMonocular)
?
(2)使用追蹤方式獲得優化后的相機位姿:
1)恒速運動模型:TrackWithMotionModel()。通過前兩幀的位姿變換估計當前位姿。使用領域半徑搜索的方法獲得匹配點對(SearchByProjection),隨后優化位姿(PoseOptimization)。
2)參考關鍵幀模型:TrackReferenceKeyFrame()。以上一幀的位姿為初始位姿。找到當前幀的參考關鍵幀,使用詞袋加速算法獲得特征點對(SearchByBoW),隨后進行位姿優化(PoseOptimization)。
3)重定位模型:Relocalization():使用詞袋加速算法獲得候選關鍵幀(DetectRelocalizationCandidates、SearchByBoW),使用EPNP估計相機位姿(SetRansacParameters)。如果內點數不夠的話,將候選關鍵幀的路標點投影至當前幀中(SearchByProjection),獲得更多的匹配點,再次進行優化。
?
(3)追蹤局部地圖(TrackLocalMap):
1)獲得局部關鍵幀(有共視關系的幀及其鄰居)和局部路標點。
2)優化位姿。如果跟蹤失敗的話,Tracking線程返回false。
?
(4)判斷關鍵幀(NeedNewKeyFrame):
必要條件:1)內點數目不能小于15。2)內點數目小于參考關鍵幀的一定的比例。
以下條件必須滿足一個:1)很長時間沒有插入關鍵幀。2)滿足插入關鍵幀的最小間隔并且localMapper處于空閑狀態。
// 創建了System類的一個對象。
ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::MONOCULAR,true);
.........
// 在System構造函數內部對Tracking進行了初始化。
mpTracker = new Tracking(this, //現在還不是很明白為什么這里還需要一個this指針 TODO mpVocabulary, //字典mpFrameDrawer, //幀繪制器mpMapDrawer, //地圖繪制器mpMap, //地圖mpKeyFrameDatabase, //關鍵幀地圖strSettingsFile, //設置文件路徑mSensor); //傳感器類型iomanip
??并且調用Tracking線程中的函數去獲得相機位姿。
// 使用單目追蹤函數處理每一幀的圖像
SLAM.TrackMonocular(im,tframe);
?
?
構造函數
Tracking::Tracking(System *pSys, //系統實例ORBVocabulary* pVoc, //BOW字典FrameDrawer *pFrameDrawer, //幀繪制器MapDrawer *pMapDrawer, //地圖點繪制器Map *pMap, //地圖句柄KeyFrameDatabase* pKFDB, //關鍵幀產生的詞袋數據庫const string &strSettingPath, //配置文件路徑const int sensor): //傳感器類型mState(NO_IMAGES_YET), //當前系統還沒有準備好mSensor(sensor), mbOnlyTracking(false), //處于SLAM模式mbVO(false), //當處于純跟蹤模式的時候,這個變量表示了當前跟蹤狀態的好壞mpORBVocabulary(pVoc), mpKeyFrameDB(pKFDB), mpInitializer(static_cast<Initializer*>(NULL)), //暫時給地圖初始化器設置為空指針mpSystem(pSys), mpViewer(NULL), //注意可視化的查看器是可選的,因為ORB-SLAM2最后是被編譯成為一個庫,所以對方人拿過來用的時候也應該有權力說我不要可視化界面(何況可視化界面也要占用不少的CPU資源)mpFrameDrawer(pFrameDrawer),mpMapDrawer(pMapDrawer), mpMap(pMap), mnLastRelocFrameId(0) //恢復為0,沒有進行這個過程的時候的默認值
??其中mState表示追蹤線程的狀態。在Tracking.h中可知追蹤線程的狀態包括:系統還沒準備好,當前無圖像、未完成初始化、正常工作、跟丟。
enum eTrackingState{SYSTEM_NOT_READY=-1, ///<系統沒有準備好的狀態,一般就是在啟動后加載配置文件和詞典文件時候的狀態NO_IMAGES_YET=0, ///<當前無圖像NOT_INITIALIZED=1, ///<有圖像但是沒有完成初始化OK=2, ///<正常時候的工作狀態LOST=3 ///<系統已經跟丟了的狀態};
mbVO:在純定位模式中使用,追蹤的地圖點小于10個,表明定位快失敗了,置為true。
mnLastRelocFrameId:上一次進行重定位的幀的ID。
?
讀取相機參數
??讀取配置文件中的相機參數,配置文件由使用的數據集和相機的類型決定。對于單目鏡頭、使用KITTI00數據集時,所使用的配置文件為:
Examples/Monocular/EuRoC_TimeStamps/KITTI00-02.yaml
??對于每一幀提取的特征點數:nFeatures,指的是所有層級的圖像金字塔中提取出的特征點數目之和。在高層提取出的特征點要投影回最底層。
??對于雙目、RGB-D相機(會計算RGB-D虛擬的基線長度),定義大于32倍基線長度的路標點為遠點(只提供旋轉信息),小于32倍基線長度的點為近點(提供旋轉、平移信息)。
?
設置局部建圖器、回環檢測器
??在追蹤線程中,追蹤線程、局部建圖線程、回環檢測線程相互影響。因此他們間彼此包含,以方便上鎖,防止數據混亂(因為線程和進程不同,多線程公用同一片內存空間)。
//設置局部建圖器
void Tracking::SetLocalMapper(LocalMapping *pLocalMapper)
{mpLocalMapper=pLocalMapper;
}//設置回環檢測器
void Tracking::SetLoopClosing(LoopClosing *pLoopClosing)
{mpLoopClosing=pLoopClosing;
}
?
獲得相機相對于世界坐標的位姿
??該函數是追蹤線程的核心,在可執行程序中,調用system.cc中的TrackMonocular函數,執行tracking線程中的GrabImageMonocular函數。函數的目的是獲得當前相機相對于世界坐標的變換矩陣Tcw。
cv::Mat Tracking::GrabImageMonocular(const cv::Mat &im,const double ×tamp)
?
轉為灰度圖
??需要注意ORB_SLAM2會將輸入的圖片轉為灰度圖然后進行處理。
?
構造當前幀
??單目初始化時所需的特征點是追蹤所需的特征點的兩倍,因此在構建當前幀時,需要分為兩部分:
(1)還沒有圖片輸入,或有圖片輸入,但沒有成功初始化的時候,構建當前幀,所需的特征點提取器是mpIniORBextractor。在這個過程中會提取特征點。
(2)成功追蹤時,特征點提取器為mpORBextractorLeft。
追蹤函數Track()
??主要功能為單目初始化—>估計當前位姿—>跟蹤局部地圖—>決定新的關鍵幀—>當前幀信息保存。
?
?
追蹤函數Track()
void Tracking::Track()
??在圖像reset或開始運行時,Tracking線程的狀態為NO_IMAGES_YET(可執行程序調用System的構造函數,初始化三個線程。在這是會調用Tracking的構造函數,將mstate置為NO_IMAGES_YET)。此時將Tracking的狀態改為NOT_INITIALIZED。(初始化之后會改為OK)
??在地圖更新過程中上鎖,保證地圖的信息不會發生變化。
?
沒初始化時候時初始化
??初始化的目的是獲得初始的位姿、路標點,方便之后的追蹤。單目初始化的話可以獲得尺度信息。
??在還沒有初始化的時候,調用初始化函數。并行計算基礎矩陣(F)和單應矩陣(H),選擇一個評分最高的。分解獲得相機的位姿,進行三角化,獲得路標點坐標。(初始化成功后,將Tracking的狀態改為OK)
??如果未初始化成功,則直接返回。
if(mState!=OK)return;
?
已經初始化時通過跟蹤估計相機位姿
??追蹤方式分為三種:運動模型(不使用對極約束求解相機位姿,而是根據恒速運動模型估計當前幀的位姿)、追蹤參考關鍵幀、重定位。
??先檢查上一幀的路標點是否被替換。因為在局部建圖線程中,可能會刪除一些路標點。(使用恒速運動模型時候,會將上一幀的地圖點投影到當前幀)
定位且建圖模式
??局部建圖線程則可能會對原有的地圖點進行替換.在這里進行檢查。
(1)運動模型為空||剛重定位完成:使用跟蹤關鍵幀模型
1、使用詞袋加速算法在當前幀、參考關鍵幀中進行特征點匹配。
2、使用重投影誤差優化位姿。
(2)在上一幀位姿已知的情況下,使用恒速運動模型
(3)追蹤失敗了,使用重定位模型(最后的補救)
定位模式
mbVO表示此幀匹配的路標點的個數。大于10則為false,小于10則為true。
mbVO為false:運動模型||參考關鍵幀模型
mbVO為true:表明追蹤的路標點已經很少了,要死了。此時就要綜合考慮運動模型和重定位模型。同時運行運動模型和重定位模型,優先考慮重定位模型。重定位模型成功的話使用重定位模式,否則使用運動模型。
通過運動模型、參考關鍵幀模型、重定位模型進行追蹤,估計相機的位姿。
這三種操作都會返回一個bool值表示其結果是否可接受,記為bOK。
?
進行局部地圖追蹤
??前面只是跟蹤一幀得到初始位姿,這里搜索局部關鍵幀、局部地圖點,和當前幀進行投影匹配,得到更多匹配的MapPoints后進行Pose優化。
??根據跟蹤局部地圖成功與否,設置Tracking線程的狀態為OK還是Lost。
?
判斷是否需要創建新的關鍵幀
(1)獲得當前幀相對于上一幀的運動模型(即上一幀轉換為當前幀的轉換矩陣)。上一幀的運動為空的話,當前幀的運動也為空。
// 更新恒速運動模型 TrackWithMotionModel 中的mVelocity
cv::Mat LastTwc = cv::Mat::eye(4,4,CV_32F);
mLastFrame.GetRotationInverse().copyTo(LastTwc.rowRange(0,3).colRange(0,3));
mLastFrame.GetCameraCenter().copyTo(LastTwc.rowRange(0,3).col(3));
// mVelocity = Tcl = Tcw * Twl,表示上一幀到當前幀的變換, 其中 Twl = LastTwc
mVelocity = mCurrentFrame.mTcw*LastTwc;
(2)清除觀察不到的路標點。
(3)檢測并插入關鍵幀(這一步條件比較寬松,因為局部建圖線程還會剔除一些關鍵幀)。
(4)刪除外點。
?
當前幀相關信息保存在Tracking的成員中
??最后保存一下當前幀的位姿等信息(前面分析過的保存相機軌跡要用)。如果當前幀的mTcw已經得到了,則進行以下操作:(1)計算當前幀相對于參考關鍵幀的變換Tcr(Tcr = Tcw × Twr),Tcr被推入Tracking的成員 mlRelativeFramePoses 中保存;(2)當前幀的時間戳推入Tracking的成員 mlFrameTimes 中保存;(3)當前幀的參考關鍵幀推入Tracking的成員 mlpReferences 中保存;(4)當前幀是否跟蹤失敗的bool信息(mState==LOST)推入Tracking的成員 mlbLost 中保存。當然跟蹤失敗的時候當前幀的mTcw未知,則前面(1)(2)(3)步都只在尾部重復保存對應三個列表的尾部的元素,然后mlbLost尾部保存true。
?
?
單目初始化
??認為單目初始化時候的參考幀和當前幀都是關鍵幀。單目初始化是很重要的一步,因此打算單獨寫一篇。
void Tracking::StereoInitialization()
約束包括:
(1)第一幀、第二幀的特征點的數量。
(2)第一幀、第二幀的特征點匹配對數。
未創建單目初始器時
(1)以第一幀為參考幀,構建初始器,并且初始化相關系數。
(2)直接返回,然后傳入下一幀,計算基礎矩陣。
?
創建單目初始器時
(1)如果第二幀的特征點數目小于100,則直接退出。
(2)對第一幀、第二幀進行特征點匹配,匹配數量小于100的話,直接退出,重新開始。
(3)進行初始化(計算F、H,使用最佳的進行分解,隨后進行三角化),獲得相機位姿變換以及路標點坐標。
(4)刪除未三角化成功的路標點。
(5)設置第一幀為世界坐標原點。
(6)創建初始化時的路標點集合。
?
?
創建初始化地圖時的路標點
??初始化成功后進行歸一化處理。
??將路標點從Point3f變為MapPoint。
void Tracking::CreateInitialMapMonocular()
設定關鍵幀
??初始化時的第一幀、第二幀都為關鍵幀。
將路標點加入到地圖中
全局優化,同時優化位姿與路標點
Optimizer::GlobalBundleAdjustemnt(mpMap,20);
進行尺度歸一化處理
??常用的尺度歸一化處理為:(1)使用初始時計算的平移向量為單位1。(2)取場景的平均深度為單位1。ORB_SLAM2采用第二種方案。
??將位姿變換矩陣、路標點坐標進行歸一化處理。
// Step 6 將兩幀之間的變換歸一化到平均深度1的尺度下// Scale initial baselinecv::Mat Tc2w = pKFcur->GetPose();// x/z y/z 將z歸一化到1 Tc2w.col(3).rowRange(0,3) = Tc2w.col(3).rowRange(0,3)*invMedianDepth;pKFcur->SetPose(Tc2w);// Scale points// Step 7 把3D點的尺度也歸一化到1// 為什么是pKFini? 是不是就算是使用 pKFcur 得到的結果也是相同的? 答:是的,因為是同樣的三維點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);}}
將該兩幀插入到局部地圖中
?
?
判斷上一幀的路標點是否被替換
??在局部建圖線程中,可能會對一些路標點進行替換(地圖點的剔除)。因此在追蹤線程開始的時候,要先判斷一下路標點是否被替換。
void Tracking::CheckReplacedInLastFrame()
?
?
參考關鍵幀追蹤模式
??Tracking線程的三種追蹤模式打算另寫一篇,感覺很重要。
bool Tracking::TrackReferenceKeyFrame()
?
?
更新上一幀位姿
??更新上一幀位姿,在上一幀深度值中生成臨時地圖點。
單目情況:只計算了上一幀的世界坐標系位姿。在恒速模型中被使用
雙目和rgbd情況:選取有深度值的并且沒有被選為地圖點的點生成新的臨時地圖點,提高跟蹤魯棒性。(優化時有更多的約束)
??但需要注意臨時地圖點后面會被刪除。因為畢竟不是真正的地圖點。
void Tracking::UpdateLastFrame()
??在追蹤模式中被使用,根據上一幀的參考關鍵幀更新上一幀。(如果上一幀沒有參考關鍵幀,則創建一些臨時地圖點)(單目相機不會調用,個人感覺是因為雙目、RGB-D相機本身就可以獲得深度信息,對于那些無法通過特征點匹配加入的地圖點,雙目相機仍可獲得深度,而單目不行。)。
?
?
獲得上一幀的位姿
創建臨時地圖點(單目不用)(感覺到ORB_SLAM2真的是將信息利用到了極致)
??對于雙目、RGB-D相機,將那些未通過特征點匹配獲得的地圖點也做為地圖點加入后序的約束中。
(1)如果雙目相機中的已知深度的點沒有作為路標點的話,則作為臨時地圖點加入到地圖中。
MapPoint* pMP = mLastFrame.mvpMapPoints[i];if(!pMP)bCreateNew = true;else if(pMP->Observations()<1) {// 地圖點被創建后就沒有被觀測,也需要創建bCreateNew = true;}
(2)停止添加臨時地圖點
條件1:當前點的深度已經超過了閾值(35倍基線長度)
條件2:已近添加夠了100個。
?
?
更新局部地圖
??每來一幀,都會進行跟蹤獲得位姿,然后進行優化,然后跟蹤局部地圖。在跟蹤局部地圖的時候會更新局部地圖,并獲得當前幀的參考關鍵幀(共視程度最高的關鍵幀)。
??局部地圖包括局部關鍵幀、局部路標點。
void Tracking::UpdateLocalMap()
將局部地圖點放入地圖中
??將局部關鍵幀、局部地圖點放入地圖中,即ORN_SLAM運行時的紅色點。
更新局部關鍵幀和局部地圖點
更新局部關鍵幀:獲得局部關鍵幀。
更新局部地圖點:在這里將局部地圖路標點加入到局部地圖中
?
?
更新局部關鍵幀
void Tracking::UpdateLocalKeyFrames()
??局部關鍵幀的選擇策略:
(1)將與當前關鍵幀有共視點的關鍵幀設為局部關鍵幀。
(2)遍歷(1)中的局部關鍵幀,選擇共視程度最高的10個關鍵幀作為局部關鍵幀。
(3)將局部關鍵幀的子關鍵幀作為局部關鍵幀。
(4)將局部關鍵幀的夫關鍵幀作為局部關鍵幀。
獲得與當前關鍵幀有共視點的關鍵幀及其代價
??將與當前關鍵幀有共視關系的關鍵幀保存在keyframeCounter(有共視關系的關鍵幀,共視次數)中。遍歷當前幀的路標點,通過函數GetObservations()獲得能觀察到該路標點的所有關鍵幀。遍歷所有關鍵幀,并將公式關系+1。
?
更新局部關鍵幀
首先將局部關鍵幀容器清空。
策略1:有共視點的關鍵幀為局部關鍵幀
??在這個過程中同時獲得共視程度最高的關鍵幀,及其共視程度。
for(map<KeyFrame*,int>::const_iterator it=keyframeCounter.begin(), itEnd=keyframeCounter.end(); it!=itEnd; it++){KeyFrame* pKF = it->first;// 如果設定為要刪除的,跳過if(pKF->isBad())continue;//尋找具有最大觀測數目的關鍵幀if(it->second>max){max=it->second;pKFmax=pKF;}// 添加到局部關鍵幀的列表里mvpLocalKeyFrames.push_back(it->first);// 用該關鍵幀的成員變量mnTrackReferenceForFrame 記錄當前幀的id// 表示它已經是當前幀的局部關鍵幀了,可以防止重復添加局部關鍵幀pKF->mnTrackReferenceForFrame = mCurrentFrame.mnId;}
策略二:取出共視程度最高的10個關鍵幀
const vector<KeyFrame*> vNeighs = pKF->GetBestCovisibilityKeyFrames(10);
策略三:將局部關鍵幀的子關鍵幀作為局部關鍵幀
const set<KeyFrame*> spChilds = pKF->GetChilds();for(set<KeyFrame*>::const_iterator sit=spChilds.begin(), send=spChilds.end(); sit!=send; sit++){KeyFrame* pChildKF = *sit;if(!pChildKF->isBad()){if(pChildKF->mnTrackReferenceForFrame!=mCurrentFrame.mnId){mvpLocalKeyFrames.push_back(pChildKF);pChildKF->mnTrackReferenceForFrame=mCurrentFrame.mnId;//? 找到一個就直接跳出for循環?break;}}}
策略四:將局部關鍵幀的父關鍵幀作為局部關鍵幀
KeyFrame* pParent = pKF->GetParent();if(pParent){// mnTrackReferenceForFrame防止重復添加局部關鍵幀if(pParent->mnTrackReferenceForFrame!=mCurrentFrame.mnId){mvpLocalKeyFrames.push_back(pParent);pParent->mnTrackReferenceForFrame=mCurrentFrame.mnId;//! 感覺是個bug!如果找到父關鍵幀會直接跳出整個循環break;}}
將與當前幀共視程度最高的關鍵幀作為參考關鍵幀
if(pKFmax){mpReferenceKF = pKFmax;mCurrentFrame.mpReferenceKF = mpReferenceKF;}
?
?
更新局部地圖點
清空之前的局部地圖點
mvpLocalMapPoints.clear();
遍歷所有局部關鍵幀,將局部關鍵幀的路標點放入局部地圖點中
for(vector<MapPoint*>::const_iterator itMP=vpMPs.begin(), itEndMP=vpMPs.end(); itMP!=itEndMP; itMP++){MapPoint* pMP = *itMP;if(!pMP)continue;// 用該地圖點的成員變量mnTrackReferenceForFrame 記錄當前幀的id// 表示它已經是當前幀的局部地圖點了,可以防止重復添加局部地圖點if(pMP->mnTrackReferenceForFrame==mCurrentFrame.mnId)continue;if(!pMP->isBad()){mvpLocalMapPoints.push_back(pMP);pMP->mnTrackReferenceForFrame=mCurrentFrame.mnId;}}
?
?
查找當前幀視野范圍內的局部地圖點,建立與特征點的聯系
??在局部地圖中查找在當前幀視野范圍內的點,將視野范圍內的點和當前幀的特征點進行投影匹配。
void Tracking::SearchLocalPoints()
遍歷當前幀的mvpMapPoints,標記這些MapPoints不參與之后的搜索(因為當前的mvpMapPoints一定在當前幀的視野中)
for(vector<MapPoint*>::iterator vit=mCurrentFrame.mvpMapPoints.begin(), vend=mCurrentFrame.mvpMapPoints.end(); vit!=vend; vit++){MapPoint* pMP = *vit;if(pMP){if(pMP->isBad()){*vit = static_cast<MapPoint*>(NULL);}else{// 更新能觀測到該點的幀數加1(被當前幀看到了)pMP->IncreaseVisible();// 標記該點被當前幀觀測到pMP->mnLastFrameSeen = mCurrentFrame.mnId;// 標記該點將來不被投影,因為已經匹配過(指的是使用恒速運動模型進行投影)pMP->mbTrackInView = false;}}}//遍歷當前幀中所有的地圖點
判斷哪些局部地圖點在視野范圍內
如果視野范圍內有點的話,建立特征點與局部地圖點的聯系
if(nToMatch>0){ORBmatcher matcher(0.8);int th = 1;if(mSensor==System::RGBD) //RGBD相機輸入的時候,搜索的閾值會變得稍微大一些th=3;// If the camera has been relocalised recently, perform a coarser search// 如果不久前進行過重定位,那么進行一個更加寬泛的搜索,閾值需要增大if(mCurrentFrame.mnId<mnLastRelocFrameId+2)th=5;// 對視野范圍內的MapPoints通過投影進行特征點匹配matcher.SearchByProjection(mCurrentFrame,mvpLocalMapPoints,th);}
?
?
追蹤局部地圖
bool Tracking::TrackLocalMap()
更新局部地圖(局部關鍵幀與局部路標點)–>跟蹤局部地圖
??其主要目的就是找到與當前幀存在約束的局部關鍵幀,隨后進行優化。
更新局部地圖與局部路標點,并建立與當前關鍵幀的特征點的聯系
// Step 1:更新局部關鍵幀 mvpLocalKeyFrames 和局部地圖點 mvpLocalMapPointsUpdateLocalMap();// Step 2:在局部地圖中查找與當前幀匹配的MapPoints, 其實也就是用局部地圖點進行跟蹤SearchLocalPoints();
再次位姿優化
??在這個函數之前,在 Relocalization、TrackReferenceKeyFrame、TrackWithMotionModel 中都有位姿優化,這里再次對當前幀的位姿進行優化。
??因為此時當前幀中路標點與特征點間的聯系變多,約束增加,所以再次進行優化。
統計跟蹤效果
??記錄當前幀追蹤的局部地圖點中為內點的數量
判斷追蹤局部地圖是否成功
??如果是剛剛重定位之后,對追蹤是否成功的要求較為嚴格。要求內點數大于50,否則大于30就行。
局部地圖追蹤失敗的話,會將Tracking線程的狀態置為LOST
?
?
判斷是否為關鍵幀
bool Tracking::NeedNewKeyFrame()
(1)只定位模式下,不需判斷。
(2)如果局部建圖線程被回環檢測線程使用。
(3)如果當前幀距離上一次重定位較近,或關鍵幀的數目超過了限制,不插入關鍵幀。
判斷關鍵幀
nRefMatches:參考關鍵幀中地圖點的數量
thRefRatio:當前幀與參考關鍵幀追蹤到的點的比例。
c1a:很長時間沒有插入關鍵幀,可以插入。
c1b:滿足插入關鍵幀的最小間隔并且localMapper處于空閑狀態,可以插入。
c2a:當前幀的內點數小于閾值*參考關鍵幀內點數。
c2b:當前幀的內點數不能小于15。
c2a&&c2b是必須滿足的條件。
是否可以插入關鍵幀
局部建圖線程空閑:可以插入關鍵幀
局部建圖線程繁忙:有一個緩存隊列,先將關鍵幀插入到緩存隊列里。等局部建圖線程空閑下來,再插入關鍵幀。
?
?
創建關鍵幀
void Tracking::CreateNewKeyFrame()
停止局部建圖線程
if(!mpLocalMapper->SetNotStop(true))return;
創建關鍵幀中新的路標點
單目相機:由于無法獲得深度,直接跳過。
雙目、RGB-D相機:遍歷特征點,將未被觀測的路標點進行排序,隨后從大到小選出路標點加入關鍵幀中。該過程的終止條件:(1)路標點的深度大于35倍基線長度。(2)最多加100個。
插入局部建圖線程
??將關鍵幀插入到局部建圖線程中,然后啟動局部建圖線程。
mpLocalMapper->InsertKeyFrame(pKF);// 插入好了,允許局部建圖停止mpLocalMapper->SetNotStop(false);
將該關鍵幀設為新的關鍵幀
mnLastKeyFrameId = mCurrentFrame.mnId;mpLastKeyFrame = pKF;
?
?
?
?
總結
??進入追蹤線程的流程為:
mono_kitti.cc—>
ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::MONOCULAR,true)---->
SLAM.TrackMonocular(im,tframe)—>
GrabImageMonocular(im,timestamp)。
??進入Tracking線程之后:
(1)初始化:第一幀為參考關鍵幀,第二幀和第一幀進行匹配。特征點匹配上的數量不滿足閾值的話,會舍棄這兩幀,然后再進入兩幀進行判斷。滿足閾值后,這兩幀都會作為局部關鍵幀插入局部地圖中。然后對位姿和路標點進行優化。
(2)相機位姿跟蹤:估計相機位姿并進行優化(每一幀都會優化位姿)。跟蹤方式分為恒速運動模型,參考關鍵幀模型、重定位模型。正常情況下使用恒速運動模型;出現意外了,無法獲得速度,使用參考關鍵幀模型;跟蹤丟了(Tracking的狀態不是OK),使用重定位模型。
(3)局部地圖跟蹤:更新局部關鍵幀和局部地圖后,獲得局部地圖點,并與當前關鍵幀的特征點進行匹配。然后根據重投影誤差進行優化(每一幀都會優化位姿)。目的是局部地圖點會提供更多的重投影誤差約束。對于雙目、RGB-D相機,會創建臨時地圖點以提供更多的約束。
(4)判斷是否生成關鍵幀。如果生成關鍵幀的話,先停止局部建圖線程,然后將關鍵幀插入局部建圖線程中,然后啟動局部建圖線程。
總結
以上是生活随笔為你收集整理的ORB_SLAM2中Tracking线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 意外怀孕多少钱啊?
- 下一篇: ORB_SLAM2单目初始化策略