這個系列文章主要是記錄自己在學習SLAM中的一些總結。
在這里,首先我們介紹以下SLAM一個相對獨立的單元-回環檢測(Loop closure detection). 先介紹這部分的原因是: 1) 它是相對獨立的一塊,可以和整個slam系統分離開,有些slam(如激光slam)就沒有這個單元, SVO也沒有; 2) 如果要學習ORB-SLAM, 前端和后端代碼中都有提到這個,有助於理解。
1. 加入回環檢測的目的
加入回環檢測的目的是為了通過找到已經走過的地方然后構建一個約束,從而達到減小累積誤差。
2. 方法
目前常用的方法是Visual Bag-of-Word。 DBoW是通過用圖像上的幾種特征來描述一幅圖。 簡單的說: 首先通過一個大的樣本數據庫,對圖像提取其特征點然后訓練為不同的words。 比如說數據庫中提取了N個特征點,需要分成k個單詞的字典。我們可以先隨機的選取k個中心點; 然后計算最近的點歸入此類; 然后對每一類在重新計算中心點; 如果中心點收斂了,則退出,否則繼續迭代。 這就是k-means的思路。
當然,k-means有固有的問題,比如隨機選取點的話可能導致每次的聚類結果不同 以及 對k的值的選取問題。 (k-means++)
有了數據庫后,我們可以根據圖像中的特征點來查找單詞。 比較naive的方法是說通過frame-to-frame的對比,很明顯效率低下。 雖然我們也可以先對其排好序,然后通過二分法來查找,效率也不高。 因此, 常用的方法是k-d tree. (數據結構的重要性!!!)
如上圖就是一個k 叉樹結構 (2叉樹實際上), 從而查詢結果只和深度有關了。
3.實踐
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 5 #include <DBoW3/DBoW3.h> 6 #include <opencv2/core/core.hpp> 7 #include <opencv2/highgui/highgui.hpp> 8 #include <opencv2/features2d/features2d.hpp> 9 10 using namespace std; 11 using namespace cv; 12 13 int main(int argc, char** argv){ 14 // 1. load vocabulary 15 cout << "read database ..." << endl; 16 DBoW3::Vocabulary vocab("vocabulary_test.yml.gz"); 17 if(vocab.empty()) { 18 cerr << "vocab is empty." << endl; 19 return 0; 20 } 21 22 // 2. load images 23 cout << " load images ..." << endl; 24 vector<cv::Mat> images; 25 for(int i = 0; i < 10; i++ ) 26 { 27 string imgPath = "../data/" + std::to_string(i+1) + ".png"; 28 images.push_back(imread(imgPath)); 29 } 30 31 // 3. extract features with descriptors 32 cout << "extract features with descriptors...." << endl; 33 34 cv::Ptr<cv::ORB> orb = cv::ORB::create(); 35 vector<cv::Mat> descriptors; 36 37 for(auto& image:images) { 38 vector<cv::KeyPoint> kp; 39 cv::Mat desp; 40 41 orb->detectAndCompute(image, cv::Mat(), kp, desp); 42 descriptors.push_back(desp); 43 } 44 45 // 4. loop closure detection 46 cout <<"loop closure detection ..." << endl; 47 DBoW3::Database db(vocab, false, 0); 48 for(auto& desp:descriptors) 49 db.add(desp); 50 cout<<"database info: "<<db<<endl; 51 52 int i = 0; 53 for(auto& desp:descriptors) { 54 DBoW3::QueryResults rest; 55 db.query(desp, rest, 4); 56 cout<<"searching for image "<< i <<" returns "<<rest<<endl<<endl; 57 i++; 58 } 59 60 cout << " done." << endl; 61 62 return 0; 63 }