MATLAB实现DQN深度强化学习路径规划:从原理到工程实战

发布时间:2026/10/20 3:02:52
MATLAB实现DQN深度强化学习路径规划:从原理到工程实战 做路径规划这行的老哥们这两年多少都听到过“强化学习”“深度强化学习”这些词。今天要聊的这个项目就是用 DQNDeep Q Network这种深度强化学习算法去解一个二维栅格地图上的路径规划问题而且整套东西是用 MATLAB 完整实现的属于典型的“算法原理 工程落地”的练手项目。这个项目解决的问题很直白传统路径规划算法A*、Dijkstra、RRT 这些依赖已知的全局地图做的是贪心搜索或随机采样。而 DQN 的思路完全换了个方向——让一个智能体自己在地图上“走”通过与环境的反复交互自己学会怎么绕开障碍物、怎么走最近的路到达终点。听起来挺玄乎但真正动手做的时候会发现地图怎么建、状态怎么编码、奖励怎么给、网络怎么搭、训练怎么调每一步都有坑等着你任何一个环节糊弄过去训练出来的策略基本就是原地打转或者疯狂撞墙。适合谁来参考正在做相关课题研究或者课程设计的学生、想从零上手深度强化学习但不想一上来就啃 Python 框架的同学、以及手里只有 MATLAB 环境、想快速验证强化学习路径规划思路的工程师。我会按照我实际踩坑的顺序把整个链路讲清楚从环境建模开始到网络搭建、训练主循环、参数调优最后到问题排查。下面直接进入正题。1. 先想明白一件事路径规划为什么要请出 DQN1.1 传统方法的痛点在哪里做路径规划的人第一反应肯定是 A* 或者 Dijkstra。它们在小规模、静态、完全已知的栅格地图上表现极好能给出确定的最优解。但问题在于这两位的隐含假设是“地图是已知的、不变的”。一旦地图中途发生变化——比如新增了障碍物、目标点移动了、或者地图本身压根就没有完整建模传统方法要么需要全部重算要么在未知区域直接抓瞎。DQN 切入的角度完全不同。它把路径规划看成“序贯决策问题”智能体站在某个栅格上观察周围环境选择一个动作往上走、往下走、往左还是往右每走一步获得一个即时奖励一路走到目标点为止。整个过程不需要提前知道全局路径不需要穷举搜索只需要通过海量的试错训练把“状态到动作”的映射关系学出来。我在实际项目里最深的一个体会是DQN 真正有价值的场景不是替代 A*而是那些“环境不完整、目标会变、或者要求训练出一个能直接复用到相似地图上的通用策略”的场景。当你需要在 100 张相似地图上反复规划路径每张图都去跑一遍 A* 可能不算慢但如果你想让一个策略拿到新地图上直接就能用A* 做不到DQN 可以。1.2 DQN 的适用范围要泼盆冷水说实话如果地图就 10×10固定不变找一条静态最短路径A* 零点几毫秒就出结果了还保证最优。这时候用 DQN 纯属给自己找麻烦。DQN 训练要跑几万步还要调网络、调奖励最后出来的路径还不一定是最优的。所以动手之前先给自己列个判断清单环境模型不精确、想在线自适应——用 DQN 方向是对的状态空间大但可以离散化栅格地图天然满足——适合需要训练一个能泛化到类似地图上的策略——适合地图极小、固定不变、必须要最优解——老老实实用 A* 或 Dijkstra训练时间只有一下午、机器配置很一般——谨慎考虑DQN 收敛没那么快。我自己做模拟项目 X 的时候最开始拿了一个 20×20 的小栅格图障碍物随机生成目标点固定。为了对比A* 和 DQN 都跑了一遍。A* 是秒出DQN 训练了差不多 2 万步才稳定出一条像样的路径。所以你要清楚DQN 买的是“泛化能力”和“决策速度”代价是“训练成本”。2. 核心原理拆解Q 表是怎么一步步变成神经网络的2.1 先回顾一下 Q-learning 的底子DQN 的前身是 Q-learning一个经典的时间差分强化学习算法。它的核心是一个 Q 表每一行是状态每一列是动作表格里的值表示“在这个状态下选择这个动作未来能拿到的累计奖励期望”。更新公式是经典的那条Q(s,a) ← Q(s,a) α [r γ·max_a Q(s,a) − Q(s,a)]翻译成大白话当前状态 s 下选了动作 a拿到奖励 r跳到状态 s然后预估一下 s 下所有动作里最好的那个未来价值用这个“现实奖励 未来估值”去修正当前 Q 值。α 是学习率γ 是折扣因子γ 越小说明智能体越短视越大说明越看重长远收益。按这个逻辑地图稍微大一点就出问题了。假设 30×30 的栅格地图状态就是 900 个格子动作 4 个Q 表才 3600 项其实还能存。但如果你想在状态里加上“智能体最近几步走没走过”“目标点的相对方位”这些信息状态空间立刻爆炸Q 表就存不下了。更麻烦的是栅格地图连续变化的话状态根本没法穷举。2.2 从 Q 表到 DQN 的三板斧DQN 做的事情本质上是用一个神经网络去拟合 Q 函数输入状态输出每个动作对应的 Q 值。不再需要一张巨大的表格而是靠网络参数把“状态—动作—价值”的映射关系压缩进去。但直接拿神经网络替换 Q 表是不行的训练根本稳不住。因为 Q-learning 本身有个毛病训练数据是连续相关的——下一步的状态取决于上一步的动作且更新目标里用到的“Max Q”又是网络自己算出来的等于拿着自己的预测去更新自己很容易震荡甚至发散。DQN 靠三个技巧把这问题压下去第一经验回放Experience Replay。把智能体每一步的转移s, a, r, s, done存进一个环形缓冲区训练时每次随机抽一小批出来。这样打破了样本之间的时间相关性也让同一条经验能被反复利用数据效率高不少。第二目标网络Target Network。把“算未来价值的网络”和“要更新的网络”拆成两份。行为网络每步都更新目标网络每隔 N 步直接复制行为网络的参数。这样更新目标就相对稳定不会每步都在变训练就稳了。这个技巧我在实际调试中体会极深——去掉目标网络训练曲线简直是心电图加上了以后曲线才像条正常的学习曲线。第三ε-greedy 探索策略。训练初期以高概率随机选动作探索以低概率选当前 Q 值最大的动作利用。随着训练推进ε 从 1.0 慢慢衰减到 0.05 左右让智能体先是“瞎逛长见识”后面“越走越精明”。ε 衰减速度如果太快它刚探索出一点点规律就被打断后面基本就是死守一个烂策略。这三个技巧是 DQN 的基石后面的代码完全围绕它们展开。3. 栅格地图建模与 MDP 五元组设计3.1 地图数据结构设计二维栅格地图在 MATLAB 里最直接的形式就是逻辑矩阵或者数值矩阵1 表示障碍物0 表示可行走区域再单独定义起点和终点坐标。比如这样的数据% 地图尺寸 mapSize [20, 20]; % 0为可行走1为障碍 map zeros(mapSize); % 随机生成障碍注意预留起点终点区域 rng(42); % 固定随机种子方便复现 obstacleRatio 0.25; for i 1:mapSize(1) for j 1:mapSize(2) if rand obstacleRatio map(i, j) 1; end end end startPos [1, 1]; goalPos [20, 20]; % 保证起点终点是可行走的 map(startPos(1), startPos(2)) 0; map(goalPos(1), goalPos(2)) 0;实际做的时候我建议不要用纯随机障碍至少加一些“墙”状障碍和“U 型障碍”不然环境太简单学出来就是个花架子。后面做泛化测试的时候也可以准备多张地图切换训练这个后面单独讲。3.2 状态空间与动作空间的设计这个设计直接决定训练难度也是新手最容易随便搞的地方。我见过很多同学直接把整个地图矩阵当状态输进去——20×20 就是 400 维输入不是不能做但训练量大了好几倍而且网络很容易把“地图背景”也记住导致换张地图就废了。更合理的做法是设计局部观测 目标方位的状态向量当前位置的横纵坐标归一化到 [0,1]当前位置与目标点的归一化差值dx, dy相当于让网络知道“目标在哪个方向”周围 8 个或者 4 个邻格是否可行走0/1 编码相当于让网络“看见”身边的障碍物。把这三类信息拼起来比如 20×20 地图上状态向量长度大概 13 到 15 维2 个坐标 2 个目标差 8 个邻格 1 个是否是终点的标识。输入维度骤降训练收敛速度完全不是一个量级。动作空间就是四个离散动作上、下、左、右。想更精细可以加四个斜向动作变成 8 方向但要注意斜向穿越的“切角”问题——比如左上角是障碍物能不能从右下格直接斜走到右上格不同场景处理方式不一样。我做基础版本用的是 4 方向简单直观路径规划效果也足够演示。% 动作表上下左右 actions [-1, 0; % 上 1, 0; % 下 0, -1; % 左 0, 1]; % 右3.3 MDP 形式化状态转移和奖励整个问题可以形式化成标准的 MDP状态就是上面定义的特征向量动作是四个移动方向状态转移是确定性的——执行上移就是坐标减一行除非碰到边界或障碍物。碰到障碍物做无效移动时一种常见做法是原地不动并给一个惩罚奖励另一种是干脆禁止该动作并直接结束回合。我在项目里用的第一种因为更接近真实机器人“撞到东西会停下来而不是直接消失”的情况。奖励函数这块是 DQN 路径规划的灵魂我单独在下一节讲。这里先把基本框架列出来每步走一步给一个小惩罚比如 -0.05 或 -1逼迫智能体找最短路径到达目标点给大正奖励比如 10 或 100撞上障碍物或者踩到已经走过的格子给负奖励比如 -1 或 -5。不要想着一次性把奖励设计到位我的经验是先跑一版最简单的看训练曲线再迭代。后面会详细展开奖励函数的调试经过。4. MATLAB 实现环境类、网络与训练主循环4.1 环境交互模块step 函数把环境封装成一个函数接口输入当前状态和动作输出下一个状态、奖励和是否结束。这一步做得好后面换地图换任务都方便。我在实现时用了一个简单的结构体来管理环境function [nextStateVec, reward, done] stepEnv(env, state, actionIndex) % env: 包含 map, agentPos, goalPos 的结构体 % 计算目标位置 nextPos env.agentPos env.actions(actionIndex, :); % 越界判断 if nextPos(1) 1 || nextPos(1) env.mapSize(1) || ... nextPos(2) 1 || nextPos(2) env.mapSize(2) nextPos env.agentPos; % 原地不动 end % 撞墙判断 if env.map(nextPos(1), nextPos(2)) 1 reward -1; env.agentPos nextPos; % 原地不动或者标记惩罚 elseif isequal(nextPos, env.goalPos) reward 10; env.agentPos nextPos; else reward -0.05; env.agentPos nextPos; end % 更新状态特征向量 nextStateVec buildStateVec(env); done isequal(env.agentPos, env.goalPos); end环境接口切分清楚以后训练主体完全不用动想换地图、换奖励、换动作集只改环境和状态构建函数就行。4.2 DQN 网络结构的构建因为是拟合离散 Q 值网络结构非常标准输入层是状态特征维度中间堆两到三层全连接输出层维度等于动作数量。我用 MATLAB 的深度学习工具箱搭的网络是这样的% 状态特征维度比如15 stateDim 15; numActions 4; dqnNetwork [ featureInputLayer(stateDim, Normalization, none) fullyConnectedLayer(64) reluLayer fullyConnectedLayer(64) reluLayer fullyConnectedLayer(numActions) ]; dqnNet dlnetwork(dqnNetwork); % 目标网络初始与行为网络一致 targetNet dlnetwork(dqnNetwork);网络规模不用太大两层 64 神经元对 20×20 栅格地图完全够用。我试过 128 甚至 256 的隐层训得更慢但效果并不更好因为状态本身维度就不高。记住一个原则网络容量跟任务复杂度匹配即可不是越大越强。4.3 经验回放缓冲区的实现经验回放本质就是一个环形队列。用 MATLAB 实现时我习惯预先分配固定大小的 cell 数组存到上限后覆盖最旧的数据bufferSize 100000; buffer cell(bufferSize, 1); bufferIdx 1; bufferCount 0; % 存入经验 function addExperience(buffer, bufferIdx, bufferCount, s, a, r, sp, done) buffer{bufferIdx} {s, a, r, sp, done}; end % 采样 function batch sampleBatch(buffer, bufferCount, batchSize) randIdx randi(bufferCount, batchSize, 1); batch buffer(randIdx); end采样注意一点只有当 buffer 里存够了 batchSize 条数据才开始训练否则前几百步都是白抽。这是不少人训练不动的隐藏原因——初始化就把 batchSize 设成 64但缓冲区里只有 20 条每次抽样全是重复数据网络直接被带偏。4.4 训练主循环核心代码逻辑训练主循环是整套代码的心脏。我按 DQN 原始论文的流程写伪代码逻辑如下numEpisodes 3000; gamma 0.95; % 折扣因子 epsilon 1.0; % 初始探索率 epsilonDecay 0.995; % 每回合衰减 epsilonMin 0.01; lr 1e-3; % 学习率 batchSize 64; targetUpdateFreq 100; % 目标网络更新频率 maxStepsPerEpisode 200; % 单回合最大步数 optimizer adamupdate(0, dqnNet.Learnables, lr); for episode 1:numEpisodes env resetEnv(env, startPos); % 重置环境到起点 stateVec buildStateVec(env); epReward 0; for step 1:maxStepsPerEpisode % ε-greedy 选动作 if rand epsilon action randi(numActions); else dlState dlarray(single(stateVec), CB); qValues predict(dqnNet, dlState); [~, action] max(extractdata(qValues)); end % 执行动作 [nextStateVec, reward, done] stepEnv(env, stateVec, action); addExperience(buffer, s, a, r, sp, done); % 这里的 s, a, r, sp 需要在具体位置赋值 % 经验足够时进行梯度更新 stateVec nextStateVec; epReward epReward reward; if done break; end end epsilon max(epsilonMin, epsilon * epsilonDecay); % 周期性同步目标网络 if mod(episode, targetUpdateFreq) 0 targetNet dqnNet; end end实际的梯度更新部分需要把采样出来的 batch 转成 dlarray然后分别算当前 Q 值和目标 Q 值用均方误差做损失再反向传播。这段代码略长但核心只有三行行 1 用行为网络算当前状态动作的 Q 值行 2 用目标网络算下一状态的最大 Q 值构造 TD 目标行 3 用均方误差反向传播更新网络。% 从batch中组装数据 sBatch cat(2, batchS{:}); % [stateDim, batchSize] aBatch batchA; % [1, batchSize] rBatch cat(2, batchR{:}); % [1, batchSize] spBatch cat(2, batchSp{:}); doneBatch cat(2, batchDone{:}); dlS dlarray(single(sBatch), CB); dlSp dlarray(single(spBatch), CB); % 当前Q值 qPred forward(dqnNet, dlS); % [numActions, batchSize] % 目标Q值下个状态的最大Q qNext forward(targetNet, dlSp); % [numActions, batchSize] [qNextMax, ~] max(qNext, [], 1); % 组装损失 rowIndex sub2ind(size(qPred), aBatch, 1:batchSize); qCurrent qPred(rowIndex); qTarget single(rBatch) gamma * qNextMax .* (1 - single(doneBatch)); loss mse(qCurrent, dlarray(qTarget, CB)); % 反向传播 grads dlgradient(loss, dqnNet.Learnables); [dqnNet, optimizer] adamupdate(dqnNet, grads, optimizer, stepIdx);梯度计算要放在dlfeval里这里为了可读性做了简写。真正跑的时候dlgradient必须在自定义训练循环的函数内使用这个细节别忘不然 MATLAB 直接报错。5. 奖励函数整个项目里最花时间的地方5.1 我踩过的奖励设计坑第一版奖励我设计得特别“着急”到达目标 100撞障碍物 -100其他每一步 -1。结果训练出来一个极其激进的智能体——它确实能“到终点”但路径全程贴着墙走恨不得每次都蹭着障碍物边缘过去因为只要撞到障碍物的概率不是 100%它就宁可赌一把赌赢了少走好几步赌输了不过重开一局。这本质上是奖励尺度不匹配导致的100 和 -100 的绝对量太大把 -1 的每步惩罚淹没了。后来我改成了到达目标 10撞障碍物 -1每走一步 -0.05无效动作越界 -1。这样每步惩罚不及到达奖励的百分之一但同时不至于让智能体为省几步去赌撞墙。训练出来的路径明显“规矩”了贴着可行走区域走不冒险。这个调整花了我一个周末回头看教训就一条奖励的绝对量级要统一考虑别让某一项的数值把其他项的信号完全淹没掉。5.2 一个有用的辅助信号距离势能单纯靠“到达终点才有正反馈”训练非常慢因为稀疏奖励导致智能体前期几乎不知道自己在往对的方向走。我加了距离势能作为中间引导每走一步如果离目标点的曼哈顿距离比上一步近了额外给 0.3 的 shap 奖励远了给 -0.3。这个辅助信号要非常小心加得太重智能体会学会“原地转圈骗奖励”——因为有些位置只要来回踱步下一步总有一边是靠近目标的。我的经验是辅助奖励的量级必须小于每步惩罚和到达奖励之间至少一个数量级只起方向引导作用不能喧宾夺主。5.3 记录奖励曲线别凭感觉调参做奖励调参最好把每回合的总奖励和每回合步数都存下来画曲线。曲线能告诉你很多信息总奖励曲线如果一路下行说明惩罚太重、探索阶段每回合都在被扣分要加重正向信号总奖励如果一直横盘不动说明奖励信号没有梯度智能体学不出东西步数曲线如果在掉说明策略确实在变好即使总奖励没怎么变也可以继续训。我在项目里用最简单的plot(episodeRewards)画了曲线配合每 100 回合打印一次“平均步数”就能非常直观地判断训练是否健康。这个习惯强烈建议保留它能帮你省下大量盲目调参的时间。6. 常见问题与排查经验照着这个清单查6.1 训练曲线不收敛奖励来回震荡高频原因有三个学习率太大、目标网络更新太频繁、batchSize 太小。调试顺序我建议先降学习率从 1e-3 降到 3e-4 再试然后把 targetUpdateFreq 调大比如 300 步一更新最后 batchSize 提到 128 试试。我遇到过的最乌龙情况是状态向量没归一化坐标值从 1 到 20 直接进网络而邻格特征都是 0/1数量级差太多网络训练一直在“波动”。把坐标除以地图尺寸归一化到 [0,1] 之后问题当天就解决了。6.2 Q 值发散一路飙到几千上万这是经典的“Q 值高估”问题。如果目标网络没有生效几乎必然发生——行为网络和目标网络是同一个每步都在更新TD 目标跟着网络一起漂。检查点确认目标网络是用targetNet.Learnables dqnNet.Learnables这样完整复制而不是赋值引用确认训练循环里算目标 Q 值时用的是targetNet而不是dqnNet。这两个错误在代码里非常隐蔽检查的时候要逐行看。6.3 智能体在原地打转或者非要远路绕典型原因之一是“走过的格子没有惩罚”智能体会走出环形路径甚至 8 字形。解决办法可以加一步“位置去重惩罚”如果当前位置之前已经访问过就额外给一个 -0.1 的小惩罚。注意惩罚别太大否则它会学出“站住不动”的消极策略。另一个原因是 ε 衰减太快探索不足。ε 从 1.0 衰减到 0.01如果 500 回合就衰减完了那智能体大概在 300 回合时还处在“半探索半利用”的混沌期就被强行切换成纯利用学出来的策略很差。我把衰减改慢让 ε 到大概 2000 回合左右才到 0.05效果立刻不一样。6.4 训练好的模型换张地图就废了这个现象几乎是必然发生的因为训练时地图是固定一张网络把这张地图的布局“背”下来了。想让策略有泛化能力有两个办法第一训练时每回合随机生成一张新地图保证起点终点可达让智能体学会“通用避障”而不是“背地图”。代价是训练时长明显增加。第二如果只要求单图最优那直接用 A* 好了——这话我再强调一遍。DQN 的应用价值只有在多图泛化或者动态环境下才体现得出来。如果非要在单图上用 DQN那就接受它是“展示强化学习流程”而不是“生产最优路径”这个定位。6.5 训练慢到怀疑人生MATLAB 跑强化学习确实比不了 Python 生态的成熟框架但代码层面的优化能省不少时间。实测下来的经验尽量避免在训练循环内部频繁调用dlarray转换一次性把整个 batch 转好不要在stepEnv里写复杂可视化训练时全程关掉绘图只存数据等训完了再单独回放路径障碍物检测用矩阵索引map(pos(1), pos(2))别用find或者循环遍历可以把地图数据做成逻辑索引提前算好减少重复计算。我手头这张 20×20 地图优化后 3000 回合大概跑十几分钟没优化的时候同样回合数能跑一个多小时差别全在那些不起眼的小函数上。7. 扩展方向与个人实操心得项目做到能成功规划一条路径其实才刚摸到门。这套 DQN 代码稍微改改就能扩展出不少方向第一Bias 网络和Double DQN的升级。上面提到的 Q 值高估问题DDQN 用得是“行为网络选动作、目标网络估值”的方式改造成本极低效果立竿见影。我后面在这个项目上顺手加了 DDQN训练曲线明显更稳最终路径也比基础 DQN 更短。建议做完基础版后把双网络结构升级成 DDQN这个升级代码量不大但对最终效果影响很大。第二动态障碍物环境。把障碍物每过一段回合做一次移动训练出来的策略就不会“背地图”而是学会了“观察到障碍就绕开”的行为模式。这个方向非常适合做演示或者写进论文的实验部分。第三多起点多目标。把起点和终点也作为状态输入的一部分网络学习的是“从任意位置通向任意目标的通用策略”这其实就是往分层强化学习甚至导航大模型的方向探了探路。第四跟传统算法结合。比如用 DQN 生成一条初解再用 A* 做局部精修或者在 A* 的启发函数里引入学习到的代价估计。这种混合方案在我实际做的某跨平台系统里效果不错比单用任何一种都稳定。最后说说我个人的真实体会。这套 DQN 路径规划项目最难的不是数学公式也不是 MATLAB 语法而是建立起“为训练过程设计正确反馈信号”的直觉。奖励函数、状态表示、探索策略、网络容量四者互相耦合改一个就得看另外三个的脸色。我调这个项目的过程中有三次把训练曲线调崩了又花两三天调回来最后才找到了那组参数。给刚开始做的朋友一个建议先照着我上面给的参数组合跑通一个简单场景拿到一条勉强能走的路径然后再开始“作”奖励函数、换地图、加障碍。千万不要一上来就追求完美路径那会让你陷入调参地狱。每一步都确保能看到明确的实验结果再走下一步这个项目就能给你带来比“一篇代码”多得多的收获。

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询