全局路径规划:从算法选择到工程落地

全局路径规划,说白了就是给机器人画一条从A到B的“大路线图”。它不考虑路上的小障碍物,只关心怎么从起点走到终点。很多新手一上来就调参数,结果越调越乱。我个人的习惯是——先搞清楚算法本身,再动手调参。

这一章我们重点聊三个东西:global_planner的参数、A*与Dijkstra怎么选、以及路径平滑的工程技巧。嗯,都是我在项目里踩过坑的地方。

全局路径规划知识体系 global_planner 参数详解 A* vs Dijkstra 路径平滑与优化 use_dijkstra use_grid_path A* 启发式 Dijkstra 保证 路径平滑 路径优化 核心思路 先理解算法原理 → 再调参数 → 最后做路径平滑

global_planner 参数详解

global_planner 是 ROS Navigation Stack 里负责全局路径规划的模块。它替代了早期的 navfn,功能更丰富,但参数也更多。我刚开始用的时候,光看参数名根本猜不出是干嘛的。

下面这张表是我整理的核心参数,每个我都亲手调过:

参数名 默认值 说明 我的建议
use_dijkstra true 是否使用 Dijkstra 算法 地图大时建议 false,用 A*
use_grid_path false 是否沿网格走直线 室内场景建议 true
old_navfn_behavior false 是否兼容旧版 navfn 升级项目时注意
allow_unknown true 是否允许通过未知区域 探索场景开,已知地图关
planner_window_x 0.0 规划窗口宽度(0表示全图) 大地图建议限制范围
default_tolerance 0.0 目标点容差 至少设 0.1-0.3 米

重点提醒:use_dijkstra 和 use_grid_path 这两个参数经常被忽略。我见过有人把 use_dijkstra 设为 true,地图 100x100 米,结果规划一次要 3 秒——机器人早撞墙了。

A* 与 Dijkstra 算法选择

这两个算法,说白了就是“贪心”和“稳妥”的区别。Dijkstra 保证找到最短路径,但慢;A* 加了启发式函数,快但不一定最优。

你想想看,什么时候用哪个?

  • Dijkstra:适合小地图、静态环境、对路径长度有严格要求的场景。比如仓库里的 AGV,路径必须最短,多走一米都是浪费。
  • A*:适合大地图、动态环境、对实时性要求高的场景。比如扫地机器人,你不可能让它原地算 5 秒再动。

我在项目里遇到过一个问题:用 A* 规划出来的路径,有时候会贴着障碍物走。为什么?因为启发式函数太“乐观”了,导致算法优先探索靠近目标的方向,忽略了障碍物。解决办法是调整启发式权重,或者改用 Dijkstra。

小技巧:如果你不确定用哪个,先试试 A*。把 use_dijkstra 设为 false,观察规划时间。如果路径质量差,再切回 Dijkstra。我一般会在 launch 文件里加一个参数开关,方便切换。

代码层面,global_planner 的算法选择其实就一行:

<param name="use_dijkstra" value="false" />  <!-- true 用 Dijkstra,false 用 A* -->

但别小看这一行。我曾经在一个项目里,因为忘了改这个参数,导致机器人每次规划都要 2 秒,客户差点退货。嗯,从那以后我每次上线前都会检查这个参数。

路径平滑与优化

规划出来的路径,往往是锯齿状的。尤其是 A* 在网格地图上,走出来的全是 45 度折线。机器人如果直接沿着这条线走,你会看到它不停地急转弯——电机和电池都受不了。

路径平滑,就是把这些折线变成平滑的曲线。global_planner 本身提供了一些平滑选项,但说实话,效果一般。我个人的习惯是:

  1. 先用 global_planner 生成原始路径
  2. 再用路径平滑算法做后处理
  3. 最后传给 local_planner 执行

常用的平滑方法有:

  • B 样条曲线:效果好,但计算量大。适合离线规划。
  • 梯度下降平滑:迭代优化路径点,让路径更短更平滑。我常用这个。
  • 路径剪枝:去掉冗余点,减少转折。简单有效。

注意:平滑不能过度。我曾经把一条路径平滑得太“圆润”,结果机器人直接穿过了障碍物的边缘——因为平滑后的路径偏离了原始路径,撞上了原本避开的障碍物。平滑一定要在保证安全的前提下做。

这里给一个简单的路径剪枝思路:

# 伪代码:路径剪枝
def prune_path(path):
    pruned = [path[0]]
    for i in range(1, len(path) - 1):
        # 如果三点共线,跳过中间点
        if not is_collinear(pruned[-1], path[i], path[i+1]):
            pruned.append(path[i])
    pruned.append(path[-1])
    return pruned

这个函数会把共线的中间点去掉。比如一条直线上的 10 个点,剪完之后只剩 2 个。机器人走起来就顺多了。

总结一下:全局路径规划不是调一个参数就能搞定的。你得先理解算法,再根据场景选参数,最后做平滑优化。我见过太多人一上来就调 planner_window_x,结果路径还是歪的——因为根本问题在算法选择上。