把“多种灰狼优化算法”和“无人机集群规划”放在一个MATLAB项目里,这事其实挺有代表性。很多人在算法库里翻到GWO(灰狼优化算法)的源码,但不知道怎么跟无人机路径规划结合;也有不少人手上有一堆UAV仿真代码,却只会跑默认参数。而这个项目把两件事焊在了一起:直接运行main.m,就能看到多只灰狼解算多架无人机三维路径的全过程,代码里还自带三套UAV_SetUp,方便你换场景做对比实验。
简单说,这东西能帮你解决一个具体问题:在多山峰、多威胁区的三维空间里,规划若干架无人机各自的飞行路线,同时满足路径短、高度合理、不撞山、飞机间不打架这些约束。它适合三类人——正在写本科或硕士论文、需要算法对比图表的学生;刚开始接触群体智能优化算法、想找个完整工程范例的开发者;以及无人机行业的仿真工程师,拿它做方案预研和参数验证都行。
我拿到这类代码一般会先干三件事:第一,把main.m从头到尾读一遍,弄清数据怎么初始化、算法怎么被调用、结果怎么画出来;第二,跑一次默认配置,看三个UAV场景分别输出什么曲线和路径;第三,把不同灰狼优化算法的结果放在一起对比。做完这三步,这套代码的含金量基本就摸清了。
1. 这个项目到底在解决什么:集群路径规划为什么难
1.1 集群规划比单机规划难在哪里
单架无人机的三维路径规划,本质是在连续空间里找一条满足约束的轨迹。约束包括路径长度、飞行高度、地形避障、转弯角限制等。这时候用灰狼优化算法,一只灰狼的位置向量就是一条完整路径,适应度函数算一下这条路好不好走,迭代几百次就能得到一条过得去的路线。
但集群规划不一样。多架无人机同时飞,不仅要各自避障,还必须考虑机间协同。最典型的是安全间距约束——两架飞机不能离得太近;其次是任务分配——每架飞机飞哪几个目标点,顺序怎么定;再往下还有能量均衡——不能让某架飞机绕远路而另一架直线飞。
从优化问题的维度看,单机的决策变量是“路径点数 × 3个坐标”,比如20个航点就是60维;集群的话,假设5架飞机、每架20个航点,决策变量就是5×20×3=300维。维度一高,很多算法直接白给,找不动。灰狼优化算法在这类高维、非凸、不可导的适应度函数面前反而有一定优势,因为它只依赖目标函数值的比较,不需要梯度信息,这就让路径规划中最头疼的复杂地形建模变得可行。
1.2 灰狼算法为什么天然适合集群路径搜索
灰狼优化算法模拟的是灰狼群体狩猎行为。种群内部有严格等级:alpha(头狼)指导全局方向,beta和delta协助决策,其余狼围绕这三只狼的位置进行更新。把这种结构映射到无人机集群上,你会发现逻辑非常顺:算法选出的头狼代表当前最优路径集合,delta和beta代表次优路径集合,普通灰狼在它们周围扰动搜索,本质上就是一群候选方案在相互学习。
更关键的是编码方式。常见实现里,一只灰狼的位置是一个长度等于总决策变量数的一维向量,解码时reshape一下,就能还原成多架无人机各自的航点列表。也就是说,算法层面它不知道“无人机”这个概念,它只知道在几百维空间里找一个让适应度最小的点,而这个点恰好编码了一整组无人机路径。这种解耦设计让代码结构非常干净,也方便扩展——今天跑3架飞机,明天改成10架,只需要改UAV配置,算法核心一个字节都不用动。
1.3 “多种灰狼优化算法”的设计意图
为什么要在同一个项目里放多种GWO变体?我的理解是,这位开发者的目标是提供算法对比的基准环境。单纯跑标准GWO,收敛曲线只有一条,说服力不够。而如果同时跑两三种改进版,比如基于混沌映射初始化的GWO、融合差分进化的GWO、带自适应收敛因子的GWO,那么同一组地图、同一批参数下,三条收敛曲线拉开差距,谁优谁劣一目了然。
这种做法的实际价值在于:你不需要自己从零写对照算法。main.m跑一遍,输出结果天然带着对比维度,写论文也好、做方案汇报也好,直接截图就是素材。我当时第一次跑完就感慨,这才是真正“为复用而写”的项目结构。
2. 代码架构拆解:从main.m开始读懂整个项目
2.1 main.m的标准运行流程
大多数同类项目的main.m,结构都有套路可循。我建议你按下面这个顺序去读,基本不会迷路。
第一步,清空环境。代码开头通常是clear;close all;clc这三联。老MATLAB用户都懂,不清空的话,上一次运行遗留的变量可能把当前结果搞乱,尤其是你改了UAV_SetUp之后忘了清工作区,新代码读到旧变量,直接给你报维度错误。
第二步,加载场景配置。这里会调用UAV_SetUp相关的函数或脚本,把地图尺寸、山峰个数、山峰高度、威胁区位置、无人机数量等信息填进一个配置结构体。通常配置包括地图范围和禁飞区。三类场景各有侧重,稍后细说。
第三步,设置算法参数。包括灰狼种群规模N、最大迭代次数MaxIter、决策变量维度dim,以及选择哪种灰狼优化变体。有些实现里是function handle数组,循环里逐个调用;有些用switch-case。看到这种结构,别慌,核心逻辑是一样的。
第四步,循环调用不同GWO算法,返回最优路径和收敛曲线。这是最耗时的一步。跑完以后,工作区里会有几个关键变量:best_pos(最优位置向量)、best_fitness(历史最优适应度)、convergence_curve(每次迭代的最优值记录)。
第五步,结果可视化。常见的是三张图:三维地形路径图、路径俯视图、收敛曲线对比图。三维地形路径图最能直观看出飞机有没有撞山、路径平不平滑;俯视图适合检查机间交叉;收敛曲线则反映算法搜索效率。
2.2 三种UAV_SetUp的典型配置
虽然不同版本代码的具体参数不一样,但以常见实现来看,三套UAV_SetUp一般是按难度递进设计的。我整理了一个典型配置表,你可以拿它跟手上的代码对照:
| 场景名称 | 无人机数量 | 地图范围 | 山峰数量 | 威胁区/禁飞区 | 适合实验目的 |
|---|---|---|---|---|---|
| UAV_SetUp1 | 3架 | 10×10×10 km | 5个 | 无 | 算法基线测试,快速验证代码能跑通 |
| UAV_SetUp2 | 5架 | 20×20×20 km | 8个 | 2个圆柱威胁区 | 考察算法在多机多约束下的寻优能力 |
| UAV_SetUp3 | 8架 | 30×30×30 km | 12个 | 3个威胁区 | 高密度集群场景,压测算法性能与稳定性 |
第一套场景适合初学者跑第一遍。地图不大,飞机少,算法迭代速度快,你可以在几分钟内看到完整效果,也能放心地反复改参数观察变化。第二套场景开始加入威胁区,这时候适应度函数里会有“禁飞区惩罚”项,你会看到灰狼算法为了绕开威胁区,哪怕多飞一点路,也绝不往里钻。第三套是压榨性能用的,8架飞机在复杂地形里协同,对算法的局部搜索能力和收敛速度都是真实考验。
注意:如果你拿到代码后发现UAV_SetUp函数里返回的不是结构体而是一堆零散变量,那是很常见的风格。关键在于弄清楚每个变量的含义,建议在main.m里设置断点,一步步看工作区生成的内容,这比干读代码高效得多。
2.3 一个灰狼解如何映射成一堆UAV路径
这是理解整套代码最关键的一步。大部分新手的困惑,就是想不明白“一只狼怎么就代表一群飞机了”。
常见编码方式是这样的:假设有M架无人机,每架飞机规划N个航点,每个航点包含x、y、z三个坐标,那么决策变量总数就是dim = M × N × 3。灰狼个体的位置向量就是1 × dim的行向量。解码时用reshape函数重排:
positions = reshape(individual, M, N, 3);这个三维数组里,positions(i,:,1)是第i架飞机的x坐标序列,positions(i,:,2)是y坐标序列,positions(i,:,3)是z坐标序列。第i架飞机的路径就是依次连接(positions(i,1,:)到positions(i,N,:))的线段。
理解这个映射关系之后,你再看适应度函数就会通透很多。因为集群规划的所有约束——路径长度、转弯角、威胁区惩罚、机间距离——全部是围绕这个三维数组展开计算的。算路径长度就是累加相邻航点的欧氏距离,算机间距离就是两两飞机逐航点求间距再惩罚小于安全阈值的部分。
提示:如果你在改代码时调整了路径点数N,一定要同步修改dim的计算。我见过太多人只改了航点数却忘了改维度,结果GWO初始化种群时报“矩阵维度必须一致”,卡了半小时不知道怎么回事。
3. 核心算法解析:GWO的公式、变体与适应度设计
3.1 灰狼优化算法的数学本质
标准GWO的核心逻辑,是让种群中的每只狼根据alpha、beta、delta三只头狼的位置,计算一个新的位置。数学上分两步:
第一步是距离估计。第t次迭代时,当前灰狼X(t)与头狼X_p(t)之间的距离为:
D = | C · X_p(t) - X(t) |
第二步是位置更新:
X(t+1) = X_p(t) - A · D
其中A和C是两个关键系数:
A = 2a · r1 - a C = 2 · r2
这里的r1、r2是[0,1]之间的随机数,a是收敛因子,从2线性递减到0。A的绝对值大小直接决定灰狼是扩大搜索范围还是收缩包围圈。当|A| > 1时,灰狼远离猎物,做全局探索;当|A| < 1时,灰狼逼近猎物,做局部开发。这个特性非常像无人机编队在执行任务时的动作——先散开侦察开阔空间,再收拢锁定最优航路。
值得注意的是,标准GWO里所有普通灰狼的位置更新,实际上是往三只头狼位置的加权中心靠拢。这意味着算法默认可行解空间是连续的,这对路径规划问题非常友好。但同时也是短板:后期收敛因子线性递减,种群容易快速集中,如果三只头狼都在某个局部最优区域,整个种群就会被带进去。
3.2 常见的GWO改进策略:四种主要变体
既然项目名里有“多种灰狼优化算法”,那核心看点就是这些变体怎么改、为什么这么改。我把最常见的四种整理出来,你跑代码时大概率能遇到其中几个。
第一种是混沌映射初始化。标准GWO用随机数生成初始种群,分布不均匀,可能一开始就漏掉大片解空间。改进做法是在初始化阶段引入Tent映射或Logistic映射生成混沌序列,让初始灰狼位置更均匀地散布在搜索空间。代价基本为零,效果却立竿见影——收敛曲线第一代就比随机初始化低不少,后期的稳定性也更好。
第二种是自适应收敛因子。标准GWO里a = 2 - 2×(t/MaxIter)是线性递减,前期探索和后期开发的节奏固定,但复杂地形需要的是“大部分时间探索、最后阶段快速收敛”的非线性节奏。常见的改进有:
a = 2 * (1 - (t / MaxIter)^0.5);或者用余弦形式:
a = 2 * cos(pi * t / (2 * MaxIter));这个改动很小,但对多峰地形有奇效。我实测下来,用非线性收敛因子的GWO在UAV_Setup2场景里能找到更短的路径,代价是前期收敛曲线会略高,因为探索范围更大了。
第三种是融合差分进化。标准GWO后期所有灰狼都在向头狼靠拢,种群多样性急剧下降。混合DE的做法是,每轮迭代后随机选一部分灰狼,执行差分进化的变异和交叉操作。变异的经典形式是:
V = X_r1 + F × (X_r2 - X_r3)
也就是从种群中随机抽三只不同的狼,用它们的差向量扰动出一个新位置。这样做能不断引入新的搜索方向,打破头狼对种群的绝对统治。
第四种是引入Lévy飞行。标准GWO的位置更新步长相对温和,难以跳出深谷型的局部最优。Lévy飞行用重尾分布来描述步长,特点是偶尔出现大步跳跃。实现方式是在更新位置后加一项:
X_new = X_alpha + α × Lévy(β) × (X_best - X)
这个“偶尔的大跳”特别适合无人机路径规划中越过整座山去搜索另一片低洼区的情景。
跑对比实验时,你会在同一张图上看到:标准GWO曲线可能前期降得快、后期卡平;混沌GWO前期就领先;DE混合GWO则在中后段突然又下一截。这就是不同变体的性格差异。
3.3 适应度函数:权衡长度、高度、威胁与机间距离
在GWO-Framework里,所有算法变体共用同一个适应度函数,这也方便公平对比。适应度函数怎么设计,直接决定算法能不能解好无人机集群规划,这些目标往往互相矛盾。
我见过的大多数工程版本,适应度函数是几个代价项的加权和:
J = w1·J_length + w2·J_height + w3·J_smooth + w4·J_threat + w5·J_separation
各项含义如下:
J_length是路径总长度代价,把所有无人机相邻航点的欧氏距离加总。它主导“路径要短”这个核心目标。
J_height是高度惩罚。如果某处地形很高而规划路径的高度过低,就产生惩罚。这个惩罚通常写成高度差或阈值触发的分段函数,避免飞机撞山。
J_smooth是平滑度代价。计算路径上相邻线段的转角,如果转角超过无人机最大转弯角约束,就会额外加惩罚。没有这一项,算法规划的路径会呈锯齿状,现实里根本飞不了。
J_threat是威胁区惩罚。判断航点或航段是否进入禁飞区范围,进入就给一个非常大的惩罚值,强迫算法绕行。
J_separation是机间距离代价。两两计算飞机在同一航点处的三维距离,若低于安全间距,就会加惩罚。这一项是集群规划区别于单机规划的核心。
权重w的取值很讲究。正常逻辑是先看各代价项的量纲:路径长度是几十上百的数量级,高度惩罚可能是几十,威胁区惩罚偶尔被罚一次就是上万。如果权重差距不拉开,威胁惩罚根本盖不住长度项的影响。经验做法是,先跑一次把各项的数量级摸清楚,再设定权重。比如把w4设成1000以上,w1保持在1附近,威胁区才能真正变成“硬约束”。这部分调参的过程,往往比调GWO算法本身更费时间。
4. 实操记录:跑通、调参、对比算法
4.1 第一次运行main.m的完整流程
如果你电脑上已经装好MATLAB,我建议按这套步骤操作,不容易出错。
第一步,解压项目文件夹,确认里面有main.m、UAV_SetUp相关的函数文件、GWO算法函数文件以及地图生成脚本。第二步,打开MATLAB,把当前工作目录切到项目文件夹。第三步,直接在命令行输入main或者点击编辑器里的运行按钮。第四步,观察命令行窗口的输出。常见的输出有当前迭代次数、当前最优适应度等,有些实现还加了进度条。
第五步,等程序跑完后,看弹出的图形窗口。一般会有两个或三个图:三维地形图显示地形和规划路径;收敛曲线图显示不同GWO变体的适应度下降趋势。
以默认的UAV_SetUp1为例,3架无人机、5个山峰,种群规模如果是30、迭代次数100,通常十几秒就能跑完。如果跑到SetUp3,8架无人机、12个山峰,计算时间会显著拉长,这时候你可以考虑把迭代次数从100降到50,先快速验证代码流程,再慢慢加回来。
注意:如果你用的MATLAB版本比较老(比如2016a之前),有些语法可能不兼容。遇到莫名其妙的报错,优先检查项目里是否用了新版才有的函数或字符串数组语法。
4.2 三个关键参数的调节心得
第一个是种群规模N。它决定每次迭代有多少个候选解参与搜索。N太小(比如10),搜索覆盖不足,容易陷入局部最优;N太大(比如100),每轮迭代的适应度计算量线性增加,跑得慢,收益却不明显。我的经验是:维度在100以下的取30,维度300左右的取50到60,再往上取80就够用了。
第二个是最大迭代次数MaxIter。它决定算法能搜索多少代。看收敛曲线来判断——如果在当前迭代次数下曲线后期已经很平,说明收敛已经完成,再多跑只是浪费时间;如果曲线还在明显下降,说明迭代不够,要加。我一般习惯先设50跑得快,看趋势后再设到200做精细搜索。
第三个是收敛因子a的递进方式。标准GWO用线性递减,改进版各不相同。如果你用的是自适应GWO变体,可以改一下指数参数试着观察效果。比如把a的递减指数从1改成0.5或2,你会发现前期探索和后期开发的比例完全不同。这块特别适合做实验写进论文。
另外,还有一个容易忽略的参数是UAV_SetUp里设置的路径点数N。路径点数太稀疏,就无法精细绕开地形和威胁区;太密集,维度暴涨,搜索难度加大。常见取值是每架飞机20到40个航点,具体要看地图的复杂程度。SetUp1用20个就够,SetUp3建议加到30到40个。
4.3 用收敛曲线判断算法和参数是否正常
收敛曲线是整个项目里信息量最大的输出,但它不会自己说话,得靠你解读。
第一,看曲线的起点。如果第一代适应度就特别低,说明初始化策略有效,或者问题本身简单。如果第一代曲线很高,甚至后面的算法结果也不理想,可能是维度太高或权重没调好,也可能是初始种群覆盖太差。
第二,看曲线下降的形态。健康的收敛曲线是先快速下降再缓慢趋平,整体呈负指数形状。如果曲线从头到尾都很平缓,可能是种群规模太小、狮子大开口步长太小,搜索效率低;如果曲线突然掉了一大截然后完全不动,说明算法在前几代就陷入了局部最优,种群多样性快速丧失。
第三,看多条曲线的对比。不同GWO变体在同一个问题上,合理的结果是各有优劣:标准GWO或许前期强,混合DE-GWO后期更强。如果某个改进版在三个场景里全都碾压其他算法,你就该怀疑是不是它的收敛因子参数设置占了大便宜,而不是算法本身质变。这时候把不同算法的关键参数统一,保证对比公平性,才是正确实验姿势。
第四,要有“曲线不骗人”的信念。即使路径图画得再好看,如果收敛曲线剧烈震荡、毫无下降趋势,那路径大概率就是碰运气撞出来的。先修算法稳定性,再谈路径优化。
5. 踩坑实录与排查技巧
5.1 维度不匹配与索引越界
这个坑的频率最高,几乎每套类似代码都能遇到。典型报错是MATLAB提示“索引超出数组边界”或者“矩阵维度必须一致”。
根因通常是你改了UAV_SetUp里的无人机数量或航点数量,但算法初始化、适应度函数、解码逻辑中的维度参数没同步更新。举个我踩过多次的例子:UAV_SetUp1的3架飞机、每架20个航点,dim=180;突然换成UAV_SetUp3的8架飞机、每架30个航点,dim=720。如果main.m里写死dim=180,那GWO初始化种群时就会崩。
排查思路很明确:先用size函数检查配置结构体里每个字段的尺寸,再用断点卡在GWO主函数入口,看传入的dim是否和UAV_SetUp里的数据匹配。维修也很简单,把dim的计算改成动态获取:size(getUAVSetUp().initialPositions)。
5.2 路径穿山、毛刺严重、飞机相撞
有一次我调整完权重直接跑UAV_SetUp2,输出的三维图里一架飞机的路径直直穿过山峰,收敛曲线还是下降得非常平滑。我当时有种“哪里不对劲”的警觉。检查之后发现问题出在高度惩罚权重太小:路径长度项权重是1,高度惩罚权重只有0.01,算法觉得穿山省下的几百米路程比绕山划算,于是疯狂穿山。把高度惩罚权重提到0.5之后,路径才老实贴着山峰轮廓走。
毛刺问题也类似。路径锯齿严重、弯来弯去,说明平滑度惩罚权重太低或者路径点太密、转弯角约束没起作用。我一般会调整平滑度权重,让J_smooth在总代价里占比达到20%到30%。
飞机相撞问题则要看机间安全距离惩罚。如果规划的路径在交叉点处两架飞机几乎重叠,那一定是J_separation的惩罚力度不够。常见的解决方法是把安全距离阈值设置得比飞机实际安全间距更大,比如实际最小距离要求500米,代码里就设800米,留足余量。
5.3 结果不稳定与局部最优的判断与应对
同一套参数跑十次,可能十次结果都不一样,这是群体智能算法的天性。如果只是适应度值在小范围内波动(比如±5%),这算正常。如果有些次跑出明显更好的解,有些次却一塌糊涂,那就有问题了。
这时候我一般会做三件事:第一,固定随机种子。在main.m开头加rng(0),确保每次实验的随机序列一致,这样对比实验和复现才有意义。第二,增加独立重复实验次数。跑20次,统计最优值、均值、标准差。标准差不应该太大。第三,检查是否算法早熟。如果种群多样性快速归零,跑五次五次都收敛到同一个很差的解附近,那就是陷入局部最优。应对方法有:调大种群规模、改用混沌初始化或DE混合变体、增加Lévy飞行跳变项。
说到这,我想起一开始跑UAV_SetUp3时遇到的诡异现象:标准GWO每次都能跑出一组路径,但有几架的路径明显绕了远路。看收敛曲线倒是正常下降,一度以为没问题。直到我手动算了一下那架飞机的路径长度,才发现它绕了一个大圈。后面把适应度函数里的机间距离惩罚和威胁惩罚都梳理一遍,才找到原因——那个大圈是为了避开另一条路径的密集区域。换句话说,算法是在用路径长度换安全性,这个取舍本身没问题,但你得知道它在干什么,才不会对结果产生误判。
6. 后续扩展思路:把项目用得更深
6.1 加时间维度的动态规划
现在项目里规划的是静态路径——地图固定、威胁区不移动。实际无人机任务里,风速、禁飞区位置、突发障碍物都是时变的。因此可以把静态路径规划扩展成四维时空规划:除了x、y、z三个坐标,再加一个时间维度,让每架飞机在某个时间戳必须到达某个位置。
这样一来,机间避撞就能变成真正的四维问题——两架飞机即使空间轨迹交叉,只要到达时间错开,就不会冲突。这比单纯加大安全距离惩罚要高明得多,代价是决策变量维度直接翻倍甚至更多。好消息是,GWO的框架不需要大改,只改编码方式和适应度函数,就能处理这种更复杂的问题。
6.2 混合算法与并行加速
GWO的另一个经典玩法是与其他算法组成混合搜索框架。常见组合有灰狼+粒子群:前期用GWO做全局勘探,后期交给粒子群做精细局部搜索;或者反过来,用PSO初始化种群,再用GWO迭代。这类混合算法在复杂地形下往往比任一单算法更稳。
跑大规模UAV_SetUp3时,如果觉得速度慢,还可以用MATLAB的并行计算工具箱。最简单的改造是:把针对不同GWO变体的循环用parfor替换,让几个算法同时跑在不同工作线程上。改造前提是各算法之间不能有共享变量的写入冲突。串行跑3个变体可能需要几分钟,并行后能压缩到原来的一半甚至更少。
6.3 从仿真到落地应用的衔接思考
仿真跑通不是终点。如果要把这套算法放到真机集群里,还需要考虑两个问题:一是路径平滑。GWO输出的航点连线是折线,真机飞出来会一顿一顿。通常的做法是用B样条曲线对航点做平滑,再交给飞控执行。二是实时性。静态规划在起飞前离线算好就行,但中途遇到突发情况需要在线重规划,对算法迭代速度有硬指标要求。这个时候,把GWO的收敛曲线压缩到几百毫秒内,就需要结合代理模型或者提前缓存部分可行解。
我的建议是,把这套项目当作算法试验田,先把不同GWO变体的性格摸透,再逐步往工程方向靠拢。
最后分享一个我自己的使用习惯:每次跑这套代码,我都会把UAV_SetUp三套场景的结果单独存成图片和.mat文件,标注好当时的参数配置。因为做对比实验时,最怕的不是结果差,而是忘了当初是怎么跑出这个结果的。有了一份带参数快照的实验记录,后面写技术报告、复盘调参路径、甚至重新生成论文图表,都会省下很多时间。这也是我跑完这批GWO代码之后,最想提醒你的一件事。