
简介面向无人机航迹规划与智能优化算法研究者的Matlab实现方案针对山地复杂地形下的三维航迹生成问题采用粒子群算法在三维解空间内搜索满足地形规避与飞行安全的最优路径涵盖从环境建模、适应度设计到迭代优化、结果可视化的完整流程能根据任务需求自动生成可飞行航迹兼顾算法效率与实现难度。压缩包共含7个文件包括4个Matlab脚本、2张运行效果图和1份算法原理介绍PPT整体仅1.11MB轻量紧凑便于直接运行和二次开发。其中环境地图定义、粒子群核心优化、路径代价计算与图形绘制等模块分工清晰配合结果图和PPT讲解可快速理解PSO的寻优机制及群体协作搜索原理并掌握参数调整、障碍规避与航迹分析方法通过对比不同参数下的结果图可直观评估规划质量。已有139人学习下载适合开展无人机三维航迹仿真、算法对比验证或相关课程实验的本科生与工程技术人员。1. 无人机山地三维路径规划为什么选粒子群算法这套 Matlab 资源让无人机在一片连续起伏的山地中从起点飞到终点二维路径规划的思路基本用不上——山峰不是一个个“障碍物”而是连续的高度场路径既要绕过山体又要尽量短、尽量平滑还得保持安全离地高度。粒子群算法Particle Swarm Optimization, PSO在这类问题上很实用它不依赖梯度信息把一条候选路径看成解空间里的一个粒子靠群体协作迭代逼近最优解。这套资源用 Matlab 实现了完整流程——定义地形、PSO 寻优、适应度评估、三维可视化正好覆盖从算法原理到工程复现的整条链路。适合正在做无人机路径规划课程设计、毕业设计或者想快速验证 PSO 在山地场景下效果的从业者。2. 从问题到模型三维路径规划到底在优化什么2.1 二维规划失效的场景山地环境建模的特殊性二维路径规划通常把障碍物抽象成二维多边形路径是一条平面折线。但山地环境下地形是连续起伏的高度场无人机在三维空间中的每个点 (x, y) 都对应一个地形高度 h(x, y)。路径不再是“绕过障碍物”而是“贴着地形起伏找到一条安全走廊”。这里的关键转变是路径的可行域由地形高度场决定。路径上每个航点的高度不能低于对应位置的地形高度加上安全离地高度比如 30~50 米否则就穿山了。同时高度也不能太高否则虽然安全但路径长度和能耗都会上去——山地任务往往要求低空飞行高度过高还会丧失地形遮蔽优势。地形建模的常见做法是用网格采样 插值。把规划区域划分成均匀网格每个网格点存储一个高度值路径规划时用双线性插值获取任意位置的地形高度。顺带说一句真实工程里地形数据来自 DEM数字高程模型课程设计里一般用解析函数生成模拟山地就够了。2.2 PSO 算法的核心迭代逻辑与路径编码方式粒子群算法的基本思想是模拟鸟群觅食一群鸟在解空间里搜索食物每只鸟既根据自身经验个体最优 pbest调整飞行方向也参考群体经验全局最优 gbest。无人机路径规划中“一只鸟”就是“一条完整路径”。路径编码是这套资源里最值得先看懂的部分。一条路径由 N 个航点组成每个航点有三维坐标于是整条路径可以表示成一个 N×3 的矩阵。粒子群算法中粒子的位置就是这个矩阵展开后的向量——每个粒子代表一条候选路径粒子的“速度”则是各航点坐标的偏移量。迭代时每个航点坐标按以下公式更新% 粒子速度更新惯性 个体认知 社会认知 v(i, :) w * v(i, :) ... c1 * rand(1, dim) .* (pbest(i, :) - x(i, :)) ... c2 * rand(1, dim) .* (gbest - x(i, :)); % 位置更新速度作用到航点坐标上 x(i, :) x(i, :) v(i, :);w是惯性权重控制粒子保持原有运动趋势的程度c1和c2是学习因子分别控制粒子向自身最优和群体最优学习的强度。rand(1, dim)生成随机扰动保证搜索的多样性。dim是路径向量的总维度等于航点数量乘以 3。这种编码方式的巧妙之处在于航点与航点之间的连线就是实际飞行路径粒子迭代过程中每个航点在三维空间中独立移动整条路径随之变形。相比直接对整条路径做交叉变异遗传算法思路PSO 的航点级更新更精细收敛也更快。代价是航点数量需要预先固定航点太少路径不够灵活航点太多搜索维度爆炸。2.3 适应度函数路径优劣如何量化适应度函数是整条路径规划链路里最需要抠细节的部分。粒子群算法本身只负责“搜索”至于搜索目标是什么完全由适应度函数决定。这套资源里的calFitness.m就是干这个的。一个工程上可用的适应度函数通常包含四项路径长度代价、安全高度代价、威胁规避代价、平滑度代价。总适应度是各项的加权和权重直接决定规划结果的行为倾向。% calFitness.m 核心逻辑伪代码结构实际为 Matlab 实现 % 输入path 航点矩阵 (N x 3)输出适应度值 fitness % 1. 路径长度代价对每段航线段求欧氏距离并累加 % 2. 高度代价航点高度与地形高度 安全高度的差值低于安全高度则惩罚 % 3. 威胁代价航点与山峰中心的水平距离过近则惩罚山地场景可选 % 4. 平滑代价相邻三段航向的夹角变化量角度突变则惩罚 totalCost w_len * pathLength w_height * heightViolation ... w_smooth * smoothnessCost w_threat * threatCost; fitness totalCost; % PSO 整体求最小值路径长度代价很好理解——飞行距离越短任务完成越快、能耗越低。高度代价的核心是“贴地飞行”路径高度必须高于地形高度加安全余量但又不能高得离谱否则偏离低空飞行意图。平滑代价约束航向角变化率防止路径出现直角转弯或折返这直接关系到固定翼或多旋翼无人机的可飞性。权重配比没有标准答案但有一条经验值长度权重给 1 作为基准高度惩罚权重给 3~8因为穿山是硬约束必须罚狠平滑权重给 1~2软约束只用来抑制过大的角度突变。这个比例可以先用默认跑一遍观察路径形态再微调比从零调参快得多。3. 源码拆解四个 .m 文件各自负责什么3.1 defMap.m地形地图的定义方式defMap.m负责生成无人机飞行的三维环境模型。从文件命名看它至少输出了一个地形高度矩阵map以及规划区域的边界参数。常见做法是先生成二维网格再用若干个高斯峰函数叠加模拟山脉连绵起伏的效果% defMap.m —— 生成模拟山地地形 function [map, X, Y] defMap(mapSize, peakNum) % 输入mapSize 地图边长网格数peakNum 山峰数量 % 输出map 地形高度矩阵X/Y 网格坐标 x linspace(0, mapSize, mapSize); y linspace(0, mapSize, mapSize); [X, Y] meshgrid(x, y); map zeros(mapSize, mapSize); % 叠加多个高斯峰模拟山体 rng(3); % 固定随机种子让地形可复现 for k 1:peakNum cx mapSize * rand; % 山峰中心 x 坐标 cy mapSize * rand; % 山峰中心 y 坐标 sigma mapSize * (0.15 0.2 * rand); % 山峰宽度 h mapSize * (0.3 0.5 * rand); % 山峰高度 map map h * exp(-((X - cx).^2 (Y - cy).^2) / (2 * sigma^2)); end % 拉高边缘避免路径从地图边界绕行 map(:, 1) map(:, 1) 500; map(:, end) map(:, end) 500; map(1, :) map(1, :) 500; map(end, :) map(end, :) 500; end这个实现里有两个细节值得注意。第一rng(3)固定了随机种子保证每次运行生成相同地形——这个习惯对调试非常友好不然每次跑地形都不一样路径对比就失去了基准。第二把地图四周边界拉高 500 米目的是人为制造“围墙”防止粒子群算法绕行地图之外因为边界外是没有地形定义的路径穿出边界会直接越界报错。等高线越密的地方坡度越陡规划出来的路径如果在这个区域高度变化过猛后面平滑代价就会很高。这是后续调参时回看地形图的参考信息。实际运行时defMap.m还会附带输出起终点坐标起终点一般选在地势较低的位置便于路径有上升和下降的过渡空间。3.2 calFitness.m穿山检测与路径代价计算calFitness.m的核心不只是求和拆开看它包含三个关键处理插值取地形高度、穿山判断、分段累加。我之前见过不少人在适应度函数里只算“航点是否穿山”这是不够的——即使每个航点都不穿山航点与航点之间的直线段仍然可能从山体中间穿过。尤其在山峰窄、航点间距大的时候这种漏检很容易翻车。% calFitness.m —— 完整适应度计算 function fitness calFitness(path, map, X, Y, safeH, mapSize) % 输入path 航点矩阵(Nx3)map 地形高度矩阵 % X/Y 网格坐标safeH 安全离地高度mapSize 地图尺寸 % 输出fitness 总代价 % --- 1. 分段插值将每段路径细分为 interpN 个小段 --- interpN 20; % 每段路径再细分 20 个点 lenCost 0; heightCost 0; smoothCost 0; for i 1:size(path, 1) - 1 % 取第 i 段路径的起点和终点 p1 path(i, :); p2 path(i1, :); % 细分点坐标线性插值 segX linspace(p1(1), p2(1), interpN); segY linspace(p1(2), p2(2), interpN); segZ linspace(p1(3), p2(3), interpN); % 逐点检查是否低于地形高度 for j 1:interpN % interp2 是 Matlab 自带二维插值函数 groundH interp2(X, Y, map, segX(j), segY(j), linear); if segZ(j) groundH safeH heightCost heightCost groundH safeH - segZ(j); end end % 累加本段路径长度用细分后的折线长度近似曲线长度 for j 1:interpN-1 dx segX(j1) - segX(j); dy segY(j1) - segY(j); dz segZ(j1) - segZ(j); lenCost lenCost sqrt(dx^2 dy^2 dz^2); end end % --- 2. 平滑代价用航向夹角惩罚 --- for i 2:size(path, 1) - 1 v1 path(i, :) - path(i-1, :); v2 path(i1, :) - path(i, :); cosAngle dot(v1, v2) / (norm(v1) * norm(v2) eps); smoothCost smoothCost (1 - cosAngle); % 夹角越大代价越高 end % --- 3. 加权求和 --- w_len 1.0; w_height 5.0; w_smooth 1.5; fitness w_len * lenCost w_height * heightCost w_smooth * smoothCost; end这段代码里最核心的细节是interp2插值。地形高度矩阵map是离散网格点上的值interp2可以在任意坐标处插出连续高度。如果不插值、直接取最近网格点会造成地形高度“阶梯跳变”路径规划会在台阶处来回抖动穿山检测也不准确。heightCost用了“低于安全高度多少就罚多少”的线性惩罚而不是一次性重罚。这样设计的好处是给粒子提供了梯度信息离地面越近的路径代价越大粒子在迭代过程中会逐渐被“推”向更高处。如果写成穿山就罚固定值比如 10000粒子收到的是一个常量惩罚无法判断往哪个方向飞能降低代价搜索效率会大打折扣。eps加在分母上是为了防止v1或v2长度为零时除零报错——两个相邻航点重合时就会触发这种情况。这是仿真代码里容易被忽略、却最常见的运行时崩溃原因。3.3 PSO.m种群初始化、迭代更新与边界处理PSO.m是整个资源的发动机舱。它负责的事情有五件初始化粒子群、计算初始适应度、迭代更新速度与位置、边界约束、记录最优解。% PSO.m —— 粒子群算法主程序 % 参数设置 popSize 50; % 种群规模 maxIter 200; % 最大迭代次数 wMax 0.9; wMin 0.4; % 惯性权重范围线性递减 c1 1.5; c2 1.5; % 学习因子 dim 3 * numWaypoints; % 每个粒子的维度航点数 x 3 % 初始化粒子群每个粒子是一条随机路径 x zeros(popSize, dim); v zeros(popSize, dim); % 给每个航点生成随机坐标在地图范围内 for i 1:popSize for k 1:numWaypoints x(i, (k-1)*31) mapSize * rand; % x 坐标 x(i, (k-1)*32) mapSize * rand; % y 坐标 x(i, (k-1)*33) mapSize * rand safeH; % z 坐标 end end % 计算初始适应度 for i 1:popSize path_i reshape(x(i, :), numWaypoints, 3); fitness(i) calFitness(path_i, map, X, Y, safeH, mapSize); end pbest x; pbestFitness fitness; [gbestFitness, gbestIdx] min(fitness); gbest x(gbestIdx, :); % 迭代寻优 w wMax; for iter 1:maxIter w wMax - (wMax - wMin) * (iter / maxIter); % 惯性权重线性递减 for i 1:popSize % 速度更新 位置更新 v(i, :) w * v(i, :) ... c1 * rand(1, dim) .* (pbest(i, :) - x(i, :)) ... c2 * rand(1, dim) .* (gbest - x(i, :)); x(i, :) x(i, :) v(i, :); % 边界约束航点坐标不能超出地图范围 x(i, x(i, :) 0) 0; x(i, x(i, :) mapSize) mapSize; % 评估新路径 path_i reshape(x(i, :), numWaypoints, 3); newFitness calFitness(path_i, map, X, Y, safeH, mapSize); % 更新个体最优和全局最优 if newFitness pbestFitness(i) pbest(i, :) x(i, :); pbestFitness(i) newFitness; end if newFitness gbestFitness gbest x(i, :); gbestFitness newFitness; end end gbestHistory(iter) gbestFitness; % 记录收敛曲线 end % 输出最优路径恢复为航点矩阵 bestPath reshape(gbest, numWaypoints, 3);这里面有两条经验。第一惯性权重w从 0.9 线性递减到 0.4 是关键设计——迭代前期需要较大的惯性权重让粒子在全局范围内探索避免过早陷入局部最优后期需要较小的权重让粒子精细收敛。如果w固定为 0.5 不动我测试过不少场景结果往往在迭代 50 次左右就停滞了最终路径明显不如递减策略的效果好。第二边界约束直接“截断”航点坐标而不是加惩罚项。原因是粒子飞出地图边界后interp2会返回NaN因为插值点超出数据范围NaN参与计算会让适应度值也变成NaN一旦某个粒子的适应度变成 NaNmin()比较全部失效整个算法立刻瘫痪。截断处理虽然粗暴但至少保证所有粒子都在有效区域内搜索。gbestHistory(iter) gbestFitness这一行是调试的后悔药。没有收敛曲线的 PSO 就是黑匣子——你不知道它到底收敛了没有、在第几次迭代停滞、当前最优解是不是改进过。我一般会把这个数组在plotFigure.m里一起画出来一眼就能看出迭代是否正常。3.4 plotFigure.m地形曲面、路径与收敛曲线的可视化plotFigure.m负责把结算结果变成直观的图。这个文件虽然不参与寻优但它决定了你能不能一眼看出路径的质量。一套完整的可视化至少包含三个视图三维地形加路径、俯视图看路径是否绕行合理、适应度收敛曲线。% plotFigure.m —— 可视化 % 1. 三维地形 路径 figure; surf(X, Y, map, EdgeColor, none, FaceAlpha, 0.8); hold on; plot3(bestPath(:,1), bestPath(:,2), bestPath(:,3), ... r-o, LineWidth, 2, MarkerSize, 4); scatter3(start(1), start(2), start(3)safeH, 100, g, filled); scatter3(target(1), target(2), target(3)safeH, 100, m, filled); xlabel(X / m); ylabel(Y / m); zlabel(Z / m); title(PSO 无人机山地三维路径规划结果); view(45, 30); % 调整视角便于观察地形起伏 % 2. 俯视图 figure; contourf(X, Y, map, 20); hold on; % 等高线填色 plot3(bestPath(:,1), bestPath(:,2), ones(size(bestPath,1),1) * 500, r-o); legend(地形等高线, 规划路径);surf画出来的地形是实体曲面路径用红色实线叠加在曲面上起点用绿色圆圈标记、终点用品红色方块标记这是最常见的配色方案。view(45, 30)把视角设置到斜上方 45 度能同时看到地形起伏和路径高度变化。俯视图用contourf画等高线填充图路径压在上面——这个视角专门用来看路径是否“顺着山谷走”还是“直穿山峰”。如果路径画出来是横穿密集等高线的说明高度约束可能没起到作用需要回头检查w_height权重。提一句有些版本会顺手把gbestHistory单独画出来纵轴是适应度值横轴是迭代次数。这个图的价值在于判断算法是否早熟如果曲线在迭代 30 次左右就变成水平线说明种群多样性和迭代次数不匹配大概率是多峰地形下陷入了局部最优。4. 从运行到调参复现这份资源的完整流程4.1 在 Matlab 里跑通这套源码的三个步骤拿到这套资源后正确顺序不是直接打开PSO.m就运行——主程序依赖defMap.m、calFitness.m、plotFigure.m三个文件路径不配好会直接报错。我习惯的顺序是% 步骤 1运行地形定义生成地图和起终点 [map, X, Y, start, target] defMap(500, 8); % 地形网格 500x500叠加 8 个高斯峰模拟山地 % 步骤 2设置路径规划参数航点数量、安全高度 numWaypoints 8; % 路径航点数 safeH 40; % 安全离地高度米 % 注意PSO.m 内使用了上述变量需在主工作区定义 % 步骤 3运行粒子群算法 [bestPath, gbestHistory] PSO(map, X, Y, start, target, numWaypoints, safeH); % 步骤 4可视化 plotFigure(map, X, Y, bestPath, start, target, gbestHistory);中间有一个隐藏依赖PSO.m里用到了numWaypoints和safeH这两个变量必须在主工作区存在才能正常运行。如果直接双击运行PSO.m它会因为找不到变量而报Undefined function or variable错误。解决方案要么像我上面这样在主脚本里先定义再调用PSO函数要么在PSO.m开头把这两个参数硬编码进去但不推荐改参数就得改文件。这个流程跑通后运行结果1.jpg和运行结果2.jpg就能复现出来。结果图应该是类似这样的预期画面三维曲面地形上有一条平滑的红色路径起点在低洼处路径先爬升绕过第一座山峰在山谷间穿过再下降到终点附近。原作者的这两张结果图对应的具体参数设置航点数、安全高度源码里没有写明需要自己跑一遍对比路径形态来判断。4.2 关键参数怎么调种群规模、迭代次数与学习因子参数调优是粒子群算法的重点也是新手最容易一头雾水的地方。四个核心参数值得逐个说种群规模popSize——50 是够用的底线100 会明显提升搜索质量但运行时间翻倍。路径维度越高航点数越多需要的种群规模越大。一般经验是种群规模取路径维度的 1.5~2 倍。如果numWaypoints 8维度是 24种群取 50 已经满足这个比例。迭代次数maxIter——200 次对这个规模的地图够用。判断标准是看收敛曲线如果曲线在最后 30 次迭代仍然明显下降说明还没收敛迭代数应该加大到 300 或 400如果曲线 100 次就开始持平加迭代次数也没有意义。注意粒子群算法存在随机性单次运行结果不能完全代表算法效果我一般每个参数组合跑 5 次取平均。惯性权重递减范围wMax和wMin——0.9 到 0.4 是经过大量验证的标准区间不需要改。唯一值得注意的场景是如果你的地形特别复杂山峰极多、通道极窄可以把wMin改成 0.3让后期搜索更精细。学习因子c1和c2——都取 1.5 是默认值。c1偏大时粒子更倾向探索自身历史最优区域路径会更多样c2偏大时粒子快速向当前全局最优靠拢收敛快但容易早熟。如果想给这个具体场景调优可以试试c1 2.0, c2 1.0更侧重探索或c1 1.0, c2 2.0更侧重收敛对比收敛曲线再做取舍。4.3 从运行结果图判断算法是否收敛跑完看结果时不要只看路径漂不漂亮要看两件事适应度收敛曲线的形态、路径与地形的贴合关系。收敛曲线正常应该是快速下降后趋于平缓的单调递减曲线。如果曲线上有“台阶”——一段水平后突然下降——说明粒子群跳出了局部最优这是好事说明多样性保持得当。如果曲线从头到尾几乎不动大概率是初始粒子生成范围太小或学习因子设置失衡。路径图判断标准有三条一是路径是否始终在地形曲面上方只有起降段可以接近地面安全高度范围内二是路径是否有明显折角折角处就是平滑代价起作用的地方三是路径是否出现了“绕远路”的情况——比如明明可以直接穿过山谷却绕到地图边缘这时要检查是不是高度权重太小导致粒子宁可选平坦但更长的路。% 一个快速检查穿山的脚本把路径高度与地形高度逐点对比 for k 1:size(bestPath, 1) groundH interp2(X, Y, map, bestPath(k,1), bestPath(k,2), linear); if bestPath(k,3) groundH safeH fprintf(航点 %d 穿山地形高度 %.1f路径高度 %.1f\n, ... k, groundH safeH, bestPath(k,3)); end end这段检查脚本在调参阶段非常有用。穿山位置会精确到航点序号配合地形图就能看出是哪个山峰导致了冲突再决定是调整安全高度还是增加航点数。5. 避坑与常见问题粒子群路径规划最容易踩的五个坑5.1 路径穿山航点没穿山但线段穿了现象运行结果图里路径看起来“贴着山坡走”但用脚本检查时发现路径中间段穿进了山体。原因适应度函数只检查了航点位置是否穿山没有对航点之间的线段做插值检查。当航点间距较大、山峰又比较尖时线段可能从山峰“肚子”里穿过。解决在calFitness.m里对每段路径做细分插值检查每段至少插入 20 个采样点。具体做法参考 3.2 节的代码——用linspace生成细分点逐点调用interp2对比高度。我遇到过把插值点加到 50 个才完全消除漏检的案例建议首先把interpN调大观察效果。5.2 路径严重绕远高度权重压过了长度权重现象规划出来的路径为了避开一个小山包绕了地图三分之一圈飞行距离比直线多了好几倍。原因w_height权重设置过大。高度惩罚在适应度里占比太高粒子宁可多飞几公里也不愿把高度抬升哪怕一米。这是典型的“过约束”——安全高度本来是软约束却被当成了绝对红线。解决检查适应度函数三个权重w_len、w_height、w_smooth的量级是否匹配。长度代价通常量级是几千到几万高度惩罚如果也放大到几万就会压制长度代价。我一般先把三个权重都设为 1 跑通再根据路径形态逐个调整。5.3 迭代后期不收敛收敛曲线剧烈震荡现象适应度曲线不下降或反弹路径在迭代后期仍然剧烈变形最终结果不是稳定最优解。原因惯性权重w没有随迭代递减。固定权重让粒子在后期依然用较大速度飞行容易越过最优解区域造成震荡。解决确认PSO.m里有惯性权重线性递减的逻辑即w wMax - (wMax - wMin) * (iter / maxIter)。这是一行代码的事但没写就是整个算法变味——这也是很多移植版本跑不出效果的隐藏原因。5.4 每次运行结果不一样无法判断路径好还是坏现象同一套参数连续运行三次三次路径差异很大不知道以哪次为准。原因粒子群算法是随机优化算法初始化粒子位置、速度更新都是随机过程。没有固定随机种子每次结果天然不同这不是 bug。解决这是玄学场景里最有实用价值的经验。调参阶段在PSO.m开头加一行rng(42)固定随机种子保证同一组参数的可复现对比确定最终参数后去掉固定种子跑 10 次或者你那台机器跑得动就 20 次取适应度均值作为算法效果的评估指标。只跑一次就说“这个参数好”样本量完全不够。5.5 interp2 返回 NaN粒子飞出了地图边界现象程序运行到一半报错说NaN出现在适应度值里或者收敛曲线突然出现断崖式下降然后全是 NaN。原因粒子在迭代过程中飞出了map的定义范围interp2在插值范围外返回NaN。这是 PSO 实现里最常见的运行时崩溃。解决在位置更新后立即做边界约束把所有超出[0, mapSize]的坐标截断回边界值。注意mapSize是地形矩阵的尺寸不是物理距离——如果linspace(0, mapSize, mapSize)生成网格x坐标在 0 到mapSize之间边界约束也要用这个范围。6. 进阶用法收敛性验证与扩展方向的三个实用技巧把基础流程跑通之后这份资源能扩展出去的方向比想象中多。第一个值得做的是“参数敏感性验证”——以numWaypoints为变量分别取 5、8、12对比最终路径形态。航点太少路径自由度不够无法贴合复杂地形航点太多维度暴涨收敛变慢且容易产生冗余弯折。观察最佳航点数与地形复杂度的对应关系是理解路径规划本质的捷径。第二个技巧是把适应度曲线接入参数调制循环。写一个for循环遍历多组c1、c2组合每组跑 5 次记录平均收敛值最后画出一张对比表——这是评估算法调参方向的最直接手段。以我这边的习惯来说PSO 的参数调节从来不是“调一次就好”而是一组一组扫过去看趋势光靠零星试跑得出“最优参数”的说法都不靠谱。% 参数扫描示例遍历不同学习因子组合 c1List [1.0, 1.5, 2.0]; c2List [1.0, 1.5, 2.0]; resultTable zeros(length(c1List) * length(c2List), 3); idx 0; for a 1:length(c1List) for b 1:length(c2List) idx idx 1; % 把 c1、c2 传入 PSO重复运行 5 次取平均适应度 avgFitness runMultipleTimes(c1List(a), c2List(b), 5); resultTable(idx, :) [c1List(a), c2List(b), avgFitness]; end end第三个扩展方向是引入动态威胁约束。在calFitness.m里把威胁代价从固定山峰改成“以某个坐标为中心、随时间扩张的禁飞区”只需要在威胁代价函数里加一个时间变量t。这种改动让纯静态规划升级为应对动态环境的预规划很多实际任务场景里这才是刚需。我自己在这套资源上踩得最深的一次教训是调参时没有固定随机种子连续换了三组参数都觉得“效果不好”后来才意识到是随机性干扰了判断。从那以后我每次跑 PSO 都强制走一遍固定种子对比 — 不加固定种子跑多次统计 — 再定参数的三步流程。走了这套流程后收敛曲线是改进还是随机扰动一眼就能分辨清楚。希望帮到你——山地三维路径规划是个坑比预期多的方向但把适应度函数和参数迭代这些关键环节理解透了后面接真实地形数据也只是换一层输入的事情。本文还有配套的精品资源点击获取