
简介这是一套基于Q-Learning的路径规划MATLAB仿真系统面向算法初学者与进阶学习者支持在任意障碍物环境中自由选取起点与目标点直观演示Q-Learning算法的路径搜索与收敛过程同时为MATLAB GUI界面开发提供完整范例。压缩包共包含36个文件以24个.m脚本为核心覆盖Q表初始化、传感器感知、动作选择、移动仿真与结果统计等环节另有txt说明文档、mat数据文件、fig界面文件和日志文件整体仅222KB轻量且便于阅读其中fig文件可直接打开查看界面布局log日志则有助于定位运行异常。资源附有系统简介与说明文档并预置仿真数据便于直接运行验证效果。目前已有265人学习适合快速上手并对照源码调试。通过这套程序读者可系统梳理Q-Learning算法的实现流程掌握MATLAB交互界面编程思路并可在现有代码基础上改进算法或扩展实验为后续学术论文写作与工程应用奠定基础。1. 从“能跑通”到“能自证有效”Q-Learning 路径规划在 MATLAB 里最该补的一课标题里的 Q-Learing 就是业界常说的 Q-Learning一个基于价值迭代的无模型强化学习算法。做路径规划时它不需要事先知道全局地图的解析模型只要在栅格地图上反复“试错”就能让智能体学会从起点绕开障碍物走到目标点。MATLAB 仿真系统是验证这套逻辑最方便的环境有强大的矩阵运算、绘图和调试工具网格世界天然适合用数组表示。但很多刚上手的工程师只跑通一次训练曲线就开始调参数忽略了“自测”这一层——也就是像单元测试一样用多组起点、多次随机种子、不同障碍密度去验证 Q 表真的学到策略而不是过拟合到某一条路径。本文围绕“基于 Q-Learning 的路径规划 MATLAB 仿真系统自测”展开从建模、训练、验证到常见坑给出一套可以直接抄的 MATLAB 实现。2. Q-Learning 路径规划仿真系统的“栅格地图 奖励函数”建模先确定环境表示。常见做法是用占据栅格地图Occupancy Grid Map背景是 0障碍是 1目标点单独标记。这样做的好处是状态空间离散Q 表可以用二维数组直接索引坐标。如果地图是 20×20状态数就是 400动作设 4 个上下左右Q 表就是一个 400×4 的 double 矩阵训练过程中的存储和访问成本都非常低。相比连续空间需要函数逼近这种离散建模适合作为第一版自测系统。2.1 栅格地图的生成与状态定义我一般用 MATLAB 直接用矩阵构造测试地图不用额外工具箱。下面的代码生成一个 10×10 的障碍地图% grid_map_setup.m gridSize 10; % 栅格尺寸设为 10x10 map zeros(gridSize, gridSize); % 0 表示可通行区域 map(3, 2:8) 1; % 第一条横向障碍物 map(6, 4:9) 1; % 第二条横向障碍物 start [1, 1]; % 起点 [行, 列] goal [10, 10]; % 目标点 [行, 列] map(goal(1), goal(2)) 2; % 标记目标避免与障碍冲突这里用zeros初始化地图行和列对齐矩阵下标。起点和终点用[row, col]表示目标点放在障碍物标记之后避免被覆盖。动作集建议定义为四个方向向量deltas [-1 0; 1 0; 0 -1; 0 1]分别对应上、下、左、右。状态直接用线性索引idx sub2ind([gridSize, gridSize], row, col)Q 表行号就是该索引这样可以省去哈希映射训练时访问更快。2.2 动作边界与状态转移逻辑智能体在栅格地图上移动时可能碰到地图边界或障碍物。处理动作非法有两种思路一是把非法转移视为“留在原地”给一个负奖励二是直接禁止该动作采样时重选。前者简单但会引入偏差让智能体学到“撞墙也能耗步数”。我的自测脚本里采用后者在动作选择后先检查下一个位置是否合法% 检查下一个状态是否合法 nextRow row deltas(action, 1); nextCol col deltas(action, 2); if nextRow 1 || nextRow gridSize || nextCol 1 || nextCol gridSize invalid true; elseif map(nextRow, nextCol) 1 invalid true; else invalid false; end这段逻辑的关键点是边界判断必须在障碍判断之前因为map索引在越界位置运行会直接报错。invalid true后就不再更新状态训练时把它当作一次没有被执行的探索不计入回合步数总数避免智能体通过在原地反复“撞墙”来虚增训练量。2.3 奖励函数的设计稀疏还是密集奖励函数是 Q-Learning 能不能快速收敛的关键。最朴素的做法是达到目标给 10碰到障碍给 -5普通格子给 0。这种稀疏奖励很容易导致前期探索没有方向尤其在 10×10 地图上随机走几千步都碰不到终点。改进的方法是给普通格子一个小的负奖励让智能体倾向于走短路径还可以加 Manhattan 距离作为稠密奖励但注意奖励绝对值不能设置太大否则 Q 值的尺度会很漂移。奖励项推荐值对训练的影响到达目标50鼓励接近终点的行为过大会忽略避障碰撞障碍-10绝对值要大于单步负奖励否则智能体会“穿墙”普通格子-0.2惩罚步数促使最短路径超出边界-10与碰撞同等对待避免出界提示奖励函数要在“尽快到达”和“绕开障碍”之间平衡建议先跑几次调整数量级不要一上来就设置太大。自测时可以用同一张地图、同一组参数只改奖励值看收敛回合数和最终路径长度是否稳定。3. 用 MATLAB 写 Q-Learning 训练主循环状态更新、动作选择与 Q 表收敛建模完成后核心训练流程就是标准的 Q-Learning 更新。你需要提前明确三个概念折扣因子gamma决定远见程度学习率alpha决定单个样本的修正幅度贪婪系数epsilon控制探索与利用的平衡。这三个参数直接决定路径规划仿真系统是否收敛也是自测时最先要检查的对象。3.1 最小可运行的训练脚本下面这段脚本是完整的 Q-Learning 训练主循环可以直接放在train_qlearning.m中运行% train_qlearning.m alpha 0.6; % 学习率 gamma 0.95; % 折扣因子 epsilon 0.2; % 探索率 episodes 400; % 训练回合数 Q zeros(gridSize * gridSize, 4); % 状态数×动作数 stepCount zeros(episodes, 1); % 记录每回合步数 rewardSum zeros(episodes, 1); % 记录每回合总奖励 for ep 1:episodes row start(1); col start(2); totalReward 0; for t 1:200 % 单回合最大步数 s sub2ind([gridSize, gridSize], row, col); % epsilon-greedy 动作选择 if rand() epsilon action randi(4); % 随机探索 else [~, action] max(Q(s, :)); % 利用当前 Q 表 end % 计算下一状态与奖励 nextRow row deltas(action, 1); nextCol col deltas(action, 2); invalid false; if nextRow 1 || nextRow gridSize || nextCol 1 || nextCol gridSize invalid true; elseif map(nextRow, nextCol) 1 invalid true; end if invalid reward -10; nextRow row; nextCol col; % 状态不变 elseif nextRow goal(1) nextCol goal(2) reward 50; nextRow row; nextCol col; % 回合结束状态不再更新 else reward -0.2; end nextS sub2ind([gridSize, gridSize], nextRow, nextCol); % Q 值更新公式 Q(s, action) Q(s, action) alpha * ... (reward gamma * max(Q(nextS, :)) - Q(s, action)); row nextRow; col nextCol; totalReward totalReward reward; if reward 50 break; % 到达目标结束当前回合 end end stepCount(ep) t; rewardSum(ep) totalReward; end这个脚本里最关键的是更新公式。gamma * max(Q(nextS, :))表示下一状态的最大未来回报reward 这个未来回报是当前动作的“真实目标”Q(s, action)减去当前估计就是时序差分误差。alpha乘以这个误差就是一次更新的步长。注意到达目标时我把nextRow和nextCol设成和当前一样是为了防止在回合结束后仍然错误地执行一次状态转移代码里用reward 50来判断终点实际工程中建议用isGoal标志变量避免奖励值改动后判断失效。3.2 训练参数怎么选学习率大于 0.8 时训练初期收敛快但后期容易震荡小于 0.2 会影响前期学习速度。我常用的保守起手值是alpha 0.6然后再以 0.1 的步长做网格搜索。折扣因子gamma在路径规划中一般取 0.90.99这个值越接近 1智能体越重视长期收益。epsilon固定 0.2 适合小地图但在地图变大或障碍复杂时固定探索率会让收敛后期策略抖动。标准做法是让epsilon从 0.8 按回合数衰减到 0.05这个过程我会在自测参数表中单独记录。参数推荐范围默认值自测时的记录项alpha0.30.70.6收敛步数是否单调下降gamma0.90.990.95是否绕远路epsilon0.050.8 衰减0.2最终策略是否仍随机跳变最大步数 t状态数×24200是否被截断回合数 episodes2001000400连续多少次未到终点3.3 动作选择策略的一个常见误用很多初版代码会在动作选择时把max(Q(s, :))直接作为动作比如[~, action] max(Q(s, :))。这个写法本身没问题但要小心平局情况。如果 Q 表初始为全零第一回合里所有动作的 Q 值相等max会固定返回第一个索引导致智能体永远先往上走。我一般会在利用分支里加一个随机打破平局的逻辑best find(Q(s, :) max(Q(s, :))); action best(randi(length(best)));另外epsilon-greedy中的rand() epsilon每次都会判断要注意随机数种子在自测时固定否则同一份代码跑两次结果不同无法定位是代码问题还是随机性导致的差异。固定种子的方法是在主脚本开头调用rng(42)。4. 仿真系统自测的三层验证收敛性、路径有效性与参数鲁棒性路径规划仿真系统不像普通算法光看奖励曲线上升还不够。我见过不少系统训练 200 回合后奖励已经稳定但把起点换到地图另一边智能体就原地打转或者改变障碍密度后路径明显撞墙。自测的核心是把“能跑通”提升到“可解释、可复现、边界清晰”的程度。下面这套三层验证方法可以直接复制到自己的项目中。4.1 第一层训练过程收敛性验证收敛性验证不是只看最后 Q 表是否不变而是看三个指标每回合总奖励是否趋近稳定值、步数是否下降、到达目标的回合占比是否接近 1。建议用一组脚本批量输出而不是只看一行disp% verify_convergence.m figure(1); subplot(2,1,1); plot(stepCount); xlabel(回合); ylabel(步数); title(每回合步数曲线); subplot(2,1,2); plot(rewardSum); xlabel(回合); ylabel(总奖励); title(每回合总奖励曲线); % 输出最近50回合的平均步数 recentWindow stepCount(end-49:end); fprintf(最近50回合平均步数%.2f\n, mean(recentWindow));注意看曲线时不能只盯着最后一个点。如果步数曲线在 200 回合附近还有突然上升的尖峰说明epsilon的探索仍然会随机走出很差的路这种尖峰次数需要记录下来。自测时可以设定一个阈值比如“最近 100 回合中超过 10 次碰到障碍则判定未收敛”。这一步的价值在于把主观的“看起来还行”变成可以自动判定的规则。4.2 第二层路径有效性批量测试训练完成后用 Q 表做贪心策略推理从不同起点跑出路径并检查是否无碰撞、是否到达目标点、路径长度是否合理。这里要注意不要把训练时固定的起点当成唯一验证点要覆盖地图的角落和障碍包围的凹槽区域。下面脚本对地图上所有可通行点做批量测试% verify_paths.m successCount 0; totalCount 0; maxPathLength 0; for r 1:gridSize for c 1:gridSize if map(r,c) 0 ~(rstart(1) cstart(2)) totalCount totalCount 1; path []; s sub2ind([gridSize, gridSize], r, c); for t 1:500 [~, action] max(Q(s, :)); nextRow r deltas(action, 1); nextCol c deltas(action, 2); % 这里需要做同样的边界和障碍检查 if invalid, break; end path [path; nextRow, nextCol]; r nextRow; c nextCol; if r goal(1) c goal(2) successCount successCount 1; maxPathLength max(maxPathLength, t); break; end end end end end fprintf(成功到达目标的比例%.1f%%\n, successCount/totalCount*100);这段代码是自测系统的核心能一次性暴露三个问题障碍物附近的起点是否会因为 Q 值偏差而撞墙、地图角落是否有状态被 Q 表忽略、路径是否出现循环。循环会体现在maxPathLength超过状态数一倍以上正常无环路径长度不会超过gridSize * gridSize。如果发现循环优先检查奖励函数中“普通格子 -0.2”的惩罚是否太小导致绕圈和直行在累计回报上没有显著区别。4.3 第三层参数鲁棒性与随机种子测试单次训练成功不代表系统自测通过。强化学习对随机种子敏感是常态但要区分“正常波动”和“结果彻底失败”。我会跑一组alpha和epsilon的交叉实验每个组合固定 5 个随机种子记录成功率和平均路径长度。alphaepsilon 初始平均成功率平均路径长度备注0.30.582%24.3收敛慢0.50.595%18.6折中0.70.591%21.1后期震荡0.50.897%19.2初期探索充分做法很简单把训练脚本封装成函数输入参数和随机种子输出指标% run_experiment.m function result run_experiment(map, start, goal, alpha, epsilon, seed) rng(seed); % 这里放训练循环训练结束后执行批量路径测试 result.successRate ...; result.avgPathLength ...; end自测时重点关注路径长度有没有随alpha变化出现“V 形”曲线。如果某个参数组合能显著减少路径长度但成功率略降那说明智能体学会了抄近道但避障裕量不够。这种情况我会在最终系统里选择更保守的参数而不是一味追求最短路径。5. 自测中最容易忽略的奖励正则化与参数衰减技巧前几轮自测通常会通过但我在调试动态避障小车路径规划和更复杂的机器人路径规划仿真时发现三个高频问题这里给成具体的处理技巧。第一个技巧是把奖励值除以一个经验尺度避免 Q 表数值过大。比如目标奖励 50、步数奖励 -0.2在长路径下累计奖励为50 - 路径长度*0.2路径长度 30 时总奖励也有 44Q 值的数量级在几到几十之间。但如果地图扩大到 100×100路径长度 300 以上总奖励可能变成负值智能体策略会急转。我的做法是根据地图对角线距离maxDist设计奖励普通格子奖励设为-1/maxDist目标奖励设为1这样所有 Q 值都能被限定在[-1, 1]附近方便跨地图调参。第二个技巧是epsilon衰减不能只看回合编号还要看当前策略是否稳定。固定的线性衰减在 400 个回合结束时可能还剩 0.05但如果前 200 回合已经收敛后面 200 回合就在浪费算力反过来如果地图复杂0.05 的探索率会长期找不到新路径。我常用分段衰减if ep 100 epsilon max(0.8 - ep * 0.005, 0.2); % 前100回合快速降到0.2 else epsilon max(0.2 - (ep-100) * 0.001, 0.01); % 后续缓慢降到0.01 end这样自测时可以直接输出每个阶段结束时的路径长度判断是“没探索够”还是“探索过头”。第三个技巧是验证 Q 表是否真的学到了障碍边界。我会在自测脚本里加一个“压力起点”在障碍物紧邻的格子附近随机选起点看智能体是否出现贴边滑行。贴边滑行的原因是 Q 值在障碍物边界状态上被负奖励污染常见修正方法是在普通格子奖励上加上障碍物邻域的惩罚项比如当前格子上下左右 1 格内有障碍就把步数奖励从-0.2改为-0.5让路径与障碍物保持距离。最后再跑一遍 4.2 的批量起点测试对比修改前后的贴边碰撞次数。这个方法看起来简单却是从仿真系统走向真实机器人部署时最能提前暴露风险的一步。本文还有配套的精品资源点击获取