
简介面向强化学习初学者的MATLAB Q-Learning迷宫路径规划资源包围绕Q-Learning算法基础与迷宫环境建模展开详细涵盖状态空间定义、动作选择策略、Q表初始化及迭代更新机制。借助迷宫逃脱这一直观场景完整演示基于价值迭代的路径规划流程适合算法入门、课程设计及路径规划相关研究参考。资源包共包含593个文件以481个m源码为主体配套mat数据文件、fig图表、c/cpp扩展源码、pdf说明文档等压缩包整体7.32MB目录结构便于检索。目前已有315人学习下载。资源包内不仅提供可直接运行的编码案例还包含实验配置参数与大量仿真结果图便于读者对照源码理解智能体试错寻优过程在此基础上可继续开展算法优化、与其他技术融合或多智能体扩展等进阶工作是理论到实践的良好过渡。1. 在MATLAB里用Q-Learning做迷宫规划它到底解决什么问题你大概率在课上或某个技术论坛里见过这样的演示一个小方块在10×10的栅格迷宫里乱撞一开始每一步的方向都是蒙的跑两三百个回合之后它居然能从起点一路绕开所有障碍走到出口。这背后就是标题里那套组合——matlab实现强化学习Q-Learning算法迷宫路径规划。说得再具体一点用MATLAB搭一个栅格迷宫环境用Q-Learning这张Q表去记录“在哪个格子选哪个方向值多少钱”最终让智能体学会迷宫逃脱。为什么这套方案在课程实验和毕设里出镜率这么高因为它不依赖神经网络不需要装深度学习框架一个稀疏矩阵就能装下智能体的全部“经验”。你甚至不需要懂深度强化学习算法的网络结构、经验回放池那些概念只要理解状态、动作、奖励、Q值更新这四件事就能跑出一个能看、能演示、能讲清楚原理的完整案例。适合谁适合正在做强化学习课程作业的本科生、准备毕设演示的研究生以及想快速验证Q-Learning收敛过程的工程师。我会按自己做这个实验的顺序来讲先建模再搭环境再写训练循环最后讲参数和踩坑。每一步都会给出能直接复现的MATLAB代码。2. 从MDP到Q表更新先吃透三行公式再写代码2.1 迷宫问题的MDP四要素状态、动作、奖励与转移Q-Learning处理的不是迷宫地图本身而是迷宫背后那个叫马尔可夫决策过程MDP的数学模型。任何强化学习任务第一步都是把问题翻译成MDP的四要素。迷宫逃脱刚好是最直观的例子。状态集S是智能体所在的所有格子一个格子就是一个状态。在这个方案里我用“线性索引”表示状态也就是把二维坐标压成一维编号动作集A是四个方向上、下、左、右。奖励函数R告诉智能体每个行为是好是坏到达终点给正奖励撞墙给负奖励普通移动给一个小成本。转移P指的是“在状态s执行动作a后会落到哪个状态”对迷宫这种确定性环境来说转移就是查一次坐标表没有随机性。把这四样理清楚Q-Learning才能开始工作。Q表的结构非常直接行是状态数列是动作数Q(s,a)就表示“在这个格子里选择这个方向往后的累计回报期望”。迷宫越小Q表越稀疏训练越快。2.2 一条更新公式看懂Q-Learning从Bellman方程到迭代实现Q-Learning的核心是一条更新公式所有代码最终都落在这三行计算上Q(s,a) ← Q(s,a) α·[r γ·max_{a}Q(s,a) − Q(s,a)]别让符号吓到。逐项拆开看r是这次迈步拿到的即时奖励可能是到达终点的100也可能是撞墙的-10。γ·max Q(s,a)是“下一步状态里最优动作的Q值”它代表未来收益的估计。两项相加就是这一步“现实能拿到的总回报”的估计。括号里再减去当前Q(s,a)得到的是“我原来估值和现实之间的差距”这叫时序差分误差Q-Learning的本质就是不断用现实修正这个差距。α是学习率控制每次修正的幅度。它越大Q表变化越猛越小学习越慢但更稳。路径规划场景里我一般设0.1到0.3之间具体到第4章再展开聊。还有一个细节值得专门说更新用的是max Q(s,a)而不是“当前策略实际会执行的动作”。这意味着Q-Learning在更新时假设下一步总是选最优动作哪怕这一步实际是随机探索走出来的。这种“用最优价值的估计来更新”的手法就是off-policy的由来。它带来的实际效果是智能体不用真的走出最优路径只要在训练过程中不断摸到终点Q值就会逐步从终点往起点传播。2.3 为什么这里选表格Q-Learning而不是深度强化学习算法很多人一提到强化学习就想到DQN、PPO这类深度强化学习算法但这套迷宫方案里用表格Q-Learning是刻意为之的。迷宫的状态数是格子数5×5迷宫25个状态20×20迷宫400个状态一个400×4的Q表在MATLAB里就是一个普通矩阵显式、可打印、可直接检查。你可以随时看某个格子的Q值分布判断算法学没学会这是表格法的绝对优势。换成深度强化学习算法意味着要定义一个神经网络结构、一个经验回放池、一个目标网络还要调网络参数和样本采样策略。在状态数只有几百个的迷宫里这套复杂度完全不值得。调试成本会高一个量级但收敛效果和表格法没有本质差别。所以如果你只是想验证Q-Learning原理、跑通迷宫逃脱入门就老老实实做Q表等你的任务变成连续状态空间比如机械臂关节角度控制、仿真器里的端到端控制再切到深度强化学习算法不迟。3. 搭迷宫环境与奖励函数矩阵地图、状态编号和动作边界3.1 用矩阵定义迷宫数字映射规则与合法性检查MATLAB里最自然的迷宫表达方式就是二维矩阵。每个格子用一个数字标记含义0表示可通行空地1表示障碍物墙2表示起点3表示终点。% 迷宫地图0空地 1障碍 2起点 3终点 maze [ 0 0 1 0 3; 1 0 1 0 0; 0 0 1 0 1; 0 1 0 0 0; 0 0 0 1 0 ]; start_state find(maze 2);这段代码里start_state用find直接拿到起点线性索引。注意find返回的是列优先顺序的下标也就是说第1行第1列索引是1第2行第1列索引是2这个顺序后续和sub2ind保持一致。地图定义完先做一次合法性检查再往下写别嫌麻烦。最常见的问题有三个一是地图里没有2或3find会返回空矩阵训练直接报错二是起点或终点被墙包死智能体永远到不了终点三是地图里出现多个终点导致奖励哨兵检测逻辑出错。我的习惯是用一行断言来兜底assert(any(maze(:) 2), 迷宫里必须有且仅有一个起点); assert(any(maze(:) 3), 迷宫里必须有且仅有一个终点);3.2 状态编号与动作集合线性索引和动作方向表Q表要用矩阵存取所以状态必须转成从1到nS的连续整数。MATLAB自带的sub2ind和ind2sub就是干这个的sub2ind把行列坐标转成线性索引ind2sub把线性索引还原成行列坐标。动作也约定成数字编号这样Q表列数固定为41代表向上2代表向下3代表向左4代表向右。为什么不用字符或字符串矩阵索引只认数字用数字可以直接写Q(s, a)取Q值代码短、速度快。方向与数字的映射注释清楚就行% 动作编号1上2下3左4右 nA 4;这套编号贯穿整个训练循环后续get_next_state函数里的switch分支就按这个编号展开。3.3 奖励函数设计每步成本、撞墙惩罚和终点回报奖励函数是这套方案里最影响收敛速度的设计它直接决定Q值的量级和学习效率。我用过很多组设置最稳定的一组如下情形奖励设计理由普通一步-1推动智能体找最短路径避免绕路撞墙或出界-10对无效动作给出明显负反馈到达终点100终局信号量级比步数惩罚高一个数量级这个表的核心思想是“让智能体优先找终点其次才是走短路径”。终点奖励100和每步-1之间差了两个数量级即使绕了二三十步累计惩罚也抵不过一个终点奖励智能体最终会学会“就算绕远一点也要找到出口”。反过来如果终点奖励只设1和步数惩罚同量级智能体很可能学成一头懒驴原地打转甚至直接躺平因为绕路惩罚和终点奖励抵消了。撞墙惩罚设在-10而不是-1是为了让智能体尽快避开无效动作。但注意撞墙惩罚绝对值过大也有副作用这个坑在第5章展开。3.4 状态转移函数get_next_state带边界检测的一次交互训练循环里每一次“迈步”都调用同一个函数输入当前状态、动作、地图输出下一状态、奖励、是否结束。我把这个交互逻辑单独抽出来写既方便训练循环保持干净也方便单独测试某个动作的结果。function [s_next, reward, done] get_next_state(s, a, map, rows, cols) % 输入当前状态编号s动作a地图map迷宫行列数 % 输出下一状态编号奖励是否到达终点 [r, c] ind2sub([rows, cols], s); switch a case 1, nr r - 1; nc c; % 上 case 2, nr r 1; nc c; % 下 case 3, nr r; nc c - 1; % 左 case 4, nr r; nc c 1; % 右 end % 越界或撞墙状态不变给惩罚 if nr 1 || nr rows || nc 1 || nc cols || map(nr, nc) 1 s_next s; % 原地不动 reward -10; done false; return; end % 正常移动计算新状态编号 s_next sub2ind([rows, cols], nr, nc); if map(nr, nc) 3 reward 100; done true; else reward -1; done false; end end这段代码有几个细节值得说。越界和撞墙的处理被合并成了一个判断因为无论出界还是撞墙在任务语义上等价于“这个动作无效”状态都不需要改变。用return提前结束函数可以避免后面继续执行导致s_next被错误覆盖。终点分支单独判断一旦踩到终点就置done为true训练循环拿到这个标志会立刻break不再让智能体继续走。关于sub2ind和ind2sub的配合需要再啰嗦一句ind2sub输入是矩阵尺寸不是矩阵本身传rows和cols进去比传size(map)更直观。如果你改成size(map)要记得它返回的是[r, c]两个值别拿一个变量去接这个问题排错时间很长。4. 核心训练循环Q-Learning迭代、epsilon衰减与收敛判据4.1 初始化Q表和超参数一张参数表说清楚每个值的依据训练开始前先把Q表清零再把超参数一次性摆出来。我的惯用初始化如下rng(0); % 固定随机种子保证实验可复现 rows size(maze, 1); cols size(maze, 2); nS rows * cols; % 状态总数 nA 4; % 动作数 Q zeros(nS, nA); % Q表初始化 alpha 0.1; % 学习率 gamma 0.9; % 折扣因子 epsilon 0.3; % 初始探索率 epsilon_min 0.05; % 探索率下限 epsilon_decay 0.995; % 探索率衰减系数 episodes 1000; % 训练回合数 max_steps 500; % 单回合最大步数每个参数的设定依据不完全相同。alpha0.1是Q-Learning在离散小状态空间里的稳定选择太大容易让Q值震荡发散太小收敛速度肉眼可见地变慢。gamma0.9意味着智能体更看重近期回报迷宫这种任务几步就能到终点不必用0.99那种偏向远期回报的设置。epsilon0.3是探索和利用的折中点初始阶段要有足够概率随机乱走才能碰到终点但也不能高于0.5导致训练过程太飘。epsilon_min0.05保证后期仍有少量探索防止Q表固化。epsilon_decay0.995的含义是每回合探索率乘一次0.9951000回合后会从0.3衰减到约0.05刚好落在预设下限附近。这个量级的训练在本地MATLAB和在线网页版都能跑完1000回合的循环在普通笔记本上也就是几秒到几十秒的事。4.2 训练主循环代码epsilon-greedy决策与Q值更新超参数就位之后训练主体就是一个双层循环外层跑回合内层跑单回合中的每一步。step_hist zeros(episodes, 1); reward_hist zeros(episodes, 1); for ep 1:episodes s start_state; % 每回合从起点出发 total_reward 0; done false; for t 1:max_steps % epsilon-greedy选动作 if rand epsilon a randi(nA); % 探索随机选一个方向 else [~, a] max(Q(s, :)); % 利用选当前Q值最大的方向 end % 执行动作获取下一状态、奖励、是否终止 [s_next, r, done] get_next_state(s, a, maze, rows, cols); % Q值更新Q(s,a) Q(s,a) alpha*(r gamma*max(Q(s_next,:)) - Q(s,a)) Q(s, a) Q(s, a) alpha * (r gamma * max(Q(s_next, :)) - Q(s, a)); total_reward total_reward r; s s_next; if done break; end end step_hist(ep) t; % 记录本回合实际走的步数 reward_hist(ep) total_reward; % 记录本回合累计奖励 epsilon max(epsilon_min, epsilon * epsilon_decay); end这套循环是Q-Learning最标准的实现。每次选动作前用rand和epsilon比较决定走探索还是利用这保证了训练前期有足够多的随机性去“踩”终点后期又能收敛到最优路径。执行动作后立刻更新Q表更新公式和2.2节完全一致。有一个容易被忽略的细节当s_next是终点时max(Q(s_next,:))是0因为终点的Q表行从未被更新过。这个0在数学上是正确的——到达终点后回合结束后续没有未来收益。所以不必额外给终点行的Q值做特殊处理保持0反而干净。训练结束后Q表里存储的就是智能体学到的全部经验。下一步可以立刻用max(Q(s,:))去取每个状态的最优动作生成策略。4.3 收敛判据与性能评估从每回合步数观察学习曲线训练循环跑完不要急着宣布成功。第一件事是画step_hist的曲线看每回合走的步数是否在下降并趋于稳定figure; plot(step_hist, LineWidth, 1); xlabel(回合数); ylabel(到达终点的步数); title(每回合步数收敛曲线);这条曲线是最直观的收敛判据。如果训练真正有效你会看到曲线从几百步迅速下坠到一个稳定区间比如十几步然后保持平稳。如果曲线始终在几百步上下大幅度抖动说明探索率衰减太快或奖励设计有问题回到第4.1节调参。步数曲线只能说明“能到终点”不能说明“走到了最优路径”。我习惯再跑一个成功率评估训练完后用纯贪婪策略从起点走50次统计到达终点的比例。纯贪婪的意思是每步都选max(Q(s,:))不再加任何随机探索。如果50次全部到终点成功率100%才能说这个智能体真正学会了迷宫逃脱。5. 迷宫路径规划的常见坑Q值发散、绕路和收敛失败的排查5.1 Q值越训越大累计奖励冲到上千——学习率与奖励量级的搭配问题我见过不少第一次跑通训练循环的人兴奋地看Q表结果发现某些格子的Q值已经涨到好几百甚至上千而终点奖励只有100。这种现象叫Q值发散原因是学习率偏大加上奖励量级差距过大时TD误差会一回合接一回合地被放大。排查方法很简单训练结束后打印Q表的最大值和最小值。fprintf(Q max %.2f, Q min %.2f\n, max(Q(:)), min(Q(:)));如果max(Q(:))远超终点奖励的好几倍优先把alpha从0.3降到0.1。Q-Learning更新是迭代逼近的过程alpha过大会让每次修正幅度过大导致估值在真实值附近来回震荡。另外检查是否把撞墙惩罚设成了-100这种极端值惩罚量级过大会在相邻状态间产生巨大的TD误差同样容易让Q值爆掉。5.2 智能体反复原地绕圈——探索率衰减策略的问题训练完后画路径发现智能体在某个局部回路里不停转圈比如在2×2的免费区域里上右下左地循环既不撞墙也到不了终点。这种翻车一般有两个原因。第一个原因是epsilon衰减太快智能体过早进入纯贪婪模式而Q表还没学到足够好的状态关联就被迫固化在当前策略里。解决方法是把epsilon_decay调到0.998以上让探索期拉长。第二个原因是撞墙惩罚设置过重比如-50。智能体学会“撞墙很疼”之后会倾向于在安全区域里绕圈因为瞎撞的期望损失比绕圈还大。这时的表现就是路径极度保守宁可绕远也不碰墙。解决方法是把撞墙惩罚降回-10左右让“绕圈”的每步成本-1在长期累计下超过偶尔撞墙的代价智能体才有动力去尝试新方向。5.3 训练结束后成功率很低——max_steps和终点状态的处理问题训练曲线看着在下坠但成功率评估只有50%到70%跑不完的回合通常卡在“超时”上。这时候先查max_steps。如果迷宫里起点到终点的最短距离是20步你把max_steps设为30智能体稍微绕一点路就超时这个回合等于白训。我的经验是max_steps取最短路径长度的5到10倍至少保证前期随机探索阶段有足够步数去撞终点。另一个隐蔽问题在get_next_state的终点分支如果终点判断和撞墙判断写反了比如把map(nr,nc)3的分支放在了越界判断里智能体即使踩到终点也不会置done它会继续在终点格子上做动作把终点奖励当成普通奖励反复拿。这种问题表现就是reward_hist里出现大量重复的100但步数曲线永远不会降到很短。检查方式是在训练循环里加一行临时断言if done map(s_next) ~ 3 error(done被置true但当前状态不是终点); end5.4 每次运行结果都不一样——随机种子与初始化问题同一份代码同一个人间隔一天重新跑训练曲线长得完全不一样。这不是算法写错了是epsilon随机探索用的随机数序列没固定。在训练开头加一行rng(0)就能让rand、randi等随机数生成器从同一个种子出发每次运行产生完全一样的探索序列。这行的意义不只是数据好看它直接影响实验可复现性别人拿你的代码跑不出你的结果就没法相信你的收敛曲线。加了种子之后如果结果还有波动怀疑是不是代码里在循环体内重置了rng。训练循环内不要调用rng只在开头固定一次。固定种子后如果你还想评估算法稳定性可以跑多次训练、每次用不同种子统计平均成功率。这是更严谨的评估方式但那是后话先把单次实验复现做好。5.5 迷宫尺寸一大就收敛不动——Q表稀疏与状态泛化问题迷宫从5×5扩到20×20状态数从25涨到400Q表从100个值涨到1600个值。你可能会发现训练回合数增加到5000仍然不够路径远谈不上最优。这不是代码写错了是表格Q-Learning的天然边界它每个状态每个动作都独立存储状态之间没有任何泛化。智能体在某条路径上学到的东西无法迁移到相邻格子上一切都要靠样本去填满Q表。这个问题的正确解法不是硬调参而是换算法视角。要么用DQN这类深度强化学习算法让神经网络在同一套参数下泛化不同格子的价值要么把迷宫地图缩小做课程学习——先训5×5再逐步扩到10×10。对毕设级别的迷宫逃脱演示我强烈建议控制迷宫规模在15×15以内表格Q-Learning在这个范围里能跑得又快又稳。迷宫规模再大就直接换深度强化学习算法路线吧。6. 进阶把训练过程可视化回放策略并验证Q表质量6.1 用imagesc画Q值热力图验证状态价值分布训练完成后把Q表压缩成每个状态的最大动作价值reshape回栅格形状用imagesc一画整个迷宫的学习情况就一目了然Q_max max(Q, [], 2); Q_map reshape(Q_max, rows, cols); figure; imagesc(Q_map); colorbar; axis equal; axis tight; title(Q值热力图);热力图上终点附近的格子应该最亮Q值从终点向外围逐圈衰减。如果整个迷宫只有终点一格是亮的说明Q值还没来得及往远处状态传播训练回合数不够如果路径沿途的格子和障碍旁边一样亮说明智能体对撞墙和多绕几步的区分度不够需要回头检查奖励函数。这个可视化技巧比盯着数字矩阵看直观得多也是排查“到底学没学会”的最快路径。6.2 用训练好的Q表做一次贪婪回放对比最短路径最后一个验证步骤是策略回放用训练好的Q表从起点开始走一遍把路径画出来s start_state; path s; for t 1:max_steps [~, a] max(Q(s, :)); % 纯贪婪策略 [s, ~, done] get_next_state(s, a, maze, rows, cols); path [path; s]; if done break; end end回放路径如果出现了撞墙说明Q表还在震荡期返回去加训练回合。如果路径顺利到终点但明显绕远说明智能体学到的是“能逃就行”而不是“最短路径”这时候可以把终点奖励从100再调高点或者把每步惩罚从-1调成-2逼它少走冤枉路。更严格的做法是用BFS跑出理论最短路径长度和回放步数对比一致才算真正学到最优。我每次做完迷宫实验都会先看一眼Q值热力图再跑一遍贪婪回放路径。热力图有没有从终点往外扩散、回放路径是不是接近最短路这两点都对了才敢说这个智能体真的学会了而不是靠随机种子蒙出来的。希望帮到你。本文还有配套的精品资源点击获取