Testing Davison's MonoSLAM
半個月前從Davison的首頁上下載下傳了monoSLAM的源碼,在ubuntu 6.10(gcc 4.1.2)上 編譯通過,并運作正常,最近又将它移植到windows平台,主要是考慮以後的相容使用。其中有一些心得和經驗,記錄于此。
1. support usb camera under linux
Davison的代碼隻支援1394 camera,在linux下我先為其添加了USB camera支援. 這個工作其實也挺簡單,參考 VW34/VWFirewire和 VW34/VWGLOW/Interface/*代碼,寫了個VWUSBCam接口,其中調用v4l2(2.6 kernel支援)API,這裡又參考了spcaview中的代碼,再修改一下makefile就行了,也就用了一天時間,麻煩的事情在于makefile的修改,automake生成的東東總是顯得過于繁雜,手動修改起來比較煩。
sequence的工程機理是這樣的:底層有一個循環,進行IO操作,不斷地讀取檔案或錄影機資料;一旦得到資料後,發送消息給上層,上層調用響應消息的回調函數,在其中進行資料處理。 這樣的循環式操作有一個弊端:CPU占用率一直比較高。特别是讀取視訊時,沒有資料時也會一直在"空轉"。不像MS的DirectShow,底層的WDM檢測到資料信号時,自動發送消息通知上層,即中斷式的操作。不過對于linux下的v4l2程式設計,目前還沒有看到過支援中斷方式視訊捕獲。
2.port to windows
移植往往是比較麻煩的工作,linux與windows的平台差異比較大。不過Oxford的VW4是cross platform的,并提供了VC7的project,monoslamglow和scenelib也是完全platform independent,并全用c++實作。這樣的工作還是挺好的,我比較喜歡這樣的方式,以前寫的一些程式也都是這麼處理的。
不過在移植過程中還是有一些問題。我使用的是VC2005,可能是目前最好的 編譯器與IDE了,對C++标準支援得很好,畢竟是Lippman上司設計的。這使得monoslam中大量的模闆 編譯能夠順利通過。
首先需要 編譯glut和glow,是opengl的一些擴充工具,我使用的分别是glut-3.7.6和glow_104, 編譯中沒有問題。
其次需要 編譯wxWidgets,目前有2.8.3,第一次嘗試搞錯了一些設計,出了錯,便選用了2.6.4,也是一樣的問題。後來分析了一下錯誤,再結合readme,明白了原因:需要設定一些宏,打開或關閉一些選項。順利通過後也懶得換高版本了,2.6.4夠用了。
現在便可 編譯VW34了,直接用其中的VC7 solution,并設定好前面幾個庫的路徑, 編譯也可以通過,但在後來 編譯MonoSLAMGlow時,卻有unresolved link error,顯然是找不到庫的子產品。回頭再看 VW34,發現了是因為有一些.cpp檔案沒有添加進solution,沒有 編譯進lib,還有就是檔案讀取的幾個template沒執行個體化,手動加上就可以了(雖說簡單,但找到錯誤卻不容易,一開始沒想到是這些問題~~)。在這裡,gcc與vc2005似乎有比較大的差别,gcc似乎不合乎标準。
最後 編譯monoslamglow和scenelib。這兩個沒有現成的工程,monoslamglow其實就是scenelib中API的簡單調用,負責一些界面處理,主要的vSLAM工作在scenelib中完成。直接将這兩個子產品放到了同一個project中,修改了一些頭檔案路徑和gcc、vc2005的語言差别,最後也能 編譯完成,但冒出來很多multiple symbols error,都是來自于 VW34,像是 VW34中已經定義了一些标準C++的庫函數,如basic_string, vector之類,細查一下又沒有,比較奇怪。google了一下,從VXL(一個比較好的computer vision library, VW34中的VNL便是它的一個子產品)的文檔中找到了答案:C++ 編譯器 編譯選項設定不一緻。solution如下:
Project->Settings->C/C++ Tab->Category: Code Generation->Use run-time library: Multithreaded DLL
于是 編譯就通過了,porting就初步完成了。
3. fix bugs
前面完成了移植工作,這隻是一部分工作而已。程式在windows上讀取Davison提供的test sequence,一會便出錯了,而且是比較莫名的錯誤:基于wxWidget的視窗一重繪便出錯。wxWidget的問題?若如此,便很麻煩了,不确定是wxWidget還是opengl或是基于opengl的glut、glow有bug,沒精力去研讀幾十M的代碼的~~
仍然 編譯了個debug版本,初步調試了一下,發現隻是在視窗重繪時出問題。像一般的界面程式一樣,重繪是基于消息的,消息又與回調函數挂鈎。在調試中發現調用回調函數的對象指針失效了,這是失敗的原因!深入VW32的底層代碼,跟蹤程式的過程,了解了它的處理方式:從一個帶虛函數的EventHandler類派生出視窗子類,再利于EventHandler儲存子類的句柄,回調時,由該句柄調用多态調用子類的重繪函數。一切看起來很合理,充分利用了C++的多态性。但EventHandler卻在重繪時失效了。不可了解。 好看月前仔細閱讀了,了解了C++一些底層的對象模型處理過程,對于虛拟繼承内部錯綜複雜的關系也比較清楚了。很快便想到:基類指針失效應該是由于指針在對象轉型時offset調整出了問題。這部分是 編譯器的工作,我們無法幹預。這便又是gcc與vc2005的一個顯著不同!看了一下MonoSLAMGlow類的聲明:多重繼承!4個類的多重繼承!令人生畏的多重繼承!其中EventHandler被放在第二個!這些類都有虛函數,很顯然,也就會有多個虛函數表,指針調整時也會比較複雜。從理論上來說,EventHandler應該設計成抽象類,并作為公有虛拟基類,這樣便不會有上述問題。我采用了一個比較簡單的修改方案:将EventHandler設為第一個繼承類,因為不是虛拟基類,它的位址便是MonoSLAMGlow的位址(vc2005中是這麼處理的),不再需要指針調整,快速、友善而又正确!就是這麼一個問題,真是得感謝Lippman,難怪侯捷竭力推薦他的這本書,很有道理。
緊接着便是第二個錯誤:運作一會程式會崩潰了。使用debug版本觀察了一下,是vector出錯了,看似是指針無效。這個錯誤很難找,整個工程大量使用了vector,無法定位是哪裡出了問題。前段時間發現這個問題時,覺得一時難以下手,也正好在忙别的事,就此擱下了。昨晚又拿起來分析了一下,初步判定是monoslamglow中的問題。今天花了一天時間調試,才找到這個問題所在。真是個非常隐晦而又陰暗的錯誤!當然,也怪我一時疏忽了,話說過來,這種bug若非親曆并嘗過艱辛,是不可能想到并很快發現的,雖然我早就知道bug原因和解決方法~~建議使用STL的程式員都好好讀讀scott meyer的effective stl,唉,其實我已經讀過兩遍了~~
這個bug便是vector::erase便得基于vector的所有iterator都失效了!很簡單的理由,在effective stl中反複說了好幾遍。比如程式中有這樣的一段代碼:
for(vector::iterator feat = feature_init_info_vector.begin();
feat != feature_init_info_vector.end();feat ++)
{
if(feat->...){}
else{
delete_partially_initialised_feature(feat); // 調用vector::erase
}
}
看似沒有問題,delete_partially_initialised_feature erase的是feat的一個副本,feat已經更新了。看過effective stl的人也都會這麼說:這是meyer推薦的一個技巧啊,後置++傳回舊值。其實不然,erase後,vector記憶體已經重新配置設定,以前的iterator都已經失效,是以更新後的feat實際是無效的,在下一次for比較中就會崩潰,錯誤提示為:incompatible iterator!一個我剛開始始終看不懂的錯誤。
修改方法就比較簡單了,修改delete_partially_initialised_feature的原型,傳回iterator,然後
feat = delete_partially_initialised_feature(feat)
即可。
注:程式中有三處這樣的錯誤,需要一一修改。
gcc對于這個問題也沒有出錯。gcc使用的是sgi的stl,不知道底層是如何處理的,按理說不應如此。還可以說明一點的是:Davison(也有可能是其他人)的c++和stl水準不怎麼樣,不應該犯這樣的錯誤的,嘿嘿!
update 1 : 2007-04-12
yet another bug of the same kind : incompatible iterator !
in Scene_single.cpp, line 301, delete_feature erases an item from feature vector and invalids all the iterators kept previously in the context, so in the next loop, the "incompatible iterator" error will jump out while comparing the iterators !
solution :
I subsitude the original prototype of delete_feature below
bool delete_feature()
with
std::vector::iterator delete_feature()
in fact, the latter conforms to the convention in STL, such as vector::erase, list::erase, etc.
update 2 : 2007-04-19
add usb camera support on both linux and windows !
It's quite interesting to run the demo with real environment.
4. conclusion
移植與修改這個程式還是很有意思的,有利于提高自己的程式水準。特别是使我們看到了大師提出的經典與意見是很有用的,經典著作不能不讀!
另外,對于gcc,我也覺得比較有意思,上面發現的一些錯誤在gcc中完全正常,雖說有些地方不符合标準,但确實好用。還是比較喜歡gcc的。
寫了這麼多,覺得可以寫封信給Davison了,report bugs to him and ask him to read some classic books on stl :)
5. forwards
以後再為monoslam加一個windows上的USB攝像頭接口,那就可以做實驗了。這倒是比較容易的,對于攝像頭采集還是很熟的,今天就到此為止吧。
我還是比較喜歡在linux下寫程式,工具比較豐富,也比較好玩,如emacs,eclipse之類的,隻是不如windows中友善,因為vs2005的功能實在太強大了!不過支援開源、支援linux!
發貼者 xuning 時間: 17:15:00

3 評論:
- 學無止境!狗狗,加油!
- 2007年4月3日 下午11:20
Testing Davison's MonoSLAM -
您好,
我最近在研究monoslam這個演算法
想請問您,
要如何得知相機在任何時間下的position & rotation呢?
- 2010年10月4日 下午2:45
Testing Davison's MonoSLAM -
您好~我想請問一下
我在ubuntu 10.04上作monoslam的編譯,可是我在編譯上一直會有錯誤,請問可以提供再linux上的完整步驟嗎?(如需要的套裝元件等等)
- 2010年10月9日 下午3:03
Testing Davison's MonoSLAM