ROS:move_base除錯
move_base是ROS下關於機器人路徑規劃的中心樞紐。它通過訂閱鐳射雷達、map地圖、amcl的定位等資料,然後規劃出全域性和區域性路徑,再將路徑轉化為機器人的速度資訊,最終實現機器人導航。這裡又要盜官網的圖了。
上面這個圖很好的展示了move_base的整個框架,下面我更加詳細的介紹一下每個模組的功能及其是如何銜接的。
首先左上角的amcl模組是ROS的導航定位模組,amcl也叫自適應蒙特卡羅定位,amcl通過訂閱scan、map和tf資訊,釋出出機器人的pose,以供move_base使用,這部分具體可以上官網看看,這裡就不做詳細介紹了,左下角odom,即機器人里程計資訊,右上角,map,顧名思義,我們需要的先驗地圖資訊,一般通過slam建圖後儲存,ROS中主流的slam演算法有:
1.gmapping:需要鐳射雷達scan資料和里程計odom資料,採用的是PF(粒子濾波)。
2.hector :基於優化的演算法(解最小二乘問題),優缺點:不需要里程計,但對於雷達幀率要求很高40Hz,估計6自由度位姿,可以適應空中或者地面不平坦的情況。初值的選擇對結果影響很大,所以要求雷達幀率較高。
3.Cartographer:累計誤差較前兩種演算法低,能天然的輸出協方差矩陣,後端優化的輸入項。成本較低的雷達也能跑出不錯的效果。
好了,就不展開了,map_server包通過解析slam建好的地圖併發布出去。右下角的感測器topic則在區域性路徑規劃時起到作用,這部分就是costmap包起到的作用了,costmap為代價地圖,目前主要的有inflation_layer、obstacle_layer、static_layer、voxel_layer四個plugins。分別為膨脹層、障礙物層、靜態層和體素層。一般我們的全域性路徑需要靜態層和膨脹層,因為全域性規劃應該只考慮到地圖資訊,所以一般都是靜態的,而區域性路徑規劃則需要考慮到實時的障礙物資訊,所以需要障礙物層和膨脹層,這裡你可能會有疑惑,為什麼不把靜態層放到區域性路徑規劃裡呢?因為我們的定位是會存在誤差的,比如輪子打滑或其他情況,這個時候amcl會起到糾正作用,如果我們把靜態層放到了區域性中,這個定位會有問題,而且,假設當前機器人出現了定位的偏差,那麼這個引入的靜態層肯定是錯誤的,再加上區域性的障礙物層(這裡的障礙物層本應該和靜態層重合的,但由於定位偏差,不會重合)可能會使得機器人誤以為自己在障礙物層內,規劃出現問題。
下面我們我們說說move_base核心的部分了,也就是框框內的部分,主要包括global planner和local planner以及一些修復機制,包括rotate_recovery和clear_cost_map_recovery。有人可能會問,global planner是怎麼和local planner聯通的呢,這裡不同的演算法可能使用了不同的方法了,但核心都是大致相同了,都是將global planner作為local planner的一個初始參考或者優化方向。這些在深入看兩個原始碼時就會有更加深入的理解了。目前ROS中可以使用的global planner主要包括:A*和Dijkstra。local planner主要有:dwa、trajectory、teb和eband等。目前自我感覺teb local planner效果會好點,有時間會詳細的介紹一樣該演算法的思路,下面我把我的關於move_base的一些配置放到下面。
首先,啟動move_base的launch,包括解析map,move_base和amcl定位三個部分,這構成了一個完整的框架,下面我們主要來看move_base.launch裡的配置。
<launch>
<param name="use_sim_time" value="false" /> <!-- EDIT THIS LINE TO REFLECT THE NAME OF YOUR OWN MAP FILE Can also be overridden on the command line --> <arg name="map" default="test_map.yaml" />
<!-- Run the map server with the desired map --> <node name="map_server" pkg="map_server" type="map_server" args="$(find dart_nav)/maps/dart.yaml"/>
<!-- Start move_base --> <include file="$(find dart_nav)/launch/tb_move_base_test.launch" />
<!-- Fire up AMCL --> <include file="$(find dart_nav)/launch/tb_amcl.launch" />
</launch>
下面是move_base.launch內的配置:
<launch> <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen" clear_params="true"> <rosparam file="$(find dart_nav)/config1/costmap_common_params.yaml" command="load" ns="global_costmap" /> <rosparam file="$(find dart_nav)/config1/costmap_common_params.yaml" command="load" ns="local_costmap" /> <rosparam file="$(find dart_nav)/config1/local_costmap_params.yaml" command="load" /> <rosparam file="$(find dart_nav)/config1/global_costmap_params.yaml" command="load" /> <rosparam file="$(find dart_nav)/config1/teb_local_planner_params.yaml" command="load" />
<param name="base_global_planner" value="global_planner/GlobalPlanner"/> <param name="planner_frequency" value="1.0" /> <param name="planner_patience" value="5.0" />
<param name="base_local_planner" value="teb_local_planner/TebLocalPlannerROS" /> <param name="controller_frequency" value="15.0" /> <param name="controller_patience" value="15.0" /> <rosparam file="$(find dart_nav)/config1/costmap_conversion_params.yaml" command="load" />
</node> </launch>
如上所示,我使用的是global_planner這個包,它預設使用的是dijkstra,當然也可以使用A*全域性路徑規劃,區域性路徑規劃我使用的是teb,同樣需要配置上面第3行到第7行的一些yaml,這些yaml是costmap和planner的一些配置檔案。我們來主要看一下local_costmap_params.yaml和global_costmap_params.yaml。這裡配置了上面提到的各個層(layers)的使用。
local_costmap_params.yaml:
local_costmap: global_frame: /odom robot_base_frame: /base_link update_frequency: 3.0 publish_frequency: 1.0 static_map: false rolling_window: true width: 6.0 height: 6.0 resolution: 0.05 transform_tolerance: 1.0 map_type: costmap plugins: - {name: obstacle_layer, type: "costmap_2d::VoxelLayer"} # - {name: social_layer, type: "social_navigation_layers::ProxemicLayer"} # - {name: social_pass_layer, type: "social_navigation_layers::PassingLayer"} # - {name: static_layer, type: "costmap_2d::StaticLayer"} - {name: inflation_layer, type: "costmap_2d::InflationLayer"}
#Configuration for the sensors that the costmap will use to update a map obstacle_layer: observation_sources: scan scan: {data_type: LaserScan, topic: /scan, marking: true, clearing: true, expected_update_rate: 0} inflation_layer: inflation_radius: 0.35
這裡由於是區域性層,所以我們要把static_map設為fasle,我們並不希望使用靜態的地圖,因為這不利於我們的區域性避障,rollung_window設為true。下面的plugins就為各個層的設定了,在區域性路徑規劃中我們只需要考慮障礙物的資訊就可以了,不需要考慮我們之前slam建立的靜態地圖,所有我們添加了兩個圖層,即障礙物層和障礙物的膨脹層,下面這個很重要,我們需要宣告我們的障礙物層的資料來源,即scan,要把topic對應你的scan的topic,否則你可能發現rviz裡的local_costmap沒有資料,這就是你沒有宣告的原因。最後兩行為設定膨脹層的半徑大小。
global_planner_params.yaml:
global_costmap: global_frame: /map robot_base_frame: /base_footprint update_frequency: 1.0 publish_frequency: 0 static_map: true rolling_window: false resolution: 0.05 transform_tolerance: 1.0 map_type: costmap plugins: # - {name: obstacle_layer, type: "costmap_2d::VoxelLayer"} - {name: static_layer, type: "costmap_2d::StaticLayer"} - {name: inflation_layer, type: "costmap_2d::InflationLayer"} inflation_layer: inflation_radius: 0.35
如上,由於在全域性路徑規劃中,我們只應該考慮靜態地圖,即我們之前slam建立好的地圖,所以我們把static_map設為true,rolling_window設為false,再看plugins:有兩個層,靜態層和膨脹層,這樣就完成了global的yaml的配置,如果你需要把障礙物層也新增到全域性地圖中,plugins加入,同時別忘了在下面宣告資料來源,但我個人不推薦將障礙物層加入到global中來。
下面就是我使用的teb的local_planner的yaml了:
TebLocalPlannerROS:
odom_topic: odom map_frame: /odom
# Trajectory
teb_autosize: True dt_ref: 0.3 dt_hysteresis: 0.1 global_plan_overwrite_orientation: True max_global_plan_lookahead_dist: 3.0 feasibility_check_no_poses: 5
# Robot
max_vel_x: 0.4 max_vel_x_backwards: 0.15 max_vel_theta: 0.4 acc_lim_x: 2.0 acc_lim_theta: 1.0 min_turning_radius: 0.0 # footprint_model/type: "circular" # footprint_model/radius: 0.40 # for type "circular"
# GoalTolerance
xy_goal_tolerance: 0.15 yaw_goal_tolerance: 0.2 free_goal_vel: False
# Obstacles
min_obstacle_dist: 0.45 include_costmap_obstacles: True costmap_obstacles_behind_robot_dist: 1.0 obstacle_poses_affected: 30 costmap_converter_plugin: "" costmap_converter_spin_thread: True costmap_converter_rate: 5
# Optimization
no_inner_iterations: 5 no_outer_iterations: 4 optimization_activate: True optimization_verbose: False penalty_epsilon: 0.1 weight_max_vel_x: 2 weight_max_vel_theta: 1 weight_acc_lim_x: 1 weight_acc_lim_theta: 1 weight_kinematics_nh: 1000 weight_kinematics_forward_drive: 1 weight_kinematics_turning_radius: 1 weight_optimaltime: 1 weight_obstacle: 300 #50 weight_dynamic_obstacle: 10 # not in use yet alternative_time_cost: False # not in use yet
# Homotopy Class Planner
enable_homotopy_class_planning: False enable_multithreading: True simple_exploration: False max_number_classes: 4 roadmap_graph_no_samples: 15 roadmap_graph_area_width: 5 h_signature_prescaler: 0.5 h_signature_threshold: 0.1 obstacle_keypoint_offset: 0.1 obstacle_heading_threshold: 0.45 visualize_hc_graph: False
這裡沒什麼好說的,簡單介紹一下teb演算法,teb是一個基於圖優化的區域性路徑規劃,瞭解slam的應該都知道BA(bundle adjustment),這裡類似,是通過把路徑問題構建成一個圖優化的問題,然後通過g2o開源庫求解優化,teb中是通過對全域性路徑規劃的點(A*或dijkstra)進行優化,也就是最小化誤差,這裡的誤差項為速度,加速度,避障,追蹤全域性路徑等等,最後優化出最優的區域性路徑.