4、代价地图配置:全局代价地图与局部代价地图、膨胀层参数、障碍物层与静态层
代价地图,说白了就是机器人对周围环境的「风险认知图」。你想想看,机器人要导航,它得知道哪里能走、哪里不能走、哪里最好绕着走。代价地图就是干这个的。
ROS 里把代价地图分成了两种:全局代价地图和局部代价地图。我刚开始接触时也觉得麻烦,后来才明白——这俩分工完全不同。
4.1 全局代价地图 vs 局部代价地图
全局代价地图,负责「宏观规划」。它覆盖整个已知环境,分辨率可以低一些,更新频率也不用太快。全局路径规划器(比如 A*、Dijkstra)就在这张图上找出一条从起点到终点的最优路径。
局部代价地图,负责「微观避障」。它只关注机器人周围一小块区域(通常是 3-5 米半径),分辨率高,更新快。局部规划器(比如 DWA、TEB)在这张图上实时调整速度,避开突然出现的障碍物。
我做过一个项目,在仓库里跑 AGV。一开始我把全局地图分辨率设得很高,结果规划一次路径要算好几秒,机器人走走停停。后来我明白了——全局地图不需要那么精细,够用就行。
| 对比项 | 全局代价地图 | 局部代价地图 |
|---|---|---|
| 覆盖范围 | 整个已知地图 | 机器人周围(3-5m) |
| 分辨率 | 较低(0.05-0.1m) | 较高(0.02-0.05m) |
| 更新频率 | 低(1-5Hz) | 高(10-50Hz) |
| 主要用途 | 全局路径规划 | 局部避障与速度控制 |
| 数据来源 | 静态地图 + 长期障碍物 | 实时传感器 + 短期障碍物 |
4.2 代价地图的三层结构
代价地图不是一张简单的灰度图。它由三层叠加而成:静态层、障碍物层、膨胀层。每一层都有自己的职责。
我画了张图,帮你理清这三层的关系:
这三层叠加在一起,最终生成一张完整的代价地图。静态层提供基础环境信息,障碍物层实时更新动态物体,膨胀层给所有障碍物加上「安全距离」。
4.3 静态层配置
静态层是最简单的一层。它直接从预先建立的地图(通常是 pgm/yaml 文件)加载数据。配置时主要关注两个参数:
- map_topic:地图话题名称,默认是 /map
- first_map_only:是否只加载第一张地图。设为 true 可以避免地图被后续数据覆盖
我建议把 first_map_only 设为 true。有一次我在调试时忘了设这个,结果机器人跑着跑着,静态地图被传感器数据覆盖了,导航直接崩了。
static_layer:
enabled: true
map_topic: "/map"
first_map_only: true
unknown_cost_value: 0
lethal_cost_threshold: 100
4.4 障碍物层配置
障碍物层是实时避障的核心。它订阅传感器数据,把检测到的障碍物标记到代价地图上。
配置时要注意几个关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| observation_sources | 空 | 传感器列表,如 "laser_scan_sensor" |
| clearing | true | 是否清除传感器视野内的旧障碍物 |
| marking | true | 是否标记传感器检测到的新障碍物 |
| obstacle_range | 2.5 | 障碍物标记的最大距离(米) |
| raytrace_range | 3.0 | 障碍物清除的最大距离(米) |
这里有个坑:obstacle_range 和 raytrace_range 的差值。如果 raytrace_range 比 obstacle_range 大太多,传感器视野边缘的障碍物会被错误清除。我建议差值控制在 0.5m 以内。
obstacle_range 设成 5m,raytrace_range 设成 6m。结果机器人把远处的墙壁当成「已清除」,直接撞上去了。后来我把差值缩到 0.5m,问题解决。
4.5 膨胀层参数详解
膨胀层,说白了就是给障碍物「加一圈安全区」。它把障碍物周围的代价从 0 逐渐增加到 255(致命代价),让规划器主动避开。
膨胀层有三个核心参数:
- inflation_radius:膨胀半径(米)。障碍物周围这个半径内的区域都会产生代价
- cost_scaling_factor:代价衰减因子。值越大,代价衰减越快
- inflation_layer:是否启用膨胀层
代价的计算公式是这样的:
cost = 255 * exp(-(distance - inscribed_radius) * cost_scaling_factor)
其中 distance 是当前点到障碍物的距离,inscribed_radius 是机器人内切圆半径。
我一般这样调:
- 先把
inflation_radius设成机器人半径的 1.5 倍 - 再把
cost_scaling_factor设成 10.0 - 如果机器人离障碍物太近,增大
cost_scaling_factor - 如果路径太绕,减小
inflation_radius
inflation_radius 设太大机器人会卡住。我一般设 0.3-0.5m,配合 cost_scaling_factor 10-15,效果不错。
4.6 参数调优实战
调参不是一蹴而就的。我总结了一套流程,你可以试试:
- 先调静态层:确保地图加载正确,没有空洞或错位
- 再调障碍物层:让机器人原地转一圈,看传感器数据是否正确映射到代价地图
- 最后调膨胀层:让机器人走一段直路,观察它离障碍物的距离
调参时可以用 rviz 实时查看代价地图。把 /move_base/global_costmap/costmap 和 /move_base/local_costmap/costmap 话题显示出来,颜色越红代表代价越高。
global_costmap:
global_frame: map
robot_base_frame: base_footprint
update_frequency: 5.0
publish_frequency: 2.0
width: 20.0
height: 20.0
resolution: 0.05
static_map: true
plugins:
- {name: static_layer, type: "costmap_2d::StaticLayer"}
- {name: obstacle_layer, type: "costmap_2d::ObstacleLayer"}
- {name: inflation_layer, type: "costmap_2d::InflationLayer"}
local_costmap:
global_frame: odom
robot_base_frame: base_footprint
update_frequency: 20.0
publish_frequency: 10.0
width: 4.0
height: 4.0
resolution: 0.025
static_map: false
rolling_window: true
plugins:
- {name: obstacle_layer, type: "costmap_2d::ObstacleLayer"}
- {name: inflation_layer, type: "costmap_2d::InflationLayer"}
注意看,全局代价地图用了 static_map: true,局部代价地图用了 rolling_window: true。这就是我前面说的——全局地图覆盖整个环境,局部地图跟着机器人走。
嗯,代价地图的配置就这些。记住一个原则:全局地图求稳,局部地图求快。稳是指地图稳定、不频繁更新;快是指响应快、能及时避障。把握好这个度,你的导航系统就不会出大问题。