第1章:代价地图(Costmap)——静态层、障碍物层、膨胀层的原理与配置
大家好,欢迎来到《机器人局部路径规划与避障策略实战》的第一章。
今天咱们聊聊代价地图。嗯,这玩意儿是机器人导航的基石。你想想看,机器人要走路,它得知道哪里能走、哪里不能走。代价地图就是干这个的。
我个人习惯把代价地图比作一张「热力图」。越红的地方越危险,越绿的地方越安全。机器人规划路径时,会尽量走绿色的区域,绕着红色的地方走。
1.1 代价地图是什么?
说白了,代价地图就是一个二维网格。每个格子(我们叫它cell)都有一个数值,代表这个位置的风险程度。
- 0:绝对安全,随便走
- 1-99:有风险,数值越大越危险
- 100:致命障碍,绝对不能走
我在项目中遇到过一个问题:有次机器人死活绕不过一个柱子,后来发现是代价地图的膨胀半径设得太小了。机器人以为自己能挤过去,结果卡住了。嗯,这就是代价地图没配好的典型例子。
1.2 三层结构:静态层、障碍物层、膨胀层
ROS的代价地图是分层的。为什么要分层?因为不同来源的信息更新频率不一样。你想想看,墙壁是固定的,但行人会动。如果混在一起处理,效率会很低。
1.2.1 静态层(Static Layer)
静态层,顾名思义,就是那些不会动的东西。比如墙壁、柱子、家具。这些信息通常来自预先建好的地图(比如SLAM建出来的栅格地图)。
配置参数很简单:
static_layer:
enabled: true
map_topic: "/map"
first_map_only: false
subscribe_to_updates: true
这里有个坑:first_map_only 如果设为 true,地图就只加载一次。如果后面地图更新了,它不会重新订阅。我建议设成 false,除非你确定地图永远不会变。
1.2.2 障碍物层(Obstacle Layer)
障碍物层处理的是动态障碍物。比如行人、其他机器人、突然出现的箱子。它通过传感器数据(激光雷达、深度相机)实时更新。
配置示例:
obstacle_layer:
enabled: true
observation_sources: laser_scan_sensor
laser_scan_sensor:
sensor_frame: base_laser
data_type: LaserScan
topic: /scan
marking: true
clearing: true
min_obstacle_height: 0.0
max_obstacle_height: 2.0
我曾经踩过一个坑:min_obstacle_height 和 max_obstacle_height 没设对。结果机器人把地面上的反光点当成了障碍物,原地打转。后来我把最小高度设成了0.1米,问题就解决了。
1.2.3 膨胀层(Inflation Layer)
膨胀层是干嘛的?它把障碍物「吹大」一圈。为什么要这么做?因为机器人有体积,不能贴着障碍物走。膨胀层就是给机器人留出安全距离。
核心参数就两个:
| 参数 | 说明 | 建议值 |
|---|---|---|
| inflation_radius | 膨胀半径,影响范围 | 机器人半径的1.5倍 |
| cost_scaling_factor | 代价衰减速度 | 10.0(默认) |
你想想看,inflation_radius 设得太大,机器人会绕远路;设得太小,容易撞墙。我个人习惯是先设成机器人半径的1.5倍,然后根据实际效果微调。
核心要点:膨胀层不是真的把障碍物变大了,而是给障碍物周围的格子赋值。离障碍物越近,代价越高。这样路径规划算法就会自动避开。
1.3 三层如何协同工作?
这三层是叠加的。最终代价地图 = 静态层 + 障碍物层 + 膨胀层。
举个例子:
- 静态层告诉你「这里有一堵墙」
- 障碍物层告诉你「墙前面有个人」
- 膨胀层告诉你「墙和人周围1米内别靠近」
机器人看到的就是一张完整的风险地图。
小技巧:调试时可以用 rviz 的 Costmap 显示插件。把三层分别显示出来,看看哪一层出了问题。我调试时经常这么干,一眼就能看出问题在哪。
1.4 配置实战:一个完整的代价地图配置
下面是一个完整的YAML配置,我直接贴出来:
global_costmap:
global_frame: map
robot_base_frame: base_footprint
update_frequency: 5.0
publish_frequency: 2.0
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: 10.0
publish_frequency: 5.0
static_map: false
rolling_window: true
width: 6.0
height: 6.0
resolution: 0.05
plugins:
- {name: obstacle_layer, type: "costmap_2d::ObstacleLayer"}
- {name: inflation_layer, type: "costmap_2d::InflationLayer"}
注意看,全局代价地图用了静态层,局部代价地图没用。因为局部地图是滚动窗口,不需要全局地图。嗯,这里要记住:局部地图只关心周围几米的情况。
警告:局部代价地图的 rolling_window 如果设为 true,必须设置 width 和 height。我见过有人忘了设,结果地图大小变成0,机器人直接罢工。
1.5 避坑指南
最后分享几个我踩过的坑:
- 地图更新频率太高:我曾经把
update_frequency设成50Hz,结果CPU跑满,机器人卡成PPT。后来改成10Hz,一切正常。 - 传感器帧不对:
sensor_frame必须和TF树里的一致。否则障碍物位置会偏移,机器人会对着空气绕圈。 - 膨胀半径和机器人尺寸不匹配:如果机器人是圆形,膨胀半径设成半径的1.2倍就够了。如果是矩形,要按对角线算。
好了,这一章就到这里。代价地图是路径规划的基础,理解透了三层结构,后面学路径规划算法会轻松很多。下一章我们聊聊全局路径规划,到时候见。