navigation架构:
- 机器人只需要发布必要的传感器信息和导航的目标位置,ROS即可完成导航功能。在该框架中,move_base功能包提供导航的主要运行、交互接口。为了保障导航路径的准确性,机器人还要对自己所处的位置进行精确定位,这部分功能由amcl功能包实现。
- 首先,导航功能包需要采集机器人的传感器信息,以达到实时避障的效果。这就要求机器人通过ROS发布sensor_msgs/LaserScan或者sensor_msgs/PointCloud格式的消息,也就是二维激光信息或者三维点云信息。
- 其次,导航功能包要求机器人发布nav_msgs/Odometry格式的里程计信息,同时也要发布相应的TF变换。
- 最后,导航功能包的输出是geometry_msgs/Twist格式的控制指令,这就要求机器人控制节点具备解析控制指令中线速度、角速度的能力,并且最终通过这些指令控制机器人完成相应的运动。
路径规划器:
- 全局路径规划(global_planner)。全局路径规划是根据给定的目标位置和全局地图进行总体路径的规划。在导航中,使用Dijkstra或A*算法进行全局路径的规划,计算出机器人到目标位置的最优路线,作为机器人的全局路线。
- 本地实时规划(local_planner)。在实际情况中,机器人往往无法严格按照全局路线行驶,所以需要针对地图信息和机器人附近随时可能出现的障碍物规划机器人每个周期内应该行驶的路线,使之尽量符合全局最优路径。本地实时规划由local_planner模块实现,使用Dynamic Window Approaches算法搜索躲避和行进的多条路经,综合各评价标准(是否会撞击障碍物,所需要的时间等)选取最优路径,并且计算行驶周期内的线速度和角速度,避免与动态出现的障碍物发生碰撞。
move_base功能包
1)话题和服务(接口)
2)参数
AMCL功能包
1)话题和服务
2)参数
3)坐标变换
- 里程计定位:只是通过里程计的数据来处理/base和/odom之间的TF变换。
- amcl定位:可以估算机器人在地图坐标系/map下的位姿信息,供/base、/odom、/map之间的TF变换。
costmap配置
- 导航功能包使用两种代价地图存储周围环境中的障碍信息:一种用于全局路径规划(global_costmap),一种用于本地路径规划和实时避障(local_costmap)。两种代价地图需要使用一些共用的或独立的配置文件:通用配置文件、全局规划配置文件和本地规划配置文件。下面将详细介绍这三种配置文件的具体内容。
costmap_common_params.yaml
map_type: voxel #地图类型 voxel(体素地图 3d) costmap(代价地图 2d)
robot_radius: 0.20 #一个圆形机器人应该清除障碍物
#footprint: [[x0, y0], [x1, y1], ... [xn, yn]] #机器人是非圆形
max_obstacle_height: 0.6 #传感器读数的最大有效高度
obstacle_range: 2.5 #障碍物探测范围,单位为m
raytrace_range: 3.0 #自由空间探测范围,单位为m
#以下参数由VoxelCostmapPlugin 使用2d地图不使用
origin_z: 0.0 #Z原点,单位为米,仅对voxel地图
z_resolution: 0.2 #Z分辨率,单位meters/cell
z_voxels: 2 #每个垂直列中的体素数目,ROS Nav功能包的默认值为10
unknown_threshold: 15 #当整列的voxel是"已知"的时候,含有的未知单元("unknown")的最大数量
mark_threshold: 0 #整列voxel是自由("free")的时候,含有的已标记的cell("marked")的最大数目
publish_voxel_map: false #是否发布基础体美音网格以进行可视化
footprint_clearing_enabled: true #如果为 true,机器人的足迹将清除(标记为自由)其移动空间。
#障碍物层参数
obstacle_layer:
enabled: true #使能
combination_method: 1 #处理障碍物层之外其他层传入数据的行为方式。取值:覆盖已值"Overwrite"(0), 取最大值"Maximum" (1), 什么也不干"Nothing"(99)。“覆盖”仅是,例如使得它们没有生效。 “取最大值”是多数时候需要的。它提取obstacle_layer或输入数据中提供的数据的最大值。 “Nothing”根本不会改变传入的数据。 请注意,这会极大地影响costmap的行为方式,具体取决于您对track_unkown_space的设置。
track_unknown_space: true #true 需要通过未知空间禁用全局路径规划
false 像素具有2种状态:致命障碍(lethal)/自由(free)
true 像素具有3种状态:致命障碍(lethal)/自由(free)/未知(unknown)
publish_voxel_map: false #是否发布底层的体素栅格地图,主要用于可视化
observation_sources: scan bump #观察数据来源 激光数据(scan) 凸点数据(bump)
scan:
topic: scan #发布话题
sensor_frame: #传感器坐标系名称
observation_persistence: 0.0 #传感器数据保存时间 0.0 将只保留最新的读数
expected_update_rate: 0.0 #传感器在数秒内读取读数的频繁进行。值 0.0 将允许在读数之间留出无限时间。此参数用作故障保护,防止导航堆栈在传感器发生故障时指挥机器人。应将值设置为比传感器的实际速率稍微宽松的值。例如,如果我们期望每 0.05 秒从激光扫描一次,我们可能会将此参数设置为 0.1 秒,以提供大量缓冲区并解释一定量的系统延迟。
data_type: LaserScan #数据类型:LaserScan 目前仅支持PointCloud/PointCloud2/LaserScan
marking: true #启用标记障碍物
clearing: true #启用清清除障碍物功能
max_obstacle_height: 0.35 #障碍物最大高度
min_obstacle_height: 0.25 #障碍物最低高度
inf_is_valid: false
bump:
data_type: PointCloud2
topic: velodyne_points
marking: true
clearing: false
min_obstacle_height: 0.0
max_obstacle_height: 0.15
# for debugging only, let's you see the entire voxel grid #cost_scaling_factor and inflation_radius were now moved to the inflation_layer ns
inflation_layer: #膨胀层参数
enabled: true #使能
cost_scaling_factor: 5.0 # 障碍成本下降的指数速度 (default: 10)
inflation_radius: 0.5 # max. distance from an obstacle at which costs are incurred for planning paths.
#代价地图膨胀半径
static_layer: #静态地图层
enabled: true #启用静态地图
unknown_cost_value: -1 #从地图服务器在地图中读取时,应视为成本的值应视为未知值。如果成本图未跟踪未知空间,则此值的成本将被视为已占用。值为零也会导致此参数未使用。
lethal_cost_threshold: 100 #从地图服务器在地图中读取时要考虑成本致命性值的阈值。
map_topic:
first_map_only: false #仅订阅地图主题上的第一条消息,忽略所有后续消息
subscribe_to_updates: false #除了map_topic,还订阅map_topic+_updates"
track_unknown_space: true #true 地图消息中的未知值将直接转换为图层 false 地图消息中的未知值将转换为FREE_SPACE中的标记
use_maximum: false #只有当静态图层不是底层时才重要。如果为 true,则仅将最大值写入主成本图。
注:关于Marking and Clearing:
- marking和clearing参数用来表示是否需要使用传感器的实时信息来添加或清除代价地图中的障碍物信息)
- 代价地图自动订阅传感器主题并自动更新
- 每个传感器用于标记操作(将障碍物信息插入到代价地图中),清除操作(从代价地图中删除障碍物信息)或两者操作都执行
- 如果使用的是体素层,每一列上的障碍信息需要先进行投影转化成二维之后才能放入代价地图中
global_costmap_params.yaml
global_costmap:
global_frame: /map # 使用map框架作为global框架
robot_base_frame: /base_footprint # 机器人最底层框架
update_frequency: 1.0 # 全局地图更新频率,1.0~5.0
publish_frequency: 0 # 发布频率,静态全局地图不需要发布
static_map: true # 是否为静态地图,全局地图一般为静态
rolling_window: false # 更新全局地图,与上一个参数相反
resolution: 0.01 # 解析度,即地图分辨率
transform_tolerance: 1.0 # tf装换最大延时
map_type: costmap # 地图类型
local_costmap_params.yaml
local_costmap:
global_frame: /odom # 使用odom框架作为global框架
robot_base_frame: /base_footprint # 机器人最底层框架
update_frequency: 3.0 # 局部地图更新频率
publish_frequency: 1.0 # 发布频率,局部地图不要发布
static_map: false # 是否为静态地图,局部地图一般为动态
rolling_window: true # 更新局部地图,与上一个参数相反
width: 6.0 # 更新的局部地图的X维长度
height: 6.0 # 更新的局部地图的Y维长度
resolution: 0.01 # 解析度,即地图分辨率
transform_tolerance: 1.0 # tf装换最大延时
map_type: costmap # 地图类型
base_local_planner_params.yaml
controller_frequency: 3.0 # 更新路径规划的频率
recovery_behavior_enabled: false # 复原行为使能
clearing_rotation_allowed: false # 清除旋转允许
TrajectoryPlannerROS: # ROS轨迹计划
max_vel_x: 0.3 # X方向最大速度
min_vel_x: 0.05 # X方向最小速度
max_vel_y: 0.0 # 差速轮机器人无Y方向速度,取0
min_vel_y: 0.0
min_in_place_vel_theta: 0.5 # 机器人最小原地旋转速度
escape_vel: -0.1 # 机器人逃离速度,负值
acc_lim_x: 2.5 # X方向最大线加速度
acc_lim_y: 0.0 # 差速轮机器人无Y方向线加速度,取0
acc_lim_theta: 3.2 # 最大角加速度
holonomic_robot: false # 全向移动机器人使能
yaw_goal_tolerance: 0.1 # 至多距离目标方向误差
xy_goal_tolerance: 0.1 # 至多距离目标位置误差
latch_xy_goal_tolerance: false # 锁定至多距离目标位置误差
pdist_scale: 0.8 # 全局路径规划和到达目的地之间权重,比 gdist_scale 大时,机器人倾向考虑全局路径规划
gdist_scale: 0.6 # 到达目的地和全局路径规划之间权重,比 pdist_scale 大时,机器人倾向考虑到达目的地
meter_scoring: true # 以米为单位
occdist_scale: 0.1 # 避开障碍物的权重
oscillation_reset_dist: 0.05 #在振荡标志被清零前,机器人必须在出行多远。
publish_cost_grid_pc: false # 是否使用cost_grid发布
prune_plan: true # 机器人行走1m后,结束动作。
sim_time: 1.0 # 规划时需要考虑未来多长时间,结合dwa
sim_granularity: 0.025 # 给定轨迹的步长
angular_sim_granularity: 0.025 # 给定角度轨迹的弧长
vx_samples: 8 # x方向速度的样本数
vy_samples: 0 # 差速轮机器人无Y方向速度,取0
vtheta_samples: 20 # 角速度的样本数,默认为20
dwa: true # 模拟未来轨迹时是否使用动态窗口法
simple_attractor: false
launch
<launch>
<param name="use_sim_time" value="false" />
<!-- 设置地图的配置文件 -->
<arg name="map" default="test_map.yaml" />
<!-- 运行地图服务器,并且加载设置的地图-->
<node name="map_server" pkg="map_server" type="map_server" args="$(find your_pkgname)/maps/$(arg map)"/>
<!-- 运行move_base节点 -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen" clear_params="true">
<rosparam file="$(find your_pkgname)/config/fake/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find your_pkgname)/config/fake/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find your_pkgname)/config/fake/local_costmap_params.yaml" command="load" />
<rosparam file="$(find your_pkgname)/config/fake/global_costmap_params.yaml" command="load" />
<rosparam file="$(find your_pkgname)/config/fake/base_local_planner_params.yaml" command="load" />
</node>
<!-- 启动AMCL节点 -->
<include file="$(find your_pkgname)/launch/amcl.launch" />
<!-- 设置一个/odom与/map之间的静态坐标变换 -->
<node pkg="tf" type="static_transform_publisher" name="map_odom_broadcaster" args="0 0 0 0 0 0 /map /odom 100" />
<!-- 运行rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find your_pkgname)/rviz/nav.rviz"/>
</launch>