第三章:地图服务与代价地图
各位同学好,我是你们的导航实战讲师。今天我们来聊聊导航里最核心的数据结构——代价地图。说实话,我见过太多人在导航参数上栽跟头,最后发现是代价地图没配好。这一章,我会把地图数据类型、三个核心图层以及参数调优的坑,一次性讲清楚。
3.1 地图数据类型:OccupancyGrid
先说说地图长什么样。在ROS2里,地图的数据类型叫nav_msgs::msg::OccupancyGrid。说白了,它就是一个二维数组,每个格子存一个值,表示这个格子被占用的概率。
值的范围是0到100:
- 0:完全空闲,可以走
- 100:完全被占,不能走
- -1:未知区域,没探测到
嗯,这里要注意:-1这个值特别容易坑人。我刚开始做项目时,以为未知区域就是空闲,结果机器人直接往墙里钻。后来才明白,未知区域要当作障碍物处理才安全。
地图的元信息也很关键:
# 地图元信息示例
origin: [-10.0, -10.0, 0.0] # 地图左下角坐标
resolution: 0.05 # 每个格子5厘米
width: 400 # 宽度400格
height: 400 # 高度400格
分辨率选多少?我个人习惯室内用0.05米,室外用0.1米。分辨率太高,计算量爆炸;太低,机器人过不了窄门。
3.2 静态地图层
静态地图层,就是加载你事先建好的SLAM地图。它不会动态变化,除非你重新加载。
配置起来很简单:
static_layer:
map_topic: "/map"
subscribe_to_updates: true
map_subscribe_transient_local: true
这里有个坑:subscribe_to_updates要不要开?我建议开着。为什么呢?因为有些场景下,你可能会通过地图服务器动态更新地图(比如有人手动修正了地图)。不开的话,更新了你也看不到。
还有map_subscribe_transient_local,这个参数的意思是:订阅时如果地图还没发布,就等一等。嗯,这个一定要设成true,否则启动顺序不对时,代价地图就收不到地图数据了。
3.3 障碍物层
障碍物层负责处理传感器数据。激光雷达、深度相机、声呐,这些数据都会汇到这里。
核心配置:
obstacle_layer:
observation_sources: laser_scan_sensor
laser_scan_sensor:
topic: /scan
sensor_frame: laser_frame
data_type: LaserScan
clearing: true
marking: true
inf_is_valid: true
我来解释几个关键参数:
- clearing:是否清除障碍物。比如障碍物移走了,传感器扫不到,就应该把原来的障碍物标记清除。
- marking:是否标记障碍物。检测到障碍物就标记为占用。
- inf_is_valid:无限远距离是否有效。有些激光雷达在测不到时返回inf,这个参数要设成true,否则会报错。
我在项目中遇到过一个问题:机器人明明看到障碍物了,但就是不避障。查了半天,发现是clearing和marking都设成了false。说白了,你既不让它标记障碍物,又不让它清除,那这层就废了。
3.4 膨胀层
膨胀层,就是把障碍物往外扩一圈。为什么要扩?因为机器人有体积,你不能让机器人的中心点贴着障碍物走。
膨胀半径怎么算?我一般这样算:
inflation_layer:
inflation_radius: 0.55 # 机器人半径 + 安全余量
cost_scaling_factor: 10.0
inflation_radius设成机器人半径加上0.1~0.2米的安全余量。比如机器人半径0.35米,我就设0.55米。
cost_scaling_factor这个参数很有意思。它控制代价从障碍物边缘到膨胀边界的衰减速度。值越大,衰减越快,路径越靠近障碍物。值越小,衰减越慢,路径越远离障碍物。
我曾经在一个仓库项目里,把cost_scaling_factor设成了5.0,结果机器人绕路绕得离谱。后来改成10.0,路径就合理多了。你想想看,这个值其实就是个权衡:安全 vs 效率。
3.5 代价地图配置与参数调优
好了,三个图层都讲完了。现在把它们组合起来,就是完整的代价地图配置。
一个典型的配置长这样:
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: 5.0
publish_frequency: 2.0
width: 3.0
height: 3.0
resolution: 0.05
plugins:
- {name: obstacle_layer, type: "nav2_costmap_2d::ObstacleLayer"}
- {name: inflation_layer, type: "nav2_costmap_2d::InflationLayer"}
注意看,全局代价地图和局部代价地图的配置不一样:
- 全局地图用map坐标系,更新频率低(1Hz),范围大(20米)
- 局部地图用odom坐标系,更新频率高(5Hz),范围小(3米)
为什么会这样?因为全局规划不需要实时更新,但需要知道整个地图。局部规划需要快速响应,但只看机器人周围几米就够了。
参数调优的几个要点:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| update_frequency | 全局1~2Hz,局部5~10Hz | 太高CPU吃不住,太低反应慢 |
| resolution | 室内0.05,室外0.1 | 分辨率越高,计算量越大 |
| inflation_radius | 机器人半径+0.1~0.2米 | 太小容易撞,太大绕路 |
| cost_scaling_factor | 8.0~12.0 | 根据场景微调 |
核心原则:代价地图的配置没有银弹。每个项目都要根据机器人尺寸、传感器类型、运行环境来调。我建议你先用默认参数跑起来,然后观察机器人的行为,再逐步微调。
小技巧:调试时可以用rviz2打开代价地图的可视化。红色区域是障碍物,蓝色区域是膨胀层。如果机器人路径贴着红色走,说明膨胀半径太小;如果绕大圈,说明膨胀半径太大。
警告:千万不要把inflation_radius设成0。我曾经犯过这个错,结果机器人直接往墙上怼。膨胀层是安全底线,必须保留。
3.6 知识体系总览
最后,我用一张图把本章的知识点串起来。这张图展示了地图数据、三个图层以及它们之间的关系:
这张图很直观:地图数据是基础,三个图层各司其职,最终合成代价地图,供路径规划算法使用。你配置的时候,要确保每个图层都正常工作,缺一个都不行。
好了,这一章就到这里。记住,代价地图是导航的基石,配好了事半功倍,配不好寸步难行。下一章我们讲路径规划算法,到时候你会看到代价地图是怎么被用起来的。
可可精品资料库