全局路径规划:从算法选择到工程落地
全局路径规划,说白了就是给机器人画一条从A到B的“大路线图”。它不考虑路上的小障碍物,只关心怎么从起点走到终点。很多新手一上来就调参数,结果越调越乱。我个人的习惯是——先搞清楚算法本身,再动手调参。
这一章我们重点聊三个东西:global_planner的参数、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 本身提供了一些平滑选项,但说实话,效果一般。我个人的习惯是:
- 先用 global_planner 生成原始路径
- 再用路径平滑算法做后处理
- 最后传给 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,结果路径还是歪的——因为根本问题在算法选择上。