4. 代价地图参数深度解析:全局代价地图与局部代价地图的配置、膨胀层、静态层、障碍物层
代价地图,说白了就是机器人对环境的「风险认知图」。
我刚开始接触Nav2时,总觉得代价地图就是个简单的黑白图。后来踩了不少坑才明白——它其实是导航系统里最核心的决策依据。全局代价地图负责「看全局」,局部代价地图负责「看眼前」。两者配合不好,机器人要么撞墙,要么原地转圈。
4.1 全局代价地图 vs 局部代价地图
先搞清楚这两个家伙的分工。
- 全局代价地图:从静态地图出发,叠加已知障碍物。它不关心动态变化,只做宏观路径规划。我习惯把它的更新频率设低一些,比如1Hz,省CPU。
- 局部代价地图:实时感知周围环境,处理动态障碍物。更新频率要高,我一般设10Hz。你想想看,如果局部地图更新慢了,机器人可能直接怼到突然出现的人身上。
嗯,这里要注意:全局地图的尺寸可以很大,但局部地图一般只覆盖机器人周围几米。我见过有人把局部地图设得跟全局一样大,结果性能直接崩了。
核心配置对比
| 参数 | 全局代价地图 | 局部代价地图 |
|---|---|---|
| update_frequency | 1.0 Hz | 5.0 - 10.0 Hz |
| publish_frequency | 1.0 Hz | 5.0 - 10.0 Hz |
| width / height | 地图尺寸(如 20m x 20m) | 3m - 5m 见方 |
| resolution | 0.05 m | 0.05 m |
| plugins | static_layer, inflation_layer | obstacle_layer, inflation_layer |
4.2 膨胀层(Inflation Layer)
膨胀层是代价地图的「安全缓冲带」。它把障碍物周围的区域标记为高代价,让机器人不敢靠近。
我曾经在项目中把膨胀半径设得太小,结果机器人贴着墙走,稍微有点定位误差就撞上了。后来我学乖了——膨胀半径至少要比机器人半径大20%。
inflation_layer:
enabled: true
inflation_radius: 0.55 # 膨胀半径,单位米
cost_scaling_factor: 10.0 # 代价衰减系数
关键参数解读:
inflation_radius:膨胀半径。越大,机器人离障碍物越远。我建议设成机器人半径的1.2倍。cost_scaling_factor:代价衰减速度。值越大,代价从障碍物边缘向外衰减得越快。我习惯用10.0,效果比较平滑。
我的经验:如果机器人经常在狭窄通道里卡住,试试把cost_scaling_factor调小到5.0。代价衰减变慢,机器人会更愿意走窄路。
4.3 静态层(Static Layer)
静态层负责加载预先建好的地图。说白了,就是告诉机器人「这堵墙是固定的,你别想穿过去」。
我记得第一次用Nav2时,忘了配静态层,结果机器人直接无视地图里的墙,规划出一条穿墙的路径。嗯,那场面挺尴尬的。
static_layer:
enabled: true
map_topic: /map
first_map_only: false
subscribe_to_updates: true
参数说明:
map_topic:地图话题。默认是/map,如果你用map_server发布地图,这个不用改。first_map_only:是否只接受第一张地图。我建议设成false,这样地图更新时能自动刷新。subscribe_to_updates:是否订阅地图更新。如果你在动态建图,这个必须开。
避坑指南:我曾经把first_map_only设成true,结果地图更新后机器人还在用旧地图规划路径,直接撞上了新摆放的货架。从那以后,我只要不是特别确定地图不变,都会把这个参数设成false。
4.4 障碍物层(Obstacle Layer)
障碍物层是动态障碍物的「实时感知器」。它从激光雷达、深度相机等传感器获取数据,实时标记障碍物。
这个层最容易出问题。我见过有人把障碍物层的最大范围设得比局部地图还大,结果传感器数据被截断,机器人对远处的障碍物完全没反应。
obstacle_layer:
enabled: true
observation_sources: laser_scan_sensor
laser_scan_sensor:
topic: /scan
sensor_frame: laser_frame
data_type: LaserScan
clearing: true
marking: true
obstacle_range: 2.5
raytrace_range: 3.0
核心参数:
obstacle_range:障碍物检测范围。超过这个距离的障碍物会被忽略。我一般设成局部地图尺寸的一半。raytrace_range:射线追踪范围。用于清除障碍物(比如障碍物移走后,清除它留下的代价)。这个值要比obstacle_range大一点。clearing/marking:是否清除/标记障碍物。两个都开,不然障碍物移走了代价还在。
多传感器融合配置:
obstacle_layer:
observation_sources: laser_scan_sensor point_cloud_sensor
laser_scan_sensor:
topic: /scan
data_type: LaserScan
obstacle_range: 2.5
raytrace_range: 3.0
point_cloud_sensor:
topic: /camera/depth/points
data_type: PointCloud2
obstacle_range: 2.0
raytrace_range: 2.5
我建议激光雷达和深度相机同时用。激光雷达精度高,深度相机能检测低矮障碍物。两者互补,效果最好。
4.5 三层协同工作流程
这三层不是各自为战的。它们按顺序叠加:
- 静态层先加载地图,标记固定障碍物。
- 障碍物层实时更新动态障碍物。
- 膨胀层最后对所有障碍物做膨胀处理。
你想想看,如果顺序搞反了,膨胀层先处理,障碍物层后更新,那膨胀效果就被覆盖了。嗯,Nav2的插件机制保证了顺序,但你自己写插件时一定要注意。
4.6 实战配置建议
说了这么多,我直接给一套我常用的配置模板吧。
# 全局代价地图配置
global_costmap:
global_frame: map
robot_base_frame: base_footprint
update_frequency: 1.0
publish_frequency: 1.0
width: 20.0
height: 20.0
resolution: 0.05
plugins:
- {name: static_layer, type: "nav2_costmap_2d::StaticLayer"}
- {name: inflation_layer, type: "nav2_costmap_2d::InflationLayer"}
# 局部代价地图配置
local_costmap:
global_frame: odom
robot_base_frame: base_footprint
update_frequency: 10.0
publish_frequency: 10.0
width: 4.0
height: 4.0
resolution: 0.05
plugins:
- {name: obstacle_layer, type: "nav2_costmap_2d::ObstacleLayer"}
- {name: inflation_layer, type: "nav2_costmap_2d::InflationLayer"}
我的习惯:全局地图的global_frame用map,局部地图用odom。这样全局地图固定不动,局部地图跟着机器人走。如果你把局部地图的global_frame也设成map,那每次定位漂移时局部地图都会抖动,规划出来的路径会很不稳定。
嗯,代价地图的配置就这些。说白了,三层各司其职:静态层管固定障碍,障碍物层管动态变化,膨胀层管安全距离。配好了,机器人跑起来就稳了。