轉載請注明出處,謝謝
原創作者:Mingrui
原創鏈接:https://www.cnblogs.com/MingruiYu/p/12347171.html
本文要點:
- ORB-SLAM2 簡介
- ORB-SLAM2 實體對象之間的關系
- ORB-SLAM2 系統概覽 (參考論文 + 代碼)
- 以思維導圖形式繪制的 ORB-SLAM2 程序導圖
寫在前面
最近准備開始做本科畢業設計,准備對 SLAM 系統中的回環檢測模塊下手。因為新冠疫情不知道什么時候才能返校,這次放假回家就帶了個 matebook 14 回來,搬磚全靠這小電腦,它苦我也苦(流淚)。
作為開源 SLAM 系統中的經典之一 —— ORB-SLAM2,自然是要拿來好好研究一番。之前閱讀了 ORB-SLAM 和 ORB-SLAM2 論文,之后配置安裝了 ORB-SLAM2 跑了個 example 看看樣子(我的博文 ORB-SLAM2 初體驗 —— 配置安裝),這回准備開始磕代碼。但越磕越發現,這玩意也太復雜了吧。為了實現較好的魯棒性,ORB-SLAM2 中加入了很多很多小 trick 來從細節上提升系統的性能。這些細節在論文里往往就是一句話帶過,但在代碼里就是一大堆環環相扣繞來繞去的東西。雖然大家都說 ORB-SLAM2 的代碼是結構清晰,注釋完整,易於理解,但本渣渣還是看的把頭發撓成了鳥窩狀。
所以,我決定通過寫博文的方式,來更好地梳理 ORB-SLAM2 代碼的框架和步驟,以加深自己的理解,也希望我的梳理能夠對大家有所幫助。
ORB-SLAM2 簡介
ORB-SLAM是15年Raul等人提出的一個單目SLAM系統,其在單目SLAM領域影響廣泛。詳情可見論文:[Monocular] Raúl Mur-Artal, J. M. M. Montiel and Juan D. Tardós. ORB-SLAM: A Versatile and Accurate Monocular SLAM System. IEEE Transactions on Robotics, vol. 31, no. 5, pp. 1147-1163, 2015. (2015 IEEE Transactions on Robotics Best Paper Award). PDF.
在單目ORB-SLAM的基礎上,17年Raul等人又提出了ORB-SLAM2,增加了對於雙目相機和RGB相機的支持。詳情可見論文:[Stereo and RGB-D] Raúl Mur-Artal and Juan D. Tardós. ORB-SLAM2: an Open-Source SLAM System for Monocular, Stereo and RGB-D Cameras. IEEE Transactions on Robotics, vol. 33, no. 5, pp. 1255-1262, 2017. PDF
ORB-SLAM2 的論文中,對於單目部分並沒有做很大修改,也沒有筆墨去重寫單目實現的細節。而其雖然增加了雙目和 RGB-D 相機的支持,但本質上還是在單目系統的基礎上加的,並不是從根本上以雙目或 RGB-D 輸入為設計出發點。所以個人認為,學習 ORB-SLAM2 還是要以單目為主,不能繞開單目去看雙目或 RGB-D 的實現。另外,因為 ORB-SLAM2 論文中並沒有重寫單目實現的細節,所以對於 ORB-SLAM2 的學習還是要從第一篇 ORB-SLAM 的論文入手。以下內容均以 ORB-SLAM 單目部分為基礎。
一些鋪墊
ORB-SLAM2 中的實體對象
ORB-SLAM2 中的實體對象包括:
- Frame
- KeyFrame
- MapPoint
- Map
- KeyFrame Database
它們之間的關系是這樣的:
- 每一個送入系統的視頻幀都會構造一個 Frame
- Frame 中比較重要的會設為 KeyFrame
- 每個 Frame 會提取很多 ORB 特征點(FeaturePoint),每一個 ORB 特征點可能會對應一個 MapPoint
- 同一個 MapPoint 會對應多個不同 Frame 中 ORB 特征
- KeyFrames 和 MapPoints 構成了 Map
- 重要的 KeyFrame 會存入 KeyFrame Database,用於回環檢測和重定位
一個有意思的地方在於,ORB-SLAM2 中並沒有給 ORB 特征點(FeaturePoint)建立一個對象。因為在 ORB-SLAM2 中,幾乎所有的匹配都是 FeaturePoint 和 MapPoint 之間的匹配,只有在做三角化的時候會有FeaturePoint 和 FeaturePoint 之間的匹配。
博文中可能出現的簡稱
- KF:表示 KeyFrame
- Current KF:表示當前 KeyFrame
- Covisible KF:表示在 Covisibility Graph 中與 Current KF 相連的 KF
- Loop KF:表示回環 KeyFrame,即和 Current KF 相匹配的 KeyFrame Database 中的 KF
- Candidate KF:表示可能是 Current KF 的 Loop KF 的候選 KF
- Reference KF:表示距離當前幀最近的上一 KF
- KF Database:表示用來存儲 KFs 的 Database,這些 KFs 會用於回環檢測和重定位
ORB-SLAM2 系統概覽
下面我會從論文和代碼(程序導圖)兩個角度出發,對 ORB-SLAM2 系統進行一個概覽。
以論文為參考
ORB-SLAM 論文中,有一張圖非常經典且重要:

ORB-SLAM 系統同時運行三個線程:
- Tracking 線程:
- 對於新讀取的幀,提取 ORB 特征
- (系統初始化)
- 當前幀位姿初值估計(根據上一幀 + motion-only BA,或進行重定位)
- 局部地圖跟蹤
- 對上一步得到的位姿初值進行進一步 BA 優化
- 局部地圖:指 Covisibility Graph 中附近的 KFs 及其 MapPoints 所組成的局部的地圖
- 決定是否將當前幀作為關鍵幀插入 Map
- LocalMapping 線程:
- 接收從 Tracking 線程插入的 KF,並進行預處理
- 剔除質量較差的 MapPoints
- 通過三角化生成新的 MapPoints
- Current KF 未與現有 MapPoints 匹配的 ORB 特征點 與其 Covisible KFs 的特征點進行匹配,並三角化
- Local BA
- 剔除冗余的局部關鍵幀
- LoopClosing 線程
- 接收 LoopClosing 送來的篩選處理后的 KF
- 檢測出一批 Candidate KFs
- 計算 Sim3,確定最終的 Loop KF
- 進行回環融合
- 優化 Essential Graph
其中 Covisibility Graph 指的是:其節點為所有 KF。一個 KF,如果它與另一個 KF 觀測到的相同的 MapPoints 的數量大於15個,則這兩個 KFs 之間有邊相連。從而組成了 Covisibility Graph。同時,Covisibility Graph 中的每條邊有權重,權重即為兩個 KFs 共同觀測到的 MapPoints 數目。
其中 Essential Graph 指的是:系統會構造一個生成樹。當一個新的 KF 插入時,將它與(與它觀測到相同的 MapPoints 的數量最多的 KF)相連,從而得到一個生成樹。Essential Graph = 該生成樹 + Covisibility Graph 中權重大於100的邊。

提出 Covisiblility Graph 的目的是以此來描述 KFs 之間的共視關系。但 Covisibility Graph 較為稠密,不便於全局優化,所以提出了 Essential Graph。從上圖可以看出,Covisibility Graph 中的邊很多,生成樹就是一條線,而 Essential Graph 介於兩者之間。
以代碼(程序導圖)為參考
上面論文里那張框架圖概括的非常好,但是這張圖還是太簡略了,光看它也搞不懂每個步驟之間的邏輯關系是怎樣。而它們之間復雜的邏輯關系用大段文字更是很難描述清楚。在通讀代碼的時候,我經常讀着讀着就忘了前面讀了什么,忘了現在讀的部分是嵌套在哪里循環里,是屬於哪個子函數。於是我想了個辦法 —— 用思維導圖來梳理代碼的結構和邏輯。試驗過后,我覺得這個辦法很好用。此處獻上我梳理的非常非常大的 ORB-SLAM2 程序導圖:
(點擊 ORB-SLAM2 程序導圖 鏈接查看清晰大圖)

這個導圖梳理的挺詳細的了,希望它能對大家有幫助。(我用的 MindMaster 這款畫思維導圖的軟件,Windows Linux ios Andriod 都支持,同時可以將導圖存儲在個人雲上多設備共享,另外也支持生成並分享導圖鏈接)
我繪制的程序導圖是以 mono_tum.cc 程序為入口,該程序是官方提供的 example,其對 TUM 數據集中的視頻序列進行 SLAM。根據該程序,我們可以清晰地看出該怎么調用整個 ORB-SLAM2 系統。
ORB-SLAM2 系統以 System.cc 為系統的入口,其負責創建各種對象,同時創建 Tracking,LocalMapping, LoopCLosing 三個線程並運行。其中,System::TrackMonocular()是啟動 Tracking 線程的入口。Tracking 線程為主線程,而 LocalMapping 和 LoopClosing 線程是通過 new thread 創建的。

關於 Tracking,LocalMapping, LoopClosing 三個線程的具體內容,我會在以后的博文中進行更為詳細的介紹和梳理。