4. 全局规划器初探:Nav2默认全局规划器NavFn原理,参数文件结构解析

全局规划器,说白了就是给机器人画一条从起点到终点的“大路”。你告诉它“我要去那里”,它就在全局代价地图上找出一条能走的路。Nav2 默认用的是 NavFn,一个基于 Dijkstra 和 A* 混合思想的规划器。我最早接触它的时候,觉得不就是个寻路嘛,后来才发现里面的门道真不少。

这一章,咱们就把它拆开看看。不搞花架子,直接聊原理、参数、还有我踩过的坑。

NavFn 全局规划器 · 核心逻辑 代价地图输入 障碍物 + 膨胀层 波前传播 (Dijkstra) 计算梯度势场 路径回溯 沿梯度下降生成路径 参数文件结构 nav2_params.yaml → 全局规划器命名空间 → NavFn 参数

4.1 NavFn 到底怎么工作的?

NavFn 本质上是一个 势场规划器。它把地图看成一张网格,每个格子都有一个“代价”值。然后从目标点开始,像水波一样往外扩散,计算每个格子到目标点的最小代价路径。最后机器人沿着“梯度下降”的方向走,就能到目标。

我个人习惯把它理解成“水往低处流”。目标点就是最低点,机器人顺着坡往下滑。嗯,当然这个坡是数学上的势场。

核心步骤:

  1. 初始化势场:目标点势能为 0,其他点设为无穷大。
  2. 波前传播:从目标点开始,用 Dijkstra 思想更新相邻格子的势能,直到覆盖整个地图或到达机器人位置。
  3. 路径生成:从机器人当前位置出发,沿着势能下降最快的方向回溯,直到目标点。

我在项目中遇到过一个问题:当障碍物把路堵死的时候,NavFn 会绕很远的路,甚至找不到路径。后来发现是 allow_unknown 参数没开,导致未知区域直接被当成障碍。你想想看,机器人还没探索的地方就被堵死了,那肯定绕路啊。

4.2 参数文件结构解析

Nav2 的参数文件通常叫 nav2_params.yaml。全局规划器的配置放在 global_costmap 和 planner_server 下面。我习惯把参数分成三块:规划器核心参数、代价地图参数、以及插件选择。

下面是一个典型的 NavFn 参数片段,我加了注释,方便你理解。

planner_server:
  ros__parameters:
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner/NavfnPlanner"
      tolerance: 0.5          # 到达目标点的容忍距离 (米)
      use_astar: false        # 是否使用 A* 加速 (默认 Dijkstra)
      allow_unknown: true     # 是否允许通过未知区域
      max_iterations: 100000  # 最大迭代次数,防止死循环
参数名默认值说明我的建议
tolerance0.5终点判定半径,机器人进入这个范围就算到达室内设 0.3~0.5,室外可适当放大
use_astarfalse是否启用 A* 启发式搜索地图大时建议 true,小地图 Dijkstra 更稳
allow_unknowntrue是否把未知区域当作可通行探索阶段建议 true,已知环境设 false 更安全
max_iterations100000规划器最大迭代次数一般不用改,地图超大时可增大

我曾经因为 use_astar 设为 true 但启发函数权重没调好,导致路径有时会贴着障碍物走。后来我干脆在复杂环境中用纯 Dijkstra,虽然慢一点,但路径更安全。看你的场景取舍吧。

4.3 全局代价地图的联动

全局规划器不是孤立的。它依赖全局代价地图提供的“障碍物膨胀层”。你想想看,如果膨胀半径设得太大,机器人可能找不到路;设得太小,又容易刮蹭。我一般先设 0.3m 的膨胀半径,然后根据机器人实际尺寸微调。

下面这个例子展示了全局代价地图中与规划器相关的参数:

global_costmap:
  global_costmap:
    ros__parameters:
      robot_radius: 0.22
      inflation_layer:
        inflation_radius: 0.55
        cost_scaling_factor: 10.0
      plugins: ["static_layer", "obstacle_layer", "inflation_layer"]

注意 inflation_radius 和 cost_scaling_factor 直接影响规划器生成的路径离障碍物有多远。数值越大,路径越保守。

千万不要把 inflation_radius 设得比机器人转弯半径还小!否则规划出来的路径,机器人实际根本拐不过去。我吃过这个亏,机器人卡在走廊里一直原地转圈……

4.4 调试小技巧:如何观察规划器行为

调参的时候,光看机器人跑是不够的。我习惯用 RViz 打开 Path 和 Potential 显示。NavFn 可以发布势场图(如果你开启了 publish_potential 参数)。

另外,你可以在终端用 ros2 param get /planner_server GridBased.tolerance 来实时查看参数值。嗯,热加载参数也是可以的,不过那是后面章节的内容了。

我个人习惯在调试时把 max_iterations 临时设小一点(比如 10000),这样如果规划失败能快速报错,不用等半天。

4.5 避坑指南

  • 路径频繁重规划? 检查 tolerance 是否太小,或者代价地图更新太快。
  • 规划失败但地图明明有路? 看看 allow_unknown 是不是 false,以及膨胀层是否把通道堵死了。
  • 规划时间过长? 试试 use_astar: true,或者减小地图分辨率(但别低于 0.05m)。

我曾经在一个仓库项目里,机器人总是规划出奇怪的“Z”字形路径。查了两天,最后发现是全局代价地图的 static_layer 加载了过期的地图,导致障碍物位置不对。所以,有时候问题不在规划器本身,而在上游数据。

本章小结: NavFn 是一个基于势场的全局规划器,通过 Dijkstra/A* 计算梯度路径。参数文件集中在 planner_server 命名空间下,核心参数包括 tolerance、use_astar、allow_unknown。调参时一定要结合全局代价地图的膨胀层,别孤立地看规划器。