3. 代价地图参数解析与矿山适配
在矿山巷道中跑 ROS2 导航,很多“机器人贴着墙走”“在窄巷里原地打转”“明明看着没障碍却报碰撞”的问题,根源往往不在规划器,而在代价地图(costmap)的参数与分层配置。代价地图本质上是把传感器数据、静态地图和机器人外形统一折算成一张“代价格栅”:每个栅格用 0~255 表示通行代价,0 为自由,253 为内切障碍,254 为致命障碍,255 为未知。规划器在这张格栅上寻找低代价路径,因此参数稍有偏差,行为就会明显不同。
ROS2 导航使用 Nav2 的分层代价地图,典型由静态层(static_layer)、障碍物层(obstacle_layer)、体素层(voxel_layer)和膨胀层(inflation_layer)叠加而成。静态层加载 SLAM 建好的 /map,提供巷道轮廓与固定设施;障碍物层把激光或深度点云按射线追踪写入栅格,并支持障碍物清除;体素层用三维体素缓存点云,能区分悬空管线、斜坡与地面凸起,适合矿山这种三维起伏环境;膨胀层则围绕障碍按代价衰减生成“缓冲区”,其半径直接决定机器人敢不敢贴近巷帮。
膨胀半径:窄巷里最敏感的一个数
膨胀层以障碍栅格为中心,按 cost_scaling_factor 指数衰减生成代价梯度。机器人内切圆半径决定了“物理上能不能过”,而 inflation_radius 决定了“规划器愿不愿意过”。设机器人宽 0.8 m、长 1.1 m,内切半径约 0.4 m;若巷道净宽 2.4 m,两侧各留 0.4 m 内切余量后仅剩 1.6 m 可规划宽度。此时若把 inflation_radius 设为 0.8 m,两侧膨胀区合计 1.6 m,可行通道被压到接近零,规划器会频繁报 No valid control 或原地振荡。矿山窄巷的经验做法是:inflation_radius 取内切半径加 0.15~0.25 m 的安全余量,即约 0.55~0.65 m,并把 cost_scaling_factor 从默认 3.0 提高到 5.0~8.0,让代价在膨胀区外快速衰减,使路径更愿意走巷道中线而不是被高代价区“推”到墙边。
inflation_layer:
plugin: "nav2_costmap_2d::InflationLayer"
inflation_radius: 0.60 # 内切0.4m + 0.2m余量
cost_scaling_factor: 6.0 # 默认3.0,窄巷调大加快衰减
障碍物层与体素层:动态矿车与三维地形
障碍物层负责把 2D 激光或降采样点云写入栅格,关键参数是 observation_sources、raytrace_max_range、obstacle_max_range 与 observation_persistence。矿山巷道常有矿车、人员临时占道,若 observation_persistence 设得过大(如 5 s 以上),障碍消失后栅格仍保留“幽灵障碍”,机器人会绕着一个已经不存在的矿车打转。建议设为 0~1 s,并确保 raytrace 开启,让传感器射线能清除已离开的障碍。体素层则解决“地面凸起被当成墙”的问题:把 z_resolution 设为 0.05~0.1 m,配合 min_obstacle_height 与 max_obstacle_height 过滤地面点和顶板,只保留 0.1~1.8 m 高度带的真实障碍,避免斜坡和道砟被误判为致命障碍。
静态层与未知区域
静态层直接读取 /map,参数少但影响大。track_unknown_space 应设为 false,让未知区按自由处理还是按障碍处理需结合矿山实际:在已建图巷道内可设为 false 提高通行性;在未勘探区域应保留 true,让规划器避开未知区。此外 subscribe_to_updates 在需要人工标注禁行区时可开启,配合 /map_updates 动态下发临时封道信息。
cost_scaling_factor 看路径是否居中;最后调障碍物层的清除与持久化参数,处理动态矿车。每次只改一个参数,用 RViz 的 costmap 显示逐层关闭叠加,定位是哪一层把通道“堵死”。判断参数是否合适,不能只看规划成功与否。可在 RViz 中打开 local_costmap 的 costmap 显示,观察巷道中线附近是否仍为浅蓝低代价区;用 ros2 topic echo /local_costmap/costmap 抽查关键栅格值;再让机器人以 0.3 m/s 低速通过一段 2.4 m 窄巷,记录是否出现减速、停顿或贴帮。若路径反复贴向一侧,多半是膨胀代价梯度不对称或静态层轮廓偏移,应回查地图与机器人 footprint 设置,而不是继续加大膨胀半径。