☰
基于蛾群优化算法MSA的无人机三维路径规划MATLAB实现详解
2026/10/22 11:56:55 网站建设 项目流程

做无人机三维路径规划时,很多人的第一反应是上 A*、RRT 这些经典搜索算法。可真拿到一个复杂三维环境,比如山地地形、城市建筑群、混合威胁区,这类方法要么栅格化之后计算量直接爆炸,要么规划出来的路径绕行严重、拐角生硬。后来我换成蛾群优化算法(MSA)来做这件事,在 MATLAB 里完整实现了一遍,最终输出最短路径图和适应度曲线,整个流程稳定,效果也让我比较满意。这篇文章就把这套基于 MSA 的无人机三维路径规划实现从头讲透,包括问题建模、算法原理、源码结构和调试心得,给做航迹规划或者群体智能算法仿真的朋友一个可以直接参考的完整方案。

这套代码的核心思路并不复杂:把路径规划看成一个高维连续优化问题,用 MSA 在三维空间中搜索一条从起点到终点、避开所有威胁区域、且总长度最短的航迹。它特别适合在 MATLAB 里快速验证算法思路,不需要额外工具箱,也适合做课程设计、毕业设计和算法对比实验时当基线方案使用。无论你是刚接触群体智能算法的初学者,还是已经跑过 PSO、遗传算法、鲸鱼算法但想换一种算法试试的开发者,这篇内容都能帮你省下不少弯路。

1. 无人机三维路径规划建模:目标函数、约束与编码方式

1.1 三维路径规划到底难在哪

很多人觉得路径规划就是找一条最短路线,二维地图上确实可以这么理解,但把问题放到三维空间之后,难度会明显上升。第一个难点是搜索空间变成了连续的高维空间:假设我们要规划一条经过 10 个中间航点的路径,每个航点有 x、y、z 三个坐标,优化变量就有 30 个维度。粒子群、遗传算法这类群体智能算法恰恰擅长处理这种连续高维问题,不需要把地图离散成栅格,也不用担心栅格分辨率选择困难。

第二个难点是约束条件多而且相互耦合。无人机路径至少要同时满足几个条件:路径总长度尽量短、飞行高度保持在合理区间、不能穿越山体或威胁区域、转弯不能太猛否则无人机动力学跟不上。这些问题互相影响,比如你想让路径短,可能就会贴着威胁区域边缘走,此时安全余量变小;你想让路径绝对安全,绕行距离又会大幅增加。加权目标函数就是在这种矛盾中找平衡。

第三个难点是环境的不确定性。很多实际场景中威胁区域的位置、地形高度都有动态变化,但静态环境下先把这个基础问题解决好,后续扩展动态避障、多机协同都会轻松很多。所以我现在做三维路径规划,第一步永远是先把静态环境下的建模和优化跑通。

1.2 目标函数怎么设计才算合理

MSA 在迭代过程中,靠目标函数值来评估一条路径的好坏,目标函数设计是否合理,直接决定了算法能不能收敛到一条既安全又经济的航迹。我常用的目标函数是一个加权和,包含以下几部分:

路径长度代价。把路径拆成相邻节点之间的欧氏距离累加,这是最基础的部分。长度代价函数如下:

L = sum( sqrt((x(i+1)-x(i))^2 + (y(i+1)-y(i))^2 + (z(i+1)-z(i))^2) )

威胁代价。对每个路径点,计算它到所有障碍物或威胁区域中心的距离,如果距离小于安全半径,就根据接近程度施加惩罚。惩罚值可以用一个反比函数来表达:

T = sum( threatCoef / (dist - safeRadius + eps) )

当路径点靠近威胁边界时,T 会迅速增大,迫使算法把航迹推离危险区域。这个系数是路径规划实现里的关键参数之一,调小会让路径贴近威胁边缘甚至穿模,调大又会让路径过度绕行。

高度代价。无人机飞行高度太高会增加能耗,太低又容易撞到地面障碍。所以我会设定一个高度区间 [zMin, zMax],超出区间就按超出量惩罚。再叠加一个高度变化惩罚,防止相邻航点间爬升或下降角度太陡。

平滑代价。路径如果折点过多,无人机在实际飞行中需要频繁转向,基本没法用。常见的做法是在目标函数中加入相邻线段夹角相关的惩罚项,夹角越小、过渡越平缓,代价越低。也可以先不加平滑项,跑完后再用 B 样条曲线做后处理平滑,两种方案我都用过,各有优劣。

最终的适应度函数可以写成:

fitness = w1 * L + w2 * T + w3 * H + w4 * S

其中 w1、w2、w3、w4 是权重系数。到这里你会发现,路径规划问题已经抽象成“给定一组航点坐标,计算一个适应度值”,剩下的就是让 MSA 反复调整航点坐标,去寻找让 fitness 最小的那组解。

1.3 路径是怎么编码给算法的

编码方式直接决定了算法搜索效率。我用的是航点序列编码:起点 P0 和终点 Pend 固定不动,中间插入 N 个可自由移动的航点,每个航点用三维坐标表示。于是每个蛾子的位置就是一个长度为 3N 的向量,对应一条完整路径。

举个例子,如果 N=10,起点是 (0, 0, 10),终点是 (100, 100, 50),那么一个候选解就是 10 个中间点的 x、y、z 坐标,共 30 个数值。算法每次更新这个向量,就相当于同时调整 10 个航点的位置,适应度函数随之重新计算。

为什么用航点序列而不是直接对整条连续曲线编码?因为连续曲线参数化非常复杂,不便于用群体智能算法直接操作。航点序列把无限维的曲线搜索问题降维成有限维的数值优化问题,这是工程上最实用的做法。航点数量 N 也不要太大,我一般取 8 到 15 个,太少路径自由度不够、难以绕开复杂障碍,太多则维度膨胀、收敛变慢。N 取 10 左右时,在复杂度和灵活性之间平衡得最好。

2. 蛾群优化算法 MSA 原理拆解:三种飞蛾角色与搜索机制

2.1 飞蛾行为给了算法什么灵感

蛾群优化算法(MSA,Moth Swarm Algorithm)是近年提出的群体智能优化算法,灵感来源于飞蛾的夜间导航行为。飞蛾在自然环境中会利用月光进行横向定位,飞行方向与月光保持固定角度,这样就能沿直线飞行;但是当它们遇到蜡烛、路灯这类人造光源时,就会被人造光吸引,沿着一条螺旋轨迹不断逼近光源,甚至最终撞上去。这种“螺旋逼近”正是算法的核心运动模式。

在优化问题里,光源可以被理解为“更优解所在的位置”。月光代表当前种群发现的全局最优位置,人造光源代表局部区域的较优位置。飞蛾向光源靠近,就是候选解向当前已知的好解靠近。人造光源的“陷阱效应”对应早熟收敛的风险,所以算法不能只让所有蛾子都扑向同一个光源,必须设计多角色分工来保持探索能力。

MSA 之所以比早期出现的蛾焰优化算法(MFO)更适应复杂工程问题,关键优势在于它把蛾群分出了不同工种,而不是所有个体都用同一种螺旋更新策略。这种分工机制让算法同时兼顾全局搜索和局部开发,避免陷入局部最优。

2.2 三种飞蛾角色如何分工协作

MSA 将种群分成三类角色,每类角色负责不同的搜索行为。

第一类是探索者蛾(Explorer Moth),数量占比通常较小。它们主要负责全局探索,位置更新时会叠加 Lévy 飞行机制。Lévy 飞行是一种短距离往返、偶尔长距离跳跃的随机行走模式,它的特点是步长分布服从重尾分布,能够以小概率产生很大的跳跃。这种长距离跳跃让探索者蛾有机会跳出当前收敛区域,去搜索整个空间中的其他潜在优质区域。探索者蛾发现更优位置后,会更新当前的“月光位置”,也就是全局最优解。

第二类是观向者蛾(Observer Moth),数量占比最大。它们沿着螺旋轨迹向光源逼近,光源既可以是全局最优的月光,也可以是当前较优的局部火光。螺旋更新公式本质上是在当前解与光源之间插入一个按螺旋收缩的偏移量,让个体在靠近光源的同时保留下一次迭代的移动能力。观向者蛾承担了主要的局部开发任务,负责在优质解附近精细化搜索。

第三类是徘徊者蛾(Hoverer Moth),在光源附近做小范围漂移。它们不像观向者那样大幅螺旋飞行,而是在已有解周围施加小幅扰动,用于进一步细化路径质量。这类角色数量不多,但作用很关键:很多高精度收敛都需要靠这些小扰动把解“磨”到更优。

三种角色的比例并不是固定不变的。我常用的比例是探索者占 20%、观向者占 60%、徘徊者占 20%。这个比例可以根据问题复杂度动态调整:地形越复杂、局部最优越多,探索者比例就应该适当提高;地形简单、希望快速收敛,则提高观向者比例。

2.3 MSA 与 MFO 的核心差异

很多资料里 MSA 和 MFO 容易混在一起,实际使用中两者差异很明显。为了让读者看清楚,我整理了一个对比表:

对比维度MFO(蛾焰优化算法)MSA(蛾群优化算法)
种群结构所有飞蛾角色一致探索者、观向者、徘徊者三分工
更新机制统一向火焰做螺旋逼近Lévy 飞行 + 螺旋逼近 + 局部漂移
探索-开发平衡靠火焰数量递减来间接平衡靠角色分工天然平衡
全局搜索能力相对偏弱更强,长距离跳跃避免早熟
工程约束问题适配一般较好,适合带复杂约束的路径规划
实现复杂度较低中等,但代码结构清晰后并不难

MFO 的思路更接近单兵作战,所有飞蛾都冲向火焰,火焰数量随迭代减少来控制开发程度。MSA 则更像团队作战,有专门负责探路的探索者,有负责包围的观向者,还有负责清理细节的徘徊者。在实际的三维路径规划任务里,MSA 的团队分工优势很明显,因为目标函数存在大量局部最优,单纯靠火焰数量递减很容易被一个局部凹坑困住。

3. MATLAB 源码实现流程:核心代码与参数调试

3.1 代码整体结构与运行流程

一个完整的 MATLAB 工程包含主脚本、地图构建模块、适应度函数、MSA 主体迭代和结果绘图五个部分。整体流程可以用下面的伪代码概括:

1. 初始化参数:地图尺寸、起点终点、航点数、种群规模、最大迭代次数 2. 生成三维地图:地形曲面 + 威胁区域 3. 初始化蛾群:随机生成 N 个候选路径,计算适应度 4. 迭代循环: 4.1 按适应度排序种群 4.2 划分探索者、观向者、徘徊者 4.3 探索者执行 Lévy 飞行 + 螺旋搜索 4.4 观向者向光源螺旋逼近 4.5 徘徊者在光源附近漂移 4.6 边界检查、更新全局最优 5. 绘制最短路径三维图和适应度收敛曲线

代码文件我建议分成几个函数来写,主脚本只负责参数设置和画图,这样调参时不会把代码理顺的过程搞乱。主函数命名可以就叫MSA_3DPathPlanning,返回最优路径、最优适应度和适应度历史曲线。

3.2 关键代码段讲解

先看地图生成部分。我常用高斯型山峰来模拟起伏地形,再加上若干个球形威胁区域。地形曲面的生成逻辑如下:

function [terrainHeight] = generateTerrain(mapSize, peakCenters) [X, Y] = meshgrid(1:mapSize, 1:mapSize); terrainHeight = zeros(mapSize, mapSize); for k = 1:size(peakCenters, 1) cx = peakCenters(k, 1); cy = peakCenters(k, 2); peak = peakCenters(k, 3); terrainHeight = terrainHeight + ... peak * exp(-((X - cx).^2 + (Y - cy).^2) / (2 * peakCenters(k, 4)^2)); end end

威胁区域则可以简化成若干个球体或圆柱体,每个威胁中心有位置坐标、半径和威胁强度。碰撞检测时计算路径点到球心的距离。

适应度函数是整个实现的关键。我习惯把路径长度、威胁惩罚、高度惩罚、平滑惩罚都放在一个函数里,输入是航点坐标向量,输出是一个标量适应度值。核心部分如下:

function [cost] = fitnessFunction(waypoints, startPt, endPt, threats, params) pts = [startPt; reshape(waypoints, [], 3); endPt]; % 路径长度 diffMat = diff(pts, 1, 1); segLen = sqrt(sum(diffMat.^2, 2)); cost = params.w1 * sum(segLen); % 威胁惩罚 for i = 1:size(pts, 1) for j = 1:size(threats, 1) dist = norm(pts(i, :) - threats(j, 1:3)); if dist < threats(j, 4) cost = cost + params.w2 * (threats(j, 4) - dist + 1); end end end % 高度惩罚 z = pts(:, 3); cost = cost + params.w3 * sum(max(0, params.zMin - z) + max(0, z - params.zMax)); end

这段代码里我故意把威胁惩罚写成“如果路径点进入威胁半径,就直接加一个大额惩罚值”,这是最简单粗暴但有效的办法。更精细的做法是把惩罚设计成连续函数,越靠近中心惩罚越重,这样算法能获得更平滑的梯度信息,收敛也更稳定。我实际工程中用的是反比式惩罚,效果最好。

再看 MSA 主循环中的三种更新。探索者蛾的 Lévy 飞行步长生成,我用经典的 Mantegna 方法,步长公式包含尺度参数和指数参数,实际实现时生成一个随机步长向量后乘到当前位置偏移上。观向者蛾的螺旋更新可以简化成:

r = spiralFactor * rand(); newPos = observerPos + r .* (lightPos - observerPos);

其中lightPos是光源位置,spiralFactor是螺旋收缩系数,控制每次靠近光源的幅度。徘徊者蛾的局部漂移则是一组小幅度高斯扰动:

newPos = hoverPos + 0.01 * randn(size(hoverPos));

当然,真正的 MSA 公式细节更多,工程里我会保留核心机制、简化次要参数,保证代码可读性和运行效率。只要核心的运动逻辑与角色比例在,算法依然能保持很强的搜索能力。

3.3 参数设置和调试建议

运行环境方面,MATLAB R2016b 以上版本都可以直接运行,不需要额外工具箱,只需要基础绘图函数和矩阵运算。我实测下来,地图大小取 50×50×50 坐标范围时,种群规模 40、最大迭代 200 次,单次运行时间在几分钟以内,完全可接受。

下面是一组我调试后比较稳的参数,直接照抄也能跑出不错的收敛效果:

参数名称推荐取值作用说明
种群规模40太小易早熟,太大计算量大
最大迭代次数200根据地图复杂程度动态调整
中间航点数10平衡路径自由度和收敛速度
探索者比例0.2地形复杂时可提高到 0.3
徘徊者比例0.2用于局部精细搜索
螺旋收缩系数0.5~1.0控制观向者逼近速度
威胁惩罚权重 w2100偏低会导致穿障,偏高绕行严重
高度惩罚权重 w310保持巡航高度区间的力度

第一次调参时不需要追求一次到位,先固定种群和迭代次数,单独观察威胁惩罚权重对路径的影响。我习惯的做法是:先用一个较保守的威胁惩罚值跑通流程,确认路径不撞障碍后,再逐步减小威胁权重,观察路径是否会变得更加直、更短。这样能理解每个参数对结果的影响方向,比盲调效率高得多。

4. 输出结果分析:最短路径图和适应度曲线怎么看

4.1 最短路径图的信息解读

程序运行结束后会输出两个核心图。第一张图是三维最短路径图,通常用plot3绘制,起点和终点用特殊标记标出,中间用折线或者平滑曲线连接。同时把地形曲面和威胁区域一起画出来,可以直观看到路径是否绕开了所有障碍。

实际观察时我会按三个顺序判断路径质量:第一,每一个威胁区域是否都在安全距离之外;第二,路径高度是否始终保持在约束区间内,有没有贴着地面飞的段落;第三,路径整体是否顺畅,有没有不必要的往返折线。

如果路径穿过了威胁区域,优先考虑的不是马上调大威胁惩罚系数,而是检查威胁区域是否定义过大、路径点数量是否不足。有时航点太少导致路径段跨度太长,即使中间点了没有穿障,但两个航点之间的连线直接切过了威胁边缘。解决方法是增加航点数量,或者在路径段中间插入采样点进行碰撞检测,而不是单纯加大惩罚。

如果路径虽然安全但绕得太远,可以把威胁惩罚系数适当降低一些,让算法敢于从威胁区域的间隙穿过。这需要反复尝试,没有一套万能的参数。

4.2 适应度曲线背后的收敛信号

第二张图是适应度曲线,横轴是迭代次数,纵轴是最优适应度值或者平均适应度值。一条健康的收敛曲线通常有比较明显的区分段:前几十次迭代曲线快速下降,说明探索者蛾在跳跃搜索中发现了明显更优的区域;随后下降速度放缓,曲线趋于平滑,说明观向者蛾和徘徊者蛾正在精细开发当前最优解附近的空间。

如果适应度曲线从一开始就几乎不下降,说明初始种群质量太差或者 Lévy 飞行的跳跃步长过大,搜索无机可寻。这时候可以尝试让种群初始化更贴近合理路径,比如把初始航点随机范围限制在起点终点连线附近,而不是在整个地图空间随机撒点。

如果曲线在迭代后期仍然剧烈波动,说明探索者比例过大或者徘徊者扰动幅度偏大,算法处在“刚找到一个好解又被扰动破坏”的状态。需要降低探索者比例或者缩小漂移幅度。

如果曲线中后期长期水平不再变化,不一定是坏事,也可能已经收敛到当前条件下的最优解。需要确认的是这个平台值是否对应一条安全且合理的路径,如果路径明显不理想,就说明陷入局部最优,需要重新运行或调整随机种子。

5. 常见问题、排查方法与实操建议

5.1 路径穿过障碍物的排查步骤

遇到这个问题,先不要急着调参。按下面几步排查:

先检查碰撞检测是否覆盖了整条路径段,而不仅仅是路径点。我早期踩过这个坑:航点都绕开了威胁中心,但相邻航点之间的直线段直接从威胁区域边缘切进去。解决办法是每段路径上等间隔采样几个插值点,用这些插值点做碰撞检测。

再检查威胁半径是否偏大。如果威胁区域本身建模就超出了真实风险范围,算法只能绕一个大圈,也会导致路径看起来不合理。

最后再调整威胁惩罚权重。建议以 50 为步长逐步增加,每次增加后观察路径形态变化,找到“刚好避开威胁但不过度绕行”的临界点。

5.2 收敛过快或陷入局部最优

三维路径规划的目标函数存在大量局部极小点,稍微复杂的地形就可能让算法陷入“看起来安全但绕行过长”的局部路径。解决手段有三个:

第一,提高探索者蛾占比,从 0.2 提到 0.3,强化全局搜索能力。第二,增大 Lévy 飞行步长的尺度参数,让长距离跳跃更频繁,帮助种群从局部凹坑中跳出来。第三,使用多起点初始化,用不同的随机种子生成多个初始种群,分别运行后取最优结果。多次独立运行能显著降低坏运气带来的影响。

5.3 路径折点多、不做平滑没法用

智能优化算法输出的原始路径通常是一系列折线,直接用于无人机飞行指令时,拐弯处非常突兀。我常用的做法是后处理:用 B 样条曲线对航点序列做平滑,让路径变成连续光滑曲线,再将曲线重新离散成无人机可执行的航点序列。也可以在目标函数中加入平滑代价项,让算法在优化阶段就直接降低相邻线段夹角,两种方法配合使用效果最好。

如果只想快速验证,可以先用普通的三次样条插值做平滑,不需要引入复杂曲线拟合工具。要注意的是平滑后的路径必须重新做一次威胁碰撞检测,因为平滑过程可能让曲线局部偏移进入威胁区域。

5.4 运行时间太长的优化思路

如果地图范围大、航点数多,运行时间会明显上升。这时优先减少种群规模,从 40 降到 30,通常对结果影响不大;再考虑降低迭代次数到 120 左右,观察适应度曲线是否已经在 100 次以内进入平台期。若已进入平台期,继续迭代只是浪费算力。

另一个有效做法是加入提前终止条件:当最优适应度在连续 N 代内的改进量小于某个阈值就停止迭代。这个策略在调试阶段尤其好用,能减少大量不必要的等待时间。我一般设为连续 15 代改进量小于 1e-3 即停止。

最后的实操体会

这套 MSA 三维路径规划代码我从建模到调试跑了很多次,总体感受是:算法的探索-开发平衡能力比传统 PSO 和 MFO 更适合这种带复杂约束的工程问题,尤其是三段角色分工,让路径规划在“绕开危险”和“路径最短”这两个目标之间找平衡时明显更从容。如果后续你想做得更深入,可以把静态地图换成真实数字高程模型,或者把威胁区域改成移动目标,再配合实时重规划模块,就是一个相当完整的无人机任务规划原型了。

最后再分享一个小技巧:当你觉得规划出的路径绕行太远、并不理想时,别急着认为是算法能力不够,先检查一下威胁惩罚权重和航点数量。很多时候只是威胁惩罚设得过大,让算法“不敢”走那些明明有安全间隙的近路。稍微调低一点,路径会给你很大的惊喜。

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

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

立即咨询