资讯动态

基于GA-ACO-DNN混合优化的三维路径规划MATLAB实现

发布时间:2026/9/19 17:58:05 来源:尧图企业网站定制
简介面向具备 MATLAB 编程基础并熟悉优化算法与机器学习的科研人员、研究生及工程技术人员这份资源是一个融合遗传算法GA、蚁群算法ACO与深度神经网络DNN的无人机三维路径规划项目实例用于在复杂三维环境中生成安全高效的飞行路线。其核心思路是构建带安全约束的三维环境模型以固定航点编码路径借助 GA 全局搜索、ACO 局部优化以及 DNN 代价快速预测同时配合真实评价函数保证路径满足安全要求系统还包含 GUI 交互支持数据生成、模型训练、路径规划、结果可视化与导出并讨论了动态障碍物、多无人机协同等扩展方向。资源包为单个 docx 文档约 116KB内含完整 MATLAB 程序代码、GUI 设计说明和逐模块代码详解读者可在 R2025b 环境运行示例并跟踪调试各模块交互逻辑。目前已有 99 人学习下载适合希望系统掌握多算法协同优化、多目标代价函数设计及 DNN 预测与真实评价融合策略的读者。1. 为什么 GA、ACO 和 DNN 要放在同一个三维路径规划框架里三维路径规划比二维最短路难在哪里不只是多了一个高度维度而是障碍物分布、飞行安全距离、爬升下降约束和能耗指标搅在一起搜索空间会随环境分辨率的提高呈立方级膨胀。传统最短路径算法在二维网格上可以靠图搜索拿到结果到三维场景里航点组合的规模让精确求解变得不现实。遗传算法擅长在非凸空间里做全局探索但后期容易聚集到局部极值蚁群算法有信息素正反馈能强化优质决策组合可前期收敛偏慢深度神经网络能从历史路径里学到“什么特征的路线更贵”却没法保证碰撞安全。单独拿出任何一个都不够于是项目里把 GA 负责全局搜索、ACO 负责局部强化、DNN 负责代价快速估计最后再用真实评价函数做安全复核。这套组合的价值在于GA 提供多样性候选ACO 在精英路径附近精修DNN 把重复计算量压下去真实碰撞检测兜住安全底线。适合正在做无人机路径规划、想对比混合优化算法效果的研究生以及需要在 MATLAB 里搭原型验证算法的工程人员。2. 三维环境建模与路径编码先让“路线”变成可计算的对象路径规划算法不直接操作三维地图它操作的是路径的数学表达。所以第一步是把环境、障碍物、飞行边界和路线形态都翻译成数值结构。这个项目采用固定航点编码也就是把一条路线表示为若干个三维中间航点的坐标序列起点和终点固定。这样做的好处是遗传算法可以直接对坐标向量做交叉变异蚁群算法也只需要在候选航点集合里做组合选择而不需要维护复杂的网格连通关系。2.1 三维栅格环境与障碍物表达项目使用三维栅格地图环境尺寸由边界向量定义。障碍物用球体或长方体包围盒近似每个障碍物包含中心坐标、半径或半边长、高度范围。我一般建议用球体做主障碍物因为碰撞检测只要算航段与球心的最近距离速度快视觉效果也直观。环境参数可以集中放到一个配置结构体里方便后续替换成真实地形数据。下表是环境模型里最常见的几个字段也是后续所有评价函数都要用的基础参数。参数含义建议取值范围mapBoundary三维空间边界[x_min, x_max, y_min, y_max, z_min, z_max]根据飞行区域设定startPos起点坐标[x, y, z]边界内避开障碍物endPos终点坐标[x, y, z]边界内避开障碍物obstacles障碍物结构体数组含中心、半径、高度数量 310safeDist安全缓冲距离视无人机尺寸而定如 25heightLimit最低和最高飞行高度与地形及法规相关2.2 固定航点编码与路径修复一条路径用numWaypoints个中间航点表示每个航点有三个坐标分量。二维情况下还可以用极坐标编码但三维场景下直接做笛卡尔坐标编码最直观。为了降低搜索难度可以把高度分量单独编码让水平坐标和高度分别经过归一化后再参与遗传操作。路径修复是容易被忽略的环节。交叉变异产生的航点一旦越界不能直接丢弃否则种群规模会缩小。常见做法是把它拉回最近的边界内。下面这段代码展示了路径向量到坐标矩阵的转换和越界修复逻辑。function pathMat decodePath(ind, config) numWp config.numWaypoints; pathMat zeros(numWp 2, 3); pathMat(1, :) config.startPos; pathMat(end, :) config.endPos; % 个体向量前 numWp*3 个分量分别对应 x, y, z coords reshape(ind(1:numWp*3), 3, numWp); bnd config.mapBoundary; % 逐维修复越界坐标 coords(:, 1) min(max(coords(:, 1), bnd(1)), bnd(2)); coords(:, 2) min(max(coords(:, 2), bnd(3)), bnd(4)); coords(:, 3) min(max(coords(:, 3), bnd(5)), bnd(6)); pathMat(2:end-1, :) coords; end这段代码的逻辑是先给路径矩阵分配numWaypoints 2行因为首尾是固定的起点和终点。然后把个体向量重新排列成numWaypoints × 3的矩阵每一行是一个航点的[x, y, z]。用min和max组合把每个坐标限制在地图边界内这就是所谓的边界修复。注意这里不能简单地截断因为如果某个航点越界太远截断后多个航点会重叠在一起后续计算转弯角度时会出现除零问题。实践中我还会在修复后加一个微小抖动避免种群多样性下降。2.3 真实评价函数长度、碰撞、高度和转弯怎么折算评价函数是整个框架的“裁判”。DNN 的预测再快最后也要用真实评价函数来校准。真实代价必须同时覆盖四个维度总路径长度、障碍物碰撞次数、最小安全距离、高度变化与转弯角度。长度直接反映能耗碰撞项用高权重惩罚安全距离项用来提前规避贴障碍物飞行的风险转弯和高度变化则是为了生成可执行的平滑路线。下面的函数是路径评价的核心骨架返回的是综合代价和一组诊断指标。function [cost, metrics] evaluatePath(pathMat, config) segLen sqrt(sum(diff(pathMat).^2, 2)); totalLen sum(segLen); % 碰撞检测与最小安全距离 [collisionCount, minDist] checkCollision(pathMat, config.obstacles, config.safeDist); % 转弯角度累计用相邻三航点构成的向量夹角 turnCost 0; for i 2:size(pathMat, 1) - 1 v1 pathMat(i, :) - pathMat(i-1, :); v2 pathMat(i1, :) - pathMat(i, :); cosAng dot(v1, v2) / (norm(v1) * norm(v2) eps); turnCost turnCost (1 - cosAng); end % 高度变化量 heightVariation sum(abs(diff(pathMat(:, 3)))); % 加权综合代价碰撞项权重远高于其他项 cost totalLen config.wCollision * collisionCount ... config.wSafe * max(0, config.safeDist - minDist) ... config.wTurn * turnCost config.wHeight * heightVariation; metrics struct(length, totalLen, collisions, collisionCount, ... minDist, minDist, turnCost, turnCost, ... heightVar, heightVariation); end参数wCollision通常设为1000以上让任何碰撞路线在种群竞争中快速被淘汰。wSafe不需要太大它只在safeDist - minDist为正时产生惩罚属于软约束。wTurn和wHeight设成0.5到2之间比较合适具体要看场景尺寸。这里每个权重都会影响最终路线形态权重过大路线会变弯权重过小路线会贴着障碍物走。这个评价函数就是 DNN 的训练标签来源后面所有预测和优化都围绕它展开。3. GA 全局搜索与 DNN 代价预测用神经网络给遗传算法做“预审”这一章是项目最核心的部分。GA 负责在三维空间里找出若干条有希望的路线但每评估一个个体都要调用evaluatePath里面的checkCollision要逐段计算与所有障碍物的距离5000 个个体跑 100 代就是 50 万次碰撞检测计算量非常大。项目引入 DNN 的目的就是让神经网络模拟真实评价函数的输入输出关系先用预测值排序再对少部分关键个体做真实复核。3.1 DNN 在协同框架中的定位参考思路DNN 不替代真实评价只做“预审”。它从路径特征映射到综合代价相当于一个代理模型。在 GA 每一代中种群个体先提取特征DNN 快速打分然后按分数排序。排名靠前的个体比如前 10%执行真实评价用真实代价修正适应度排名靠后的个体直接淘汰或者重新交叉变异。这样做的效果是大量明显差的个体不需要经过完整碰撞检测计算焦点集中在精英候选上。需要说明的是DNN 的预测误差会存在所以项目里保留了一个安全机制最终选出的路线必须经过真实评价函数复核并重新计算最小安全距离和碰撞次数。也就是说DNN 负责“猜得准”真实评价负责“保得稳”。3.2 五维路径特征与训练数据生成输入到 DNN 的特征需要既能压缩路径信息又能还原主要成本因素。项目采用了五个维度的统计特征路径总长度、最大高度差、平均转弯角度、最小障碍物距离的近似值、航段长度方差。这五个特征覆盖了距离成本、高度成本、平滑度成本和安全成本的绝大部分信息。训练数据通过随机采样生成。具体是在环境边界内随机生成大量航点组成候选路径再用evaluatePath计算真实代价形成特征到代价的映射数据集。生成 50000 条五维模拟数据是可行的关键在于随机航点要覆盖多种路线形态不能只生成直线附近的路径否则神经网络学不到碰撞惩罚的模式。function [X, Y] generateTrainingData(config, numSamples) X zeros(numSamples, 5); Y zeros(numSamples, 1); numWp config.numWaypoints; for i 1:numSamples % 随机生成航点起点终点固定 randPts rand(numWp, 3) .* (config.mapBoundary(2:2:end) - config.mapBoundary(1:2:end)) ... config.mapBoundary(1:2:end); pathMat [config.startPos; randPts; config.endPos]; % 计算特征向量 segLen sqrt(sum(diff(pathMat).^2, 2)); totalLen sum(segLen); heightVar max(pathMat(:,3)) - min(pathMat(:,3)); turnAngles zeros(numWp, 1); for j 2:size(pathMat,1)-1 v1 pathMat(j, :) - pathMat(j-1, :); v2 pathMat(j1, :) - pathMat(j, :); cosAng dot(v1, v2) / (norm(v1)*norm(v2) eps); turnAngles(j-1) acos(max(-1, min(1, cosAng))); end avgTurn mean(turnAngles); stdLen std(segLen); % 这里用近似最小距离降低数据生成成本 approxMinDist minApproxDistToObstacles(pathMat, config.obstacles); X(i, :) [totalLen, heightVar, avgTurn, approxMinDist, stdLen]; Y(i) evaluatePath(pathMat, config); end end生成数据时要注意rand生成 0 到 1 之间的值需要放大到地图边界范围内。由于起点终点固定随机航点产生的路径大部分是折线碰撞频率较高这正是 DNN 需要的负样本。如果只采样平滑路径DNN 会低估碰撞带来的代价上升。approxMinDist可以用航点与球心的距离代替航段距离虽然会有误差但作为特征足够。3.3 fitrnet 回归模型构建与防过拟合R2025b 环境里fitrnet是官方支持的回归神经网络训练接口。注意不要使用LearnRate、MiniBatchSize、L2Regularization这些在其他框架里常见的参数R2025b 的 fitrnet 并不兼容。项目里应该用LayerSizes定义隐藏层尺寸用Activations指定激活函数用Standardize做特征标准化。rng(42); layers [64, 32]; % 两个隐藏层 net fitrnet(XTrain, YTrain, ... LayerSizes, layers, ... Activations, relu, ... Standardize, true, ... Verbose, 0); % 预测并计算误差 YPred predict(net, XTest); rmse sqrt(mean((YPred - YTest).^2));LayerSizes设置为[64, 32]表示第一个隐藏层 64 个神经元第二个 32 个。对 5 维输入、1 维输出的回归问题这个规模足够拟合非线性关系。Standardize必须设为true因为路径长度和障碍物距离的量纲差异很大。训练完成后用测试集算 RMSE如果 RMSE 明显大于训练集上的值说明过拟合此时需要增加数据量或减少隐藏层节点数。我一般会把数据按 8:2 划分训练集和测试集观察训练曲线是否平滑。3.4 GA 搜索主循环DNN 快速排序与真实评价复核GA 的染色体就是路径向量交叉采用算术交叉变异采用高斯扰动。每一代流程是提取特征、DNN 预测代价、按预测值排序、对前eliteRatio的个体执行真实评价、用真实代价覆盖预测值、选择父代、交叉变异生成下一代。function bestPath gaSearchWithDNN(config, net) popSize config.ga.popSize; numGen config.ga.numGenerations; numWp config.numWaypoints; chromLen numWp * 3; % 初始化种群 pop rand(popSize, chromLen) .* 2 - 1; % 归一化到 [-1, 1] for gen 1:numGen % 1. 特征提取 feat extractFeaturesBatch(pop, config); % 2. DNN 快速预测 predCost predict(net, feat); % 3. 精英个体真实评价 [~, sortIdx] sort(predCost); eliteNum max(1, floor(popSize * 0.1)); realCost zeros(popSize, 1); for k 1:eliteNum idx sortIdx(k); pathMat decodePath(pop(idx, :), config); realCost(idx) evaluatePath(pathMat, config); end % 4. 合并预测代价与真实代价 mergedCost predCost; mergedCost(realCost 0) realCost(realCost 0); % 5. 选择与交叉变异锦标赛选择 newPop zeros(size(pop)); for i 1:popSize p1 tournamentSelect(pop, mergedCost, 3); p2 tournamentSelect(pop, mergedCost, 3); child arithmeticCrossover(p1, p2, config.ga.crossoverProb); child gaussianMutation(child, config.ga.mutationProb, config.ga.mutationScale); newPop(i, :) child; end pop newPop; % 保存每代最优真实代价 bestRealCost(gen) min(mergedCost); end bestPath decodePath(pop(sortIdx(1), :), config); end这段代码里有个关键细节mergedCost不是简单的替换而是只把精英个体的真实代价写回数组。这样做能让种群既保留 DNN 的快速排序能力又不会让预测误差污染精英选择。tournamentSelect每次随机抽 3 个个体把代价最小的选为父代。算术交叉让子代取两个父代向量在某个点上的加权平均适合连续参数优化。高斯变异用mutationScale控制扰动幅度mutationScale如果太大算法就退化成随机搜索太小则容易早熟。4. ACO 局部优化在精英路径附近做信息素强化GA 搜出的是全局较优的骨架但航点精度有限因为遗传操作是基于概率的很难对某一段航线做微调。蚁群算法在这里的角色不是从头再搜一遍而是围绕 GA 的精英路径建立候选航点集合用信息素正反馈找到局部最优组合。4.1 蚁群算法在这里不从头搜索传统 ACO 需要定义完整的节点图让蚂蚁从起点开始逐点移动。场景大时节点数量爆炸效果并不好。项目里的做法是取 GA 精英路径的每个航点在它周围按一定半径生成若干个候选航点构成一个“走廊”。每只蚂蚁经过每一层时从候选点中选一个最终形成一条完整路径。这个设计把问题从大范围图搜索缩小成局部航点选择计算量大幅下降。4.2 候选航点集合与启发式信息候选航点生成可以理解为对精英路径做加扰采样。假设第i个航点为p_i候选集合就是p_i r * randn其中r是搜索半径一般设为航段长度的 10% 到 20%。启发式信息用当前候选点与上一航点、下一航点的距离差来衡量距离越短启发式值越高。这样蚂蚁会更倾向于选择让路径更平顺的点。function candidates generateCandidates(seedPath, numCand, radius) % seedPath: 精英路径矩阵, 尺寸 (N x 3) numPts size(seedPath, 1); candidates cell(numPts, 1); for i 1:numPts if i 1 || i numPts candidates{i} seedPath(i, :); % 起点终点固定 else noise randn(numCand, 3) * radius; candidates{i} seedPath(i, :) noise; end end endnumCand是每层候选点数量设成 5 到 10 即可。太大虽然搜索更细但蚂蚁循环次数会成倍增加。radius的设置要合适GA 路径本身质量不错半径过大会破坏原来的路径形态半径过小则局部寻优没有意义。我一般先用 0.8 倍的航段平均长度试跑观察收敛曲线的下降幅度再调整。4.3 信息素更新与局部路径修正蚂蚁循环流程分三步按照信息素和启发式概率选择每个航点组成完整路径用真实评价函数计算代价根据代价更新经过路径上的信息素。因为候选点集合每轮会变信息素不能直接存坐标而我习惯用一个二维矩阵存“第几层选第几个候选点”的频次信息。function [bestPath, bestCost] acoLocalSearch(seedPath, config, params) cands generateCandidates(seedPath, params.numCand, params.radius); numPts size(seedPath, 1); pheromone ones(numPts, params.numCand); for iter 1:params.maxIter % 每只蚂蚁独立选择路径 for ant 1:params.numAnts sel zeros(numPts, 1); sel(1) 1; sel(end) 1; for i 2:numPts-1 % 概率选择信息素 * 启发式^beta heur computeHeuristic(cands, i, sel(i-1)); prob pheromone(i, :) .^ params.alpha .* heur .^ params.beta; prob prob / sum(prob); sel(i) randsample(params.numCand, 1, true, prob); end % 组装路径 pathMat assemblePath(cands, sel); [cost, ~] evaluatePath(pathMat, config); % 更新当前代最佳 if cost bestCost bestCost cost; bestPath pathMat; end % 局部信息素衰减 pheromone pheromone * (1 - params.rho); % 增强本次选择的航点信息素 for i 2:numPts-1 pheromone(i, sel(i)) pheromone(i, sel(i)) params.Q / cost; end end end endalpha控制信息素权重beta控制启发式权重。通常alpha1、beta2。rho是挥发系数设 0.1 到 0.3 之间太小会导致信息素累积过多算法失去随机性太大则历史经验很快丢失蚂蚁像随机搜索。Q / cost是信息素增量代价越低的路线增益越高。需要注意的是这里cands每轮是固定的如果 GA 路径已经比较差ACO 只能在小范围内修正因此 GA 输出的骨架质量非常关键。4.4 与 GA、DNN 的衔接逻辑整个项目的算法流是一条链GA 用 DNN 加速搜索产出若干精英路径对每条精英路径执行 ACO 局部优化得到局部最优路线最后把所有局部最优路线统一做真实评价按综合代价排序选代价最小的作为最终结果。这个衔接关系可以用一个很短的伪代码表示。% 主流程 net trainDNN(config); [pop, realCost] gaSearchWithDNN(config, net); elitePaths extractElite(pop, realCost, 3); finalCandidates zeros(3, 3, length(elitePaths)); for i 1:length(elitePaths) seed decodePath(elitePaths{i}, config); [bestPath, ~] acoLocalSearch(seed, config, acoParams); finalCandidates{i} bestPath; end [finalCost, ~] evaluatePath(finalCandidates{1}, config);这里的finalCandidates是选了 3 条精英路径分别做 ACO 后的结果。为什么不是只选 1 条因为 DNN 预测存在误差GA 排名第一的路径未必是真实最优保留前几条做局部搜索能降低预测误差带来的风险。这属于工程上常用的“多候选并联”策略比单纯增加 GA 迭代次数更划算。5. 路线自动选择与 GUI 验证三个必查的调试点最终阶段做的事情不是画个图就结束而是让整个搜索流程能稳定复现。这里重点说三个我在调试项目时最容易出错的地方路线自动选择的判据、GUI 回调里数据刷新的时机、以及 fitrnet 兼容性相关的边界问题。5.1 自动选择最优路线的判据自动选择不能只看综合代价还要检查真实评价结果是否满足硬约束。综合代价里碰撞权重很高但理论上仍有极小概率出现碰撞代价被其他项抵消的情况。所以我会在自动选择函数里增加一个过滤条件collisions 0且minDist safeDist只有满足硬约束的路线才能进入候选列表。如果所有路线都碰撞再退而求其次选碰撞次数最少的路线并给出告警提示。validIdx find([metrics.collisions] 0 [metrics.minDist] config.safeDist); if ~isempty(validIdx) [~, bestIdx] min([cost(validIdx)]); finalCost cost(validIdx(bestIdx)); else [~, fallbackIdx] min([metrics.collisions]); finalCost cost(fallbackIdx); end这个判据放在 GA 与 ACO 组合完成之后是最后一道安全门。GUI 里“自动选择”按钮回调的主体逻辑就是这样先读地图参数再调规划函数最后把选中的路线绘制到三维坐标区并把各项指标输出到状态显示区。5.2 GUI 界面设计与数据刷新GUI 部分我用figure、uicontrol、surf、plot3实现没有用uilabel、uieditfield或uigridlayout这是为了兼容 R2025b 的稳定接口。界面布局上左侧是参数控制区包含起点终点坐标、障碍物数量、GA 代数、ACO 迭代次数、DNN 训练样本数等输入框右侧上方是三维地图区下方左边是收敛曲线区下方右边是状态信息区。规划完成后需要刷新三个对象三维地图上的旧路径线、收敛曲线上的旧曲线、状态区的文本信息。刷新时要先cla清除坐标区再重新绘制否则多次运行后图形会重叠。另一个容易忽略的点是输入框的数值读取。uicontrol的Style为edit时取到的内容是字符串必须用str2double转换。转换失败时会得到NaN如果直接传入decodePath路径修复逻辑会失效。所以我通常在读取函数里加一个校验if isnan(val) val defaultVal; end。5.3 调试中容易翻车的地方下表是实测中比较容易踩的问题和排查思路也是我建议新手入手时优先检查的位置。现象可能原因排查方法DNN 预测 RMSE 很大特征未标准化或隐藏层过浅开启Standardizetrue增大LayerSizesGA 收敛曲线早期下降很慢碰撞权重过低或变异尺度太大检查wCollision是否在 1000 以上ACO 优化后路径比 GA 差候选点半径过大或alpha过高缩小 radius 到航段长度 0.1 倍GUI 点击规划按钮无响应回调里排错了变量作用域将handles用guidata保存并在回调内更新最后一类兼容性问题值得单独说。R2025b 中fitrnet默认会做内部训练集划分如果样本量较小可能提示交叉验证参数设置问题。此时可以显式设置ValidationData把测试集传进去让训练过程显示验证损失。另外训练数据里的路径特征如果包含Inf或NaNfitrnet会直接报错。随机生成航点时如果出现两个航点重合diff算出的段长为 0转弯角度里的acos输入会变成NaN。我处理时在数值上加了eps并且在生成数据后做一次isfinite过滤把无效样本剔除。这也是为什么数据生成是 50000 条但真正进模型的可能少一点属于预期内的数据清洗。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价