简介面向无人机导航、路径规划与强化学习领域的技术研究人员和开发者内容围绕低空限制地图下的无人机避障问题给出基于Q-learning的三维航迹规划完整实现。涵盖状态空间、动作空间与奖励函数设计并基于Matlab完成仿真实验验证算法可有效规避障碍、平衡路径长度同时对收敛速度与参数敏感性等性能问题做了分析。资源压缩包共14个文件以10个Matlab脚本为主体配合3张运行结果图片和1份DOCX说明文档整体约242KB便于对照代码理解算法细节其中包括主程序、奖励函数与地形建模等模块。已有240人学习/下载可借助脚本复现Q-learning训练与航迹生成过程也可将结果图与文档作为撰写论文或项目报告时的参考素材适合作为Q-learning在低空无人机导航中落地的研究起点。1. 用Q-learning做无人机三维避障航迹规划它到底能解决什么问题无人机在低空执行任务时最头疼的往往不是飞不起来而是怎么在密密麻麻的禁飞区、高层建筑和地形起伏之间找出一条既安全又不绕太远的路。传统A*算法和人工势场法在二维平面表现不错一上三维就要面对状态空间爆炸和容易陷入局部极小值的问题。而Q-learning作为强化学习里最容易上手的一种算法不需要提前知道环境的全局模型靠不断试错就能逐步收敛出一条满足低空高度限制、避开障碍物的三维航迹——这正是它在无人机路径规划里越来越受关注的原因。这套技术方案的落地路径非常清晰把三维空域网格化让无人机在网格之间反复探索每走一步根据当前状态选择动作、获得奖励、更新Q值最终从收敛的Q表里提取出完整航迹。适合谁适合已经会用MATLAB跑普通脚本、但对强化学习还停留在理论层面的开发者也适合手头正好有一个低空航迹规划仿真任务、想找一个比A*更灵活方案的在读学生或算法工程师。核心代码量不大但坑不少——状态怎么编码、奖励怎么设计、Q表怎么防止内存爆炸每一样都决定了算法最后是收敛出漂亮航迹还是原地转圈。2. 环境建模低空限制地图和三维离散栅格怎么搭2.1 低空限制地图的本质是把哪些约束写进栅格低空限制地图不是单纯的一张地形图它至少包含三层信息数字高程模型DEM代表的起伏地表、固定高度禁飞区比如建筑群、信号塔周围、以及可以飞行但需要保持最小离地高度的空域。在做三维航迹规划时这些约束最后都要合成一个三维代价数组每个栅格要么标记为可通行要么标记为碰撞区再叠加一个高度权重让算法倾向在合理高度层飞行而不是一味往上爬。常见的做法是先把二维DEM数据读进来按任务需要的分辨率切分成网格比如每格20米。然后对每个网格判断它最低能飞多高、最高不能超过多少形成一个逐格的高度区间数组。禁飞区则直接把这个区间整体标记成非法。Q-learning里状态空间的每个点就是这些栅格中的一个可行位置无人机在这个离散空间里游走每个回合从起点出发尝试走到终点。MATLAB里可以用三维矩阵直接表达这个地图。下面这段代码生成一个简单的30×30栅格的示例环境其中地形高度用正弦函数模拟再叠加两个柱状障碍物最后生成三维占用地图。% 生成30x30的低空限制地图示例 gridSize 30; [x, y] meshgrid(1:gridSize, 1:gridSize); % 1. 地形高度用正弦组合模拟起伏范围0~30米 demHeight 10 15 * sin(x/4) .* cos(y/4); % 2. 禁飞区两个柱状建筑高度40米和50米 obstacleMap zeros(gridSize, gridSize); obstacleMap(10:14, 18:22) 40; obstacleMap(20:24, 8:12) 50; % 3. 最终最低安全高度地形以上至少5米禁飞区标为NaN minSafeHeight demHeight 5; minSafeHeight(obstacleMap 0) NaN; % NaN表示禁飞这段代码的逻辑是先铺地形再叠加禁飞区最后把禁飞区位置的最低安全高度设成NaN。参数里gridSize 30决定了状态空间的规模最小安全高度5米是低空任务里比较保守的离地距离设定。NaN的用法很关键——在后续Q表初始化时可以直接把非法状态排除掉避免无人机在初始化阶段就踩进禁飞区。2.2 高度层离散把三维空间切成可数的状态点三维航迹规划的难点在于状态数量随着栅格分辨率呈三次方增长。如果x、y各取50格高度层取30层总状态量就有75000个Q表矩阵的大小是状态数×动作数勉强还能接受但如果分辨率提高到100格×100格×50层状态数直接来到50万Q表开始变得臃肿训练时间也会成倍拉长。所以高度层的划分必须结合任务需求来定不是越细越好。我一般会先把任务空域的物理高度范围定下来比如从最低安全高度的上限15米到最大飞行高度120米然后每隔10米分一层得到11层。这样做的好处是状态空间可控而且10米的间隔对于多数低空物流或巡检场景已经够用。如果你面对的场景需要更高的高度精度可以在局部区域做二次细化而不是全局加密。具体到MATLAB实现高度层用一个简单向量表示同时记录每层的物理高度值方便后期把栅格坐标转换回经纬度或平面坐标。% 高度层离散15米到120米每10米一层 altLayers 15:10:120; nLayers length(altLayers); % 把2D地图扩展成3D占用矩阵size [gridSize, gridSize, nLayers] occupancy3D false(gridSize, gridSize, nLayers); for k 1:nLayers currentAlt altLayers(k); % 禁飞区(高度为NaN)或低于最小安全高度的层都标为占用 layerMap minSafeHeight currentAlt | isnan(minSafeHeight); occupancy3D(:, :, k) layerMap; end这里有一个比较容易忽略的细节occupancy3D中true代表该位置不可通行。MATLAB的false默认是逻辑0三维逻辑矩阵做碰撞检测时非常高效直接索引就能判断不需要用find去搜索。三层循环不是必须的向量化写法性能更好但在学习阶段保持可读性比省那几十毫秒更重要。2.3 状态编码三维坐标到单索引的映射方法Q-learning的Q表是二维矩阵行是状态、列是动作所以必须把三维栅格坐标编码成单一状态编号。这个操作看似简单却直接影响后续代码的简洁程度和调试效率。最常用的方案是线性索引编码把(x, y, z)转换成一个整数公式为idx x (y-1)*gridSize (z-1)*gridSize*gridSizeMATLAB里直接调sub2ind即可反向用ind2sub还原坐标。这样编码的优点是整个航迹规划过程只用整数做运算不需要维护复杂的数据结构。配合宽度优先搜索或A*的其他数据结构也能直接复用。需要注意的是如果地图里存在非法状态编码后状态编号会不连续Q表里对应行应该被置为-inf或直接不入表。这就引出了一种更节省内存的方案用容器组containers.Map只给合法状态分配编号。% 使用containers.Map把合法状态映射为连续索引压缩Q表尺寸 stateMap containers.Map(KeyType, char, ValueType, double); stateList []; count 0; for x 1:gridSize for y 1:gridSize for z 1:nLayers if ~occupancy3D(x, y, z) count count 1; key sprintf(%d_%d_%d, x, y, z); stateMap(key) count; stateList(count, :) [x, y, z]; end end end end nStates count;容器组方案相比直接用sub2ind最大的优势在于Q表行数等于合法状态数而不是全栅格数。比如上面例子中30×30×11共9900个格子减去禁飞区后合法状态可能只有8000多差别不明显但换成100×100×50的稠密栅格后压缩比就非常可观了。stateList用来记录每个状态对应的三维坐标后面做航迹回溯时直接查表不需要重新调用ind2sub。3. Q-learning算法核心状态、动作、奖励函数与收敛判定3.1 状态空间和动作空间怎么根据真实飞行约束定义状态空间就是上一章建出来的合法三维位置集合每个状态本质是一个栅格坐标。而动作空间需要结合无人机动力学来设定常用于低空航迹规划的离散动作是六方向移动前、后、左、右、上、下每个动作移动一个栅格步长。有些任务还会加入斜向运动比如前后左右的同时攀升或下降让航迹更顺滑但这会增加动作数到18个甚至26个训练难度也同步上升。对低空任务来说六方向动作其实是性价比最高的选择。因为它已经覆盖了三维空间中所有基本运动方向斜向平滑效果完全可以在路径提取后用B样条曲线处理用不着在Q-learning阶段引入那么多复杂度。动作编号直接映射成坐标偏移量存在一个actions矩阵里既方便查表也方便更新坐标。% 六方向动作每行对应[x偏移, y偏移, z偏移] actions [ 1 0 0; % 前 -1 0 0; % 后 0 1 0; % 左 0 -1 0; % 右 0 0 1; % 上 0 0 -1]; % 下 nActions size(actions, 1);动作定义里上下方向的偏移是±1层对应实际物理高度±10米。如果你希望垂直方向的运动更平缓可以把上下动作的步长拆分成分层爬升比如每次只移动5米对应的层数但这要求高度层划分密度足够。六方向动作的边界约束也要在实现时同步处理——越界动作要么禁止并返回额外惩罚要么只允许执行不越界的动作这个决策直接关系到奖励函数的写法。3.2 奖励函数设计稀疏奖励和稠密奖励的取舍奖励函数是Q-learning航迹规划里最影响收敛效果的设计点。一个常见的失败案例是只用稀疏奖励——到达终点给100、撞障碍物给-100、其他情况给0。这种设计思路简单但无人机在前几百个回合里几乎得不到有效反馈探索全靠运气收敛速度令人崩溃。反过来如果每一步都根据位置给细粒度奖励又容易让算法钻奖励漏洞比如一路盘旋避免扣分。我在实际处理这类问题时用的是分层奖励结构。底层是每走一步的小惩罚用来促使算法找最短路径中间层是靠近目标的正向奖励用来引导探索方向顶层是到达终点的正反馈和碰撞的负反馈用来保证最终行为正确。这四层拼一起既不过度稀疏又不容易被钻空子下面给出完整的奖励计算函数。function reward getReward(s_next, isDone, stepReward, distPrev, distNext) % 基础步长惩罚鼓励少走弯路 reward stepReward; % 例如 -0.1 % 如果撞到障碍物或越界直接给大惩罚 if isDone reward -100; return; end % 到达终点正反馈 距离引导奖励 if s_next.distToGoal 0.5 reward 100; else % 距离缩短则给正向增量距离拉远给负向增量 reward reward (distPrev - distNext) * 2; end end参数说明stepReward是每步固定惩罚取值在-0.1到-1之间。取-0.1时路径长度对总奖励影响占比小算法更偏向避障安全取-1时路径长度影响大航迹会更短但可能贴着障碍物走。距离引导项的系数2是一个经验值环境规模越大这个系数应该越大否则距离引导作用会被步长惩罚淹没。distPrev - distNext的含义是「这一步离目标近了多少」正值表示靠近负值表示远离。奖励设计的另外一个关键点是障碍物碰撞的惩罚应该大于到达终点的奖励绝对值。比如终点100、碰撞-100算法在极端情况下可能会选择穿越障碍物然后到达终点总奖励为零。把碰撞惩罚设到-150甚至-200才能让安全优先级真正压过到达目标。越界同样按碰撞处理不能让无人机绕出地图边界再飞回来。3.3 Q-learning迭代主循环ε-greedy策略和Q值更新公式Q-learning的核心迭代方程是Q(s,a) ← Q(s,a) α[R γ·max Q(s′,a′) − Q(s,a)]其中 α 是学习率γ 是折扣因子。这个公式的含义是当前状态动作对的价值等于原有估计加上实际奖励和下一状态最优估计的折现再减去原有估计的误差修正。无人机在环境中反复试错Q表逐步逼近真实的状态动作价值函数。探索策略我用的是经典的ε-greedy以概率ε随机选择动作以1-ε选择当前Q值最高的动作。ε的衰减非常关键刚开始要让无人机多尝试后面逐渐收敛到确定性策略。最常见的问题就是把ε设成固定值导致算法永远无法从探索模式切换到利用模式。正确做法是随回合数线性或指数衰减比如从0.9衰减到0.01。下面给出一个完整的Q-learning训练主循环这是整个项目的核心代码包含探索、动作执行、状态转移、Q值更新四件事。% Q-learning训练主循环 alpha 0.1; % 学习率 gamma 0.9; % 折扣因子 epsilon 0.9; % 初始探索率 epsilonMin 0.01; epsilonDecay 0.995; maxEpisodes 2000; maxStepsPerEpisode 200; Q zeros(nStates, nActions); % Q表初始化 for episode 1:maxEpisodes % 每个回合重置起点随机选合法起点距离终点足够远 startIdx randi(nStates); [sx, sy, sz] deal(stateList(startIdx, 1), stateList(startIdx, 2), stateList(startIdx, 3)); % 如果起点离目标太近就重选 while abs(sx - goalPos(1)) abs(sy - goalPos(2)) abs(sz - goalPos(3)) 5 startIdx randi(nStates); [sx, sy, sz] deal(stateList(startIdx, 1), stateList(startIdx, 2), stateList(startIdx, 3)); end currentState startIdx; totalReward 0; for step 1:maxStepsPerEpisode % ε-greedy选择动作 if rand epsilon action randi(nActions); % 探索随机动作 else [~, action] max(Q(currentState, :)); % 利用Q值最大动作 end % 计算下一状态坐标 nextPos stateList(currentState, :) actions(action, :); if nextPos(1) 1 || nextPos(1) gridSize || ... nextPos(2) 1 || nextPos(2) gridSize || ... nextPos(3) 1 || nextPos(3) nLayers % 越界给惩罚更新Q值后跳过该步 Q(currentState, action) Q(currentState, action) alpha * (-100 - Q(currentState, action)); continue; end % 检查碰撞 if occupancy3D(nextPos(1), nextPos(2), nextPos(3)) Q(currentState, action) Q(currentState, action) alpha * (-100 - Q(currentState, action)); currentState startIdx; % 撞了直接回到起点 continue; end % 计算下一状态编号和奖励 nextKey sprintf(%d_%d_%d, nextPos(1), nextPos(2), nextPos(3)); nextState stateMap(nextKey); distPrev abs(sx - goalPos(1)) abs(sy - goalPos(2)) abs(sz - goalPos(3)); distNext abs(nextPos(1) - goalPos(1)) abs(nextPos(2) - goalPos(2)) abs(nextPos(3) - goalPos(3)); isDone distNext 0.5; reward getReward([], isDone, -0.1, distPrev, distNext); % Q值更新 Q(currentState, action) Q(currentState, action) alpha * (reward gamma * max(Q(nextState, :)) - Q(currentState, action)); totalReward totalReward reward; currentState nextState; if isDone break; end end % 探索率衰减 epsilon max(epsilonMin, epsilon * epsilonDecay); end这段代码有几个值得注意的参数选择。学习率α0.1是强化学习里比较稳的取值α太大会导致Q值震荡不收敛太小则收敛慢。折扣因子γ0.9意味着算法看重未来100步左右的累积奖励如果任务路径特别长γ可以调到0.95甚至0.99。maxEpisodes2000对30×30×11的简单环境足够如果换成更大栅格可能需要5000回合以上。碰撞处理这里用的是「撞了直接回起点」的策略这是Q-learning在路径规划里常用的做法。回起点而不是停在原地可以让无人机更快地重新探索不至于在一个死胡同上反复撞墙浪费时间。每回合200步的上限也是防止无人机在空旷区域无限绕圈超过步数强制开始下一回合。4. 三维航迹生成与结果分析从Q表到可视化路径4.1 最优策略提取贪心决策下的航迹回溯训练完成后Q表已经收敛提取航迹的过程不需要再随机探索直接每一步选择Q值最大的动作即可。具体流程是从起点出发查询当前状态的Q表行找出最大值对应的动作移动到下一个状态重复这个过程直到到达终点或达到步数上限。这里容易踩的一个坑是Q表在未收敛区域可能所有Q值都为零或接近零导致贪心策略变成随机游走。所以在提取航迹前先检查一下起点对应的Q表行是否存在明显的高值动作如果整行最大值都接近0说明训练还没收敛需要增加回合数或调整奖励参数。% 从收敛的Q表提取最优航迹 path []; currentState startIdx; visited zeros(nStates, 1); % 防止死循环 for step 1:maxStepsPerEpisode if visited(currentState) break; % 回到已访问状态说明陷入死循环 end visited(currentState) 1; [~, action] max(Q(currentState, :)); nextPos stateList(currentState, :) actions(action, :); % 越界检查 if nextPos(1) 1 || nextPos(1) gridSize || nextPos(2) 1 || nextPos(2) gridSize || nextPos(3) 1 || nextPos(3) nLayers break; end if occupancy3D(nextPos(1), nextPos(2), nextPos(3)) break; % 不应该发生但保留检查 end path(end1, :) stateList(currentState, :); currentState stateMap(sprintf(%d_%d_%d, nextPos(1), nextPos(2), nextPos(3))); % 到达目标 if norm(nextPos - goalPos) 1.5 path(end1, :) goalPos; break; end end路径提取的关键变量是path它逐行记录无人机经过的栅格坐标。visited数组的作用是防止贪心策略在Q表未收敛的区域陷入循环——如果算法在两个状态之间来回跳这个数组会检测到并终止提取流程。提取出的path是离散栅格点序列还需要做两个后处理把栅格坐标转换回物理坐标乘以分辨率再用平滑算法把折线路径变成连续曲线。4.2 收敛性分析累积奖励曲线和Q值变化趋势怎么看结果分析不只是把路径画出来就算完成更关键的是确认训练过程真正收敛了。我会在每个回合并后记录当回合的总奖励totalReward和到达终点的标志isArrived然后画两条曲线一条是总奖励随回合数的变化曲线另一条是最近100回合的到达率滑动平均。收敛的标准有三个第一总奖励曲线从开始的剧烈波动逐渐变得平稳第二到达率最终稳定在80%以上第三单回合步数逐渐缩短并稳定在一个较低水平。如果曲线始终剧烈震荡先检查学习率是不是太大如果曲线稳定但到达率很低说明奖励引导出了问题比如距离引导系数太小无人机找不到朝目标方向的动力。% 训练过程记录和绘图 % recordTotalReward 和 recordArriveNum 在训练循环中逐回合记录 figure; subplot(1,2,1); plot(1:maxEpisodes, recordTotalReward); xlabel(回合数); ylabel(总奖励); title(Q-learning累积奖励收敛曲线); grid on; subplot(1,2,2); windowSize 100; arriveRate movmean(recordArriveNum, windowSize); plot(1:maxEpisodes, arriveRate); xlabel(回合数); ylabel(到达率); title([到达目标比率 num2str(windowSize) 回合滑动平均]); grid on;movmean是MATLAB自带的滑动平均函数窗口设为100回合能有效滤掉单回合的随机波动让收敛趋势更明显。到达率指标比总奖励更直观因为它直接反映了算法最终能不能完成任务。如果到达率后期稳定在0.9以上说明策略已经可靠如果一直在0.5附近徘徊大概率是探索率衰减过快或者起点分布太难。4.3 航迹三维可视化用plot3把结果画出来最后一个环节是把提取出的航迹放到三维地图上展示。MATLAB里用plot3把路径画成三维折线scatter3画出禁飞区障碍物再用surf绘制地形曲面。颜色的选择要突出航迹本身——路径用粗线条障碍物用半透明色块地形用浅色网格。% 三维航迹可视化 figure; % 先画地形曲面 surf(x, y, demHeight, EdgeColor, none, FaceAlpha, 0.4); hold on; % 画禁飞区柱体简化为顶部平面 for i 1:gridSize for j 1:gridSize if obstacleMap(i,j) 0 plot3(i, j, obstacleMap(i,j), rs, MarkerSize, 8, MarkerFaceColor, r); end end end % 画航迹 pathCoords [path(:,1), path(:,2), altLayers(path(:,3))]; plot3(pathCoords(:,1), pathCoords(:,2), pathCoords(:,3), b-o, LineWidth, 2.5, MarkerSize, 4); % 标记起点和终点 scatter3(startPos(1), startPos(2), altLayers(startPos(3)), 80, g, filled); scatter3(goalPos(1), goalPos(2), altLayers(goalPos(3)), 80, m, filled); xlabel(X (格)); ylabel(Y (格)); zlabel(高度 (米)); title(Q-learning三维避障航迹); view(45, 30); % 调整3D视角 grid on;可视化阶段做的就是把抽象状态编号还原成物理坐标。注意altLayers(path(:,3))这一步path里存的是层编号要映射回物理高度才能和地形、障碍物画在同一坐标系里。view(45,30)设置的视角要能在航迹、障碍物和地形三者之间找到平衡实际调试时多切换几个角度看会不会有障碍物遮挡关键航段。5. 常见问题与避坑指南Q-learning航迹规划的五处深坑5.1 Q表爆炸导致内存不足现象把栅格分辨率设得太高时MATLAB报内存不足错误或者训练速度慢到无法接受。原因Q表大小是合法状态数×动作数。100×100×50的栅格外加26个动作Q表双精度矩阵直接逼近几十GB普通办公电脑根本扛不住。解决先用containers.Map压缩合法状态并只编号合法点不要用全部栅格然后去掉斜向动作只保留六方向最后在需求允许的情况下降低高度层密度。我一般会把高度层控制在10层以内横向分辨率也别低于20米——无人机航迹规划不是越精确越好够用就成。5.2 奖励函数系数不当导致算法「钻空子」现象航迹明明收敛了但路径贴着障碍物边缘飞离障碍物只有一两个栅格的距离或者无人机在高空平飞不下降。原因步长惩罚设得太大算法为了减少步数宁愿冒险贴近障碍物或者高度层的负向引导不够无人机发现长期停留在中间高度层最「安全」失去了下降动力。解决把碰撞惩罚从-100提高到-200同时把每步惩罚降到-0.05让安全优先于路径长度在奖励函数里加入高度倾向项当无人机位于最低安全高度附近时额外给一点正向奖励鼓励它保持低空飞行而不是一直在中间层盘旋。5.3 起点到终点之间必须穿越禁飞区现象地图中存在完全包围目标的禁飞区起点和目标点之间物理上不可能连通Q-learning永远训练不出可到达的路径。原因这不是算法问题是任务设计问题。任何路径规划算法都无法穿越物理上无法连通的空间。解决训练前先用快速连通性检查——用宽度优先搜索或bwlabeln对二进制占用矩阵做连通域标记确认起点和终点属于同一个连通域。如果它们不在同一连通域要么放宽禁飞区要么换地标起点和终点。我在做这类任务时会把这一步放在所有工作之前省得白跑几小时训练。5.4 ε衰减过快导致探索不足现象训练前期到达率看起来在上升但400回合后突然停滞最终只有40%左右的回合能到达目标且无论怎么增加回合数都不见改善。原因ε衰减因子设置得太小比如0.9指数衰减十几回合后探索率就跌到了接近0.01的水平无人机过早进入纯利用模式后续遇到未探索区域时没有随机性去覆盖相当于停在一个局部最优策略上。解决把衰减因子改成0.995或0.998并给ε设置一个更低的底值例如0.005。同时可以加一个「如果连续100回合到达率没有提升就临时调高探索率」的动态机制帮助无人机跳出局部最优。5.5 三维路径穿越建筑物侧面现象从三维视角看航迹紧贴着柱状障碍物侧面拐过去视觉上很惊险转成实际飞行参数后无人机几乎没有余量应对风扰和定位误差。原因栅格地图里路径规划只考虑「碰撞/不碰撞」布尔判断没有考虑安全距离裕量。只要目标栅格不同时占据路径就会贴着障碍物走。解决在生成occupancy3D时对障碍物做膨胀处理——用imdilate对这个障碍物区域做形态学膨胀让禁飞区在每一层向外多扩展1~2个栅格。膨胀半径根据无人机尺寸和定位精度定通常取2个栅格。这样规划出的路径天然带安全裕量比事后平滑处理更可靠。6. 从仿真到可用奖励整形、参数调优和航迹平滑的三板斧6.1 用奖励整形加速收敛势场引导和中间航点Q-learning在状态空间较大时收敛慢的根源是探索的盲目性。纯ε-greedy探索在空旷大环境中有一大半动作是无效的等于在浪费回合。奖励整形是对抗这个问题的标准手段核心思路是在原始奖励里加入势场引导项让无人机在距离目标逐渐靠近时获得持续正向反馈。最常见的做法是用距离的负梯度作为引导r_potential η * (d_prev - d_next)其中d_prev和d_next分别是上一步和当前步到目标的距离η是势场增益系数。这样无人机每靠近目标一步都能得到正向反馈即使最终没到达终点它也会逐步学会朝目标方向移动而不是完全随机游走。η的取值和栅格分辨率相关当栅格间距为20米、目标距离用曼哈顿距离计算时η取1~3效果都比较稳定。另一个稳定的技巧是设置中间航点。在地图复杂、单次学习很难遍历整个状态空间时把路径切成起点到中间点、中间点到终点两段分别训练。每段的状态空间更小Q表也相应砍半训练效率翻倍。最后把两段航迹拼接起来压掉中间点周围的一两个栅格做平滑过渡。6.2 参数调优顺序和敏感性认识Q-learning在航迹规划问题上可调的参数不多但每个参数对结果的影响巨大。我的调优顺序是固定的先定奖励函数再调探索率衰减最后微调学习率和折扣因子。奖励函数是地基地基错了后面怎么调都别扭探索率衰减决定算法能不能找到全局最优比学习率的影响还大学习率和折扣因子在0.05~0.2和0.85~0.99的常见范围内对最终结果影响相对温和不需要太高精度。一个实用习惯是每次只动一个参数固定其他参数跑完整个训练记录到达率和平均路径长度然后对比再做下一个调整。不要同时改两个参数否则你就是靠感觉在做实验出了问题根本定位不到原因。我会准备一个简单的参数对比脚本批量跑几种组合并把结果汇总成表用数据说话少凭感觉拍板。6.3 航迹平滑三次样条还是B样条Q-learning输出的是离散栅格折线直接给飞控用会很别扭——无人机在拐角处需要急剧改变方向实际飞控根本执行不了这么激进的动作。平滑处理的目的就是把折线变成有界曲率的连续曲线。我用的是MATLAB自带的三次样条插值对路径坐标分别做平滑但直接把全部路径点丢给样条插值会带来震荡问题因为折线拐角处导数的剧烈变化会让样条产生过冲。正确做法是先用路径压缩去掉冗余的共线点只保留真正改变方向的拐点再对这些拐点做三次样条插值。% 路径压缩保留方向变化的拐点去掉共线点 compressedPath path(1, :); for i 2:size(path, 1) - 1 v1 path(i, :) - path(i-1, :); v2 path(i1, :) - path(i, :); % 方向变化超过阈值则保留该点 if norm(cross(v1, v2)) 0.5 compressedPath(end1, :) path(i, :); end end compressedPath(end1, :) path(end, :); % 对压缩后的拐点做三次样条插值 nSamples 200; xq linspace(compressedPath(1,1), compressedPath(end,1), nSamples); xSmooth spline(compressedPath(:,1), xq); ySmooth spline(compressedPath(:,2), xq); zSmooth spline(compressedPath(:,3), xq);路径压缩的判定标准是前后两点向量之间的叉积模长叉积数值越大说明方向变化越剧烈超过0.5就保留该点。这个阈值可以调整阈值越大保留的拐点越少、曲线越平滑但可能会削掉必要的避障细节。样条插值出来的曲线可能存在轻微穿越障碍物边缘的情况最后还需要对平滑后路径做一次碰撞检测若某段穿障则在该段多保留一个原始栅格点重新平滑。这层「压缩-样条-碰撞校验-修复」的循环是我处理航迹平滑的固定流程。这个项目做到最后我最大的教训是不要一上来就调算法先把地图约束想清楚。地图膨胀半径、高度层密度、惩罚函数系数这些环境参数比算法参数对结果的影响至少大三倍。先把环境调合理Q-learning哪怕用最朴素的默认参数都能收敛出满意路径环境不合理再花哨的强化学习改动都是给烂地基装修。希望帮到你。本文还有配套的精品资源点击获取 SEO 优化官网定制响应式建站教育培训建站