3. 全局路径规划参数:A*算法参数解析、Dijkstra参数对比、全局代价地图参数设置

各位同学,咱们今天聊点硬核的——全局路径规划。说白了,就是让机器人在出发前,先在地图上画出一条从A到B的“大路”。我这些年调过的机器人,十有八九的问题都出在这一步。你想想看,全局路径都没规划好,后面的局部避障再牛,那也是白搭。

这一节,我带你手撕三个核心模块:A*算法、Dijkstra算法,还有全局代价地图。嗯,咱们一个一个来。

3.1 A*算法参数解析

A*算法,说白了就是“带方向的Dijkstra”。它比Dijkstra聪明的地方在于,它知道自己要去哪,所以不会像个无头苍蝇一样到处乱撞。

我个人习惯把A*的核心参数分成三组:启发式函数、搜索策略、路径平滑。

3.1.1 启发式函数(Heuristic Function)

这是A*的灵魂。启发式函数决定了“估算距离”的准确度。我见过很多新手直接拿欧几里得距离当启发式,结果路径拐来拐去,像喝醉了一样。

启发式类型 公式 适用场景 我踩过的坑
曼哈顿距离 |dx| + |dy| 四方向移动(如室内走廊) 在斜向移动时路径会“锯齿状”
欧几里得距离 sqrt(dx² + dy²) 八方向或自由移动 计算量大,且容易低估代价
切比雪夫距离 max(|dx|, |dy|) 八方向移动(如AGV) 路径偏“直”,但可能撞角
对角线距离 dx + dy + (√2 - 2) * min(dx, dy) 八方向移动(推荐) 我最常用,平衡了速度和精度
我的经验: 在ROS的navfn或global_planner中,默认用的是对角线距离的变体。如果你发现路径总是贴着障碍物走,试着把启发式权重调大一点(比如乘以1.1),让算法更“贪心”一些。

3.1.2 搜索策略参数

A*的搜索过程,说白了就是维护一个“待探索列表”。这里有几个关键参数,我一个个说。

  • open_set大小限制:默认是无限大。但在大场景下,我建议设个上限(比如50000个节点)。否则内存爆了,你哭都来不及。
  • 搜索步长(resolution):默认是地图分辨率。我个人习惯设成0.5米,太大路径会“跳”,太小计算量爆炸。
  • 路径剪枝(path pruning):这个参数很多人忽略。开启后,算法会去掉路径上不必要的拐点。嗯,我建议默认开启。
注意: 我曾经在一个仓库项目里,把open_set限制设得太小,结果机器人每次走到一半就“迷路”了——因为路径被截断了。后来我把限制放宽到100000,问题解决。所以,这个值要根据场景大小灵活调整。

3.1.3 路径平滑

A*出来的路径,往往是“折线”。直接让机器人走,它会一顿一顿的,像在跳机械舞。所以我们需要平滑。

ROS里常用的平滑方法有两种:

  • 梯度下降平滑:迭代优化路径点,让它们更平滑。我一般迭代50次,再多就过拟合了。
  • B样条曲线:用控制点生成平滑曲线。效果好,但计算量大。适合离线规划。
# 我常用的A*参数配置(yaml格式)
A_star:
  heuristic_type: "diagonal"  # 对角线距离
  heuristic_weight: 1.1       # 稍微贪心一点
  search_resolution: 0.5      # 米
  open_set_limit: 50000       # 防止内存爆炸
  smooth_iterations: 50       # 平滑迭代次数
  path_pruning: true          # 剪枝

3.2 Dijkstra参数对比

Dijkstra算法,说白了就是A*的“老大哥”。它没有启发式,所以会遍历所有可能的路径,直到找到最短的那条。优点是保证最优,缺点是慢。

我什么时候用Dijkstra?嗯,场景不大(比如10x10米以内),或者对路径最优性要求极高的时候。比如,机器人要穿过一个狭窄的通道,A*可能因为启发式“骗”自己走了弯路,但Dijkstra不会。

对比项 A* Dijkstra
搜索效率 高(有方向) 低(无方向)
路径最优性 近似最优(取决于启发式) 绝对最优
内存占用 低 高(需要存储所有节点)
适用场景 大场景、实时规划 小场景、离线规划
ROS默认实现 navfn / global_planner navfn(可切换)
核心区别一句话: A*是“有目标的搜索”,Dijkstra是“地毯式搜索”。如果你时间紧,用A*;如果你要绝对最优,用Dijkstra。

在ROS里,切换其实很简单。global_planner的use_dijkstra参数设为true,它就变成Dijkstra了。我建议你做个实验:同一个场景,分别跑A*和Dijkstra,看看路径差异。你会发现,Dijkstra的路径往往更“圆润”,因为它考虑了所有可能性。

3.3 全局代价地图参数设置

全局代价地图,说白了就是一张“打分表”。每个格子都有一个代价,机器人规划路径时,会尽量走代价低的格子。

代价地图的参数,我分成三类:膨胀层、静态层、障碍物层。

3.3.1 膨胀层(Inflation Layer)

这是最重要的层。它把障碍物“膨胀”一圈,让机器人离障碍物远一点。膨胀半径设多大?嗯,我一般设成机器人半径的1.5倍。太小了容易刮蹭,太大了路径绕远路。

# 膨胀层参数示例
inflation_layer:
  inflation_radius: 0.55    # 米,机器人半径0.35m,乘1.5
  cost_scaling_factor: 10.0 # 代价衰减速度,越大衰减越快

cost_scaling_factor这个参数,我吃过亏。有一次设成5.0,结果代价衰减太慢,机器人为了避开一个很小的障碍物,绕了半个地图。后来改成10.0,路径就正常了。你想想看,这个值越大,代价从障碍物边缘到膨胀半径边缘的“过渡”就越陡峭,机器人会更倾向于贴着障碍物走。

3.3.2 静态层(Static Layer)

静态层就是地图本身。参数不多,但有一个坑:map_topic。我见过有人把话题名写错,结果机器人一直用空地图规划路径,直接撞墙。

  • map_topic:地图话题名,默认是"/map"。
  • subscribe_to_updates:是否订阅地图更新。如果你用动态地图(比如SLAM实时建图),建议设为true。
  • track_unknown_space:是否把未知区域当作障碍物。我建议设为false,否则机器人会“害怕”没去过的地方。

3.3.3 障碍物层(Obstacle Layer)

障碍物层负责处理传感器数据(激光雷达、深度相机等)。参数比较多,我挑几个关键的讲。

参数 说明 我的建议值
observation_sources 传感器列表,如"scan" 按实际传感器名称写
obstacle_range 障碍物最大检测距离 2.5米(室内)
raytrace_range 射线追踪范围(清除障碍物) 3.0米(比obstacle_range大一点)
max_obstacle_height 障碍物最大高度 2.0米(高于机器人高度)
min_obstacle_height 障碍物最小高度 0.0米(地面杂物也算)
避坑指南: 我曾经把obstacle_range设成5.0米,结果机器人把远处的墙也当成障碍物,导致全局路径绕了一大圈。后来改成2.5米,路径就合理了。记住,全局代价地图不需要看到太远,够用就行。

3.4 知识体系总览

说了这么多,我画了一张图帮你理清思路。全局路径规划的核心,就是“算法+地图”的组合。算法决定怎么找路,地图决定哪里能走。

全局路径规划知识体系 全局路径规划 路径规划算法 全局代价地图 A*算法 Dijkstra算法 膨胀层 静态层 障碍物层 启发式函数 搜索策略 路径平滑 open_set限制 无启发式 绝对最优 内存占用高 适合小场景 inflation_radius cost_scaling_factor 膨胀半径 代价衰减速度 map_topic subscribe_to_updates track_unknown_space 静态地图配置 observation_sources obstacle_range raytrace_range 传感器参数 核心:算法决定怎么找路,地图决定哪里能走 A* vs Dijkstra → 速度 vs 最优性

嗯,这张图把咱们今天讲的内容串起来了。左边是算法,右边是地图。你调参的时候,要同时考虑这两边。比如,你把膨胀半径设大了,A*的搜索空间就变小了,路径可能更安全,但也可能绕远路。这就是个trade-off。

我的调试习惯: 先调地图参数(膨胀半径、障碍物范围),再调算法参数(启发式权重、搜索步长)。因为地图是基础,地图没调好,算法再牛也白搭。

好了,这一节的内容就到这。记住,全局路径规划不是一蹴而就的,需要反复试错。你调参的时候,多看看Rviz里的路径变化,慢慢就有感觉了。

可可精品资料库