ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

移动机器人路径规划:A*、RRT与DWA算法原理及融合实战

移动机器人路径规划:A*、RRT与DWA算法原理及融合实战 一个移动机器人要从A点走到B点中途有人走过来地面上多了临时堆放的纸箱你要是没给机器人配好路径规划它就只能面临两个结局要么直愣愣撞上去要么停在原地死等直到障碍自己消失。路径规划算法要解决的就是这个绕不开的问题而在这个领域里A*、RRT、DWA 是三个绕不开的名字A* 负责在地图上找出一条不撞墙的宏观路线RRT 负责在没有完整地图时也能探出一条可达的路DWA 负责盯着眼前这一两秒让底盘既避障、又走得不毛躁。我是做移动机器人导航的这几年和这三个算法打了太多交道踩过的坑能装满一抽屉。这篇文章想做的只有一件事把它们的原理拆碎讲透各自擅长什么、死穴在哪再把“A* 全局规划 DWA 局部避障”这个最常见的融合方案怎么落地、怎么调参、怎么排故从头到尾给你捋一遍。刚接触自主导航的人可以把它当成一张入门地图已经在调导航参数的人可以直接跳到后面的速查表和排故清单。1. 三个算法先站稳全局、采样、局部各管哪一段1.1 A* 是地图上的“路线规划师”A* 不是某个机器人专用的算法它是图搜索家族的明星也是我做全局规划时最先上手的方案。你手里有一张栅格地图每个格子要么可走、要么有障碍机器人要从起点格挪到终点格A* 做的就是告诉你沿哪一串格子走代价最小。它的思路朴素但好用维护两个集合open list 和 close list。每轮从 open list 里挑一个 f 值最小的格子扩展f g h。g 是从起点走到当前格已经花掉的真实代价h 是从当前格到终点的一个乐观估计代价常用欧几里得距离。每扩展一个格子就把它的邻居丢进 open list同时把父节点指向当前格子循环直到终点被扩展。一句话总结它是从所有候选路径里优先探索“看起来最有希望”的那几条。类比一下。你在陌生城市赶去一家饭店手里有地图。你不会把所有路口都走一遍而是先估算哪些路口方向离饭店最近优先走过去。A* 的 h 就是这个估算它能把搜索方向强烈地拽向终点所以比 Dijkstra 那种无差别扫描通常要快一个量级。补充一些实用考虑。网格邻居用四邻居还是八邻居直接决定路径是只能横竖走还是可以斜穿。用八邻居时斜向移动的代价要按 sqrt(2) 倍计算否则地图上会出现“斜着走和横着走一样快”的错觉路径会失真。另一个常被忽略的点是启发式函数的权重如果地图很大可以给 h 乘一个大于 1 的权重来提速这叫加权 A*。我试过把权重从 1.0 拉到 1.5 左右在大地图上规划耗时能明显下降路径长度只多出百分之几。但权重太大就会牺牲最优性甚至看起来像“贪心”所以别一味求快。1.2 RRT 是“摸着石头过河”的探险家RRT 和 A* 长相完全不同它不依赖栅格也不需要先建一张完整的环境图。它的工作方式是从起点长一棵随机树随机撒一个点在树上找离它最近的节点沿这个方向按固定步长生长出一个新节点如果新节点和父节点之间的线段没有碰到障碍就把它挂上树。这个循环重复几百上千次树会慢慢长满整个搜索空间等某个新节点距离终点足够近就顺着父节点回溯出一条完整路径。这个机制让它天然适合 A* 难以建模的问题。比如机械臂在关节空间里找运动轨迹每个关节角度都是维度栅格化代价极高再比如地图外沿有大量未知区域建图本身就很勉强。这类问题里RRT 只需要判断“两点之间能不能走”不要求全局地图完整往往是更现实的选择。但随机性是把双刃剑。同一张地图跑十次RRT 给出的路径每次都完全不一样而且很容易带一堆无用拐角路径长度也可能明显差于最优解。工程上很少直接用裸 RRT一般会用它的两个改进版本。RRT* 在每次插入新节点后会把附近节点的父指针重新优化让解渐近收敛到最优双向 RRT 则从起点和终点同时向中间长树遇到狭窄通道时成功率提升明显。从我实际调试的经验看如果你急着让一个粗糙但能用的导航系统跑起来双向 RRT 加简单路径裁剪性价比其实很高。1.3 DWA 盯着“下一秒怎么走”DWA 的视角和上面两个完全不同。A* 和 RRT 都在回答“从全局看走哪条路”DWA 则在回答“下一个控制周期底盘该给多大线速度和角速度”。它不评估远方只看眼前一小段。算法流程是这样的根据当前速度、底盘最大加速度、控制周期算出一个下一周期可行的速度组合窗口对窗口里的每一组速度按恒定速度向前模拟一小段轨迹然后给每条轨迹打分主要看它会不会撞障碍、末端朝向是不是趋近目标、速度够不够快最后选得分最高的速度组合发给电机。每个控制周期都重新算一遍因此对动态障碍物的反应很快这也是它成为局部规划主力的原因。DWA 的“动态窗口”这四个字指的就是每步约束出来的可行速度集合。这个集合不是凭空来的而是由当前运动状态和机器人物理极限共同决定的。明白了这一点就明白 DWA 为什么会出那些经典毛病只看脚下路径会陷入局部最优目标太远时容易对着墙反复试探。这也是为什么生产级方案几乎不会单独用 DWA而是把它挂在全局路径后面。1.4 选型参考什么场景用谁直接把三种算法放进应用场景里对比场景特征推荐组合原因地图完整、环境基本静止A* DWAA* 能给出最短且稳定的全局路线DWA 负责沿途避障地图未知或频繁变化双向 RRT DWA不依赖全局精确栅格能快速摸出可达路径人流多、动态障碍密集A* 强化局部规划全局路线让机器人不绕远局部层要留足反应空间机械臂等关节空间规划RRT/RRT*高维空间难以栅格化采样法几乎是唯一实用选项选型不是比谁更高级而是看环境缺什么信息、底盘缺什么约束。对绝大多数轮式移动机器人来说第一行“A* DWA”就是最稳的起点。先把它跑通再谈别的这套组合也是后面要展开的融合主线。2. A* 和 RRT 的全局规划实战原理与抠参数2.1 A* 的代价函数怎么写最稳直接给你一段可以反复用的伪代码骨架# A* 主循环伪代码 open_list [start] closed_list [] while open_list: current open_min_f(open_list) # 每次取 f 最小的节点 if current goal: return reconstruct_path(current) open_list.remove(current) closed_list.append(current) for neighbor in get_neighbors(current): if neighbor in closed_list: continue tentative_g current.g distance(current, neighbor) if tentative_g neighbor.g or neighbor not in open_list: neighbor.g tentative_g neighbor.h heuristic(neighbor, goal) neighbor.parent current open_list.add(neighbor)对有经验的读者来说这段代码太“标准”了但标准正好说明核心逻辑简单。真正影响效果的是后面三件容易被忽略的事。启发式函数选型八邻居网格里用欧几里得距离是自然选择曼哈顿距离只能用在四邻居场景否则会严重高估移动代价。h 权重大于 1 时规划更快但结果次优我一般在大地图上把权重压在 1.2~1.5 之间。open list 的实现方式节点很多时线性扫描取最小值会明显变慢。工程上建议用二叉堆数据量大再考虑更复杂的数据结构。我见过有人拿着基础实现处理 500×500 的地图一帧规划卡了 300 多毫秒换成堆之后降到十几毫秒这是纯数据结构的收益不是算法本身变高级了。闭合检查也要留意neighbor 同时由多条路径到达时要正确比较并更新 g否则路径可能在角落反复横跳。细节虽小但出问题时会表现为地图上出现“同一点被反复选中”的卡顿排查起来容易怀疑人生。2.2 栅格分辨率、膨胀半径和路径毛刺实际导航中A* 的问题通常不在搜索算法本身而在“地图喂得对不对”。一个常见组合是地图分辨率 0.05 米机器人半径 0.3 米膨胀半径设 0.35 米。这个配置下A* 找出来的路径一般会离障碍物半米左右局部规划器跟起来很舒服。分辨率如果改到 0.2 米效果就开始变质。窄通道在地图上可能只剩一个格子机器人物理尺寸根本过不去但 A* 不知道路径照样从那里穿过去。所以地图分辨率要和机器人尺寸挂钩经验上单个栅格边长别超过机器人外接圆半径的一半。地图精度也不是越高越好0.02 米和 0.05 米在大多数室内场景根本没区别但计算量会暴涨。膨胀本身也会带来问题。膨胀半径设得太大A* 会认为所有路都被堵死在地图上找不到解设得太小路径贴着墙走给局部规划器留的调整空间不足。比较稳的做法是膨胀半径 机器人外接圆半径 安全余量 定位误差。安全余量通常给 5~10 厘米定位误差按实际传感器水平估。然后是路径毛刺。A* 在栅格上搜出来的路径由一串相邻格子组成折角多斜线少。直接拿给 DWA 跟轮子会频繁修正方向。后期可以做一个剪枝从起点开始往前找能无碰撞直连的最远点删掉中间点再对剩下的点做轻量平滑。这个步骤不用引入复杂的曲线优化就能把路径长度和角度跳变降下来一大截。2.3 RRT 的关键参数步长、碰撞检测与迭代次数RRT 的参数比 A* 少很多但并不代表好调。步长是最关键的一个太大新节点可能一步跨过窄缝漏掉可行路径太小树长得慢到达终点要更多迭代规划耗时增加。按地图尺度来定比如地面机器人场景步长取 0.5 米左右是一个常用起点再按地图障碍密度微调。碰撞检测也要注意。判断“节点之间能否直连”时不能只检查两个端点要在线段上均匀采样逐点看是不是处于障碍或膨胀区域。采样太粗会漏检路径看着能通过实际会撞。我习惯把采样间隔设成地图栅格分辨率的二分之一以下。迭代次数决定成功率。迭代太少树还没有覆盖到终点方向容易超时失败迭代太多耗时白白浪费。工程上可以先跑一次看第多少次迭代找到解然后把这个数字乘 1.5 作为上限。如果你用的是 RRT*还要额外关心重连半径和优化迭代次数。重连半径太小节点优化效果基本为零太大每次插入节点都扫描一堆邻居耗时上升。我在二维平面上常用“步长的 2~3 倍”作为起始值再根据实测调整。2.4 全局规划怎么打分规划结果好不好不能靠感觉。我自己评估全局规划主要看四个指标规划耗时、路径长度、平滑性累计转角、成功率。算法耗时路径长度平滑性适用场景基础 A*中短一般栅格完整、可重复规划加权 A*短略长一般大图、实时要求高RRT*中较长差高维空间、连续空间双向 RRT中较长差狭窄通道、地图不完整路径长度和平滑性往往是一对矛盾。过分追求最短路径必然贴障碍、折角多追求平滑路径会变长。全局规划层只需要求“大致合理”把大量平滑工作交给局部规划层去处理这是分层架构的核心思想也是后面融合方案的出发点。3. DWA 局部规划器拆解速度窗口、轨迹模拟与评分3.1 速度窗口怎么算DWA 的第一步是确定“动态窗口”。假设机器人当前线速度是 0.2 米/秒最大线加速度是 0.5 米/秒²控制周期是 0.1 秒那么下一周期能达到的线速度范围就是 0.15~0.25 米/秒。角速度也一样由当前角速度和最大角加速度约束。这个范围还要和机器人允许的绝对速度上限取交集比如底盘最高线速度 0.8 米/秒你就不能算出一个 0.9 的采样点。如果问题只有加减速约束DWA 还算简单。真正复杂的是刹车约束就算某个速度组合在加减速范围内如果机器人以上这个速度冲出去在可预测的距离内刹不住、会撞上障碍那它也是不可行的。所以窗口最终是“加速度窗口 ∩ 速度极限 ∩ 刹车安全区”。这个逻辑就是 DWA 被称为“动态”的根源窗口不是固定值而是随当前状态不断变化。给个直观例子。机器人正以 0.5 米/秒冲向一个 0.4 米外的箱子最大减速度 0.8 米/秒²那它至少需要 0.156 米的刹车距离按匀减速公式 v²/(2a) 算勉强来得及但任何加速选项都会被剔除。如果箱子在 0.2 米外能用的速度选项基本就只剩下“原地制动”了。3.2 轨迹模拟与预测时间确定速度窗口后DWA 会对每个可行速度组合做一次短时模拟假设在接下来的一小段时间里机器人保持这个速度不变沿着一条圆弧或直线前进生成一条模拟轨迹。模拟的预测时间一般取 0.5~1.5 秒这是个很讲究的参数。预测时间太短机器人只看得见眼前一小段障碍稍微远一点就“看不见”会显得反应过激频繁急停预测时间太长它又会过度考虑远期轨迹是否撞墙在通道里容易犹豫不决明明贴着墙就可以过它却觉得前面会撞。我用来回测试的起点通常是 1.0 秒障碍密集时缩短到 0.6 秒开阔环境拉到 1.2 秒。另一个容易被忽略的点是采样分辨率。线速度采样 5 档、角速度采样 10 档就有 50 条模拟轨迹每个控制周期都要重新算一遍计算量完全可接受。但采样太粗时速度选择不连续相邻周期可能从“猛转”跳到“直行”机器人就会抖。采样太细则差异太小容易在几个相似选项中反复横跳。实际操作中观察模拟轨迹的疏密再对比机器人实际路径的平稳度就能大致判断该往哪边调。3.3 评价函数的三项方向、距离、速度每条模拟轨迹的得分由三个量决定score α × 方向偏差 β × 障碍距离 γ × 速度方向偏差衡量轨迹末端朝向和目标的夹角障碍距离衡量轨迹与最近障碍的间距速度项则奖励走得快。如果一个轨迹会撞上障碍分数直接记负无穷或直接淘汰。权重配平是 DWA 调试的核心。α 过大机器人会特别“犟”非要正对目标不可结果贴着障碍边缘硬挤β 过大机器人安全第一绕远路也不在乎在窄通道里会墨迹γ 过大机器人贪快动态障碍高发区容易刹不住。我建议的初始配平顺序是先把 β 固定在一个中间值比如 0.8再调 α 和 γ 的比值。一组能跑通大多数室内场景的起始权重是 α0.5、β0.8、γ0.3但这不是万能值。有一个关键改动要从融合的角度理解在纯 DWA 里“目标方向”直接指向终点一旦把它挂在全局路径后面“目标”应该改为局部前瞻点方向而不是远方的最终终点。否则机器人会在接近终点前就开始犹豫或者在岔路口做出不合理的转向。这个改动一句话就能说完但很多人融合失败就是栽在这。3.4 DWA 三个经典故障和处理方向原地旋转不停常见原因是目标方向项权重 α 太大机器人还没往前挪就急着转向或者局部目标点离机器人太近速度小、转向要求又高。先试着降低 α或把速度项权重提上去也要看是不是局部目标一直贴在机器人正前方很近处。左右抖动相邻控制周期选出的速度组合频繁跳变。处理办法通常是把预测时间拉长一点或者把采样分辨率调粗也可以给速度输出加限制让每个周期的变化量不能超过某个比例。速度全部被拒绝日志里找不到可发送的可行速度机器人直接死锁。优先检查目标点是不是被代价地图膨胀成了障碍其次看当前速度窗口是不是太小最后看刹车距离计算有没有因为减速度设置太小而把所有选项都掐死。4. A* DWA 融合落地路径跟随、前瞻目标与参数联动4.1 为什么不直接让 DWA 追终点把终点直接丢给 DWA理论上也能走但实际表现很糟距离远时终点方向在机器人坐标系里的变化很慢DWA 缺少“远方该绕哪儿”的指引遇到第一个障碍就可能被带偏。局部规划的每一步都只保证“这一秒最优”多个“这一秒最优”拼起来整体路线往往不是最短甚至会在障碍前磨蹭。融合的基本结构可以理解成一个“宏观意志下放”的过程A* 在地图上算出一条全局路径这条路径被切分成一串离散点机器人实时找回离自己最近的点把前方一段距离外的那个点当作临时目标DWA 只需要负责把这个临时目标吃进评价函数输出速度。这样全局的绕行决策和局部的避障动作就自然分开了。这样的分层架构还有个额外好处地图更新时不需要让局部规划器重新思考全局。全局规划以较低频率重算局部规划器每个控制周期都允许动态障碍改变它的决策两者节奏不一各司其职。4.2 实现流程路径重采样、目标切换与坐标对齐把前面说的结构落到代码上可以拆成四个步骤全局路径重采样把 A* 产出的一串稠密格点按固定间距比如 0.2 米转成离散点序列。计算机器人当前位置到每个路径点的距离找到最近点。从最近点向前取一段距离这个位置作为局部目标。前瞻距离通常取 1~2 个车身长度或者按“当前线速度 × 1.5~2 秒”估算。把局部目标点从全局坐标转换到机器人坐标系再交给 DWA 的评价函数使用。这里最关键的是前瞻距离的选择。前瞻太短机器人对全局路径的跟随太“粘”路径稍微有波动就跟着急刹走过直角弯时会一顿一顿前瞻太长则过弯时机器人会切内线离障碍物很近甚至把全局路径的弯道“拉直”了。我调试时有个快速经验如果机器人过弯顺畅但路径贴边就把前瞻距离拉大一点如果机器人频繁停车、转弯迟钝就把前瞻距离缩近一点。这个参数通常不需要精确公式而是根据实测路径形态反推。4.3 更高级的融合低分辨率 A* RRT* 局部规划A* DWA 是“栅格世界的标准答案”但当环境里局部障碍非常复杂、栅格图又不可靠时可以再加一层 RRT*。一种可行的混合策略是全局层用低分辨率 A*以很低的频率更新给出一个大致方向上的粗路径当机器人进入障碍密集区域时局部层用 RRT* 在这个区域快速重规划出一条无碰撞的短线段替代原来那一段的全局路径DWA 继续负责把这条短线段平滑地执行出来。背后的思路是分层降低不确定性A* 提供方向稳定性RRT* 提供对复杂局部的“可绕过”判断DWA 提供底盘物理约束下的平滑执行。这种三明治结构比两层结构复杂得多代价地图也要分多层维护但它确实能处理很多“A* 认为走得通、实际现场却乱成一团”的场景。如果你刚开始做融合我不建议一上来就搞三层先把 A* DWA 调顺再考虑往里塞 RRT*否则出问题都不知道去哪一层找。4.4 参数联动控制周期、最大速度、前瞻距离怎么一起调融合系统的参数不是独立存在的必须联动调整。我习惯按照下面顺序取数控制周期由底盘接口决定通常是 0.05~0.1 秒。最大线速度由现场安全要求决定取值时得保证紧急刹停距离远小于传感器可视范围。前瞻距离按“最大线速度 × 1.5~2 秒”估算。比如最大线速度 0.5 米/秒前瞻距离约 0.8~1.0 米。最大加速度取一个让单周期速度增量不超过最大线速度 10%~15% 的值这样底盘起步、刹车都相对平稳。举个例子。我在一个模拟巡检项目里用过这组配置控制周期 0.1 秒最大线速度 0.5 米/秒最大角速度 1.0 弧度/秒最大线加速度 0.4 米/秒²最大角加速度 2.0 弧度/秒²前瞻距离 0.9 米预测时间 1.0 秒。这个组合跑起来整体平顺动态障碍从侧面插进来时DWA 也能在 1 米左右的距离做出明显的减速避让。注意这只是起点不同底盘和传感器下差异很大一定要按自己的机器人实测。5. 避坑经验、排故速查与调试心得5.1 地图分辨率与机器人尺寸不匹配很多人第一次翻车都翻在这地图用 0.2 米分辨率机器人外接圆半径却接近 0.35 米。A* 规划出来的路径在地图上看着穿过了某个窄口实际上机器人物理上根本挤不进去由于膨胀半径当时没包含机器人尺寸路径也会贴着墙角走导致局部规划器一路疯狂修正。对策分两层地图分辨率尽量满足单格边长小于机器人半径的一半这是硬指标膨胀半径一定要含上机器人外接圆半径、安全余量和定位误差。否则你调再多的 DWA 权重也救不回来因为问题的根源在“地图对机器人来说压根不可通行”。5.2 DWA 死锁所有采样轨迹都被判失败现象比较典型机器人停在原地速度始终为零日志刷满了“找不到可行轨迹”。我排查这个问题时一般按顺序做三件事。第一看局部目标点。代价地图的膨胀层可能把目标点附近的区域全标成了障碍目标点本身不可达这时机器人不管怎么采样都“撞墙”。处理办法是让全局规划把终点或局部目标改成最近的自由格点。第二看膨胀半径是不是过大。机器人明明离障碍还有 30 厘米但代价地图认为已经是禁区也会把可行速度全掐断。第三看刹车约束计算。最大减速度设得太小会让刹车距离变大动态窗口被进一步压缩速度选项就少了。把减速度调大一点再给原地转向留一条可选速度死锁往往会解除。5.3 全局路径贴墙局部规划蛇形走位这种现象表现为机器人路线总体正确但轮子一直在左右修方向走起来像蛇。先别急着怪 DWA多半是全局路径本身离障碍太近。A* 的代价函数只优化距离不优化“安全间距”所以如果膨胀半径没设置好或者加权 A* 权重太大导致路径靠墙抄近路就必然出现这种结果。处理办法是给全局层的障碍距离加权或者干脆把膨胀半径再加大一圈让 A* 主动绕开“刚刚好能过”的区域。我还会在后处理阶段对路径做一次“向外推”的轻量操作把每个路径点往远离最近障碍物的方向挪一小段距离再重新检测碰撞。这个操作在走廊环境里尤其有用。5.4 重规划震荡机器人左右摆头融合系统跑起来后最常见的动态问题不是撞而是“左右摆”。A* 全局路径不变但每个控制周期算出来的局部目标点都在细微变化DWA 的评分对目标方向非常敏感于是速度指令来回跳底盘表现为左右摆头。给目标切换加一个滞回区问题能消掉大半。具体做法是当前局部目标点与上一个目标点的距离小于某个阈值比如 0.1 米时继续沿用旧目标不切新的。这本质上是给离散控制加了一点“惯性”让决策不要因为微小扰动而来回翻转。另一个辅助手段是略微降低角速度采样分辨率减少相邻周期速度指令的跳变幅度。5.5 排故速查表现象可能原因检查点处理方向机器人怼墙地图分辨率过低路径穿过不可行窄缝栅格边与机器人半径比提高地图精度加大膨胀原地死锁DWA 评分全失败目标点是否被膨胀减速度设置修正目标点调大减速度蛇形走位全局路径贴障碍、折角多膨胀半径、路径采样点加大膨胀路径平滑左右摆头局部目标频繁切换前瞻距离、目标切换逻辑加滞回区降低采样分辨率规划超时卡顿A* 在大地图上搜索过慢open list 数据结构、h 权重换二叉堆、用加权 A*排故的核心思路是分层定位先分清是全局路径的问题还是局部执行的问题再动参数。不要一上来就调 DWA 的权重很多时候根子在前面那层。5.6 调试心得把内部状态画出来比盲调参数快十倍这是我最想强调的一点。调导航参数时不要只看机器人最终跑得好不好要把中间状态全部可视化全局路径点、当前局部目标、DWA 模拟出来的几十条轨迹、代价地图、还有每条轨迹的评分全都画在同一张图上。一旦机器人出现怪动作先通过画面定位是哪一层的问题。全局路径在障碍物里穿说明 A* 这层错了全局路线上局部目标乱跳说明前瞻逻辑有问题全局路径没问题但模拟轨迹全贴着障碍那就去调 DWA 的评价函数。我见过很多开发者凭感觉在参数文件里来回改改半天也不知道是哪一层导致就是因为没看到中间状态。另外一个容易出效果的小操作是把 DWA 的预测时间临时调大比如从 1.0 秒调到 2.0 秒你就能在可视化面板里看到机器人“预判”的轨迹到底倾向哪边这时候评分的倾向性一目了然。看完再调回实际值调参效率会高很多。我个人这几年做下来最大的体会是路径规划这个领域真正难的从来不是某个算法本身的公式而是怎么让不同工作节奏的算法在一台机器人身上协同工作。A* 想的是全局要不要绕路RRT 想的是到底有没有一条路DWA 想的是这一秒怎么走稳。把这三层接好之后机器人才会像一个有判断力的司机而不只是条件反射的避障器。最后再分享一个很小的习惯每次只改一个参数改完把评分日志打出来对比数据别一次动三四个参数。路径规划这种系统牵一发动全身盲改参数翻车之后你连复现问题都困难。数据说话比手感可靠得多。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表