MATLAB实现Q-Learning迷宫路径规划:从Q表到Dijkstra基线对比
2026/9/16 17:52:16 网站建设 项目流程

简介:一份基于MATLAB的强化学习Q-Learning算法迷宫路径规划资源,面向强化学习初学者、研究人员及路径规划相关领域从业者,系统展示了智能体通过Q表试错更新在迷宫中寻找最优路径的完整流程。压缩包共593个文件、7.32MB,以481个.m源码文件为核心,覆盖Q-Learning核心逻辑、环境仿真与主控脚本;同时包含26个.mat数据文件、20个.fig结果图像、10个.txt说明及pdf文档等,可结合文档理解参数配置与实验输出。已有314人学习下载,资料不仅对Q-Learning原理、迷宫环境建模、Q表初始化与更新规则、MATLAB编码步骤进行了细致拆解,还给出实验结果分析和未来优化方向,如算法改进、与其他方法融合、多智能体协作及真实场景迁移。借助这些脚本、数据和图示,可快速复现迷宫逃脱实验,并在此基础上开展二次开发,深入掌握基于价值迭代的路径规划思路,同时加深对强化学习机制的理解。

1. 迷宫路径规划,为什么我先把钱花在Q-Learning上

做迷宫路径规划,很多同学第一反应是 A* 或 Dijkstra,但这类算法要求环境模型完整摆在面前,地图一变就要重新跑一遍图搜索。Q-Learning 则不需要预知全局地图,智能体靠试错更新 Q 表,就能逐步逼近最优策略,这正好切中“动态环境 + 奖励可调”的诉求。这篇文章用 MATLAB 写一个可复现的迷宫逃脱 demo:10×10 网格地图、四个动作、终点给正奖赏,训练出 Q 表后按贪心策略走路径。适合有基础 MATLAB 语法的开发者和刚接触强化学习的研究者:新手能照步骤跑通,熟手可以从中拆奖励设计和超参调优的边界。源码包里的dijkstra.cpp等 MEX 文件也正好拿来当对比基线,量化 Q-Learning 学到的东西离“最优”还差多远。

2. Q-Learning 原理与迷宫环境建模

2.1 从马尔可夫决策过程到 Q 表

强化学习把迷宫问题建模成马尔可夫决策过程,典型五元组是<S, A, P, R, γ>。在迷宫里,状态 S 是智能体所在网格坐标,动作 A 是上、下、左、右四个移动方向,转移概率 P 在静态地图中接近确定性,R 是每步获得的奖励,γ 是折扣因子,控制未来奖励对当前决策的影响程度。

Q-Learning 是离策略、无模型的时序差分算法,核心更新公式是:

Q(s,a) ← Q(s,a) + α · (r + γ · max_{a'} Q(s',a') − Q(s,a))

这个公式的直观理解是:执行动作 a 到状态 s′ 后,用“真实奖励 r + 下一状态里最大 Q 值的折现”作为目标值,减去当前 Q 值得到误差,再乘以学习率 α 更新。因为它取的是max_{a'},所以学习目标是最优策略的期望回报,而实际采集中使用 ε-greedy 产生动作,属于典型的 off-policy 方式。

迷宫场景下状态空间不大,用一张二维表格存 Q 值完全够用,不需要引入神经网络,这也是表格型 Q-Learning 最适合入门的原因。常见参数配置如下:

参数推荐范围说明
α 学习率0.1 ~ 0.5越大更新越快,但太大会震荡
γ 折扣因子0.9 ~ 0.99小则短视,大则更重视远期奖励
ε 初始值1.0训练初期保证充分探索
ε 衰减率0.99 ~ 0.999每 episode 乘一次,后期转入利用
episode 数500 ~ 200010×10 地图一般 800 轮足够

2.2 迷宫网格与奖励函数设计

先说地图设计。这里用一个 10×10 矩阵表示迷宫,1 是墙,0 是通道,起点在左上角(1,1),终点在右下角(10,10)。MATLAB 实现时我习惯直接用矩阵画迷宫:

map = zeros(10, 10); map(2, 3:9) = 1; % 第二行一堵横墙 map(5, 1:4) = 1; % 第五行左侧墙 map(8, 6:10) = 1; % 第八行右侧墙 map(4, 7) = 1; % 独立障碍 map(7, 3) = 1; % 独立障碍 nrow = size(map, 1); ncol = size(map, 2); N = nrow * ncol; % 状态总数,实际可访问状态要排除墙 start_idx = sub2ind([nrow, ncol], 1, 1); goal_idx = sub2ind([nrow, ncol], 10, 10); reward = -0.04 * ones(nrow, ncol); % 每走一步的小额成本 reward(10, 10) = 10; % 终点给正奖励 Q = zeros(N, 4); % 4 个动作:1上、2下、3左、4右

这段代码里,reward = -0.04是老玩家常说的“步成本”,它让智能体学会走路时少绕弯。如果不加步成本,只给终点正奖励,Q-Learning 很可能学出“走空地绕圈子”的奇怪行为,因为中途所有状态价值看起来都一样。撞墙或越界不能在奖励函数里一视同仁,我一般给 −1,否则智能体会贴着墙试错很久。终点的 +10 需要盖过整条路径的负累积,10×10 地图最长路径不会超过几十步,−0.04×20 大约是 −0.8,差距足够明显。

3. MATLAB 实现:学习率、折扣因子和贪心系数怎么控制训练主循环

3.1 训练主循环结构与完整实现

训练主循环是 Q-Learning 的核心。每一 episode 从起点出发,按 ε-greedy 选动作,执行动作得到奖励和下一状态,然后更新 Q 表,直到走到终点或超过最大步数。完整代码如下:

alpha = 0.1; % 学习率 gamma = 0.95; % 折扣因子 epsilon = 1.0; % 探索率 epsilon_min = 0.01; % 最小探索率 decay = 0.995; % 每 episode 衰减因子 max_episodes = 800; max_steps = 300; actions = [-1 0; 1 0; 0 -1; 0 1]; % 上、下、左、右 episode_rewards = zeros(max_episodes, 1); for ep = 1:max_episodes s = start_idx; total_reward = 0; for step = 1:max_steps % epsilon-greedy 选择动作 if rand < epsilon a = randi(4); else [~, a] = max(Q(s, :)); end % 执行动作,计算新位置 r = ceil(s / ncol); c = mod(s - 1, ncol) + 1; nr = r + actions(a, 1); nc = c + actions(a, 2); % 越界或撞墙:留在原地,给惩罚 if nr < 1 || nr > nrow || nc < 1 || nc > ncol || map(nr, nc) == 1 next_s = s; r_step = -1; else next_s = sub2ind([nrow, ncol], nr, nc); r_step = reward(nr, nc); end % Q 表更新 Q(s, a) = Q(s, a) + alpha * (r_step + gamma * max(Q(next_s, :)) - Q(s, a)); total_reward = total_reward + r_step; s = next_s; % 到达终点后结束本 episode if nr == 10 && nc == 10 break; end end episode_rewards(ep) = total_reward; epsilon = max(epsilon_min, epsilon * decay); end

这段代码有几个容易踩的细节:r_step和终点奖励的关系,如果nr == 10 && nc == 10reward(10,10)取到的是 10,更新后立刻 break,不再用终点的next_s继续往后算。撞墙时next_s等于s,更新公式里会出现max(Q(s,:))和旧状态发生耦合,这是合理的,表示原地惩罚。

rand < epsilon是标准的 ε-greedy 写法:均匀随机数小于 epsilon 就随机探索,否则取 Q 值最大的动作。很多人在训练后段发现路径不稳定,往往是因为 epsilon 没有衰减到 0.1 以下。

3.2 探索与利用的平衡:epsilon-greedy 细节

探索与利用的平衡是 Q-Learning 能不能收敛的关键。训练初期 Q 表全是零,如果直接取max(Q(s,:)),智能体会一直选前几个动作,比如总是向上走,导致永远到不了终点。所以初始 epsilon 要足够大,常见做法是 1.0 开始,让前几十轮完全随机。

衰减速度也要控制。我的经验是:10×10 迷宫,decay=0.995大约在 300 个 episode 后 epsilon 降到 0.22,之后慢慢进入利用期;如果decay=0.95,80 轮后 epsilon 已经低于 0.02,地图还没探索完就变成纯贪心,最后 Q 表会收敛到局部次优解。更稳妥的做法是记录走过的步数,超过阈值再默认从起点重新开始,但这对入门代码反而复杂,不如直接用衰减。

超参数的影响可以做一张速查表:

现象可能原因调整方向
训练后路径过长探索不足或 α 过大调大epsilon_min,调低 α
路径频繁贴墙步成本过小reward中普通步设为 −0.1
后期奖励曲线震荡γ 过大或 ε 衰减过快γ 保持 0.95,decay 调到 0.998
前期一直撞墙ε 下降太快提高epsilon_min,延长探索期

3.3 收敛判断与 Q 表检查

不能只看某一次运行结果是好的,就说算法收敛。我一般会保存每个 episode 的累计奖励,然后画移动平均线,观察曲线前 100 轮快速升高、中段平稳、后段无明显下降趋势。累计奖励通常是负的,因为步成本会一路累积,直到最后拿到 +10 之后才可能整体转正,因此看负值的走势并不可怕,关键是不要一直停留在同一个低水平。

也可以直接检查 Q 表:max(Q, [], 2)得到每个状态的最大价值,打印起点附近的值,看是否呈现从起点向终点递增的梯度。如果起点价值反而是全图最高,说明奖励设置或导数项计算有代码问题。

4. 运行、可视化与迷宫逃脱调试

4.1 项目文件结构与 dijkstra.cpp 的作用

资源包内除了主 MATLAB 脚本,还包含dijkstra.cppfind_nn.ckernel_function.cmexCCACollectData.c等文件。这些是 C 编写的 MEX 辅助源文件,并不是全部需要编译。dijkstra.cpp的作用很清晰:提供标准最短路径计算,用来和 Q-Learning 的结果做对比。find_nn.c通常是最近邻搜索工具,和迷宫主流程关系不大,可以暂时忽略。

使用dijkstra.cpp前需要在 MATLAB 中编译:

mex dijkstra.cpp

编译成功后当前目录会出现.mexw64.mexa64后缀的二进制文件。如果你的机器没装配置好的 C 编译器,mex会报错,常见解决办法是安装 MinGW-w64 工具箱,或用mex -setup检查当前编译器。主训练脚本不依赖这些 MEX 文件,纯 MATLAB 也能跑通,所以排错时先把.cpp放一边,专注主脚本。

4.2 复现路径与绘制收敛曲线

训练完成后,最好写一个函数把学习到的路径画出来,直观确认迷宫逃脱的效果:

function q_steps = plot_q_path(map, Q, start_idx, goal_idx, nrow, ncol) map_show = 1 - map; imagesc(map_show); colormap(gray); axis xy; axis equal; hold on; s = start_idx; q_steps = 0; actions = [-1 0; 1 0; 0 -1; 0 1]; while s ~= goal_idx && q_steps < 300 [r, c] = ind2sub([nrow, ncol], s); plot(c, r, 'ro', 'MarkerSize', 6, 'MarkerFaceColor', 'r'); [~, a] = max(Q(s, :)); r = r + actions(a, 1); c = c + actions(a, 2); s = sub2ind([nrow, ncol], r, c); q_steps = q_steps + 1; end end

这段代码把可通行区域显示成白色、墙显示成黑色,走过的路径用红点标出。imagesc后加axis xy是为了让纵坐标从下往上增加,和矩阵行列的自然顺序一致,否则路径会上下翻转。绘制红点时用'ro'MarkerFaceColor,输出更清楚。

再画累计奖励曲线:

figure; plot(movmean(episode_rewards, 50), 'LineWidth', 1.5); xlabel('Episode'); ylabel('Smoothed Total Reward'); grid on;

movmean的第二个参数 50 表示取前后 50 个 episode 的均值,可以过滤掉单次 episode 的随机波动。如果曲线整体缓慢上升并趋于平稳,说明学习过程正常;如果出现断崖式下跌,大概率是 epsilon 衰减太快导致提前进入利用期。

4.3 常见失败模式与排查思路

根据我调试这类迷宫代码的经验,失败大多是这几类:

现象根因排查/解决办法
智能体永远在起点附近转Q 表没更新成功,奖励没有进入状态检查reward(10,10)是否真的为 10,打印每一步的r_step
到达终点前反复绕圈步成本太弱,绕远路和近路价值差不大把步成本从 −0.04 调到 −0.1
路径撞到墙后卡住撞墙惩罚过大,智能体不敢离开当前状态撞墙惩罚从 −1 降到 −0.1
训练时间很长但路径时好时坏ε 没有衰减到较小值,一直在随机动作确认epsilon = max(epsilon_min, epsilon * decay)在 episode 外层执行
运行报错说数组索引超出维度状态索引写成了二维数组,Q(s,:)的 s 不是线性索引统一用sub2ind/ind2sub做转换

还有一个排查技巧:在训练循环里定期输出“当前 episode 是否到达终点”,如果 500 轮仍然从未到达过终点,优先检查奖励矩阵和终点判定逻辑,而不是着急调超参数。MATLAB 的disp开销不大,每 50 轮打印一次命中终点的计数,能比看累计奖励更快定位问题。

5. 用 Dijkstra 基线校准 Q-Learning 策略质量

训练结束只说明“智能体能到达终点”,但没法直接回答“路径是否接近最优”。这时候上面提到的dijkstra.cpp就该上场了。Dijkstra 在静态图上计算从每个可通行格子到终点的最短步数,是标准的最优解,用来做评估基线非常合适。

mex dijkstra.cpp; D = dijkstra(map, goal_idx); % 返回与地图同尺寸的最短距离矩阵 baseline_steps = D(1, 1); q_steps = plot_q_path(map, Q, start_idx, goal_idx, nrow, ncol); ratio = q_steps / baseline_steps; fprintf('Q-Learning 步数: %d, Dijkstra 最优步数: %d, 比值: %.2f\n', ... q_steps, baseline_steps, ratio);

ratio是衡量策略质量的核心指标。等于 1 说明学到的就是最优路径;在 1 ~ 1.2 之间说明策略可用但仍有绕路;超过 1.5 基本可以断定奖励设计或超参数有问题。注意dijkstra的输入输出格式要以包内源码注释为准,我这边用的是D = dijkstra(map, goal_idx)的约定,具体 MEX 返回值可能是矩阵也可能是索引数组,先size(D)确认一下。

在此基础上还能做更细的验证:把终点奖励从 10 改成 20,看ratio是否变化;或者把步成本从 −0.04 改为 −0.2,观察 Q-Learning 是否更接近 Dijkstra。另一个实用技巧是把地图中某个障碍随机移动,让智能体重新训练 20 次,统计平均ratio和到达率。能稳定维持在 1.2 以内的模型,才说明不是碰运气跑通,而是真正学到了可泛化的策略。最后把Qepisode_rewardsratio一并存到results.mat里,每次调参后都用同一组评估代码做对比,这样后续调优才有依据。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询