ROS2+cartorgrapher+激光雷达建图并保存
目錄
- 寫在前面
- 修改記錄
- 安裝cartographer
- 查看cartographer包名字并安裝
- cartographer初了解
- 激光雷達(dá)底層驅(qū)動
- 編譯
- 編譯驅(qū)動時遇到的bug
- 編譯完后文件樹
- 配置文件
- 啟動文件
- 配置文件
- frame_id理解理解學(xué)習(xí)
- ROS坐標(biāo)系
- cartographer建圖過程
- .lua文件
- map_builder.lua 參數(shù)理解
- trajectory_builder_2d.lua 參數(shù)理解
- .launch.py文件
- 自己的.launch文件
- 補(bǔ)充
- 啟動及其調(diào)試
- 善用rqt_graph
- 安裝并運(yùn)行rqt
- 善用rqt_tf_tree
- 建圖效果
- 地圖保存
- 后記
寫在前面
最近在做一個類似菜鳥外賣小車的項(xiàng)目,需要在ROS2下進(jìn)行建圖以及導(dǎo)航,但是國內(nèi)ROS2參考的資料還挺少,遇到許多bug都需要在外網(wǎng)進(jìn)行查閱。這里把自己最近單單使用激光雷達(dá)建圖的過程記錄下來,以便于未來翻閱。
環(huán)境:
- Ubuntu22.04
- ROS2 humble
- 激光雷達(dá):鐳神激光雷達(dá)M10P 網(wǎng)口版
修改記錄
2023.3.5:我們后續(xù)測試了將里程計(jì)數(shù)據(jù)和IMU數(shù)據(jù)進(jìn)行融合再將其添加到cartographer建圖中,發(fā)現(xiàn)效果更好了,后續(xù)寫一篇博客補(bǔ)上這部分的內(nèi)容
2023.2.9:修改了文章順序和一些錯誤,并添加了自己的建圖結(jié)果
安裝cartographer
查看cartographer包名字并安裝
由于ROS2發(fā)布了許多的版本,因此,我們在要安裝適配于自己版本的包之前可以先查看能適配的包
# 查找符合的包 sudo apt-cache search cartographer返回了許多與humble有關(guān)的包,我們安裝這兩個
sudo apt install ros-humble-cartographer ros-humble-cartographer-ros等待安裝完之后,查看是否有安裝好
ros2 pkg list | grep cartogrpaher返回 即代表安裝好
cartographer_ros cartographer_ros_msgs魚香ROS有解釋,為啥看不到cartographer,這里貼出來
魚香ROS動手學(xué)習(xí)ROS2 安裝cartographer
“可能你會好奇為什么沒有cartographer,因?yàn)閏artographer包的編譯類型原因造成的,不過沒關(guān)系,cartographer_ros依賴于cartographer,所以有cartographer_ros一定有cartographer。”
在安裝完了cartographer之后,其實(shí)我們就可以理解成我們在Ubuntu里面安裝了一個ROS2的功能包,只是這個功能包并不在我們自己的功能區(qū)下面,所以我們只需要啟動這個功能包,他就能發(fā)布他自己的node和topic。
cartographer初了解
在建圖的過程當(dāng)中,傳感器的信息非常重要。在ROS2當(dāng)中,我們需要使用的傳感器信息的方式非常的簡單,即訂閱傳感器topic發(fā)布的信息,在接收信息之后,cartographer包會利用其內(nèi)部的算法對其進(jìn)行解析構(gòu)建,進(jìn)而建圖。cartographer整體算法主要依賴的是激光雷達(dá)的數(shù)據(jù)。
因此我們在使用cartographer建圖之前,我們需要先看懂這個算法是怎么拿到激光雷達(dá)的數(shù)據(jù)。我的入手點(diǎn)是從商家的激光雷達(dá)驅(qū)動文件入手進(jìn)行學(xué)習(xí)。
整體學(xué)習(xí)的邏輯順序如下:
激光雷達(dá)底層驅(qū)動
任何一家激光雷達(dá)商家都會給你他們驅(qū)動文件,我們需要在自己的工作區(qū)下編譯商家給的驅(qū)動文件,然后驅(qū)動文件中的激光雷達(dá)的節(jié)點(diǎn),這樣才能在ROS2的環(huán)境下接收其數(shù)據(jù)。這里拿鐳神的這個M10P激光雷達(dá)驅(qū)動做例子。
編譯
進(jìn)入其驅(qū)動含有README.md的同層目錄,使用colcon build編譯
編譯驅(qū)動時遇到的bug
在編譯驅(qū)動的驅(qū)動的時候,遇到了這么一個bug
針對這個問題,其實(shí)就是安裝一個包即可,如果同類型的問題也是這個解決思路
ROS2報(bào)錯缺少“diagnostic_updater“,CMake did not find diagnostic_updater. 解決思路
編譯完后文件樹
鐳神給的驅(qū)動文件文件樹在工作區(qū)
. ├── build ├── install ├── lslidar_driver ├── lslidar_msgs ├── README.md └── version.txt配置文件
然后來查看激光雷達(dá)的啟動和配置文件,這樣有助于后續(xù)建圖過程的搭建。
啟動文件
lsm10p_net_launch.py
'''省略import文件部分''' def generate_launch_description():driver_dir_1 = os.path.join(get_package_share_directory('lslidar_driver'), 'params', 'lsx10_1.yaml')driver_node_1 = LifecycleNode(package='lslidar_driver',executable='lslidar_driver_node',name='lslidar_driver_node', #設(shè)置激光數(shù)據(jù)topic名稱output='screen',emulate_tty=True,namespace='lidar_1',parameters=[driver_dir_1],)rviz_dir = os.path.join(get_package_share_directory('lslidar_driver'), 'rviz', 'lslidar.rviz')rviz_node = Node(package='rviz2',namespace='',executable='rviz2',name='rviz2',arguments=['-d', rviz_dir],output='screen')return LaunchDescription([driver_node_1,rviz_node,])可以看到商家的啟動文件就是啟動了兩個節(jié)點(diǎn)
首先是第一個driver_node_1,這個就是雷達(dá)的驅(qū)動文件節(jié)點(diǎn)了,使用的配置參數(shù)文件是lsx10_1.yaml,因此后續(xù)修改驅(qū)動文件的時候只需要修改這個文件即可。
然后是第二個節(jié)點(diǎn)rviz_node,這個其實(shí)就是ROS2里面的可視化工具,非常的好用,在后續(xù)我們也會用上。這里面的啟動節(jié)點(diǎn)名字、路徑位置都可以自定義修改
配置文件
/lidar_1/lslidar_driver_node:ros__parameters: frame_id: laser_link #激光坐標(biāo)group_ip: 224.1.1.2add_multicast: false device_ip: 192.168.1.200 #雷達(dá)目的ipdevice_ip_difop: 192.168.1.102 #雷達(dá)源IPmsop_port: 2368 #雷達(dá)目的端口號difop_port: 2369 #雷達(dá)源端口號lidar_name: M10_P #雷達(dá)選擇:M10 M10_P M10_PLUS M10_GPS N10angle_disable_min: 0.0 #角度裁剪開始值angle_disable_max: 0.0 #角度裁剪結(jié)束值min_range: 0.0 #雷達(dá)接收距離最小值max_range: 200.0 #雷達(dá)接收距離最大值use_gps_ts: false #雷達(dá)是否使用GPS授時scan_topic: /scan #設(shè)置激光數(shù)據(jù)topic名稱interface_selection: net #接口選擇:net 為網(wǎng)口,serial 為串口。serial_port_: /dev/ttyUSB0 #串口連接時的串口號 # pcap: /home/ls/work/2211/M10_P_gps.pcap #雷達(dá)是否使用pcap包讀取功能這里,其實(shí)大部分都沒有什么東西,但是我們需要重點(diǎn)關(guān)注的是frame_id這個東西,因?yàn)橹皼]有接觸過,因此這里就開始學(xué)習(xí)這部分
frame_id理解理解學(xué)習(xí)
參考博客
ROS探索總結(jié)(二十)——發(fā)布導(dǎo)航需要的傳感器信息
要看懂這個參數(shù),我們先來看一下ros2中激光雷達(dá)的消息接口有一些什么消息即這個接口
Sensor_msgs/msg/LaserScan
使用查看一下接口的消息類型
ros2 interface show sensor_msgs/msg/LaserScan返回
# Single scan from a planar laser range-finder # # If you have another ranging device with different behavior (e.g. a sonar # array), please find or create a different message, since applications # will make fairly laser-specific assumptions about this datastd_msgs/Header header # timestamp in the header is the acquisition time ofbuiltin_interfaces/Time stampint32 secuint32 nanosecstring frame_id# the first ray in the scan.## in frame frame_id, angles are measured around# the positive Z axis (counterclockwise, if Z is up)# with zero angle being forward along the x axisfloat32 angle_min # start angle of the scan [rad] float32 angle_max # end angle of the scan [rad] float32 angle_increment # angular distance between measurements [rad]float32 time_increment # time between measurements [seconds] - if your scanner# is moving, this will be used in interpolating position# of 3d points float32 scan_time # time between scans [seconds]float32 range_min # minimum range value [m] float32 range_max # maximum range value [m]float32[] ranges # range data [m]# (Note: values < range_min or > range_max should be discarded) float32[] intensities # intensity data [device-specific units]. If your# device does not provide intensities, please leave# the array empty.消息頭有三部分的數(shù)據(jù),int32 sec,uint32 nanosec,string frame_id,這三個參數(shù)的理解就是
sec和nanosec就是時間戳,代表著發(fā)布消息的秒和納秒。
frame_id 是消息中與數(shù)據(jù)相關(guān)聯(lián)的參考系id,例如在在激光數(shù)據(jù)中,frame_id對應(yīng)激光數(shù)據(jù)采集的參考系(坐標(biāo)系)。
因此,frame_id就是某一個物體的參考系的坐標(biāo)名字。然后,我們需要學(xué)習(xí)另一個東西,什么是ROS的常見坐標(biāo)系
ROS坐標(biāo)系
原文鏈接:
ROS坐標(biāo)系統(tǒng),常見的坐標(biāo)系及含義
ros-rep-0105
1.base_link
base_link坐標(biāo)系和機(jī)器人的底盤直接連接。其具體位置和方向都是任意的。對于不同的機(jī)器人平臺,底盤上會有不同的參考點(diǎn)。不過ROS也給了推薦的坐標(biāo)系取法。
x 軸指向機(jī)器人前方
y 軸指向機(jī)器人左方
z 軸指向機(jī)器人上方
2.odom
odom是一個固定在環(huán)境中的坐標(biāo)系也就是world-fixed。它的原點(diǎn)和方向不會隨著機(jī)器人運(yùn)動而改變。但是odom的位置可以隨著機(jī)器人的運(yùn)動漂移。漂移導(dǎo)致odom不是一個很有用的長期的全局坐標(biāo)。然而機(jī)器人的odom坐標(biāo)必須保證是連續(xù)變化的。也就是在odom坐標(biāo)系下機(jī)器人的位置必須是連續(xù)變化的,不能有突變和跳躍。
在一般使用中odom坐標(biāo)系是通過里程計(jì)信息計(jì)算出來的。比如輪子的編碼器或者視覺里程計(jì)算法或者陀螺儀和加速度計(jì)。odom是一個短期的局域的精確坐標(biāo)系。但是卻是一個比較差的長期大范圍坐標(biāo)。
3.map
map和odom一樣是一個固定在環(huán)境中的世界坐標(biāo)系。map的z軸是向上的。機(jī)器人在map坐標(biāo)系下的坐標(biāo)不應(yīng)該隨著時間漂移。但是map坐標(biāo)系下的坐標(biāo)并不需要保證連續(xù)性。也就是說在map坐標(biāo)系下機(jī)器人的坐標(biāo)可以在任何時間發(fā)生跳躍變化。
一般來說map坐標(biāo)系的坐標(biāo)是通過傳感器的信息不斷的計(jì)算更新而來。比如激光雷達(dá),視覺定位等等。因此能夠有效的減少累積誤差,但是也導(dǎo)致每次坐標(biāo)更新可能會產(chǎn)生跳躍。
map坐標(biāo)系是一個很有用的長期全局坐標(biāo)系。但是由于坐標(biāo)會跳躍改變,這是一個比較差的局部坐標(biāo)系(不適合用于避障和局部操作)。
而在開放環(huán)境中,我們需要定義一個全球坐標(biāo)系
如果在開發(fā)中這個約定不能完全保證,也要求盡量滿足。比如對于沒有GPS,指南針等傳感器的機(jī)器人,仍然可以保證坐標(biāo)系z軸向上的約定。如果有指南針傳感器,這樣就能保證x和y軸的初始化方向。
在結(jié)構(gòu)化的環(huán)境中(比如室內(nèi)),在定義坐標(biāo)系時和環(huán)境保持對應(yīng)更有用。比如對于有平面圖的建筑,坐標(biāo)系可以和平面圖對應(yīng)。類似的對于室內(nèi)環(huán)境地圖可以和建筑物的層相對應(yīng)。對于有多層結(jié)構(gòu)的建筑物,對每一層單獨(dú)有一個坐標(biāo)系也是合理的。
4.earth
這個坐標(biāo)系是為了多個機(jī)器人相互交互而設(shè)計(jì)的。當(dāng)有多個機(jī)器人的時候,每個機(jī)器人都有自己的map坐標(biāo)系,他們之間的map坐標(biāo)系并不相同。如果想要在不同的機(jī)器人間共享數(shù)據(jù),則需要這個坐標(biāo)系來進(jìn)行轉(zhuǎn)化。
如果map坐標(biāo)系是一個全局坐標(biāo)系,那么map到earth坐標(biāo)系的變化可以是一個靜態(tài)變換。如果不是的話,就要每次計(jì)算map坐標(biāo)系的原點(diǎn)和方向。
在剛啟動的時候map坐標(biāo)系的全局位置可能是不知道的。這時候可以先不發(fā)布到earth的變換,直到有了比較精確的全局位置。
坐標(biāo)系之間的關(guān)系
坐標(biāo)系之間的關(guān)系可以用樹圖的方式表示。每一個坐標(biāo)系只能有一個父坐標(biāo)系和任意多個子坐標(biāo)系。
earth -> map -> odom -> base_link
按照之前的說明,odom和map都應(yīng)該連接到base_link坐標(biāo)系。但是這樣是不允許的,因?yàn)槊恳粋€坐標(biāo)系只能有一個父坐標(biāo)系。
坐標(biāo)系變換的計(jì)算
odom到base_link的變換由里程計(jì)數(shù)據(jù)源中的一個發(fā)布
map到base_link通過定位組件計(jì)算得出。但是定位組件并不發(fā)布從map到base_link的變換。它首先獲取odom到base_link的變換然后利用定位信息計(jì)算出map到odom的變換。
earth到map的變換是根據(jù)map坐標(biāo)系選取所發(fā)布的一個靜態(tài)變換。如果沒有設(shè)置,那么就會使用機(jī)器人的初始位置作為坐標(biāo)原點(diǎn)。
Map之間的切換
如果機(jī)器人的運(yùn)動范圍很大,那么極有可能是要切換地圖的。在室內(nèi)環(huán)境下,在不同的建筑物中,和不同的樓層地圖都會不同。
在不同的地圖間切換的時候,定位組件要恰當(dāng)?shù)陌裲dom的parent替換成新的地圖。主要是map到base_link之間的變換要選取恰當(dāng)?shù)牡貓D,然后在轉(zhuǎn)換成map到odom之間的變換。
odom坐標(biāo)系的連續(xù)性
在切換地圖的時候,odom坐標(biāo)系不應(yīng)該受到影響。odom坐標(biāo)系要保證連續(xù)性。可能影響連續(xù)性的情況包括進(jìn)出電梯,機(jī)器人自身沒有運(yùn)動,但是周圍環(huán)境發(fā)生很大的變化。還有可能由于運(yùn)動距離太遠(yuǎn),造成數(shù)據(jù)溢出。這些都要特殊進(jìn)行處理。
看完了這部分,我們也就知道了,frame_id其實(shí)就是標(biāo)注了這部分?jǐn)?shù)據(jù)的來源參考id,在配置文件參數(shù)中寫的laser也就是來源于laser代表的參考系ID。
cartographer建圖過程
開始接觸cartographer的時候,對ROS2這種節(jié)點(diǎn)的概念還沒有完全建立,最開始理解要使用這個包來建圖的時候,我以為需要打開什么客戶端,或者說跑一個什么程序。但是隨著學(xué)習(xí)的深入,我逐漸理解到
cartographer就是ROS2的一個功能包,和我們自己在ROS2的工作空間下建立的功能包是一個道理。我們?nèi)绻枰褂眠@個功能包,其實(shí)只需要簡單的用launch文件啟動這個功能包里面所帶有的節(jié)點(diǎn),然后啟動我們自己激光雷達(dá)的節(jié)點(diǎn),然后cartographer訂閱激光雷達(dá)節(jié)點(diǎn)發(fā)布的消息,當(dāng)然,接收的消息格式和接收的節(jié)點(diǎn)的名字都需要我們一開始配置好,也就是使用.lua和.launch.py(在ROS2中)文件。
即使用ros2 launch cartographer_ros filenames.launch
而要完成使用Cartographer進(jìn)行建圖,需要兩個節(jié)點(diǎn)的參與,整個過程的計(jì)算流圖如下:
/cartographer_node節(jié)點(diǎn):
該節(jié)點(diǎn)從/scan和/odom話題接收數(shù)據(jù)進(jìn)行計(jì)算,輸出/submap_list數(shù)據(jù).
該節(jié)點(diǎn)需要接收一個參數(shù)配置文件(第二部分寫的那個)參數(shù)。
/occupancy_grid_node節(jié)點(diǎn):
該節(jié)點(diǎn)接收/submap_list子圖列表,然后將其拼接成map并發(fā)布
該節(jié)點(diǎn)需要配置地圖分辨率和更新周期兩個參數(shù)。
參考原網(wǎng)址:小魚的文檔
那么其實(shí)我們需要學(xué)習(xí)理解的就是catorgrapher的.lua和.launch.py文件是如何配置的,就能夠?qū)W會怎么運(yùn)行起來這個算法
.lua文件
根據(jù)網(wǎng)上給出的建議,我們最好從cartographer官方給出的配置文件進(jìn)行修改,因此,拿出官網(wǎng)的一個.lua文件進(jìn)行學(xué)習(xí)和修改,來看backpack2d.lua。
注:我后面增加了一些其他可以修改的參數(shù),然后我基于官方的backpack2d.lua改成自己需要的配置文件,我的激光雷達(dá)的frame_ID是laser_link
ros2 humble cartographer會下載到電腦的路徑為/opt/ros/humble/share/cartographer_ros/configuration_files/
參考網(wǎng)址:
cartographer官網(wǎng)
https://zhuanlan.zhihu.com/p/563264225
參數(shù)含義:
-
map_frame: 構(gòu)建地圖所使用的坐標(biāo)系,一般就使用我們前面提到的map即可
-
tracking_frame: SLAM算法跟蹤的幀的ROS幀ID。如果要使用IMU,它應(yīng)該在這個地方被選用,盡管它可能會漂移。常見的選擇是“imu_link”。
? tracking_frame一般設(shè)置為發(fā)布頻率最高的傳感器的frame_id,cartographer將會把其他數(shù)據(jù)都轉(zhuǎn)移到該坐標(biāo)系下進(jìn)行計(jì)算。如果只使用雷達(dá)數(shù)據(jù)進(jìn)行2D建圖,那就只需要將其設(shè)置為雷達(dá)數(shù)據(jù)話題的frame_id,一般為laser。如果使用雷達(dá)數(shù)據(jù)+IMU進(jìn)行2D或者3D建圖,因?yàn)镮MU的發(fā)布頻率明顯高于雷達(dá),所以需要設(shè)置為imu數(shù)據(jù)話題的frame_id,一般imu_link。 -
published_frame: 要用作發(fā)布坐標(biāo)的子幀的ROS幀ID。例如,如果“odom”框架由系統(tǒng)的不同部分提供,則設(shè)置為“odom“。在這種情況下,將發(fā)布map_frame中“odom”的坐標(biāo)。否則,將其設(shè)置為“base_link”可能是合適的。
cartographer發(fā)布的tf樹最后將指向published_frame,即published_frame不是cartographer提供的,這里如果沒設(shè)置正確,tf樹就不能連接成功,建圖也就不能正常進(jìn)行。這個一般設(shè)置為底盤的frame_id,也就是URDF文件中的底盤的link name,一般為base_link、base_footprint之類的名字。 -
odom_frame: 僅當(dāng)provide_odom_frame為true時使用。通常是“odom”。
-
provide_odom_frame: 如果enable, 則local, non-loop-closed, continuous pose 將作為 odom_frame發(fā)布在 map_frame.
- 在大多數(shù)情況下,設(shè)置 provide_odom_frame 為 true 是有意義的,因?yàn)樗梢詾槠渌?jié)點(diǎn)提供一個 odom 坐標(biāo)系,這些節(jié)點(diǎn)可以使用機(jī)器人的里程計(jì)數(shù)據(jù)來估計(jì)機(jī)器人的運(yùn)動,而不必直接處理傳感器數(shù)據(jù)。例如,可以使用 robot_localization 包來融合多個傳感器數(shù)據(jù),并估計(jì)機(jī)器人的位姿。
-
publish_frame_projected_to_2d: 如果enable, 則發(fā)布姿態(tài)將嚴(yán)格限制在純2D位姿下(不包含roll pitch和z-offset坐標(biāo)),這個可以防止出現(xiàn)一些由于pose extrapolation step步驟出現(xiàn)的預(yù)期之外不需要的平面外姿態(tài)
-
use_odometry: 如果enable,則訂閱topic為odom中的nav_msgs/Odometry。這種情況下必須提供里程計(jì).在SLAM過程中也會使用這個消息進(jìn)行建圖。注意:這里如果設(shè)置為true,則需要在ROS2節(jié)點(diǎn)中發(fā)布一個名為odom的topic讓carto訂閱
-
use_nav_sat: 如果enable, 則訂閱主題為fix中的sensor_msgs/NavSatFix。這種情況下必須要使用導(dǎo)航數(shù)據(jù)
-
use_landmarks:如果enable,則訂閱主題為landmarks中的cartographer_ros_msgs/LandmarkList,必須提供LandmarkLists數(shù)據(jù),如1cartographer_ros_msgs/LandmarkEntry中的cartographer_ros_msgs/LandmarkList
-
num_laser_scans: 要訂閱的laser scan的主題數(shù)量。為1時,訂閱sensor_msgs/LaserScan中的scan主題,或者為多臺激光掃描訂閱主題的scan_1,scan_2
-
num_multi_echo_laser_scans: 要訂閱的multi-echo laser scan的主題數(shù)量,為1時,訂閱echoes下的sensor_msgs/MultiEchoLaserScan,或者多個echoes_1, echoes_2
-
num_subdivisions_per_laser_scan: 將每個接收到的(多回波)激光掃描分成的點(diǎn)云數(shù)。細(xì)分掃描可以使掃描儀移動時獲取的掃描不變形。有一個相應(yīng)的軌跡生成器選項(xiàng),可以將細(xì)分的掃描累積到一個點(diǎn)云中,用于掃描匹配。若把默認(rèn)10改為1,1/1=1等于不分割
-
num_point_clouds: 要訂閱的point cloud的主題數(shù)量。為1時,訂閱points2主題的sensor_msgs/PointCloud2,或者為多臺點(diǎn)云訂閱主題的points2_1,points2_2
-
lookup_transform_timeout_sec: 用于使用tf2查找轉(zhuǎn)換的超時秒數(shù)。
-
submap_publish_period_sec: 發(fā)布子圖姿勢的時間間隔(以秒為單位),例如 0.3 秒。
-
pose_publish_period_sec: 發(fā)布姿勢的時間間隔(以秒為單位),例如 5e-3 表示頻率為 200 Hz。
-
publish_to_tf: 啟用或禁用提供 TF 轉(zhuǎn)換
-
publish_tracked_pose: 允許將跟蹤姿勢作為geometry_msgs/PoseStamped 發(fā)布到主題“tracked_pose”。
-
trajectory_publish_period_sec: 發(fā)布軌跡標(biāo)記的時間間隔(以秒為單位),例如 30e-3 30 毫秒。
-
rangefinder_sampling_ratio:測距儀消息的固定比率采樣。
-
odometry_sampling_ratio: 里程計(jì)消息的固定比率采樣。
-
fixed_frame_sampling_ratio: 固定幀消息的固定比率采樣。
-
imu_sampling_ratio IMU: IMU消息的固定比率采樣。
-
landmarks_sampling_ratio: 地標(biāo)消息的固定比率采樣。
-
use_pose_extrapolator: Node里的位姿估計(jì)器,作用是融合里程計(jì)和IMU,推測出一個位姿。 如果use_pose_extrapolator參數(shù)為true,發(fā)布出的這個位姿不準(zhǔn),因?yàn)槭窍闰?yàn)的位姿,沒有經(jīng)過雷達(dá)校準(zhǔn),除非IMU和里程計(jì)特別準(zhǔn)。因此這個參數(shù)一般都是false。如果參數(shù)publish_tracked_pose為false,use_pose_extrapolator其實(shí)就無效了
TF2是ROS2使用的坐標(biāo)轉(zhuǎn)換的工具
因此,我們其實(shí)可以根據(jù)我們自己的需求來配置我們所需要的.lua文件
除此之外,我們還可以配置兩個文件中的參數(shù),也就是頭文件中引入的"map_builder.lua" 和 "trajectory_builder.lua"
map_builder.lua 參數(shù)理解
include "pose_graph.lua"MAP_BUILDER = {use_trajectory_builder_2d = false, //是否使用2d建圖use_trajectory_builder_3d = false, //是否使用3d建圖num_background_threads = 4, //使用幾線程pose_graph = POSE_GRAPH,collate_by_trajectory = false, //用于控制是否將數(shù)據(jù)按照軌跡進(jìn)行分組。//如果將 collate_by_trajectory 設(shè)置為 true,則 Cartographer 將會按照每個軌跡的 ID 將數(shù)據(jù)進(jìn)行分組。在建圖過程中,Cartographer 將每個軌跡的數(shù)據(jù)單獨(dú)處理,然后將它們合并到最終地圖中。這對于多個軌跡的數(shù)據(jù)進(jìn)行建圖時非常有用。//如果將 collate_by_trajectory 設(shè)置為 false,則 Cartographer 將忽略軌跡信息,并將所有數(shù)據(jù)都視為同一個軌跡進(jìn)行處理。在這種情況下,Cartographer 會將所有數(shù)據(jù)合并到一起進(jìn)行建圖,生成一個單一的地圖。}這其中又含有pose_grapher.lua文件,其中的參數(shù)解析參考
cartographer pose_graph.lua 參數(shù)解析
trajectory_builder_2d.lua 參數(shù)理解
查看這篇網(wǎng)址即可
cartographer trajectory_builder_2d.lua參數(shù)備忘
.launch.py文件
在下載了cartographer包之后,我們可以根據(jù)launch文件的名字來選擇我們需要的文件,命名規(guī)則如下:
按照功能劃分,分為以下幾類:
(1)利用已有數(shù)據(jù)集進(jìn)行2d/3d建圖,如demo_backpack_2d.launch(其又調(diào)用了backpack_2d.launch)
(2)利用先驗(yàn)地圖及數(shù)據(jù)集進(jìn)行全局定位,如demo_backpack_2d_localization.launch
(3)顯示pbstream文件
launch文件命名規(guī)則標(biāo)明了其作用:用戶根據(jù)需要選擇launch文件
- offline_backpack_2d.launch:離線快速構(gòu)建全局地圖,事先記錄的數(shù)據(jù)集被多倍快速播放
- demo_backpack_2d_localization.launch:基于先驗(yàn)地圖進(jìn)行全局定位
- demo_backpack_2d.launch:同時定位和建圖,需要跑數(shù)據(jù)包
- backpack_2d.launch:同時定位和建圖,使用真實(shí)的傳感器數(shù)據(jù)
- assets_writer_my_robot.launch:用于從.pbstream先前 Cartographer 執(zhí)行的記錄中提取數(shù)據(jù)。
來源于https://blog.csdn.net/qq_18276949/article/details/113174339
我們就需要使用launch文件來啟動我們的cartographer功能包的節(jié)點(diǎn),同樣,我們打開前面.lua文件對應(yīng)的.launch文件——backpack_2d.launch.py(在ROS2中,由于python語言特性,已經(jīng)從.launch后綴改為了.launch.py后綴)
from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription from launch.conditions import IfCondition, UnlessCondition from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node, SetRemap from launch_ros.substitutions import FindPackageShare from launch.launch_description_sources import PythonLaunchDescriptionSource import osdef generate_launch_description():## ***** Launch arguments *****# 是否使用仿真時間,真實(shí)的機(jī)器人我們不需要,設(shè)置為Falseuse_sim_time_arg = DeclareLaunchArgument('use_sim_time', default_value = 'False')## ***** File paths ******# 找到cartographer功能包的地址pkg_share = FindPackageShare('cartographer_ros').find('cartographer_ros')## ***** Nodes *****#=====================聲明三個節(jié)點(diǎn),cartographer/occupancy_grid_node/rviz_node=================================cartographer_node = Node(package = 'cartographer_ros',executable = 'cartographer_node',parameters = [{'use_sim_time': LaunchConfiguration('use_sim_time')}],arguments = ['-configuration_directory', FindPackageShare('cartographer_ros').find('cartographer_ros') + '/configuration_files','-configuration_basename', 'backpack_2d.lua'],remappings = [('echoes', 'horizontal_laser_2d')],output = 'screen')# 可視化節(jié)點(diǎn)rviz_node = Node(package='rviz2',namespace='rviz2',executable='rviz2',name='rviz2',output='screen')cartographer_occupancy_grid_node = Node(package = 'cartographer_ros',executable = 'cartographer_occupancy_grid_node',parameters = [{'use_sim_time': True},{'resolution': 0.05}],)return LaunchDescription([use_sim_time_arg,# Nodesrviz_node ,cartographer_node,cartographer_occupancy_grid_node,])cartographer_node 節(jié)點(diǎn)中有一個remap的一個重映射,意思就是將前一個話題的名字重映射為后面的話題名字,就類似于一個改名的操作。
自己的.launch文件
from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription from launch.conditions import IfCondition, UnlessCondition from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node, SetRemap from launch_ros.substitutions import FindPackageShare from launch.launch_description_sources import PythonLaunchDescriptionSource import osdef generate_launch_description():## ***** Launch arguments *****use_sim_time_arg = DeclareLaunchArgument('use_sim_time', default_value = 'False')## ***** File paths ******pkg_share = FindPackageShare('cartographer_ros').find('cartographer_ros')# urdf_dir = os.path.join(pkg_share, 'urdf')# urdf_file = os.path.join(urdf_dir, 'backpack_2d.urdf')# with open(urdf_file, 'r') as infp:# robot_desc = infp.read()## ***** Nodes *****# robot_state_publisher_node = Node(# package = 'robot_state_publisher',# executable = 'robot_state_publisher',# parameters=[# {'robot_description': robot_desc},# {'use_sim_time': LaunchConfiguration('use_sim_time')}],# output = 'screen'# )cartographer_node = Node(package = 'cartographer_ros',executable = 'cartographer_node',arguments = ['-configuration_directory', FindPackageShare('cartographer_ros').find('cartographer_ros') + '/configuration_files','-configuration_basename', 'my_robot.lua'],remappings = [('scan', 'scan')],output = 'screen')cartographer_occupancy_grid_node = Node(package = 'cartographer_ros',executable = 'cartographer_occupancy_grid_node',parameters = [{'use_sim_time': False},{'resolution': 0.05}],)rviz_node = Node(package='rviz2',namespace='rviz2',executable='rviz2',name='rviz2',output='screen')return LaunchDescription([use_sim_time_arg,# Nodes# robot_state_publisher_node,rviz_node,cartographer_node,cartographer_occupancy_grid_node,])補(bǔ)充
在進(jìn)行了融合IMU和里程計(jì)數(shù)據(jù)之后,對carto的使用有了新的理解
對于carto而言,其核心需要使用的數(shù)據(jù)是激光雷達(dá)發(fā)布的/scan話題,因此我們一定需要提供/scan話題,并提供對應(yīng)的數(shù)據(jù)形式(無論你的數(shù)據(jù)是激光雷達(dá)得到的數(shù)據(jù)亦或者是其他方式得到的)。而對于carto的使用而言,它訂閱數(shù)據(jù)和frameID是沒有任何關(guān)系的,對于ROS2里面,拿到數(shù)據(jù)的方法是訂閱topic,因此參數(shù)中的map_frame,tracking_frame,published_frame都和carto訂閱數(shù)據(jù)無關(guān),這些frame_ID影響的是其TF樹的建立,而與數(shù)據(jù)訂閱無關(guān)。
在后續(xù)的測試當(dāng)中,我們發(fā)現(xiàn)carto訂閱的激光雷達(dá)數(shù)據(jù)就是/scan話題,訂閱的里程計(jì)數(shù)據(jù)就是/odom話題,我們暫時還沒找到有參數(shù)修改能改變其訂閱的數(shù)據(jù)話題名字,比如將其改變?yōu)橛嗛唎dom_optimize,后續(xù)如果繼續(xù)學(xué)習(xí)的過程中發(fā)現(xiàn)方法,也會繼續(xù)補(bǔ)充,也歡迎理解的朋友在評論區(qū)補(bǔ)充,感謝!
啟動及其調(diào)試
我們使用ros2 launch cartographer_ros my_robot.launch.py 注意,這里的my_robot.launch.py是自定義的啟動文件,需要自己配置和修改。
在啟動之前注意要先啟動激光雷達(dá)的驅(qū)動,讓激光雷達(dá)發(fā)布自己的數(shù)據(jù)。
善用rqt_graph
rqt_graph是一個非常好用的一個工具,我們一定要靈活的使用它
當(dāng)我打開了雷達(dá)的驅(qū)動節(jié)點(diǎn)之后,其顯示為
當(dāng)我再把cartographer啟動之后,節(jié)點(diǎn)就變成了
這個工具非常有利于我們看不同的節(jié)點(diǎn)是否成功訂閱了話題
安裝并運(yùn)行rqt
sudo apt update # 注意,這里需要安裝符合自己ros版本的rqt # 可以使用sudo apt-cache search 包名字 在apt源里尋找?guī)?/span>sudo apt install ros-humble-rqt*# 運(yùn)行 rqt_graph善用rqt_tf_tree
我們也可以使用該工具來查看各個坐標(biāo)之間的變換關(guān)系
安裝方式如下:
# 查看tf2坐標(biāo)關(guān)系 # 安裝 sudo apt install ros-humble-tf2-toolsros2 run tf2_tools view_frames # 查看tf坐標(biāo)關(guān)系建圖效果
我最開始建圖的方式是在vmware虛擬機(jī)上跑Ubuntu接激光雷達(dá),然后用手拿著激光雷達(dá)在家里進(jìn)行建圖,但是我發(fā)現(xiàn)建出來的圖,始終在亂飛,效果如下圖所示:
搜索了很多資料,都沒有發(fā)現(xiàn)能夠徹底解決的方式。于是后面想把IMU的數(shù)據(jù)也加到carto的算法當(dāng)中去,但是發(fā)現(xiàn)里面的配置參數(shù),urdf學(xué)起來一陣頭大,陸續(xù)學(xué)了一段時間,搞不定。(后續(xù)已經(jīng)解決,未來會寫一篇博客,記得關(guān)注噢)
最近開學(xué)測試,直接使用Ubuntu,而不是虛擬機(jī)來跑carto算法,但是把雷達(dá)放到了自己要做的小車上推著進(jìn)行建圖。同樣的配置參數(shù)和代碼,發(fā)現(xiàn)建圖效果好了起來,如下圖所示:
這其中,我認(rèn)為可能是激光雷達(dá)傳到虛擬機(jī)中出現(xiàn)了一些時間上的差異導(dǎo)致了建圖時間不匹配出現(xiàn)建圖亂飛,由于時間所限,也沒繼續(xù)深究了。
地圖保存
參考:
https://blog.csdn.net/PC2721/article/details/128303807
保存地圖需要另一個叫map_saver的節(jié)點(diǎn)
在建圖完畢之后,我們在我們想要保存地圖的地方運(yùn)行
注:這里保存用到了nav2,下一篇繼續(xù)總結(jié)。
ros2 run nav2_map_server map_saver_cli -f map注意:在調(diào)用map_saver節(jié)點(diǎn)之前不要關(guān)閉Cartographer節(jié)點(diǎn),不然會丟失地圖
會生成.pgm和.yaml兩個文件
后記
后面會繼續(xù)琢磨用nv2來導(dǎo)航,繼續(xù)努力
總結(jié)
以上是生活随笔為你收集整理的ROS2+cartorgrapher+激光雷达建图并保存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: http——基础知识
- 下一篇: 线性稳压器与开关稳压器的对比分析