
简介本资源是一套面向机器学习初学者与强化学习实践者的MATLAB教学程序包聚焦网格迷宫这一经典路径规划问题完整实现Q-learning、Sarsa及Sarsa-Lambda三种时序差分算法。代码模块清晰、注释详实涵盖策略选择ε-greedy、状态转移、奖励设计与Q表更新等核心环节适用于算法原理验证、课程实验及小规模MDP建模练习。压缩包共含9个.m文件总大小仅6KB全部为可直接运行的MATLAB脚本包括主算法入口如WGW_Qlearning.m、动作执行movement.m、探索策略tcegreedy.m等关键组件结构简洁、无外部依赖便于逐行调试与对比分析。目前已有4536人学习下载读者可即刻获取三类算法的完整实现逻辑、统一迷宫环境接口及可复用的强化学习基础函数是理解值函数迭代与在线策略优化的理想入门材料。1. 项目缘起从理论到实践的强化学习第一课如果你刚开始接触强化学习面对那些充斥着贝尔曼方程、价值迭代、策略梯度等术语的论文和教程可能会感到一阵头大。理论固然重要但没有什么比亲手实现一个能跑起来的智能体更能让人理解其精髓。这正是“网格迷宫”这个经典问题经久不衰的原因——它足够简单让你能聚焦于强化学习的核心逻辑又足够直观智能体每一步的探索与决策都清晰可见。而MATLAB凭借其强大的矩阵运算能力和友好的可视化界面成为了实现这个想法的绝佳工具。今天要分享的就是一个基于MATLAB环境使用Q-Learning算法解决网格迷宫问题的完整程序包。这个项目不是为了炫技而是我作为过来人希望能提供一个“开箱即用”的起点。它封装了环境建模、智能体训练、策略评估和结果可视化的全过程。你不需要从零开始写矩阵运算也不用头疼怎么画图直接运行就能看到一个智能体如何从对迷宫一无所知到最终找到最优路径的全过程。这对于学生理解算法、研究者快速验证想法或是工程师进行算法原型测试都是一个非常实用的工具。2. Q-Learning算法核心一张不断更新的“经验地图”在深入代码之前我们必须先搞清楚智能体的大脑是如何工作的。Q-Learning是一种无模型的时序差分强化学习算法它的核心是维护一张名为Q表Q-table的“经验地图”。在网格迷宫问题中这张地图的维度是[状态总数 动作总数]。状态就是智能体所在网格的坐标比如(2,3)动作通常是上、下、左、右四个方向。Q表里的每个值Q(s, a)代表了在状态s下采取动作a所能获得的长期累积奖励的期望。智能体一开始这张地图是空白的全部初始化为0或很小的随机数它通过不断地在迷宫中试错来填充和更新这张地图。更新的规则就是Q-Learning的精华所在其公式如下Q(s, a) Q(s, a) α * [ R γ * max_a Q(s, a) - Q(s, a) ]这个公式看起来有点复杂我们拆解一下s, a当前状态和采取的动作。R执行动作a后环境反馈的即时奖励。在迷宫问题里到达终点给一个大正奖励比如10撞墙或走入禁区给一个负奖励比如-1普通移动给一个很小的负奖励比如-0.1鼓励快速到达。s执行动作后到达的新状态。max_a Q(s, a)在新状态s下所有可能动作中最大的Q值。这代表了智能体“认为”从s出发未来能获得的最好回报。α (alpha)学习率。取值范围0到1它控制着新信息覆盖旧信息的程度。α0意味着完全不学习α1意味着完全用新估计替换旧值。通常设置为一个较小的值如0.1。γ (gamma)折扣因子。同样取值范围0到1它衡量未来奖励的重要性。γ0意味着智能体只关心眼前利益γ接近1意味着它非常重视长远回报。一般设为0.9或0.99。公式中括号内的部分[R γ * max_a Q(s, a) - Q(s, a)]被称为时序差分误差。你可以把它理解为“现实与预期的差距”。智能体用这个差距按照学习率α的比例来修正自己对Q(s, a)的估计。经过成千上万次这样的试错和修正Q表最终会收敛此时每个状态下的最优动作就是拥有最大Q值的那个动作。注意max_a Q(s, a)这个操作是Q-Learning作为离策略算法的关键。它用于更新时评估的是“最优策略”下的未来价值但智能体实际执行的动作可能来自探索性的策略如ε-greedy。这使得它能更高效地学习最优策略。3. 程序包架构与核心模块详解这个MATLAB程序包采用模块化设计结构清晰主要分为环境、智能体、训练和可视化四大模块。下面我们逐一拆解每个模块的关键实现。3.1 迷宫环境类 (GridWorld.m)环境是智能体交互的对象它需要定义状态空间、动作空间、奖励函数和状态转移动力学。在GridWorld类中我们通常用矩阵来表示迷宫。classdef GridWorld handle properties gridSize % 迷宫尺寸如 [10, 10] obstacleMap % 障碍物地图1表示障碍0表示可通行 startState % 起始状态坐标如 [1, 1] goalState % 目标状态坐标如 [10, 10] currentState % 当前状态坐标 rewardGoal % 到达目标的奖励 rewardObstacle % 撞到障碍的奖励 rewardStep % 每走一步的奖励 isDone % 回合是否结束标志 end methods function obj GridWorld(size, start, goal, obstacles) % 构造函数初始化迷宫 obj.gridSize size; obj.startState start; obj.goalState goal; obj.currentState start; obj.obstacleMap zeros(size); obj.obstacleMap(obstacles) 1; % 设置障碍位置 obj.rewardGoal 10; obj.rewardObstacle -5; obj.rewardStep -0.1; obj.isDone false; end function [nextState, reward, isDone] step(obj, action) % 执行动作返回新状态、奖励和结束标志 proposedState obj.currentState; switch action case 1 % 上 proposedState(1) proposedState(1) - 1; case 2 % 下 proposedState(1) proposedState(1) 1; case 3 % 左 proposedState(2) proposedState(2) - 1; case 4 % 右 proposedState(2) proposedState(2) 1; end % 边界和障碍检查 if obj.isValidState(proposedState) obj.currentState proposedState; reward obj.rewardStep; else % 撞墙或出界状态不变给予惩罚 reward obj.rewardObstacle; end % 检查是否到达目标 if isequal(obj.currentState, obj.goalState) reward obj.rewardGoal; isDone true; obj.isDone true; else isDone false; end nextState obj.currentState; end function reset(obj) % 重置环境到初始状态 obj.currentState obj.startState; obj.isDone false; end function valid isValidState(obj, state) % 检查状态是否有效在边界内且不是障碍 valid all(state 1) all(state obj.gridSize) ... obj.obstacleMap(state(1), state(2)) 0; end end end关键设计点状态表示这里用二维坐标[行 列]直接表示状态直观且易于计算。另一种常见做法是将二维坐标线性化为一维索引这可以简化Q表的维度从[行列动作]变为[状态索引动作]但在可视化时可能需要转换回来。奖励函数设计这是引导智能体学习的关键。rewardStep设置为一个小的负值-0.1称为“生存成本”鼓励智能体尽快找到目标避免无效徘徊。rewardObstacle的惩罚要足够大让智能体学会避障。rewardGoal的正向奖励要显著高于其他值。动作执行与检查在step函数中先计算目标状态然后进行有效性校验。这种“先计算后校验”的模式比“边移动边判断”更清晰。无效动作导致状态不变并给予惩罚这模拟了现实中的“撞墙”效果。3.2 Q-Learning智能体类 (QLearningAgent.m)智能体类封装了Q表和学习算法。classdef QLearningAgent handle properties numStates % 状态总数如果状态线性化 numActions % 动作总数通常是4 QTable % Q表核心数据结构 learningRate % α discountFactor % γ epsilon % ε-greedy策略中的探索率 epsilonDecay % ε衰减率 minEpsilon % ε的最小值 end methods function obj QLearningAgent(stateDims, numActions, alpha, gamma, epsilon, decay, minEps) % 初始化智能体 obj.numStates prod(stateDims); % 假设状态线性化 obj.numActions numActions; obj.QTable zeros(obj.numStates, numActions); obj.learningRate alpha; obj.discountFactor gamma; obj.epsilon epsilon; obj.epsilonDecay decay; obj.minEpsilon minEps; end function action chooseAction(obj, state) % 根据ε-greedy策略选择动作 stateIdx obj.stateToIndex(state); if rand obj.epsilon % 探索随机选择一个动作 action randi(obj.numActions); else % 利用选择当前状态下Q值最大的动作 [~, action] max(obj.QTable(stateIdx, :)); end end function learn(obj, state, action, reward, nextState, done) % Q-Learning更新规则 stateIdx obj.stateToIndex(state); nextStateIdx obj.stateToIndex(nextState); currentQ obj.QTable(stateIdx, action); if done targetQ reward; % 回合结束没有未来状态 else % 关键使用下一个状态的最大Q值作为未来回报的估计 maxNextQ max(obj.QTable(nextStateIdx, :)); targetQ reward obj.discountFactor * maxNextQ; end % 更新Q值 obj.QTable(stateIdx, action) currentQ ... obj.learningRate * (targetQ - currentQ); % 衰减探索率可选 if obj.epsilon obj.minEpsilon obj.epsilon obj.epsilon * obj.epsilonDecay; end end function idx stateToIndex(obj, state) % 将二维坐标状态转换为一维索引假设网格世界 % 例如对于10x10网格(3,4) - (3-1)*10 4 24 idx (state(1)-1) * obj.gridSize(2) state(2); % 注意这里需要智能体知道环境尺寸更好的做法是通过构造函数传入。 % 本例为简化说明实际代码中需要处理此依赖。 end end end关键设计点ε-greedy策略这是平衡探索与利用的经典方法。在训练初期epsilon值较高如0.9智能体倾向于随机探索收集环境信息。随着训练进行epsilon逐渐衰减智能体越来越依赖学到的Q表利用来选择最优动作。衰减机制避免了早期陷入局部最优也保证了后期策略的稳定性。Q表更新时机learn方法在智能体执行动作并获得环境反馈后被调用。注意它更新的是上一个状态-动作对(s, a)的Q值使用的是当前获得的奖励r和下一个状态s的估计。状态索引转换stateToIndex函数至关重要。它将二维的网格坐标映射到Q表的一维行索引。这要求智能体了解环境的尺寸信息通常需要在初始化时从环境对象获取。确保这个映射是唯一且可逆的。3.3 训练主循环 (train.m)这是将环境和智能体连接起来的“导演脚本”。它控制着训练的总回合数、每回合的最大步数并记录训练过程中的关键指标。function [agent, stats] train(env, agent, numEpisodes, maxStepsPerEpisode) stats.episodeRewards zeros(numEpisodes, 1); stats.episodeSteps zeros(numEpisodes, 1); stats.epsilonHistory zeros(numEpisodes, 1); for episode 1:numEpisodes env.reset(); state env.startState; totalReward 0; steps 0; done false; while ~done steps maxStepsPerEpisode % 1. 智能体根据当前状态选择动作 action agent.chooseAction(state); % 2. 环境执行动作返回反馈 [nextState, reward, done] env.step(action); % 3. 智能体从经验中学习 agent.learn(state, action, reward, nextState, done); % 4. 转移到新状态更新统计 state nextState; totalReward totalReward reward; steps steps 1; end % 记录本回合数据 stats.episodeRewards(episode) totalReward; stats.episodeSteps(episode) steps; stats.epsilonHistory(episode) agent.epsilon; % 每100回合打印一次进度 if mod(episode, 100) 0 fprintf(Episode %d, Total Reward: %.2f, Steps: %d, Epsilon: %.3f\n, ... episode, totalReward, steps, agent.epsilon); end end end训练流程解析 这个循环是强化学习的核心范式感知-决策-学习-循环。每一步智能体观察状态state做出决策action环境给出反馈reward, nextState智能体立即用这个反馈更新自己的知识learn。这种“在线学习”的方式是时序差分算法的特点。参数设置经验numEpisodes训练回合数对于简单的10x10迷宫5000-10000回合通常足够收敛。可以通过观察episodeRewards曲线是否平稳来判断。maxStepsPerEpisode每回合最大步数这个值需要设置得合理。如果太小智能体可能还没找到目标就被强制终止学不到有效路径如果太大训练效率会降低。一般可以设置为网格单元格数量的若干倍如10x10迷宫设200步。打印日志定期输出回合信息非常重要它能让你实时监控训练是否正常奖励是否在增加步数是否在减少以及探索率epsilon的衰减情况。3.4 可视化与评估模块 (visualize.m)训练完成后我们需要直观地看到结果。可视化模块通常包括以下几个部分学习曲线图绘制episodeRewards和episodeSteps随训练回合变化的曲线。这是评估训练过程是否收敛最直接的指标。一个成功的训练会显示累计奖励上升并最终稳定每回合步数下降并最终稳定在一个较低值接近最优路径长度。figure; subplot(2,1,1); plot(smooth(stats.episodeRewards, 50)); % 平滑处理便于观察趋势 xlabel(Episode); ylabel(Total Reward); title(Learning Curve - Reward); grid on; subplot(2,1,2); plot(smooth(stats.episodeSteps, 50)); xlabel(Episode); ylabel(Steps per Episode); title(Learning Curve - Steps); grid on;最终策略可视化在迷宫地图上用箭头绘制出每个状态非障碍、非终点下Q值最大的动作即最优策略。这能一目了然地看到智能体学到的路径规划。function plotPolicy(env, agent) [rows, cols] size(env.obstacleMap); hold on; % 绘制障碍物 [obsRow, obsCol] find(env.obstacleMap 1); plot(obsCol, obsRow, ks, MarkerSize, 10, MarkerFaceColor, k); % 绘制起点和终点 plot(env.startState(2), env.startState(1), go, MarkerSize, 10, MarkerFaceColor, g); plot(env.goalState(2), env.goalState(1), ro, MarkerSize, 10, MarkerFaceColor, r); for r 1:rows for c 1:cols if env.obstacleMap(r, c) 0 ~isequal([r,c], env.goalState) state [r, c]; stateIdx agent.stateToIndex(state); [~, bestAction] max(agent.QTable(stateIdx, :)); % 根据bestAction绘制箭头 switch bestAction case 1 % 上 quiver(c, r, 0, -0.3, b, LineWidth, 1.5, MaxHeadSize, 2); case 2 % 下 quiver(c, r, 0, 0.3, b, LineWidth, 1.5, MaxHeadSize, 2); % ... 类似处理左和右 end end end end axis equal; axis([0.5 cols0.5 0.5 rows0.5]); set(gca, YDir, reverse); % 让矩阵的行号从上到下增长 title(Learned Optimal Policy); hold off; end路径演示让训练好的智能体从起点开始完全根据学到的策略epsilon0纯利用走一遍迷宫并动态展示其移动轨迹。这是最令人满意的成果展示。4. 实战调参让智能体真正学会走迷宫有了代码框架直接运行可能效果并不理想。强化学习的性能极大程度上依赖于超参数的选择。下面是我在多次实验中总结出的调参经验和常见问题排查。4.1 超参数敏感度分析与调优指南参数典型范围影响调优建议学习率 (α)0.01 ~ 0.5控制Q值更新步长。太大导致震荡不收敛太小导致学习过慢。从0.1开始尝试。如果奖励曲线剧烈波动降低α如果学习速度太慢适当增加α。简单环境可用稍大的值如0.2。折扣因子 (γ)0.9 ~ 0.99衡量未来奖励的重要性。越接近1智能体越有远见。对于迷宫这类有明确终止目标的任务通常设为0.9或0.95。如果智能体总是在终点前徘徊可以尝试稍微降低γ让它更关注近期奖励。初始探索率 (ε)0.9 ~ 1.0训练初期随机探索的概率。必须足够高以确保充分探索状态空间。通常从0.9或1.0开始。探索率衰减 (ε_decay)0.995 ~ 0.9995每回合后ε的衰减乘子。衰减不宜过快否则探索不充分。设为0.999意味着1000回合后ε约降至0.37。可以配合minEpsilon如0.01使用保证始终有微小探索。每步奖励 (rewardStep)-0.05 ~ -0.5每走一步的“生存成本”。轻微的负奖励如-0.1能有效鼓励智能体寻找最短路径。如果设为0智能体可能学会在迷宫里无限绕圈而不去终点因为它没有时间压力。目标奖励 (rewardGoal)10 ~ 100到达终点的正向奖励。必须显著高于其他奖励的绝对值之和。例如如果最优路径需要20步每步-0.1则总生存成本为-2那么目标奖励至少应大于2。通常设为10或50。障碍惩罚 (rewardObstacle)-1 ~ -10撞墙或出界的惩罚。需要足够大以阻止危险行为但不宜过大导致智能体过于保守。通常设为-5左右。一个实用的调参流程固定环境先设计一个简单的迷宫如5x5无障碍或少量障碍。设定基线使用一组常用参数α0.1 γ0.9 ε1.0 ε_decay0.999 rewardStep-0.1 rewardGoal10 rewardObstacle-5进行训练。观察曲线运行1000-2000回合观察奖励和步数曲线。理想情况奖励快速上升后稳定在高位步数快速下降后稳定在最优步数附近。奖励不上升可能是学习率α太小或探索率ε衰减太快导致智能体被困在局部策略。尝试增大α或降低ε_decay。奖励波动大可能是学习率α太大或目标奖励rewardGoal相对于rewardStep过高导致Q值更新不稳定。尝试减小α。智能体找不到目标检查rewardGoal是否足够高gamma是否过低导致它不重视未来奖励。同时确保初始探索率ε足够高。迭代优化每次只调整1-2个参数观察变化逐步逼近最优性能。4.2 训练过程中的典型问题与排查智能体原地打转或重复无效动作现象学习曲线停滞策略可视化显示在某些状态下来回移动。可能原因陷入了局部最优。某个动作偶然获得了稍高的即时奖励比如rewardStep为0时任何移动都没成本导致Q表更新后智能体反复执行该动作。解决方案确保rewardStep为负值增加时间成本。检查rewardGoal是否足够高形成强烈对比。增加环境的随机性例如以很小概率让动作执行失败状态不变这能打破一些循环。尝试更复杂的探索策略如随时间衰减的ε-greedy或使用上限置信区间UCB等。Q值爆炸或变成NaN/Inf现象训练后期程序报错或策略出现匪夷所思的箭头。可能原因Q值更新公式中如果gamma等于或非常接近1且智能体处在一个有循环的路径中未来奖励可能会被无限次累加导致Q值理论上趋于无穷大。在MATLAB数值计算中就会溢出。解决方案确保gamma严格小于1如0.99。在learn函数中可以为Q值设置一个裁剪范围如-100, 100但这只是权宜之计。更根本的方法是确保环境有终止条件如到达目标或超过最大步数每个回合必须结束。收敛速度过慢现象需要非常多的训练回合如数万回合才能学到像样的策略。可能原因状态空间较大或探索效率低。解决方案优化探索使用ε衰减策略但初始值要高衰减要慢。奖励塑形设计更密集的奖励信号。例如除了终点奖励还可以给予“向目标靠近”的奖励。这需要更精细的环境设计但能极大加速学习。调整学习率可以尝试使用自适应学习率在训练初期用较大的α快速学习后期用较小的α精细调整。5. 超越基础程序包的进阶扩展思路当你跑通基础版本后这个程序包可以作为一个平台进行各种有趣的扩展深化对强化学习的理解。5.1 算法变体实现SARSA (State-Action-Reward-State-Action)同样是时序差分算法但它是同策略的。它与Q-Learning的唯一区别在于更新公式。SARSA使用实际执行的下一个动作a的Q值来更新而不是下一个状态的最大Q值。% Q-Learning: targetQ reward gamma * max( Q(s, :) ) % SARSA: targetQ reward gamma * Q(s, a_next) % 其中 a_next 是智能体在 s 状态下根据当前策略如ε-greedy实际选择的下一个动作。在learn函数中你需要多传入一个参数nextAction。SARSA通常更保守因为它沿着实际策略更新而Q-Learning更激进直接学习最优策略。在悬崖漫步等有危险的环境中SARSA学到的策略可能更安全。Double Q-Learning为了解决Q-Learning在某些情况下可能存在的过估计问题。过估计是指Q值系统性地高于真实值导致学习不稳定。Double Q-Learning维护两张Q表Q1和Q2更新时随机选择一张表用来选择动作另一张表用来评估价值。if rand 0.5 % 用Q1选择动作用Q2评估 [~, bestAction] max(Q1(nextStateIdx, :)); targetQ reward gamma * Q2(nextStateIdx, bestAction); % 更新Q1 Q1(stateIdx, action) Q1(stateIdx, action) alpha * (targetQ - Q1(stateIdx, action)); else % 用Q2选择动作用Q1评估 % ... 类似更新Q2 end这种技巧在更复杂的环境中能提升学习的稳定性和最终性能。5.2 环境复杂度升级随机动态环境让障碍物随机出现或消失或者让动作执行有一定概率失败如指令“向上”有10%概率执行成“向左”。这能测试智能体对不确定环境的鲁棒性。部分可观测马尔可夫决策过程智能体不能直接看到自己的精确坐标只能看到周围局部网格的信息。这更接近现实中的机器人导航需要引入记忆机制如RNN或使用深度Q网络。连续动作空间将动作从离散的{上下左右}改为连续的速度和方向。这需要引入策略梯度类算法如Actor-Critic已超出传统Q-Learning范畴但可以作为后续学习的方向。5.3 工程化与性能优化经验回放这是深度Q网络中的关键技术但思想同样可用于表格型方法。将智能体与环境交互产生的经验(s, a, r, s, done)存储在一个固定大小的回放缓冲区中。学习时随机从缓冲区中采样一批经验进行更新。这打破了经验之间的相关性能使学习更稳定、更高效。并行化训练MATLAB支持并行计算。你可以同时运行多个智能体在多个迷宫实例或同一迷宫的不同起始点中探索然后汇总它们的经验来更新一个共享的Q表。这能极大地提高数据采集效率。保存与加载模型将训练好的Q表、超参数以及学习曲线保存为.mat文件。下次可以直接加载智能体进行测试或继续训练方便实验管理和结果复现。这个MATLAB Q-Learning网格迷宫程序包就像一把打开强化学习大门的钥匙。它从最本质的表格方法入手让你清晰地看到价值迭代的每一个步骤。通过调整迷宫布局、修改奖励函数、尝试不同的超参数你能直观地感受到强化学习智能体是如何被“奖励”所塑造的。当你熟练掌握了这个基础版本并成功实现了上述的一些扩展你对强化学习的理解将不再停留在公式层面而是拥有了将其应用于更复杂、更实际问题的信心和能力。本文还有配套的精品资源点击获取