ORB-SLAM代碼理解及筆記

最近在看orb-slam,orb-slam方案在slam領域的地位就不用說了,我花了三天大概理清了一下代碼的邏輯和思路。具體的細節還沒有仔細去看,由於本人也是剛剛學完高博的視覺slam十四講,所以有一些地方有錯誤的還望批評指正。另外我參考的資料除了論文以外,還有在泡泡機器人上面找到的吳博和馮兵兩位老師的視頻以及PPT,非常感謝泡泡機器人這個平臺,學習到了很多知識!

首先還是看論文裏面的這幅圖,當時就沒有太在意,後來才發現這幅圖的邏輯非常的清晰,真的是非常的好,另外orb-slam的代碼邏輯也非常的清晰,Local Mapping和Loop Closing線程裏面都有一個run函數,可以很直觀的找到每一步在做一些什麼事情。這裏寫圖片描述
我看了吳博老師的ppt感覺也是非常的清晰,所以爲了加強自己的記憶,我就把吳博的整個流程圖自己又畫了一遍,雖然畫的不是特別好,但是加深了一下記憶,我覺得加強了自己的理解也是非常不錯的。另外在馮兵老師的PPT裏面,我找到了可以比較快理清關於frame,feature,map point之間關係的圖。
這裏寫圖片描述
然後接下來就是代碼的流程圖以及我個人參考各路博客資料整理的一些說明吧。後面的我主要是看的單目的,所以雙目和立體的都沒看,以下針對的都是單目情況。
這裏寫圖片描述
這個就是一個相當於預處理的一個過程,在這裏提到了一個單目初始化,是一個非常重要的東西,ORB-SLAM通過自動單目初始化。

在單目構造幀的時候,如果此時未進行初始化,那麼提取的特徵數將會是已經進行初始化之後特徵數的2倍。在進行單目初始化的時候,有兩種方案,分別是計算單應矩陣和基礎矩陣的R,t,然後對兩個矩陣進行分值評估,根據分值確定選擇哪一種方案。具體的計算方案如下:這裏寫圖片描述
然後就開始進入tracking線程了。解釋以下,前面英文是函數名,後面是中文的一個解釋或者理解吧。
這裏寫圖片描述
主要包括初始化,跟蹤,重定位,確定關鍵幀。

MonocularInitialization()(初始化):兩幀的特徵點數目都大於100,則可以用於初始化,然後進行匹配,如果兩幀的匹配特徵點較少,則重新確定兩幀。通過H模型或F模型進行單目初始化,得到兩幀間相對運動、初始MapPoints,刪除無法三角化的點,得到Map Point。

在跟蹤裏面又有三類跟蹤,勻速跟蹤,跟蹤上關鍵幀、重定位以及跟蹤局部地圖。

TrackWithMotionModel()(勻速跟蹤):採取的時假定勻速運動也就是說相鄰兩幀之間的運動認爲相同,然後得到一個初始T和r,將上一幀的3D點(Map Piont)投影到當前幀,利用最小化重投影誤差來進行優化。

TrackReferenceKeyFrame()(跟蹤上一關鍵幀):在運動模型還未建立或者剛剛進行了重定位的時候,首先通過BOW進行匹配來加快當前幀和上一關鍵幀的匹配,然後利用把關鍵幀的位姿設定爲當前幀的位姿,通過最小化重投影誤差進行優化。

跟蹤局部地圖:局部地圖跟蹤就是通過更多的3D點(Map Point)進行約束,然後優化得到更好的位姿。

Relocalization()(重定位):首先通過BOW進行特徵匹配,選出候選關鍵幀(在ORB-SLAM中所有關鍵幀都會被記錄到BOW數據庫中)如果匹配數目超過15個則進行EPnP求解,通過5次迭代RNASAC得到相機初始值,然後進行優化相機位姿。如果優化後匹配點小於50,則通過投影對之前未匹配的點進行再次計算。

TrackLocalMap()(局部地圖跟蹤):首先需要建立一個局部地圖,局部地圖是指與當前幀相鄰的關鍵幀(定義相鄰爲重合特徵點數大於15)。跟蹤局部地圖也就是跟蹤更多的3D點來建立約束,使其優化效果更好。

NeedNewKeyFrame()(是否需要關鍵幀):很長時間沒有插入關鍵幀、局部地圖空閒、跟蹤快要跪、跟蹤地圖MapPoints的比例比較少在這幾種情況下當前幀可以認爲是關鍵幀。ORB-SLAM的關鍵幀准入是比較輕鬆的,有點類似於國外大學,寬進嚴出。

CreateNewKeyFrame()(生成關鍵幀):把當前幀構造爲關鍵幀,同時把當前關鍵幀設爲當前幀的參考幀。

接下來將進入局部地圖線程

局部地圖線程主要包括處理新的關鍵幀、Map Point剔除,新的Map Point生成、局部優化以及關鍵幀剔除這五個部分。
這裏寫圖片描述
ProcessNewKeyFrame()(處理新的關鍵幀):首先從緩衝區讀取一個關鍵幀,然後計算該關鍵幀的BOW映射,將當前關鍵幀與局部Map Point進行關聯,更新關鍵幀的連接關係,然後將當前幀插入到地圖當中。

MapPointCulling()(Map Point剔除):已經是壞點的MapPoints直接從檢查鏈表中刪除,能找到該點的幀少於理論上觀測到該點的幀的1/4,從創建該map point開始到現在已經過了至少2個關鍵幀,但是觀察到該點的關鍵幀不超過2個。

CreateNewMapPoints()(生成新的Map Point): 相機運動過程中與相鄰關鍵幀通過三角化恢復出一些MapPoints。首先在當前幀的共視關鍵幀中找到共視程度最高的20幀相鄰幀,遍歷這20幀,當基線足夠長的時候,根據兩幀的位姿計算運動矩陣,通過極限約束進行匹配,然後三角化,隨後將這些點放入檢測。所有生成的Map Point都需要進行檢測。

SearchInNeighbors()(對Map Point進行融合):首先找到與當前幀共識點最多的前20幀,再找出這20幀公示點最多的5幀,將這些關鍵幀的Map Point與當前幀進行融合,更新當前幀Map Point的描述子.Map point保存下列信息: 它的3D世界座標系位置;視圖方向Ni, 這是它的所有視圖方向的平均單位向量(視圖方向爲那些連接觀測到這個點的關鍵幀的光心和這個點的射線);一個代表ORB 描述符 Di, 它是在這個Map point 被觀測到的那些關鍵幀裏面的所有相關聯描述符裏,加權平均距離最小的一個關聯描述符;根據ORB特徵的尺度不變限制條件, 這個點能夠被觀測到的最大距離 dmax 和最小距離 dmin.最後更新當前幀的MapPoints後更新與其它幀的連接關係。

LocalBundleAdjustment()(局部優化);首先將當前幀加入關鍵幀,找到它的一級關聯幀(共同Map Point大於10)加入到lLocalKeyFrames,遍歷所有相鄰關鍵幀,將他們觀測到的Map Point都加入到lLocalMapPoints當中,找到能被LocalMapPoints觀測到的關鍵幀但是不屬於LocalKeyFrame的關鍵幀,將其固定,優化過程中不進行優化。把關鍵幀和Map Point做爲圖優化的頂點,邊的觀測值爲對應特徵的座標信息。

KeyFrameCulling()(關鍵幀剔除):根據Covisibility Graph提取當前幀的共視關鍵幀,提取每個共視關鍵幀,提取每個共識關鍵幀的Map Point,遍歷該局部關鍵幀的Map Point計算是否90%都能被其他幀觀測到,如果是,則剔除該幀。

然後就進入了迴環檢測階段

主要由檢測迴環、計算Sim3,、閉環融合以及優化Essential Graph四個大的部分所組成。
這裏寫圖片描述
DetectLoop() (檢測迴環):首先從隊列中取出一個關鍵幀,如果距離上次閉環沒多久(小於10幀),或者map中關鍵幀總共還沒有10幀,則不進行閉環檢測。否則就遍歷所有的共識關鍵幀計算所有共識關鍵幀的得分。mpKeyFrameDB->DetectLoopCandidates(mpCurrentKF,
minScore)這個函數就是用於選擇出候選關鍵幀的,具體操作如下:將與當前幀相連的局部關鍵幀剔除然後遍歷所有關鍵幀,找出與當前關鍵幀具有相同單詞的關鍵幀,然後統計所有閉環候選幀中與當前關鍵幀具有共同單詞最多的單詞數,將最多單詞的80%設置爲閾值,然後找出所有單詞書超過閾值,且相似度檢測大於相鄰關鍵幀最低分數的關鍵幀。然後將這些關鍵幀和與他自己相鄰最緊密的前10個關鍵幀設定爲一組(注意這裏的組是以每一個關鍵幀爲中心,加上與其相鄰的關鍵幀所形成的,一個關鍵幀可以在多個組裏面)。然後計算每組的總得分以及每組得分最高的關鍵幀,然後以組得分最高的0.75作爲閾值,找出高於這個閾值的所有組裏面得分最高的幀,作爲候選幀。此時這個函數結束,返回到detectLoop中,然後進行一致性檢驗。所謂一致性檢驗就是通過兩個for循環將當前幀及其相鄰幀與候選關鍵幀及其相鄰幀進行匹配,也就是是否有相同的相鄰關鍵幀,如果大於3那麼就認爲當前幀通過檢測。但是此時還是有一些候選的關鍵幀。

ComputeSim3()(Sim3求解):對於每一個候選幀都進行求解。首先通過BOW進行匹配,如果改幀的匹配特徵點少於20,則直接刪除,然後進行sim3求解,求解之後進行inliner檢測,只要有一次的是合格的就可以返回,最多隻能迭代5次。然後通過求得的Sim3完善通過BOW進行的匹配,然後算出大致的匹配區域再進行Sim3優化,在優化過程中,先迭代5次,然後剔除誤差過大的邊(卡方檢驗,這裏設定的閾值是10),然後繼續對剩下的邊進行優化,然後得到優化結果,只要優化後的nInliers大於等於20就認爲通過迴環檢驗。同時停止對其他候選幀的優化。然後取出閉環匹配上關鍵幀(也就是剛剛通過迴環檢驗的這一幀)的相鄰關鍵幀提取出來得到一個集合(同時把匹配上的關鍵幀也加入到這個集合中),再把它們所對應的Map Point取出來(這裏也包括匹配上的關鍵幀的Map Point,不過這個需要標記一下,避免重複添加),得到一個集合。然後把這些Map Point全部投影到當前關鍵幀上,進行匹配,根據Sim3確定一個大致區域,然後在附近區域搜索,得到匹配。然後根據匹配判斷是否超過40個匹配點,如果超過則認爲匹配成功,否則將有Local Mapping送進來的隊列清空,等待下一次。
這裏寫圖片描述
CorrectLoop()(閉環校正):首先通知局部地圖,讓他停止關鍵幀的插入。然後根據更新之後的共視關係更新當前幀與其它關鍵幀的聯繫,根據位姿傳播得到與當前幀相連關鍵幀閉環後的Sim3,然後根據得到的閉環Sim3(也就是每一個關鍵幀需要調整的位姿)更新Map Point的位置,將sim3轉換爲SE3,修正閉環幀的位姿。然後再根據共視關係,重新更新各個關鍵幀的連接關係。然後再檢查當前幀的Map Point與閉環匹配幀的MapPoint是否存在衝突,對衝突的Map Point進行替換或填補。然後通過將閉環時相連關鍵幀的Map Point投影到這些關鍵幀中,進行Map Point檢查與替換(如有衝突以閉環幀及其相鄰幀的Map Point爲準,這是因爲前期可以認爲沒有誤差積累,後期這些衝突是由於在運行中產生的積累誤差)。再次更新圖關係,得到了因閉環Map Point融合之後得到的圖關係。這裏面需要注意一下,更新後的連接關係分爲兩個部分(一部分是本來就有的連接關係,另外一部分是由於閉環之後所產生的連接關係)
這裏寫圖片描述
OptimizeEssentialGraph()(EssentialGraph優化):這裏的優化對象由三個部分組成,擴展樹連接關係、閉環產生的連接關係和一些共識關係非常好的邊(這裏設定的是共識權重超過100的),由着三部分組成的圖進行優化。

GlobalBundleAdjustemnt()(全局優化):最後建立一個全局優化,優化所有的關鍵幀和Map Point.

整個思路大致就是這樣,還有很多細節可能不太完善,歡迎批評指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章