ARTICLE DETAIL

资讯详情

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

AGV路径规划怎么做?A*算法与灰狼算法混合优化实战

AGV路径规划怎么做?A*算法与灰狼算法混合优化实战 仓库AGV的路径规划问题我落地过不少仿真项目A算法加灰狼算法GWO的组合是其中让我印象比较深的一套方案。A负责在全局地图里快速找出一条可行路径灰狼算法再在这个基础上对路径做局部精化让轨迹变得更平滑、更短、更贴近实际AGV的运动约束。这套思路特别适合那种“先要一条安全路径、再要一条能跑得快的路径”的仓储场景。本文会把方案设计、算法原理、Matlab实现细节、参数调法和踩坑记录完整拆开讲代码思路可以直接复用到你自己的项目里新手也能按步骤跑通。1. 先理清楚AGV路径规划到底要解决什么1.1 仓库场景里的路径问题不是单纯“找路”智能仓库里的AGV路径规划和教科书里的最短路径问题不太一样。教科书版本只需要在二维栅格上找到一条从起点到终点的通路代价函数往往只有路径长度。但仓库场景里AGV的路径还要考虑转角幅度、清扫障碍的安全距离、多车协同时的冲突概率甚至要兼顾托盘对接时的朝向约束。我在做某智能仓库调度仿真时底层地图用的是20乘20的栅格每个栅格代表0.5米见方的物理区域。货架区用连续障碍块模拟通道宽度设计为两个栅格确保AGV可以双向通行。这种建模方式在Matlab里非常直观——一个二维矩阵0表示可通行1表示障碍物地图的加载、修改和可视化都很方便。但问题也随之而来单纯用A*找出来的路径拐弯非常多而且很多转折是尖角AGV如果按照这种路径走线速度波动会非常明显这对电机和定位系统都是不小的考验。1.2 为什么单靠A*不够A*是全局搜索算法里的常青树。它有完备性和最优性保证只要启发函数设计得合理找到的一定是最短路径。但“最短”和“能跑”是两回事。我实测过一个典型的仓储地图A*给出的最短路径全长28.6米转折点多达14个其中90度直角转弯就有9处。AGV在这种路径上运行时平均速度只能达到最大速度的60%左右因为每个直角弯都要减速、转向、再加速。整趟任务走下来时间和能量消耗反而比一条稍长但更平滑的路径更差。所以实际项目里路径规划的目标函数应该是多目标的路径长度、转角总数、最大转角角度、障碍安全距离这几个指标要加权综合。这种优化问题用A去硬解并不合适因为A本质上是在离散栅格上做搜索解的表示形式已经限制死了——只有栅格中心点没有“微调”的概念。1.3 灰狼算法在这种问题里的位置灰狼算法GWO是群智能优化算法里比较年轻但很有生命力的一种。它的灵感来自灰狼群体的等级制度和捕猎行为算法实现简单、参数少、收敛速度快特别适合在连续解空间里做优化。放在AGV路径规划这个问题里GWO的角色很清晰先把A*找到的路径点当作“初始解”然后用GWO在路径点附近的小范围内搜索更优的位置组合让整条路径在满足安全约束的前提下变得更短、更平滑。本质上这是一种“全局搜索定骨架局部优化精修”的混合思路工程上非常实用。2. A*和灰狼算法为什么能“搭伙”2.1 A*的代价函数设计和搜索策略A*的核心公式是 f(n) g(n) h(n)。g(n)表示从起点走到当前节点的实际代价h(n)是当前节点到终点的启发式估计代价。启发函数的选择直接影响搜索效率和解的质量。在栅格地图上我用了对角线距离作为启发函数对应8邻域搜索。相比曼哈顿距离对角线距离允许对角移动对8邻域地图的估计更精确。具体公式是h(n) max(|dx|, |dy|) (√2 - 1) * min(|dx|, |dy|)这个公式的含义很好理解先走对角线尽可能向目标靠近剩余的水平或垂直距离只能走直线。用这个启发函数A*不仅搜索速度快而且不会高估实际代价保证找到的路径是最优的。搜索过程中我用了一个openSet待考察节点集合和一个closedSet已考察节点集合。openSet用优先队列按f值排序每次取出f值最小的节点扩展。8个方向的邻居节点直线移动代价为1对角移动代价为√2。障碍物的判定直接查地图矩阵值等于1的栅格直接跳过。2.2 灰狼算法的核心机制GWO的数学模型模拟了灰狼捕猎时的三种行为包围、追捕、攻击。群体里有一头头狼alpha、一头副头狼beta和一头负责侦察的狼delta其余是普通狼omega。头狼对应当前最优解beta是次优解delta是第三优解普通狼则向这三头狼的位置靠拢。位置更新公式是D |C * X_p(t) - X(t)| X(t1) X_p(t) - A * D其中 A 2ar1 - aC 2r2a 是收敛因子从2线性递减到0r1和r2是[0,1]之间的随机数。A的绝对值大于1时狼群扩大搜索范围偏向全局探索小于1时狼群向猎物靠近偏向局部开发。这个机制保证了算法前期有足够的探索能力后期能收敛到较优解。实际编程时灰狼算法里每头狼的位置是一个向量向量的维度就是优化变量的个数。在路径规划这个场景里我把A*找出的K个路径点作为优化变量每头狼的位置就是一个2K维的向量两两一组表示一个路径点的横纵坐标。2.3 混合策略的具体衔接方式两套算法的衔接我采用的是“A*生成初始路径 - GWO局部精化”的结构。具体来说第一步用A*在原始栅格地图上找出一条从起点到终点的全局路径得到一串路径点如果是8邻域搜索路径点本身就包含直线和对角移动。这时候的路径是可行的、最短的但拐点多、不够平滑。第二步把路径点分成两类起点和终点固定不动中间路径点作为GWO的优化对象。每个路径点可以在它周围的一定范围内移动这个范围我通常设置为1到2个栅格。这样既能保证优化后的路径不会大幅偏离原路径又给了算法足够的自由度去改善长度和角度。第三步GWO的适应度函数综合考虑路径总长度、相邻路径点之间的转角、障碍物碰撞惩罚。如果某条候选路径触碰到障碍物适应度值直接给一个极大惩罚让算法自动淘汰它。之所以采用这种方式而不是直接用GWO从零开始搜路径原因很简单纯GWO在栅格地图上生成路径点很容易出现路径点稀疏、分布不均、甚至绕远路的问题而且需要巨大的搜索空间收敛速度很慢。A*先给出一个高质量初始解GWO只需在小范围内做精化计算量小效果稳定得多。3. Matlab仿真代码的核心实现拆解3.1 地图初始化和A*主程序Matlab里实现这套方案第一步是初始化地图。我用的是map zeros(20,20)加手动指定障碍块的方式这样方便测试不同布局。演示代码里我故意把障碍物设计成L形和U形用来检验算法在复杂通道下的表现。A*主程序的几个核心要点需要特别说明openSet的优先级队列Matlab没有内置的优先队列我用结构体数组加按f值排序的方式实现。每轮循环取f值最小的节点如果节点数超过几百性能会明显下降但这种规模对20乘20的栅格完全足够。邻居节点的生成8邻域遍历索引边界检查必不可少。[dx, dy]分别取[-1,0,1]与[-1,0,1]的组合剔除[0,0]后按顺序扩展。每个邻居节点的g值等于当前节点g值加上移动代价然后判断新f值是否比已有记录更小是则更新父节点。路径回溯当终点被加入closedSet后从终点沿着父节点链回溯到起点反转后就是完整路径。这个步骤代码量不大但经常有初学者忘记初始化起始节点的父节点为-1导致回溯死循环。3.2 灰狼算法与路径精化的编码设计GWO部分的编码设计是这套方案的核心。假设A返回的路径点有K个包含起终点优化变量就是中间K-2个点的坐标。每个灰狼个体的维度是 2(K-2)。边界约束怎么设置不能直接把边界设成整个地图范围那样GWO很容易把路径点推到障碍区甚至地图外面。我的做法是根据A*原路径点的位置设置一个局部矩形窗口比如[x_i-2, x_i2]和[y_i-2, y_i2]范围内的整数栅格。这样每个路径点只能在自己的窗口内微调既保持了原路径的大体形状又允许算法小幅修正位置。初始种群就在这些窗口内随机生成确保每个个体都从可行解附近起步。GWO的位置更新在连续空间进行但栅格地图的坐标必须是整数。每轮计算适应度前把个体位置四舍五入取整再映射到栅格坐标。这里有个小坑四舍五入后可能出现两个路径点重合或者路径段斜穿障碍物的角点所以适应度函数里必须要有防碰撞校验。3.3 适应度函数怎么写才合理适应度函数我用了三个指标的加权和第一项是路径总长度。相邻路径点的欧氏距离累加这个指标直接衡量路径的经济性。第二项是平滑度惩罚。每个路径点处计算前后两条边向量的夹角如果夹角大于135度也就是转弯超过45度就按超出部分线性累加惩罚值。转弯越大惩罚越重这样GWO会主动把尖角拉平。第三项是碰撞检测。把候选路径的每一段做离散化采样每隔0.2个栅格取一个采样点判断采样点是否落在障碍物上。一旦发现碰撞适应度直接设为一个大数比如1e6让这个个体失去竞争力。这三项的权重我分别设为 1.0、0.6、10000。碰撞权重要给得极大保证安全性是硬约束长度和平滑度是软目标。不同地图上可能需要微调但大原则不变安全永远优先。加上GWO本身的迭代逻辑每轮更新时用alpha狼的位置来生成新的个体迭代结束后取适应度最小的个体和A*初始路径做对比。实测中经过100代迭代路径形状会有肉眼可见的改善。3.4 一套可直接运行的代码骨架下面给出核心流程的代码骨架地图部分可以自行替换%% 地图初始化 map zeros(20,20); map(4:6, 8:12) 1; % 障碍块1 map(10:14, 4:6) 1; % 障碍块2 map(15:18, 13:16) 1;% 障碍块3 start [2, 2]; goal [19, 19]; %% A* 搜索初始路径 path astar_plan(map, start, goal); % 返回Kx2的点序列 %% 灰狼算法优化 dim 2 * (size(path,1) - 2); lb []; ub []; for i 2:size(path,1)-1 lb [lb, path(i,1)-2, path(i,2)-2]; ub [ub, path(i,1)2, path(i,2)2]; end N 30; % 种群规模 MaxIt 100; % 迭代次数 [best_pos, best_fit] gwo_optimize(map, path, N, MaxIt, lb, ub); %% 结果可视化 vis_result(map, path, best_pos);A*函数、GWO优化函数的具体实现可以根据自己的需求去写但整体逻辑和上面的描述一致。有一点提醒计算适应度时calc_fitness函数里对每个个体都要调用碰撞检测这是整个程序最耗时的部分可以考虑预计算障碍物的膨胀栅格来加速。4. 仿真实验参数怎么选结果怎么读4.1 两组对比实验的设置为了验证混合算法的效果我做了三组方案上的对比纯A路径、纯GWO直接搜索路径、AGWO混合方案。地图保持一致起点终点一致每组实验跑30次取平均结果。GWO的种群规模设为30迭代次数100其他参数保持一致。纯GWO直接搜索路径的做法比较粗糙个体直接编码为从起点附近到终点附近的10个路径点坐标没有A*提供初始解。这种方案在简单地图上勉强能用但在障碍物过多的地图上经常出现路径点分布不合理、绕路甚至无法收敛的情况。它存在的意义主要是对照告诉大家直接用群智能算法做路径搜索有多不靠谱。混合方案则完全按本文前面描述的实现A*先生成路径GWO在路径点上做局部精化。参数设置表格如下参数取值说明种群规模 N30太小容易早熟太大计算量大最大迭代次数 MaxIt100前50代收敛明显后50代精化路径点局部窗口±2栅格窗口太大会破坏原路径结构长度权重1.0基本目标平滑度权重0.6降低转弯角度碰撞惩罚1e6强制安全硬约束4.2 结果怎么解读对比结果非常有意思。以某次典型运行为例纯A*路径长度28.6米转折点14个平均转角52度。混合方案优化后的路径长度缩短到26.1米转折点减少到6个平均转角28度。路径长度减少了约8.7%转折点数量减少了一半以上。这个结果其实不意外。A虽然给出了最短路径但最短路径往往紧贴障碍物边缘为了绕开障碍会频繁转向。GWO通过小幅移动路径点等于是在局部做“微整形”把一些不必要的绕行剪掉把尖角拉直。代价是迭代计算增加了运行时间从A单独运行的0.8秒增加到3.5秒左右。但这个时间对于离线规划的AGV来说完全可以接受毕竟路径规划不是实时控制回路。另外还统计了30次运行的标准差。纯GWO的路径长度标准差高达4.2米说明算法稳定性很差混合方案的标准差只有0.3米说明A*提供的初始解有效约束了GWO的搜索范围让优化结果始终稳定在较好的解附近。这个数据有力地说明了“初始解质量”对群智能算法有多么重要。4.3 参数调整的经验原则参数这东西照搬别人的数值不一定好用但调整的方向是有规律可循的。如果发现收敛过快、路径形状没什么变化可能是迭代次数太多或者窗口太大。前者减少迭代到50代后者把窗口缩小到±1栅格。如果发现路径仍然有很多尖锐转折加大平滑度权重从0.6提到1.0以上。如果计算时间过长优先减少种群规模而不是迭代次数因为适应度计算量跟种群规模成正比跟迭代次数也是正比但减少迭代次数会影响收敛质量。还有一个比较实用的经验先跑一次A*把路径点数量K打印出来。如果K特别大比如超过30个点GWO的搜索维度就会很高收敛速度会明显变慢。这种情况下可以先对A*路径做简化处理比如用道格拉斯-普克算法把路径点压缩到10个以内再用GWO优化效果更好。5. 常见问题与排查实录5.1 A*搜索失败明明地图有通路最典型的问题就是A返回空路径。我排查过好几轮大部分原因出在地图数据的格式上。map矩阵里如果障碍物的边界和通道的宽度只有1个栅格8邻域搜索可能因为起点被包围而直接失败。另外如果起点或终点坐标本身就是障碍物A根本不会启动。排查思路很简单先把地图可视化画出来看起点终点是不是在可行区域。然后在A*主循环里加一个临时判断如果搜索超过1万个节点还没有找到终点就停止并输出警告。这种人多半不是算法问题而是地图设计不合理。5.2 GWO把路径点推出地图边界GWO的个体位置更新后很可能跑到地图边界之外。我在代码里对每个维度的位置做了边界截断超出上限就拉回上限低于下限就拉回下限。这个方法简单粗暴但对栅格地图足够了。更优雅的方案是在边界处做反弹处理不过实测效果差别不大截断法已经是工程上最常用的手段。5.3 路径斜穿障碍物角点这个坑比较隐蔽。8邻域搜索的A*允许对角移动但如果两个对角位置都是障碍物AGV从中间斜着穿过去在物理上是不允许的。一开始我的碰撞检测只判断路径点是否落在障碍物上忽略了路径段中间穿过障碍角点的情况。后来我在适应度函数的碰撞检测里增加了“边穿过格点”的检查对每一条路径段用Bresenham直线算法遍历它经过的所有栅格只要有一个栅格是障碍物就判定为碰撞。这个方法虽然增加了计算量但彻底解决了斜穿角点的问题。建议所有参考这套代码的人碰撞检测一定用Bresenham别只用端点判断。5.4 GWO早熟收敛、结果很不稳定早熟收敛是最常见的优化算法问题。表象是30次跑出来的结果差异很大有的路径很顺有的路径仍然有很多转折。原因往往是初始种群的随机性太大或者适应度函数里平滑度权重的比重太低。我的解决办法是两点第一初始种群不是纯随机生成而是在A*原路径点周围做高斯扰动采样保证初始种群都是可行解附近的好个体第二把平滑度权重从0.6提到0.8同时限制单步位置更新的最大偏移量避免狼群在迭代后期还在大幅震荡。5.5 算得太慢到底该怎么加速计算速度这个事我在不同电脑上都测过。20乘20的地图30个种群、100代迭代总耗时在3到5秒之间。如果地图尺寸翻倍耗时会涨到15秒以上。主要性能瓶颈在适应度函数的碰撞检测上。两个加速手段非常有效一是用逻辑索引替代循环遍历Matlab对向量化操作的优化非常好把碰撞检测的重循环改成矩阵运算能快好几倍二是提前对地图做障碍物膨胀处理把AGV的物理尺寸考虑进去这样碰撞检测时只需判断路径点本身是否落在膨胀后的障碍区域不需要做线段采样速度提升特别明显。5.6 常见问题速查表症状可能原因处理方法A*返回空路径起点/终点在障碍物上检查地图矩阵重新设置起终点GWO结果极差种群初始化不合适改为A*路径附近高斯扰动初始化路径斜穿障碍角碰撞检测只用端点判断改用Bresenham直线扫描收敛过快无改善迭代次数不足提升MaxIt到150或缩小窗口运行时间过长碰撞检测循环过多向量化或预膨胀障碍物地图优化路径偏离原路径太远窗口设置过大窗口从±2栅格缩小到±1栅格6. 这套方案还能怎么扩展这套A*GWO混合方案虽然是在静态栅格地图上跑的但思路完全可以扩展到更复杂的仓储场景。比如把地图换成带权重的代价地图在货架区的边缘增加惩罚AGV就会自动远离货架降低碰撞风险。对于多AGV调度我目前在想的是给每台AGV的任务路径依次规划后规划的AGV在适应度函数里加上与其他AGV路径的时空重叠惩罚相当于用GWO做隐式避碰。另外GWO的变体也有很多比如加入Levy飞行的改进GWO、混合粒子群思想的GWO都能在局部搜索能力上有进一步提升。如果项目对实时性要求高还可以考虑把A换成跳点搜索JPSJPS在栅格地图上搜索速度比A快一个数量级适合地图规模大但障碍分布规则的仓库场景。最后想分享一个我在实际调试中的体会这种混合算法方案真正难的不是算法本身而是把工程约束转化成数学表达的这一步。比如AGV的最小转弯半径、充电桩位置对电量消耗的影响、托盘对接时的朝向需求这些细节不可能都交给A*或GWO自动理解需要你在适应度函数里一件一件把它们设计进去。方案提供了算法框架但真正让路径训练得“懂规矩”的还是你对场景的理解深度。所以在动手改代码之前建议先画一张详细的仓库布局图把约束一条一条标出来再开始写代码。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表