又有好几个月没有写博客了,今天风和日丽,于是乎想写一篇关于自己对于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)进行优化,也就是最小化误差,这里的误差项为速度,加速度,避障,追踪全局路径等等,最后优化出最优的局部路径,最近在研究teb的算法,有时间再开一个博客,上面大多都是自己的想法,如果有什么问题或错误,欢迎纠正。
————————————————
原文链接:https://blog.csdn.net/shenghuaijing3314/article/details/80005629