Q-learning无人机三维航迹规划:MATLAB低空避障地图实现与避坑指南

📅 发布时间:2026/10/18 23:50:41
Q-learning无人机三维航迹规划:MATLAB低空避障地图实现与避坑指南
简介面向无人机导航、路径规划及强化学习方向的技术研究与开发人员尤其适合已了解Q-learning基本思想、希望将算法落地到三维航迹规划的读者这套基于Matlab的完整方案覆盖了从低空环境建模、障碍物分布设定到算法验证与结果分析的闭环流程。整包共14个文件核心为10个Matlab脚本分别承担地形建模、障碍物设置、奖励函数设计、Q表更新与主流程串联另附3张仿真结果截图和1份学术说明文档便于直观对比避障效果并查阅实验分析。资源总计242KB轻量便携已有240人学习下载。借助该资源可理解状态空间、动作空间与奖励函数的设计细节并完整复现Q-learning训练与测试流程仿真结果表明算法能有效避开低空限制区与障碍物兼顾路径安全与航迹长度。文档还结合运行结果讨论了收敛速度、参数敏感性等算法局限给出后续改进方向便于研究者依自身场景做参数调优与课题延伸。1. 为什么说Q-learning做无人机三维航迹规划瓶颈不在算法在地图粒度第一次用Q-learning做无人机低空限制地图避障时我跑了一整晚训练第二天看轨迹图无人机在山谷里绕了二十多圈才到终点还贴着禁飞区边缘飞。后来才发现问题根源不在算法收敛性而在地图建模粒度——栅格选得太细状态空间膨胀到Q表装不下选得太粗路径直接穿山。这篇笔记围绕MATLAB环境下用Q-learning做低空限制地图避障的完整链路展开从DEM地形栅格化、禁飞区数字化、限高面叠加到动作空间与奖励函数设计、训练循环实现、运行结果判读再到五条高频踩坑的排障记录。适合正在做无人机三维航迹规划课题、想用强化学习替代传统规划算法、或者已经跑通二维Q-learning正准备向三维扩展的开发者。2. 为什么三维航迹要选Q-learning从MDP建模到收敛条件2.1 Q-learning与A*、人工势场的选型差异全局已知时它并不占优先说结论如果地图完全已知、长期不变、状态空间不大A会比Q-learning快得多也稳得多。Q-learning的价值不在更快找到路径而在学会一种策略。同一张低空限制地图上要反复规划不同起终点的航线时A每次都要全图搜索Q-learning只需要查表训练一次长期复用。这个差异在三维栅格地图上尤其明显因为三维A*的启发函数设计难度比二维高而且禁飞区、限高面这类叠加约束很难写进启发式里。人工势场法的问题则是局部极小。无人机在狭窄山谷或者禁飞区包围的走廊里引力势场和斥力势场很容易出现零合力点无人机悬停或震荡。Q-learning不依赖势场模型它把规划建模成序贯决策通过试错积累Q值天然跳过力平衡这个陷阱。代价是训练时间但训练时间是离线成本运行时的查表决策是微秒级的。2.2 状态、动作与奖励把无人机飞行决策写成MDPQ-learning处理三维航迹规划核心是把问题映射成马尔可夫决策过程四元组。状态空间定义为离散化的三维栅格坐标动作空间定义为相邻栅格间的位移状态转移在确定性环境下概率为1奖励函数则是地形代价、禁飞区惩罚、目标奖励、航程代价的加权组合。映射关系如下状态s是栅格坐标(x, y, h)h是高度层动作a是向六个邻域移动一格奖励r由当前动作的结果决定。折扣因子γ设为0.9时未来第10步的奖励折现后只剩约0.35这意味着算法更看重近期收益轨迹会倾向于尽快接近目标而不是绕远路。三维与二维的本质差别不在算法而在状态空间规模——高度维把栅格数乘了一个因子Q表增长是乘积性的这正是三维实现里最容易失控的地方。2.3 ε-greedy与Q表更新训练收敛的两个观察点Q-learning的更新公式是Q(s,a) ← Q(s,a) α[r γ·max(Q(s,a)) - Q(s,a)]。括号里的差值叫时间差分误差表示实际收益与原有估计的偏差。α是学习率控制每次更新的修正幅度γ是折扣因子控制远期收益的权重。训练采用ε-greedy策略以概率ε随机探索以概率1-ε选择当前Q值最大的动作。ε从0.9起步按轮次衰减到0.1左右保证前期充分探索、后期稳定利用。收敛观察有两个可靠指标。一是Q表变化量每隔若干轮计算一次全表更新幅度幅度趋近于零说明Q值稳定二是成功率曲线用当前策略在测试集上跑如果成功率稳定在某个区间不再上升基本可以判断训练完成。只看累计奖励曲线不靠谱因为奖励塑形会让数值偏高后面第5章会专门讲这个坑。3. 低空限制地图建模DEM栅格化与禁飞区数字化3.1 低空限制地图的数据结构地形、禁飞区与限高面低空限制地图和普通障碍物地图的区别在于约束来自三个叠加的层面。第一个层面是地形高程地图范围内有一座山或连续山脊时飞行高度低于地形的栅格全部不可通行第二个层面是禁飞区水平投影落在禁飞区内的所有高度层都不可通行这是对空域规则的硬约束第三个层面是限高面空域管理会给一个最大飞行高度超过这个高度层的栅格全部不可通行。三层约束叠加之后三维栅格中每个格子要么可通行要么不可通行没有中间状态。实现时我一般用一个三维整数矩阵map3D表示可通行性取值只有两种0表示可通行-1表示不可通行。用-1而不是1是为了方便和地形高度值区分排查问题时用disp观察切片更直观。生成地图的先后顺序不影响结果但逻辑上按地形阻挡→禁飞区→限高面依次置-1容易对应到实际业务规则也方便别人接手代码时理解建图依据。3.2 MATLAB地形环境生成从高程矩阵到三维栅格下面这段MATLAB代码生成一张40×40网格、10个高度层的低空限制三维地图。地形用两个高斯函数叠加模拟山峰实际应用时把这个位置替换成读入的DEM矩阵即可。禁飞区用二维逻辑掩膜表示限高面通过直接置空最高两层来模拟。% 低空限制三维地图生成 gridSize 40; % 水平方向栅格数 layerMax 10; % 最大高度层数 % 1) DEM地形模拟真实场景替换为读入的DEM矩阵并裁剪到gridSize [xg, yg] meshgrid(1:gridSize, 1:gridSize); terrain 2 3*exp(-((xg-12).^2 (yg-25).^2)/80) ... 2*exp(-((xg-30).^2 (yg-8).^2)/100); terrain ceil(terrain); % 高度换算成层数向上取整 % 2) 禁飞区掩膜true表示该水平位置禁止穿越 noFly false(gridSize, gridSize); noFly(30:35, 5:10) true; noFly(12:18, 28:33) true; % 3) 合成三维可通行性矩阵0可通行-1不可通行 map3D zeros(gridSize, gridSize, layerMax); for h 1:layerMax for ix 1:gridSize for iy 1:gridSize if h terrain(ix, iy) % 地形阻挡低于地形高度的栅格 map3D(ix, iy, h) -1; end if noFly(ix, iy) % 禁飞区整柱不可通行 map3D(ix, iy, h) -1; end end end end % 4) 限高面以上不可通行假设最高飞到第8层 map3D(:, :, 9:layerMax) -1; % 5) 起点与终点坐标必须验证可通行 start [4, 4, 6]; % (x, y, heightLayer) goal [36, 36, 5];代码里地形高度terrain向上取整意味着高度等于地形层数的格子已经是障碍飞行只能在地形之上。这是故意留的1格安全裕度后面第5章会讲到膨胀处理时再展开。禁飞区用二维掩膜乘到三维矩阵上写法虽然用了双重循环但逻辑直观地图规模不大时性能可接受。起点终点选好后最好加一行断言强制验证assert(map3D(start(1), start(2), start(3)) 0, 起点不可通行); assert(map3D(goal(1), goal(2), goal(3)) 0, 终点不可通行);这一步能省掉后面大量无效训练时间。起点都不可通行时整个训练过程都是在学习怎么从障碍里逃出去成功率永远上不去而且问题非常隐蔽。3.3 分辨率选择状态空间规模与路径可行性的权衡三维Q-learning最需要提前想清楚的是栅格分辨率。分辨率直接决定状态空间大小而状态空间又决定Q表内存和训练轮次。下表是同一张2000m×2000m地图在不同分辨率下的规模估算动作数按6邻域计算Q值用double类型占8字节水平栅格尺寸高度层数状态空间Q表内存路径质量50m1016000约0.77MB锯齿感明显需后处理平滑25m20128000约6.1MB可接受训练时间明显增加10m502000000约96MB内存压力大不推荐直接建Q表我常用的经验法则是水平分辨率取飞行器最小转弯半径的2倍左右高度层数控制在5~15层。转弯半径决定了无人机在原地绕过障碍需要的最小空间栅格小于这个值会让规划出的轨迹在物理上不可执行。高度层数不要跟着水平分辨率等比缩放因为大部分低空任务的高度范围本身就有限比如50m到400m按50m一层切10层就够用。如果任务要求10m级精度正确做法不是硬撑Q表而是用50m栅格粗规划出一条走廊再在走廊内部做局部精修。这个思路第6章还会用到。4. Q-learning三维航迹规划实现动作集、奖励塑形与训练循环4.1 动作空间设计6邻域还是26邻域三维栅格上的动作空间有两个常用选择。6邻域对应前后左右上下六个方向每次移动距离为一个栅格单位实现简单、转移逻辑清晰、训练收敛快缺点是轨迹只能走直角折线看起来不太自然。26邻域把对角方向和斜上斜下也纳入动作集轨迹更光滑但动作选择变多、探索变慢而且对角移动的欧氏距离比轴向长奖励计算需要对移动距离做归一化否则无人机倾向于斜着飞。我的建议是第一次实现用6邻域把训练流程、奖励函数、结果判读全部跑通之后再扩展成26邻域或者加上水平8邻域作为中间方案。扩展时只需要改动作表和转移函数Q表维度和更新公式不用动。还有一个边界细节动作执行后如果出了地图边界或者落入-1格状态不变但给予碰撞惩罚这个惩罚和撞山一视同仁。很多实现里把出界动作直接跳过这会让无人机学会贴边飞行去蹭边界因为出界没有代价。% 6邻域动作定义每行对应一个方向的坐标增量 actions [1, 0, 0; % x -1, 0, 0; % -x 0, 1, 0; % y 0,-1, 0; % -y 0, 0, 1; % h 爬升 0, 0,-1]; % -h 下降动作表本身是普通矩阵训练循环里按动作编号取出增量加在当前位置上就是下一个候选状态。用矩阵而不是switch-case方便后期把6行扩成26行训练代码一行都不用改。4.2 奖励塑形公式每步代价、碰撞惩罚与目标奖励的配比奖励函数是整个Q-learning实现里最像玄学的部分但它也是三维航迹规划中决定成败的核心。纯稀疏奖励——只有到达目标给正奖励、碰撞给负奖励——在三维栅格空间里收敛极慢因为agent在16000个状态里随机游走碰到目标的概率太低。实际实现必须加距离引导项让agent每走一步都能感知自己是在靠近目标还是远离目标。function reward getReward(nextState, goal, isCollision, dOld, dNew) if isCollision reward -50; % 碰撞惩罚 elseif isequal(nextState, goal) reward 100; % 到达目标 else reward -1 0.3 * (dOld - dNew); % 航程代价 距离引导 end end参数配比有一个基本逻辑碰撞惩罚-50与目标奖励100意味着一次碰撞的代价相当于50个普通步的累计代价让agent对障碍有足够强的回避倾向。每步-1是航程代价防止无人机为了刷距离引导奖励原地转圈。距离引导项0.3乘以前后两步到目标的欧氏距离差当无人机朝目标方向移动时获得正奖励远离时被扣分。这个0.3系数需要调太大agent只顾追梯度、贴着障碍跑太小引导信号被每步-1淹没agent学不出趋近目标的趋势。我调参的习惯是固定碰撞和目标奖励只动这一个系数每组参数跑300轮看成功率曲线不凭单条轨迹下结论。4.3 训练循环与轨迹回放MATLAB代码与参数说明训练主循环可以用矩阵化的写法对新手来说下面这种结构最直观外层循环遍历episode内层循环走完一个episode的每一步每一步执行动作、获得奖励、更新Q表。% 初始化Q表与超参数 nS gridSize * gridSize * layerMax; nA 6; Q zeros(nS, nA); alpha 0.3; % 学习率 gamma 0.9; % 折扣因子 epsilon 0.9; % 初始探索率 epsilonMin 0.1; decay 0.995; % 每轮探索率衰减系数 episodes 800; rng(42); for ep 1:episodes state idx(start); % 当前状态线性索引 stepCount 0; while ~isequal(state, idx(goal)) stepCount 2000 action chooseAction(Q, state, epsilon, nA); [nextState, reward, done] stepEnv(state, action, map3D, goal); bestNext max(Q(nextState, :)); Q(state, action) Q(state, action) ... alpha * (reward gamma * bestNext - Q(state, action)); state nextState; stepCount stepCount 1; end epsilon max(epsilon * decay, epsilonMin); endchooseAction的实现在这里不展开逻辑就是生成一个0到1之间的随机数小于ε时随机挑动作否则找当前状态最大Q值对应的动作。stepEnv负责查表判断下一格是否可通行、是否到达目标、计算奖励然后返回下一状态和是否结束。这两个子函数是训练循环的边界把环境逻辑和算法逻辑分离后续换地图、换动作集只改stepEnv不动训练主循环。训练结束后输出三样东西来判读结果。第一是收敛曲线记录每个episode的累计奖励曲线应该从很低的负值爬升然后在一个区间内波动——波动正常只要不持续下坠就说明策略在稳定第二是成功率曲线每隔若干episode用当前Q表跑一遍固定测试集记录到达终点的比例第三是轨迹可视化把规划出的轨迹画在地形上% 三维轨迹与地形叠加显示 figure; surf(xg, yg, terrain, FaceAlpha, 0.6); hold on; plot3(traj(:,1), traj(:,2), traj(:,3), r-o, LineWidth, 2); xlabel(x栅格); ylabel(y栅格); zlabel(高度层); view(45, 30); grid on;注意traj(:,3)是高度层数和surf绘制的terrain单位一致不需要再乘分辨率。如果轨迹贴在山体表面过去视觉上几乎重合说明安全裕度不足要把地图膨胀参数调大一格。5. 常见问题与避坑不收敛、内存爆炸与贴障飞行5.1 训练几十轮后轨迹仍乱撞奖励配比失衡现象训练跑了300个episode轨迹图上看无人机仍然在地图里乱转没有形成从起点到终点的稳定通路成功率曲线在低位徘徊。原因碰撞惩罚与距离引导系数配比失衡。最常见的是距离引导系数0.3被改成0.8甚至1.0agent只顾着往目标方向冲刺撞了山也不在意因为冲刺收益远大于碰撞惩罚另一种情况是碰撞惩罚只给-10agent认为绕开障碍的绕路代价比撞一下更高每次撞完调整方向继续走形成一个撞-绕-撞的坏循环。解决把奖励函数恢复成基准配比即碰撞-50、目标100、每步-1、距离引导0.3一次只改一个参数。血泪经验是同时调两个以上参数时出了问题根本分不清是哪个引起的。5.2 细化栅格后内存暴涨Q表存储与分辨率权衡现象把栅格尺寸从50m细化到20mQ表从0.77MB涨到约6MBMATLAB还扛得住继续细化到10mQ表接近96MB训练循环每轮都要做全表扫描速度慢得难以接受。原因状态空间随分辨率三次方增长三维栅格比二维更陡峭。解决不要盲目细化栅格。先用粗栅格跑通E2E流程确认奖励和参数没问题再用细栅格只做局部重规划。如果确实需要全图细粒度Q表考虑用稀疏结构只存访问过的状态——训练初期绝大多数格子没被访问过稀疏存储能省下大量内存。5.3 成功率震荡无法收敛ε衰减与学习率配合现象训练曲线每过几十轮就出现一次大波动成功率在40%到80%之间来回跳感觉几十轮白练了。原因ε衰减太快agent过早停止探索Q表里大量状态的动作价值没有被充分修正一旦环境稍有变化就选错动作或者α偏大大于0.5单次回放对Q值的修正幅度过大导致学习过程震荡。解决ε从0.9起步、每轮乘0.995、下限0.1这样前200轮保持较高探索后面逐步切换到利用。α用0.1到0.3之间γ用0.9对大多数栅格地图都是稳定的起点。判断是否是探索率问题有一个快速办法把ε设成常数0.2跑100轮如果成功率波动明显减小就说明之前的衰减策略太激进。5.4 轨迹贴着障碍物边缘飞安全裕度与膨胀处理现象轨迹确实避开了所有障碍但从三维图上看航线距离山体表面只有一个栅格的距离有些线段几乎贴着禁飞区边界过去。原因建图时把障碍边界当成了精确边界碰撞判定在进入障碍格才触发规划出的路径没有安全裕度实际飞行中定位误差、风扰、控制延迟都会让无人机偏离规划线贴边航线的坠毁概率很高。解决地图膨胀处理把障碍物周围一圈栅格也标成不可通行。MATLAB里可以对二维掩膜做腐蚀操作再叠加到三维地图上% 对禁飞区掩膜做膨胀腐蚀逻辑取反再腐蚀再取反 se strel(square, 3); noFlyDilated imdilate(noFly, se);膨胀的半径按需选择旋翼机取1到2个栅格固定翼因为转弯半径大取3个以上。注意膨胀处理要在建图阶段完成不要试图用奖励函数实现安全裕度——奖励函数只能改变agent的选择倾向不能保证所有状态下的路径都留出物理间距。5.5 二维跑得顺畅三维一放开就不收敛现象把高度维冻结固定在中层跑二维Q-learning一切正常一旦放开高度动作训练开始发散轨迹在高空乱窜。原因高度维把状态空间扩大了5到10倍奖励信号密度骤降加上水平动作和高度动作混合agent在海拔变化上获得的正反馈不足学不会何时该爬升、何时该下降。解决用课程学习思路做降维热身。先固定高度层训练150到300轮得到一个水平避障策略然后把水平维度的Q值保留高度维动作的Q值清零继续训练相当于让agent在已有的避障能力基础上只学高度调整。这个方法让我少走了很多弯路三维训练失败时优先怀疑维度扩展带来的稀疏性而不是算法本身。6. 演示代码之外蒙特卡洛验证与滚动窗口改造训练完只看一条轨迹就写报告早晚翻车。单条轨迹可能是运气好恰好绕开了所有障碍。我的验证习惯是固定50次随机起终点测试统计成功率和平均航程trials 50; success 0; pathLen zeros(trials, 1); for i 1:trials [traj, ok] runEval(Q, map3D, randStart(map3D), randGoal(map3D)); if ok success success 1; pathLen(i) size(traj, 1); end end fprintf(成功率: %.1f%% 平均航程: %.1f栅格\n, ... success/trials*100, mean(pathLen(pathLen0)));成功率低于90%不建议投入使用。从固定地图到动态障碍常见做法是滚动窗口Q表只负责全局导航给出一个参考方向局部规划器只考虑前方5到10个栅格在这个小窗口内重新规划以规避新出现的障碍绕行后回到Q表引导的参考线上。这个混合架构是工程上最稳的方案——Q表处理静态约束局部规划器处理动态变化谁也不拖累谁。如果地图完全已知且没有动态障碍另一个可用思路是用A*生成参考走廊Q-learning只在走廊内微调状态空间直接砍掉一个数量级训练时间从小时级降到分钟级。我第一次把规划轨迹接进某无人机仿真平台时轨迹在山脊处离障碍只有一格仿真里无人机直接撞上去了原因是建图时没做膨胀。把地图膨胀参数从0改到1之后仿真跑通那一刻才真正理解规划算法的价值不在demo里那张好看的轨迹图而在你敢不敢把它接进真实的反馈回路。希望帮到你。本文还有配套的精品资源点击获取