ROS中slam_gmapping、map_server原始碼解讀及其librviz的使用
SLAM全稱simultaneous localization and mapping,即實時定位與地圖構建。也就是說導航離不開地圖,目前常用的地圖構建方法有三種:
下面言歸正傳說說ros中slam_gmapping包和map_server包的作用。
一、slam_gmapping和map_server
這兩個包是在建圖時是用得最多的。
1、slam_gmapping
前面也所過,slam_gmapping包也是依賴開源openslam_gmapping庫。下載openslam_gmapping後編譯會生成幾個動態庫,libgridfastslam.so,libscanmatcher.so,libsensor_base.so,libsensor_odometry.so,libsensor_range.so和libutils.so,原始碼沒有仔細研究,不過看名字也大概可以知道它們的作用。
在slma_gmapping包中編譯後會生成好幾個節點,不過真正需要呼叫的建圖節點的主要實現在slam_gmapping.cpp中,通過roslaunch slam_gmapping slam_gmapping.launch或者rosrun gmapping slam_gmapping scan:=scan啟動,開始掃圖。
首先是初始化,載入或者配置引數。
然後呼叫startLiveSlam函式開啟建圖模式,在這個函式中會發布三個話題,/enropy(機器人姿態分佈熵的估計),/map(建圖過程中的資料,用佔有網格資料描述,其值在0~255之間),/map_metadta(地圖的描述訊息);會發佈一個服務,/dynamic_map,也是為了獲取地圖資料;會訂閱鐳射資料和tf座標轉換後的資料,還用到了message_filters方法,也是為了加快資料傳輸效率。此外,還開啟了一個執行緒,用於不斷廣播map和odom之間的座標變換。
其次的關鍵就是對獲取的點雲scan資料進行處理,這個在回撥函式laserCallback中實現。一旦第一次獲取到scan資料,會呼叫initMapper函式形成一個初始的地圖形狀,這個函式裡面也是進行了大量tf變換,然後通過getOdomPose函式獲取機器人初始位置,正常情況下該函式只會執行一次,再往後獲取到的scan資料則由addScan函式處理了。addScan函式中會先得到里程計資料,然後處理scan資料。最後更新地圖資料,由updateMap函式完成,並呼叫computePoseEntropy函式計算機器人位置分佈熵,同時釋出map和map_metadata訊息。
slam_gmapping包的主要工作就這些,就是將資料處理為了在ros通訊機制下的,其核心演算法還是在openslam_gmapping裡面實現的。
2、map_server
map_server的功能就是地圖伺服器。編譯map_server後會生成map_server_image_loader動態庫,這個用於處理pgm格式的圖片,其依賴了SDL庫;還會生成map_server和map_saver節點。
map_server節點的原始碼很容易看懂,主要就是為了載入pgm格式的地圖,用yaml檔案描述的,同時釋出map_metadata和map話題,以及static_map服務,其目的都是為了方便其他節點獲取到地圖資料。指令為rosrun map_server map_server mymap.yaml。
map_saver的作用就是為了將slam_gmapping建圖資料儲存下來,主要就是訂閱map訊息。rosrun map_server map_saver -f mymap。
二、librviz的使用
前面的操作都是在指令下進行的,如果想看到建圖時的實時效果可通過rviz軟體檢視。但是如果想自己開發軟體,可使用librviz。正如官網所說,rviz不僅僅是一個視覺化工具,還是一個庫,可被其他程式所呼叫。
官網給的例子還是挺簡單的。此外,rviz用的qt4庫,至少ros indigo版本是的。
載入rviz顯示的關鍵程式碼如下:
rviz::RenderPanel * render_panel_;
render_panel_ = new rviz::RenderPanel;
rviz::VisualizationManager *manager_;
manager_ = new rviz::VisualizationManager(render_panel_);
render_panel_->initialize(manager_->getSceneManager(), manager_);
manager_->initialize();
manager_->startUpdate();
rviz::Display *map = manager_->createDisplay("rviz/Map", "adjustable map", true);
map->subProp("Topic")->setValue("/map");
首先是宣告一個rviz的RenderPanel,這個面板類最終也是繼承了QWidget,然後宣告一個視覺化管理類,繼承於QObject,再初始化,VisualizationManager就可以開始不斷更新了。上面程式碼段顯示瞭如何載入地圖,也是通過建立一個顯示(createDisplay),然後新增topic值。
結合官方給的例子,可以自寫一個呼叫librviz的小程式,執行效果如下:
Enjoy!