☰
MSA蛾群优化算法在无人机三维路径规划中的MATLAB实现与调优
2026/10/22 11:56:53 网站建设 项目流程

无人机三维路径规划这几年在学术竞赛和工程项目里出现的频率越来越高。前两天有位做毕设的同学拿着“基于matlab的蛾群优化算法(MSA)无人机三维路径规划”这个题目来问我,说代码能跑出最短路径图和适应度曲线,但论文里要解释算法原理、参数含义,却不知道从哪里下手。其实这类问题我见得多,算法本身并不复杂,关键是把蛾群行为和数学优化语言对应起来。

实际上,MSA这种群智能优化算法,在路径规划场景中的应用已经不算冷门,网上能找到的源码版本也很多,但大部分注释少、参数封装不清晰,拿过来直接跑可以,想二次开发就比较痛苦。这篇文章就把我实际调试这类代码的经验完整整理一遍,从算法原理到环境建模,从种群划分到参数调优,争取让刚接触路径规划的人也能把MSA讲清楚、调明白。文中的思路和调试方法都是我在项目里反复用过的,照着搭一份完全可行,同时也会指出那些容易让人卡壳的细节。

1. 蛾群优化算法(MSA)原理拆解

1.1 蛾群行为与优化思想

飞蛾在夜间有一种天然的趋光行为,绕光源旋转,始终保持身体轴线与光线方向呈固定角度。早期的一些优化算法把这种螺旋飞行路径直接转换成位置更新公式,但对整个蛾群的协作行为刻画得比较粗略。MSA则更进一步,把种群内部看成有分工的群体:一部分负责大范围探索,一部分负责向光追赶,一部分负责局部精搜,分别对应探路者、勘探者和观察者三类角色。

这个分工结构是模仿真实蛾群在夜间的行为模式。探路者蛾飞出较远距离去发现新的光源方位,勘探者蛾沿着相对照明的方向移动,观察者蛾集中在已找到的光源附近悬停扰动。转化成算法语言就是:先用一部分候选解扩大覆盖率,再用一部分解做定向收敛,最后用一部分解做精细打磨。三类角色互补,兼顾了全局探索和局部开发,这也是MSA在路径规划这类高维约束问题上比较稳定的原因。

对路径规划场景来说,这个分工尤其重要。三维路径搜索空间大、地形起伏复杂,如果全种群一体化更新,很容易在某一座山峰附近抱团,最后只能得到一条绕行距离偏远的次优路径。MSA的三类角色相当于每一代都有探索队、攻坚队和精修队同步作业,全局覆盖和局部收敛同时推进,避免了经典群优化算法常见的“前期收敛快、后期原地打转”问题。

1.2 MSA与常见群智能算法的差异

很多人会把MSA和MFO搞混,因为名字相近。MFO是“飞蛾扑火优化”,所有蛾围绕火焰做对数螺旋式更新,每一代都朝最优火焰压缩。MSA则做了角色分层,不同类型的蛾采用不同的更新机制。这个设计带来的直接好处是:种群不容易因为全体向最优个体挤压而过早停滞,探路者个体保留了在后期跳出局部极值的可能。

我做过一个简单对比,同样的三维地形规划问题,MFO在迭代50次左右基本收敛,但容易出现路径在地形凹陷处兜圈子;换成MSA并把探路者数量控制在种群15%左右,路径质量明显稳定,原因是探路者的扰动机制在后期仍能制造一定的位移突变。这一点在实际代码调试中会有直观感受:适应度曲线在后段不再是死水一潭,而是偶尔出现向下跳变,那往往是探路者发现了一条新的可行通道。

再往深一些说,MSA和粒子群算法PSO的思路也有相似之处,PSO靠惯性权重和个体历史最优引导运动,MSA则把“历史最优”换成了当前种群的层级结构,并通过不同随机扰动机制保持多样性。如果读者已经接触过PSO,把MSA理解成“带角色分工和多搜索策略的变种PSO”会更轻松。路径规划属于典型的约束优化问题,搜索策略越多样,找到优良通道的概率就越可观。

2. 三维环境建模与路径规划问题定义

2.1 数字地形与搜索空间

三维路径规划第一步不是写算法,而是把环境建成可计算的对象。常见做法是用栅格高程模型,把地图划分成m乘n网格,每个格点对应一个海拔高度。自己构造测试环境时,可以采用多个高斯峰叠加的方式,模拟山区地形。这个公式生成的地形起伏可控,便于验证算法是否能绕开高峰。工程上如果使用真实地形数据,一般把DEM数据转成矩阵,再用插值函数读取。我用matlab的griddata和interp2,前者负责把离散测得点插成规则网格,后者负责在飞行路径采样时查询对应高度。

搜索空间的大小直接影响优化难度。如果路径起点到终点直线距离是100,同时高程波动在0到120之间,那么限定x、y和z的边界就是算法搜索范围。过大的边界会让随机初始化的蛾群里几乎没有合法路径,增加无效计算;过小的边界又容易让最优解落在边界上。实际调试时,我会先跑一轮纯随机采样,统计合法路径比例,如果低于10%就适当放宽z的下限或减少山峰高度。

关于地形公式,我常用的测试地形可以写成峰值函数叠加的形式。多峰地形看看算法能不能跳出来,陡峭地形看看路径会不会穿山,平缓地形看看收敛速度是否偏慢。这些场景可以在一套代码中通过改动地形参数快速切换。另外要提醒的是,地形矩阵的分辨率会影响碰撞检测的拟真程度,分辨率太高则计算量大,太低则路径可能在地形锯齿之间穿行。我通常把地图分辨率设为100乘100左右,平衡速度和精度。

2.2 路径编码与约束表达

路径规划里的决策变量是整条航线。为了平衡计算量和路径表达精度,一般不会把一连串密集航迹点都作为优化变量,而是选择少数关键航点,然后通过三次样条插值生成整条曲线。比如把路径定为8个节点,其中第1个是起飞点、最后一个是降落点,中间6个由算法优化,一只蛾的位置向量就是18维。

这只蛾的位置向量在代码中通常直接是一个18维的行向量。前6维对应中间节点的x坐标,中6维对应y坐标,后6维对应z坐标。更新位置后,把三段拆开,拼成节点矩阵,再做插值和平滑,最后采样N个路径点进行适应度评估。多个约束条件都放在适应度函数里,用惩罚项处理,而不是硬性剔除,目的是让不可行解保留在种群中提供代谢压力。

约束条件一般包括地形避让、飞行高度、转弯角度。地形避让最简单,采样点高度减地形高度如果小于安全余量,就累加惩罚;飞行高度上限对应禁飞区或能耗限制,高出上限同样惩罚;转弯角则通过路径点处前后两个向量夹角度量,角度突变就扣分。最终适应度设计成加权和形式,权重根据实际需求调整。

适应度函数的量纲需要注意。路径长度可能是几十到几百米量级,高度惩罚可能是每点几十的量纲,转弯角度是弧度量级。三者直接相加会导致某一部分主导适应度,其他约束形同虚设。我一般先对三项做无量纲处理,比如长度除以直线距离,高度惩罚除以安全余量平方,转角惩罚除以最大允许转角,再乘以各自权重。处理好量纲之后,适应度曲线的变化才会真实反映优化进度。

2.3 目标函数选择与权重调参经验

目标函数在文献里经常是一句话带过,但实际调权重能调出完全不同的路径。如果权重设置不当,可能出现路径长度很短但贴着山峰飞,或者路径极其绕远但飞行高度始终很高。我常用的初始权重是长度0.5、高度0.3、转角0.2。注意这个比例在不同地形下表现不同,建议每次换地形时先用几组权重做个快速对比。

这里可以分享一个快速实验方法:在算法迭代次数固定的前提下,把三组权重分别跑5次,记录平均路径长度和平均安全高度余量,做成一张小表,一眼就能看出权重分配是否合理。比如某个权重下平均余量接近零,就说明安全权重偏低;如果路径长度明显偏长,说明长度权重不够。这比单次运行后靠感觉调参要稳妥得多。

还有一种情况需要注意:当惩罚项过于严苛时,适应度值会趋向于很大的数量级,适应度曲线看起来就像一条竖直下降的线,难以区分优劣解。这时先不急着调整算法,把惩罚系数缩小到能让曲线呈现正常梯度再继续调试。

3. MATLAB实现核心步骤

3.1 参数配置与种群初始化

整段代码的核心入口是主函数。我习惯把所有可调参数集中在开头,方便反复测试。种群规模建议设50到80之间,迭代次数100到200,节点数可以根据地形复杂度增减。维度计算公式要写清楚,否则后面reshape时容易出错。一般计算维度就是中间节点数乘以三,同时再把边界数组和维度对应好。

初始化时要保证每个候选解落在合理区间。简单的做法是把每个维度的上下界作为数组传入,然后利用rand生成随机位置。这里有个容易踩坑的地方:如果z方向直接取到地形最大值以上,随机飞行高度过高,很多蛾就始终沿着高空飞,适应度虽不至于无穷大,但路径长度会虚高;如果z方向下界低于地形高度,又会产生大量穿地路径。我倾向于把z下界设成地形在该区域附近的最大值再加一个安全余量,这样初始化种群就天然满足部分约束。

角色划分按适应度排序后分段处理:排序第一区间是探路者,区间长度约为种群数量的15%;随后是勘探者,约40%;剩余为观察者。这个比例不是固定值,需要看目标函数复杂度。目标地形是多峰问题时,我常把探路者比例提到20%,让大范围搜索更充分。下面给出一组我常用的参数参考表:

参数名推荐范围备注
种群规模50-100过小覆盖不足,过大计算慢
迭代次数100-300看适应度曲线拐点
中间节点数6-10节点越多维度越高
探路者比例10%-20%多峰地形可调高
勘探者比例30%-50%定向收敛主力
观察者比例剩余部分局部精修

3.2 三类蛾的位置更新机制

探路者采用Levy飞行策略。Levy飞行本质上是具有重尾分布的随机游走,大多数步长很小,偶尔出现长距离跳跃。这个特性非常适合在搜索空间中探测新区域。实现上可以简化成利用Levy分布生成一个扰动向量,叠加到位置向量上,再对越界分量进行回拉。常见生成方式有两种,一种是用马特雅斯公式近似生成Levy步长,另一种是用幂律分布直接采样。工程上只要保证偶尔出现大跳跃,不要求严格符合数学定义。

勘探者的更新机制更接近经典螺旋飞行,以某只探路者的位置为核心,模拟蛾绕光源的对数螺旋线。代码层面就是先计算这个勘探者与选中的探路者的差值,再用螺旋系数控制收敛节奏。这里要注意螺旋系数的范围,系数太大会导致每一步都跨得太远,在大范围搜索和精细搜索之间来回震荡。我一般把螺旋系数设为随迭代次数递减的线性函数,从2降到0.5,前段保证探索能力,后段平滑收敛。

观察者则是围绕当前全局最优位置进行高斯采样,采样半径随迭代次数逐渐减小。这个机制可以理解为在最优解附近做精细打磨,相当于局部搜索算子。三种机制混合使用,既有一开始的大范围探索,又有后期的快速收敛,整体配合比较均衡。运行结束时会看到适应度曲线先快速下降,到后期趋于平缓,偶尔有阶梯形跳变。

代码结构可以参照下面这个框架:

function [bestPath, bestFitness, fitnessCurve] = MSA_main(...) % 参数设置 N = 60; T = 100; dim = ... % 初始化种群 positions = initPopulation(N, dim, lb, ub); % 迭代优化 for t = 1:T % 排序并划分角色 [values, idx] = sort(fitness(positions)); pathfinders = ... explorers = ... observers = ... % 按角色更新位置 positions(idx(1:n1),:) = updatePathfinder(...); positions(idx(n1+1:n1+n2),:) = updateExplorer(...); positions(idx(n1+n2+1:end),:) = updateObserver(...); % 越界处理 positions = boundaryCheck(positions, lb, ub); % 记录适应度 fitnessCurve(t) = min(values); end end

这段代码不是完整可运行版本,但它标出了MSA主循环的骨架。写代码时最容易出错的就是索引变换,因为排序后的索引和角色编号要一一对应。我的做法是先把原始索引保存在idx里,之后所有更新都用idx提取原始位置行号,避免后续处理时搞混。如果索引写错,适应度曲线会出现周期跳变,排查起来很麻烦。

3.3 路径插值、碰撞检测与可视化

坐标更新完之后,下一步是把离散节点变成可评价的连续路径。三次样条插值是最常用的方案,调用matlab的spline或者pchip都行。spline生成的曲线更光滑,适合评价路径长度;pchip更不容易过冲,适用于计算地形避让。实际使用中,如果地形起伏剧烈,我用spline生成的路径偶尔会穿山,改用pchip之后改善明显,这点很值得注意。

碰撞检测就是利用插值后密集采样,查询每个采样点的地标高程,并与飞行高度比较。如果飞行高度低于地形高程加安全距离,惩罚量就按超出部分的平方累加,让算法尽量避开山地。采样点数一般设为路径长度的函数,采样越密判断越精确,但计算量也越大,需要在精度和速度之间平衡。我在代码里通常设置为100到200个采样点,既能平滑地反映地形变化,又不会让每轮适应度计算拖慢整体速度。

可视化输出主要有两个:三维路径图和适应度收敛曲线。三维路径图要把地形网格、起点终点标记、规划出的路径都画出来,使用surf、plot3和scatter3分别绘制地形面、路径线和航点。适应度曲线用semilogy绘制效果更好,因为后期适应度下降幅度越来越小,线性坐标容易被压扁。这两个图就是代码运行后最常见的交付内容,也是评审最容易挑毛病的地方,值得多花点时间做得规范。

4. 实验结果输出与关键曲线解读

4.1 最短路径图如何才算合格

很多人拿到代码跑出来的路径图,只看到一条线贴着地形走,就以为完成了任务。实际操作中,合格的三维路径图至少应该体现四件事:路径起点终点明确、地形遮挡关系清楚、路径没有明显穿地、路径走向有明确避让逻辑。

我会在绘图时把视点调整到合适角度,让读者能同时看到路径和山峰的相对位置。用view函数设定方位角和俯仰角,比如view(45,30)。同时把路径颜色加深,航点用空心圆标出来。如果地形遮挡严重,还可以把地形面设置成半透明,这样路径在峰后的部分也能看见。这种细节在论文或汇报里会显得专业很多。

路径穿地问题是最常见的图形异常。如果看到路径中一段明显进入山峰内部,可以先判断是不是碰撞检测采样点太少,把采样点数翻倍再试。如果仍然穿地,就要检查插值方式,用pchip替代spline,或者在被穿位置增加一个优化节点,让算法有更多自由度绕开障碍。还有一个容易被忽略的原因:地形插值函数输入的坐标范围和路径点坐标范围不一致。比如地形网格定义在0到100,而路径点被初始化到50到150,那插值时就查不到正确高度,导致路径显示正常穿地。这个问题排查起来很隐蔽,建议先绘图把所有坐标范围显示出来对比一下。

4.2 适应度曲线的三段特征

适应度曲线是评价算法收敛质量的重要依据。MSA的曲线通常分为三个阶段:前10%迭代次数对应快速搜索阶段,适应度直线下降;中间阶段是稳定优化,曲线波动较小;最后阶段如果探路者发现新通道,会出现一次或几次“台阶式”下降,这是MSA区别于部分群体算法的特征。

如果整个曲线从一开始就非常缓慢下降,说明参数设置有问题。最常见的两种情况:种群规模过小导致覆盖不足,或者搜索边界设置过宽导致每代有效更新太少。如果曲线前段下降很快但后期完全平直,可能存在过收敛,可以增大探路者比例或者提高位置更新中的随机扰动强度。

用semilogy画曲线还有一个好处,就是能在对数尺度下看到收敛初期的剧烈下降过程。即使目标函数数量级差异很大,也能在一个图上展示完整的收敛过程。工程汇报时最好同时放线性坐标和半对数坐标两幅图,线性图展示量级,半对数图展示收敛速度。适应度曲线往往有几条对比线,比如不同参数下的MSA曲线、与MFO或PSO的对比曲线。颜色和图例不要用matlab默认的彩虹色,选两到三种区别明显的深色,看起来更专业。

4.3 从曲线反推算法调试方向

有些读者只看最终结果,中间曲线基本不看,这是比较可惜的。适应度曲线就是算法的运行日志,几乎所有参数问题都会先在曲线形态上反应出来。比如前期适应度值普遍偏高,就检查初始化是否合理,惩罚系数是否过重;中期曲线波动太大,就检查螺旋系数和高斯扰动半径是否衰减过快;后期曲线还在明显下降,就说明迭代次数不够,需要加大T。

我把这个思路用在实际调试中效果很好。有一次路径总是绕到地图边缘,我看了适应度曲线发现后期出现大阶跃,说明观察者高斯采样半径衰减太慢,导致最优解附近持续被大扰动破坏。把衰减系数调大之后,阶跃消失,路径也回到合理区域。所以积累一套“曲线形态对应问题”的经验,比盲目改参数效率高很多。

5. 参数调优与问题排查实录

5.1 关键参数对结果的影响

我在多次测试后形成了一套参数调试顺序,这里分享出来。优先调整的是迭代次数和种群规模,它们决定结果稳定性的上限。先把种群规模固定为60,迭代次数设为100,看看适应度曲线的趋势是否到了拐点。如果100代结束曲线仍在继续下降,就把迭代加大到200;如果50代以后明显平缓,说明不需要太多迭代。

其次是节点数量和插值方式。节点数过少时,路径自由度低,可能找不到绕开山峰的通道;节点数过多时,维度膨胀,同样种群规模下收敛变慢。我常用的做法是最初设6个中间节点,如果发现路径因为自由度不足而总是穿山或绕远路,增加到8个,再做一次测试。每增加一个节点,维度增加3,当节点数超过10之后,需要明显增大种群规模才能保持同样覆盖能力。

最后是适应度权重。路径长度权重、飞行高度限制、转弯角惩罚这三个权重要根据任务场景调整。如果追求航程最短,长度权重可以高一些;如果追求安全性,把最低高度惩罚设得更重。我这里给出一组我常用的初始权重,长度0.5,高度0.3,转角0.2,对多数仿真地形都有不错效果。换地形后建议再跑一轮随机采样,用统计结果确认权重是否合适。

5.2 常见运行错误与调试记录

第一类错误是维度不匹配。很多读者反馈代码报错说维度超出索引范围,多半是节点数改动后没有同步修改初始化上下界数组和适应度函数里的索引。我建议把节点数定义成单个变量,所有涉及维度的地方都用这个变量派生,不要硬编码数字。

第二类错误是适应度全为inf。这种情况通常是因为惩罚太大,初始随机种群的所有个体都严重穿地或超高超限。解决方法是先降低惩罚系数,让部分较差的个体有限值可用;或者修改初始化策略,让z坐标直接取地形高程以上某个范围,使初始解更多落在可行区域。

第三类错误是曲线震荡不收敛。开头的震荡往往是因为步长过大。可以尝试把螺旋系数从固定值改成随迭代递减的值,前段探索、后段开发。观察者采样半径也需要随迭代递减,否则后期最优位置附近被扰动过度,曲线一直抖动。

我把这些常见问题整理成一张速查表:

问题现象可能原因解决思路
维度索引报错节点数与上下界长度不一致用变量统一管理节点数并派生维度
适应度全为inf惩罚过大或初始化边界不合理降低惩罚系数或调整z方向初始化范围
路径穿地插值方式不合适或采样点太少改用pchip,增加密集采样点数
曲线震荡不收敛螺旋系数或高斯半径衰减过慢改成随迭代递减的线性或指数衰减
收敛过早探路者比例偏低提高探路者比例或增大扰动强度
路径绕远长度权重偏低或地形边界过宽调整适应度权重,压缩搜索边界

5.3 一个完整调试案例

为了让读者更有感觉,我描述一个实际遇到的案例。某个模拟地形有两座高度接近的山峰,初始参数下MSA一直走两山之间的低谷通道,适应度卡在一个平台期不再下降。我把探路者比例从15%调到20%,再跑一轮,发现第70代左右曲线出现一个明显台阶,最终路径绕到了左侧山峰外的平缓走廊。这说明地形中的可行通道不止一条,算法能否发现更优通道,很大程度上取决于探路者的扰动能力。

这个案例也说明一个问题:不是每次调参都能立刻看到更好结果。在另一组地形下,同样提高探路者比例反而让中期收敛变慢,最终结果变差。因为我所用的地形通道非常狭窄,过强的全局扰动会频繁打断勘探者沿通道收敛的过程。所以调参本质上是一个针对当前地形反复试错的过程,没有一套参数能普适用于所有场景。

写到这里,我对MSA在无人机三维路径规划上的应用基本梳理完了。最后再分享一点个人体会:这种群智能算法项目,代码能跑只是第一步,真正值钱的是对算法行为和参数关系的理解。建议你拿到源码后先逐个函数读一遍,用fprintf在关键位置输出中间变量,亲自感受三类蛾在每一代里的运动轨迹。连续调试几次之后,你就不会再对着一个黑箱代码无从下手了。

如果还想继续扩展,可以从两个方向入手。一是给路径规划加入威胁区域模型,把雷达探测范围或禁飞区建模成球体或柱体,放到适应度惩罚函数里,算法自然会绕开这些区域。二是改成多无人机协同场景,多架机从不同起点飞往不同目标点,MSA的三类角色可以分别负责不同机型的航线搜索,这种框架在毕业设计里也很加分。总之,先把单机的MSA路径规划吃透,后面扩展就会顺很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询