多目标模拟退火算法在无人机三维路径规划中的MATLAB实现

发布时间:2026/9/18 12:45:50
多目标模拟退火算法在无人机三维路径规划中的MATLAB实现 简介一套基于多目标模拟退火算法MOSA的无人机三维路径规划项目实例面向具备MATLAB编程基础的研发人员与无人机路径规划研究者解决复杂三维环境下的路径规划问题综合优化路径长度、飞行安全与能耗等目标。资源压缩包共1个文件内含1份docx文档大小约79KB内容涵盖项目背景、挑战分析、模型架构、详细代码及GUI设计便于在MATLAB环境中对照调试。文档针对高维环境建模、多目标冲突权衡、算法收敛与全局搜索平衡等难点按环境建模层、路径编码层、多目标评估层、模拟退火搜索层和结果输出层进行讲解并给出函数级代码示例和逻辑说明。同时介绍了自适应权重调整、能耗动力学耦合、非劣解集动态维护等创新点可直接衔接智能物流、灾害应急、环境监测等应用场景。目前已有174人学习适合希望深入理解多目标路径规划算法并动手实验验证的读者。1. 三维路径规划问题与多目标模拟退火算法的适配点假设一架无人机要在60米乘60米、最高45米的丘陵地带完成勘测任务起点在西南角目标点在东北角途中至少有两座通信塔作为威胁源。你要同时保证航程尽可能短、不要飞进威胁范围、转弯角不要过陡。这三个要求天然冲突绕开威胁必然增加航程强行压缩航程又会穿过风险区域。传统做法是给三个要求分配权重合并成单目标再用模拟退火或遗传算法求解但权重一旦设错最后得到的往往是一条“看起来能用但不敢用”的路径。多目标模拟退火算法MOSA不再依赖人工权重而是用帕累托支配关系同时保留一组非劣解最终你把选择权交给使用者而不是交给一个固定参数。下面用MATLAB从地形建模、目标函数、核心迭代到GUI界面完整走一遍即使没有全局优化工具箱也能在当前主流版本的MATLAB上通过脚本复现。2. 无人机三维路径规划的多目标建模与MOSA求解框架多目标模拟退火和单目标最大的不同在于建模阶段就必须明确哪些因素是目标哪些因素是约束。约束可以放进目标函数当作惩罚项也可以用强制边界处理。三维路径规划里地形碰撞更适合作为边界约束而威胁距离、航程、平滑性更适合作为目标。这一章先把计算环境搭建起来。2.1 三维地形与威胁场的数学表达在验证阶段我一般不用真实DEM数据因为MATLAB读取GeoTIFF需要映射工具箱而且栅格分辨率过大会拖慢每次的目标函数计算。更快的做法是用peaks函数生成一个平滑起伏的测试地形并叠加少量随机噪声模拟凹凸不平。下面是生成地形和威胁源的代码。% 生成测试地形 step 1.5; [X, Y] meshgrid(0:step:60, 0:step:60); Z 5 * peaks(40) 20; % 将峰值抬升到20~30米附近 Z(Z 0) 0; % 定义威胁源每个威胁保存中心坐标、半径和强度 threats [ struct(x, 20, y, 25, z, 12, r, 8, w, 50); struct(x, 42, y, 18, z, 20, r, 6, w, 40); ];这段代码里step1.5意味着横向网格间距1.5米60米范围共有41个节点飞行路径的中间节点会落在这41乘41的网格附近。peaks(40)生成一个41乘41的矩阵5*peaks20把地形抬升到约15~35米既避免了平地无风险也不至于让大部分随机路径因为过低而无法飞行。威胁源采用结构体数组保存后续computeObjectives中可以用循环遍历。注意这里的z只是威胁中心的参考高度并不强制表示地形高程。参数说明step越大搜索空间越小算法跑得更快但路径细节会丢失step1.5是一个在仿真和性能之间的折中。如果实际工程中有真实地形数据可以把X、Y、Z直接替换为dem.X、dem.Y、dem.Z威胁源也要映射到同一坐标系中。2.2 优化目标的量化路径最短、能耗与安全代价在三维路径规划中目标的选择直接决定了结果的可用性。我在实际项目中几乎固定使用下面这三个目标目标表达式对应工程含义航程长度 L(L \sum_{i1}^{n-1} |p_{i1} - p_i|)飞行耗电量与任务时间安全代价 S(S \sum_i \sum_j \frac{w_j}{d(p_i, \text{threat}_j)^2 \epsilon})对雷达、禁飞区的暴露程度平滑代价 C(C \sum_{i2}^{n-1} \arccos\left(\frac{(p_i-p_{i-1})\cdot(p_{i1}-p_i)}{|p_i-p_{i-1}||p_{i1}-p_i|}\right))无人机动力学可行性这里不对三个目标做加权求和而是把它们作为三个独立输出交给MOSA用支配关系去取舍。安全代价的写法要特别小心如果直接用w/d^2当路径点恰好通过威胁中心时d等于0结果会变成无穷大所以需要加一个小量epsilon0.01。平滑代价使用acos取值范围是0到π当三个连续点构成180度直线时代价为0直角转弯时约等于1.5708。function [L, S, C] computeObjectives(path, Z, threats) % path: Nx3 矩阵每一行是 (x,y,z) % Z: 地形高程矩阵用于检测穿地 % threats: 威胁源结构体数组 n size(path, 1); % 航程长度 diffs diff(path); L sum(sqrt(sum(diffs.^2, 2))); % 安全代价穿地惩罚 威胁源代价 S 0; for i 1:n px path(i,1); py path(i,2); pz path(i,3); % 地形碰撞检测 ix max(1, min(size(Z,1), round(px / 1.5) 1)); iy max(1, min(size(Z,2), round(py / 1.5) 1)); if pz Z(ix, iy) S S 500; % 穿地惩罚 end % 威胁源代价 for j 1:length(threats) dx px - threats(j).x; dy py - threats(j).y; dz pz - threats(j).z; d sqrt(dx^2 dy^2 dz^2); if d threats(j).r S S threats(j).w * 100; else S S threats(j).w / (d^2 0.01); end end end % 平滑代价 C 0; for i 2:n-1 v1 path(i,:) - path(i-1,:); v2 path(i1,:) - path(i,:); cosTheta dot(v1, v2) / (norm(v1) * norm(v2) eps); cosTheta min(max(cosTheta, -1), 1); C C acos(cosTheta); end end这个函数是后面所有计算的基础性能很关键。computeObjectives中有两层循环路径点数和威胁数。如果路径点固定为8个威胁源只有2个单次计算量可以忽略。但如果你把路径点扩展到30个威胁源变成10个单次计算量会到300次距离计算整个MOSA的迭代次数会明显变慢。因此建议路径点控制在10个以内邻域操作只改变其中少数几个点飞行轨迹的细节交给后处理平滑。2.3 多目标模拟退火的整体求解流程MOSA的顶层流程并不复杂它仍然沿用单目标模拟退火的“生成邻域解-接受判断-降温”框架只在接受判断和“最优解”的记录方式上做了改进。整体流程可以概括为以下五步生成初始路径起点和终点固定中间插入8个随机节点。设定初始温度、降温比、内循环次数。在当前路径上生成邻域路径。计算新旧路径的三个目标使用多目标接受准则判断是否替换当前路径。把新路径及其目标值放入帕累托档案淘汰被支配个体温度逐步降低迭代到终止条件。下面这段代码给出了主循环骨架后续章节中的具体函数会逐步填充。% 主循环骨架 params.nInit 8; % 初始路径中间节点数 params.T0 600; % 初始温度 params.alpha 0.9; % 降温比 params.Lk 400; % 每个温度下迭代次数 params.maxOutIter 80; % 外循环轮数 params.stepBase 4; % 初始邻域扰动步长 % 假设已执行2.1中的X、Y、Z和threats生成代码 startPos [2, 2, 10]; endPos [58, 58, 25]; current initPath(startPos, endPos, params.nInit); currentObjs computeObjectives(current, Z, threats); archive struct(path, {}, objs, {}); T params.T0; for outer 1:params.maxOutIter stepScale max(0.5, T / params.T0 * params.stepBase); for inner 1:params.Lk newPath neighborMove(current, stepScale); newObjs computeObjectives(newPath, Z, threats); if dominates(newObjs, currentObjs) current newPath; currentObjs newObjs; elseif acceptRule(newObjs, currentObjs, T) current newPath; currentObjs newObjs; end archive updateArchive(archive, newPath, newObjs); end T T * params.alpha; end参数说明初始温度600对应目标函数中“安全代价”可能达到的单点最大值如果威胁权重不匹配温度要相应调整。降温比alpha0.9意味着每轮温度乘0.980轮后温度约为600乘以0.9的80次方大约0.2搜索从高温的广域探索过渡到低温的局部精细调整。如果alpha太接近1比如0.98则需要更多外循环轮数才能降到低温太接近0.7则容易在早期就失去多样性。params.maxOutIter和params.Lk相乘等于总迭代次数32000次在普通笔记本上完成整个MOSA大约需要20到40秒正好适合GUI演示。3. 用MATLAB编写MOSA核心代码状态更新、接受准则与帕累托存档第二章的框架里用了initPath、neighborMove、dominates、acceptRule、updateArchive五个函数。这一章逐个实现并说明每个环节在多目标下为什么要这样写。3.1 路径编码与邻域扰动策略路径编码采用“固定起点终点8个中间节点”的形式路径点个数保持不变便于生成矩阵和计算目标函数。中间节点初始值可以均匀分布在起点和终点的连线上再叠加随机偏移这样初始路径不会和地形离得太远也不会一开始就产生巨大的安全代价。function path initPath(startPos, endPos, n) path zeros(n2, 3); path(1,:) startPos; path(end,:) endPos; for i 1:n ratio i / (n1); base startPos (endPos - startPos) * ratio; offset [randn * 2, randn * 2, randn * 1.5]; path(i1,:) base offset; end end初始节点的随机偏移量不宜过大。过大会导致大量穿地惩罚目标函数数值被拉得过高MOSA在搜索初期要花很多代才能回到可行区域。2米的水平偏移和1.5米的垂直偏移在60米尺度下算是比较温和的。邻域操作决定算法探索能力常见的有单点扰动、两点互换、小范围重置三种。它们在三维路径规划里的适用场景不同。操作类型实现方式适用场景单点扰动随机选一个中间节点加高斯偏移局部微调最常用两点互换交换两个中间节点的坐标路径绕行方式大改适合高温阶段小范围重置把连续几个节点重新初始化跳出威胁区域包围我一般只保留单点扰动因为两点互换在固定起点终点且节点不带顺序语义时容易产生大角度折线反而增加平滑代价。单点扰动需要让偏移幅度随温度下降而收缩下面这个实现把步长和温度绑定。function newPath neighborMove(path, stepScale) newPath path; n size(path, 1); idx randi([2, n-1]); % 不移动起点和终点 newPath(idx,:) newPath(idx,:) stepScale * randn(1,3); % 边界限制x、y在[0,60]z在[3,45] newPath(idx,1) min(max(newPath(idx,1), 0), 60); newPath(idx,2) min(max(newPath(idx,2), 0), 60); newPath(idx,3) min(max(newPath(idx,3), 3), 45); endstepScale从主循环传入max(0.5, T/T0 * stepBase)保证温度降到很低的最后阶段仍然有0.5米的微调能力。randn产生高斯分布偏移比rand产生的均匀分布更容易兼顾小幅微调和偶尔的较大跨越。如果使用均匀分布2*rand-1搜索范围会集中在步长边界附近不利于精细收敛。3.2 多目标Metropolis接受准则多目标模拟退火和单目标最实质的差别在“接受准则”。单目标中新解代价高于当前解时接受概率是exp(-(E_new - E_old)/T)多目标中能量差是一个向量我们需要把向量差异压缩成一个标量。这里采用一种直接有效的做法先判断帕累托支配关系再在非支配情况下使用平均变差计算接受概率。function flag dominates(objA, objB) % 当 objA 在所有目标上不差于 objB且至少一个目标更优时返回 true flag all(objA objB) any(objA objB); end function accept acceptRule(newObjs, currentObjs, T) diff newObjs - currentObjs; % 1x3 deltaAvg mean(max(diff, 0)); % 只统计新解变差的部分 accept rand exp(-deltaAvg / (T 1e-6)); enddominates用来判断严格支配关系三个目标都不差且至少一个更好。这样新解即使航程更短但安全代价相同也算支配。acceptRule中的max(diff,0)把三个目标里“变好”的部分归零只保留“变差”的部分再取平均。这个值代入exp(-delta/T)后当温度高时接受概率高温度低时接受概率低和单目标退火保持一致。用这个规则时要注意一个情况如果当前解支配新解但温度很高仍有概率接受较差解。这是有意保留的随机性目的和单目标模拟退火一样允许算法跳出局部帕累托前沿。如果完全拒绝所有被支配解MOSA很容易收敛到最初几条路径附近帕累托档案多样性将非常差。3.3 档案管理与降温策略帕累托档案用于保存当前搜索过程中发现的所有非支配解。每次产生新路径就把这条路径和它的三个目标值追加到档案末尾然后遍历所有解删除被支配的个体。这个操作虽然简单但要注意MATLAB中结构数组的赋值方式先追加再筛选避免在原数组上边删边遍历造成索引错乱。function archive updateArchive(archive, newPath, newObjs) archive(end1) struct(path, newPath, objs, newObjs); dominated false(length(archive), 1); for i 1:length(archive) for j 1:length(archive) if i j continue; end if dominates(archive(j).objs, archive(i).objs) dominated(i) true; break; end end end archive archive(~dominated); end双层循环的时间复杂度是O(K^2)K是档案大小。当K小于200时这个开销可以接受但当外循环运行80轮、每轮400次内循环时档案可能长期保持几十个解计算量并不大。若想进一步压性能可以在每次追加前先判断新解是否被现有解支配被支配则直接丢弃减少后续比较次数。档案大小不设上限会带来两个问题一是比较时间变长二是最终GUI里绘制帕累托前沿时会挤成一团。常见做法是在更新后检查length(archive)是否超过预设上限比如100超过时用拥挤距离排序优先删除“周围解最密集”的那个解。这里为了保持代码可读性不把拥挤距离计算塞进去实际工程中可以在updateArchive末尾调用一个pruneArchive子函数。温度策略采用指数降温T T * alpha这是模拟退火家族中最稳妥的方案。alpha接近1时收敛慢但质量好接近0.9时速度快但可能牺牲极端目标值的覆盖。如果你用的是MATLAB较新版本可以考虑temperature按T / log(1 outer)下降但实测下来指数降温更不容易在后期产生温度骤降所以我更推荐保留简单的折线乘式降温。4. 构建MATLAB GUI把MOSA求解器接入三维路径演示很多人写完算法就不想碰GUI但这类三维路径规划任务最终交付对象往往是没读过源代码的老师或同事。没有界面交互每跑一次都要改脚本里的坐标效率极低也看不出算法到底在搜索什么。这一章用MATLAB App Designer实现一个轻量级演示界面。4.1 用App Designer设计主面板App Designer在R2016a之后成为MATHLAB推荐使用的GUI环境相比老GUIDE生成的.fig文件它把代码和界面统一在一个.mlapp文件里版本兼容性和回调管理都更好。主面板建议分成三个区域控件区域主要控件作用参数输入区起点X/Y/Z滑块终点X/Y/Z滑块设置起终点坐标算法控制区迭代次数滑块运行按钮重置按钮控制求解过程结果展示区三维坐标轴UIAxes二维坐标轴ParetoAxes显示路径和帕累托前沿界面的具体布局可以拖拽完成不需要手写代码生成控件。但有一个关键点按钮回调里不能直接用工作区的变量任何数据都要通过app这个对象传递。比如app.IterSlider.Value是滑块的当前值app.UIAxes是三维坐标轴的句柄。% 按钮回调读取输入并调用核心求解器 function RunButtonPushed(app, event) app.StatusLabel.Text 正在求解请稍候...; drawnow; startPos [app.StartX.Value, app.StartY.Value, app.StartZ.Value]; endPos [app.EndX.Value, app.EndY.Value, app.EndZ.Value]; params.nInit 8; params.T0 600; params.alpha 0.9; params.Lk 300; params.maxOutIter round(app.IterSlider.Value); params.stepBase 4; [bestPath, archive] runMOSA(startPos, endPos, params); % 绘制地形和最佳路径 cla(app.UIAxes); surf(app.UIAxes, X, Y, Z, EdgeColor, none, FaceAlpha, 0.4); hold(app.UIAxes, on); plot3(app.UIAxes, bestPath(:,1), bestPath(:,2), bestPath(:,3), r.-, LineWidth, 2); scatter3(app.UIAxes, bestPath(:,1), bestPath(:,2), bestPath(:,3), 30, filled); view(app.UIAxes, 45, 30); app.StatusLabel.Text [完成帕累托解数量: , num2str(length(archive))]; end这里把params.maxOutIter直接对应到界面上的“迭代次数”滑块方便用户观察不同计算量对结果的影响。cla(app.UIAxes)用来清空坐标轴hold(app.UIAxes,on)保证地形表面和后续的路径曲线在同一坐标系上叠加。要注意的是X、Y、Z和threats在App Designer的启动函数startupFcn中需要预先加载或生成这样回调里才能直接使用。4.2 求解器与GUI之间的参数封装不能把上面章节里的每一段脚本都摊在GUI回调里否则回调会膨胀到几百行。更好的做法是把整个MOSA计算封装成一个顶层函数runMOSAGUI只负责传入参数结构体和接收结果。function [bestPath, archive] runMOSA(startPos, endPos, params) % 生成地形和威胁源也可以外部传入 [X, Y, Z] createTerrain(); threats createThreats(); current initPath(startPos, endPos, params.nInit); currentObjs computeObjectives(current, Z, threats); archive struct(path, {}, objs, {}); T params.T0; for outer 1:params.maxOutIter stepScale max(0.5, T / params.T0 * params.stepBase); for inner 1:params.Lk newPath neighborMove(current, stepScale); newObjs computeObjectives(newPath, Z, threats); if dominates(newObjs, currentObjs) || acceptRule(newObjs, currentObjs, T) current newPath; currentObjs newObjs; end archive updateArchive(archive, newPath, newObjs); end T T * params.alpha; end bestPath current; end封装后的好处是GUI和核心算法之间的数据交换只发生在这一个入口函数上。如果你想换成真实地形文件只需要改createTerrain内部实现如果想加入更多威胁源也只需要调整createThreats函数。回调代码保持精简调试时也不需要在GUI和脚本之间反复切换。4.3 在GUI中绘制路径与帕累托前沿除了三维路径GUI里最好再放一个二维坐标轴展示帕累托前沿。这个图可以直接揭示算法的搜索质量解是否分布均匀、是否覆盖了两个极端目标、有没有出现大量冗余的中间点。% 在第二个坐标轴绘制帕累托前沿 objs reshape([archive.objs], 3, length(archive)); scatter(app.ParetoAxes, objs(:,1), objs(:,2), 20, objs(:,3), filled); xlabel(app.ParetoAxes, 航程长度); ylabel(app.ParetoAxes, 安全代价); colorbar(app.ParetoAxes);[archive.objs]会把结构数组中的所有objs字段拼接成矩阵再通过reshape整理成三列。scatter的第三个参数20是点大小第四个参数是颜色向量这里用平滑代价作为颜色相当于把第三维目标映射到色彩上。如果不想要颜色映射直接把第四参数省略就是单一颜色点。运行结束后还可以提供一个“导出结果”按钮把bestPath用writematrix保存为CSV或把当前三维坐标轴用exportgraphics输出成PNG这样验收时直接有图片和路径数据可看。5. 调参与验证判断多目标模拟退火没有陷入局部收敛5.1 从帕累托前沿的形状判断多样性多目标结果不是单一点因此不能只看“路径长度最短的那条线”有没有贴近起点到终点的直线距离。更有效的做法是观察GUI里的帕累托前沿图如果所有点挤在目标空间的左下角说明算法只优化了航程和安全中的某一个方向如果点散布成一条明显的“凹曲线”说明两个目标之间的权衡已经被充分挖掘。一组健康的结果里至少应该有一条航程较短但安全代价稍高的路径也有一条航程较长但安全代价非常低的安全路径。如果每次运行都只有前一种解优先怀疑温度下降太快。5.2 关键参数的经验范围参数调整按影响程度排序依次检查初始温度、降温比、邻域步长。下面这张表是多次运行后总结出的经验范围参数经验范围失效时的表现T0400~800过低时前期探索不足前沿偏向一侧alpha0.85~0.95过小则多样性差过大会拖慢收敛Lk300~600过小则档案更新不充分解集稀疏stepBase2~6过大会频繁撞边界过小只在局部打转这些参数之间存在耦合。提高alpha到0.95时最好同步把maxOutIter从80提高到120否则温度还没来得及降低程序就结束了。降低stepBase时可以适当增大Lk用更多局部样本来弥补步长的不足。每次只动一个参数对比帕累托前沿的跨度比盲目调参更有效。5.3 末端平滑处理与威胁复核一个特别实用的技巧不要直接使用bestPath作为最终航迹。MOSA在最后几个温度阶段可能还在接受小幅扰动导致路径末端出现一两个折返点。从档案中挑一条路径后对其进行滑动平均平滑能显著减小无人机实际飞行时的转向过载。function smoothed smoothPath(bestPath, k) smoothed bestPath; n size(bestPath, 1); for i 2:n-1 lo max(2, i-k); hi min(n-1, ik); smoothed(i,:) mean(bestPath(lo:hi,:), 1); end endk取5~7比较合适它会保留起终点不动只对中间节点做窗口平均。平滑之后必须重新调用computeObjectives检查如果平滑导致某个节点穿入地形或威胁半径就把该节点恢复为平滑前的值。这一步是“最后一公里”的质量保障很多演示项目跑出的MOSA路径看起来合理但一放大就发现贴地飞过山头问题就出在缺少平滑后的碰撞数控。最终输出应当以平滑后且通过威胁复核的路径为准将这组数据导出CSV写入飞行任务文件时才有真正的工程参考价值。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询