资讯动态

Q-learning在Matlab随机迷宫路径规划中的实战与调参心得

发布时间:2026/10/17 3:31:38 来源:尧图企业网站定制
在路径规划这个方向上摸爬滚打了几年我发现一个很有意思的现象很多人一上来就直奔各种号称“最强”的算法结果环境稍微改一改性能掉得难看得要命。反倒是那些最基础的东西吃透了之后哪儿都能用。Q-learning就是这种看着简单但真把它用在随机生成的迷宫上踩过的坑、调参的心得足够写一篇长文了。这篇文章我就用一次实际的实验来展开环境是Matlab任务是让一个智能体从方形迷宫左上角出发避开随机障碍物找到到达右下角的最短路径。全程围绕Q-learning和ε-greedy策略这两个核心展开从迷宫怎么生成、奖励函数怎么设、训练循环怎么写到训练不收敛、路径绕远、矩阵维度报错这些常见问题的排查思路都会聊到。先说这个项目是什么。它是一个典型的强化学习入门级实战状态空间是迷宫网格点动作空间是上、下、左、右四个方向的移动环境是一个每轮随机生成障碍物的方形迷宫。智能体通过不断试错学习一个从“状态-动作”到累积回报的映射表也就是Q表。训练完成后直接查询Q表就能得到一条从起点到终点的路径。适合谁来参考呢如果你刚开始接触强化学习或者做过路径规划但想换个思路又或者想在Matlab里搭一个方便可视化的实验平台这篇都适配。基础部分我会讲得比较细有经验的朋友可以直接跳到训练循环和问题排查那一段。1. 内容整体设计与思路拆解1.1 为什么选Q-learning而不是深度强化学习这是我经常被问到的问题。其实答案很直接问题够简单没必要上重武器。随机迷宫规模不大比如10×10、15×15状态总数最多几百个状态乘以4个动作用矩阵就能存下Q值训练速度快调试方便每一步发生了什么都可以清清楚楚地打印出来。换成深度网络一是杀鸡用牛刀二是一旦不收敛你很难定位是网络结构的问题、奖励设计的问题还是探索策略的问题调试成本成倍上升。强化学习领域有一条“奥卡姆剃刀”原则优先用最简单的算法去验证你的环境建模和奖励设计是否正确。只有Q-learning效果不理想才考虑用DQN或更复杂的算法。事实证明在方形迷宫这个场景下Q-learning配ε-greedy已经能稳定收敛到接近最优的策略完全够用。1.2 迷宫环境的核心设计逻辑环境设计是整个项目的骨架设计得好训练事半功倍。我用的是标准的方形网格迷宫逻辑上就是N×N的二维网格每个单元格有几种状态空白可以走、障碍物不能走、起点左上角、终点右下角。每一轮训练开始前程序会随机选择一部分格子设为障碍物然后用这些信息初始化迷宫矩阵。有个细节要特别注意障碍物比例是环境设计中最关键的参数之一。设太低了比如5%迷宫几乎畅通无阻智能体随便走都能到终点学到的策略无意义设太高了比如40%起点和终点之间可能被障碍物完全隔断智能体永远找不到终点训练直接失败。我实测下来10×10的迷宫障碍物比例控制在10%到20%难度和收敛速度比较平衡。另外随机生成障碍物后必须做一个连通性检查确保起点和终点之间至少有一条可行路径否则这轮生成的数据就是废数据会严重干扰训练。这里用的是一个带起点和终点的格子世界本质上属于离散状态空间非常适合Q-learning直接建表求解。1.3 奖励函数与目标设计奖励设计是这个项目里最容易被低估的环节。很多人上来就写到达终点奖励1碰壁处罚-1其他情况奖励0然后训练出来发现智能体在原地打转或者永远学不会避开障碍物。问题不在于算法在于奖励信号太稀疏智能体在庞大的状态空间里漫无目的地探索运气差一点几千个回合都撞不到终点一次。我用的方案是分层的稀疏奖励到达终点100撞上障碍物或试图走出边界-10每移动一步-1每步-1这个设计很关键它给智能体一个“时间成本”的暗示促使它寻找最短路径。不然的话只要最终能到终点绕多远都行路径质量会很差。还有一个细节值得拎出来说撞墙的惩罚要和到达终点的奖励匹配。如果惩罚只有-1智能体可能更倾向于直接撞墙也不愿意多绕路。合理的量级关系是“一次撞墙的代价应该小于绕远路的总代价但大于单步成本”这需要根据你实际迷宫的大小去试。我试过10×10的迷宫-10和100是比较合适的一组值。如果迷宫改成20×20可能要把惩罚调成-20甚至更高因为路径长度变长了智能体对每一步的“厌恶感”需要更强才能抑制绕路和撞墙行为。2. 核心算法拆解与关键参数选择2.1 Q-learning更新公式与代码结构Q-learning的核心更新公式是Q(s, a) ← Q(s, a) α [R γ max Q(s, a) - Q(s, a)]直观理解当前状态s下做了动作a得到一个立即回报R到达新状态s然后查看s下所有动作的最大Q值用它和当前Q值之间的差值按学习率α做一次更新。这个公式最大的特点是在线策略off-policy更新后的Q值并不依赖实际采取的动作而是依赖于最优动作的估计值Q(s, a)。把旧的Q值朝“实际回报未来最优估计”方向拉动这就是Q-learning的训练机制。在Matlab里这段代码的核心表达很直观% 核心更新代码 next_q_max max(Q(next_state, :)); Q(current_state, action) Q(current_state, action) alpha * (reward gamma * next_q_max - Q(current_state, action));这四行代码是整个训练循环的灵魂。训练的本质就是反复执行“选择动作-执行动作-观察回报和下一状态-更新Q值”这个闭环跑上几千回合Q表自然收敛。2.2 ε-greedy策略与探索-利用权衡ε-greedy是整个算法中控制“探索-利用平衡”的关键策略核心逻辑是智能体以ε的概率随机探索以1-ε的概率选择当前Q值最大的动作即利用已有经验。用大白话说这就是一个“冒险”和“保守”之间的选择题。只利用不探索智能体很容易困在局部最优第一次撞到一条能到终点的路之后就再也不走别的路错过更好的路径只探索不利用经验积累永远无法沉淀下来学到后来还是在瞎逛。ε-greedy就是给这个困境一个简单的出口大部分时间走已知最好的路小部分时间跳出去看看有没有更好的路。% 动作选择代码 if rand() epsilon action randi([1, 4]); % 随机探索 else [~, action] max(Q(current_state, :)); % 利用已知最优 end这里有两个值得一提的经验其一ε初始值不宜太高0.1是一个不错的起点。太高了智能体长期处于“半随机”状态收敛极慢太低了又容易错过最优路径。其二ε可以做衰减。训练初期允许智能体大胆探索训练后期逐渐降低随机性让Q表的利用占据主导优势收敛会更稳定。衰减方式我比较推荐线性衰减比如每100回合乘以0.95可以算作一种简易调度。实测下来效果比固定ε更稳定尤其是障碍物密度较高时早期探索不够后期很难找到一条完整路径。2.3 学习率和折扣因子的取值与调试这两个参数一个管“新信息覆盖旧信息的速度”一个管“智能体对未来的重视程度”放在一块调试是有讲究的。学习率 α来自Q表的更新幅度。α1时每次都完全信任新信息过渡频繁α0时什么也学不到。常用区间是0.1到0.5。我初版的配置是α0.5收敛速度快但曲线波动明显后来调到0.1训练时间长了但Q值曲线更光滑、更稳定。带随机障碍物的环境推荐α0.1~0.2随机性大步子迈小一点更稳。折扣因子 γ表示未来奖励在当前时刻的价值。γ0时只看当前回报γ接近1时长远规划能力更强。迷宫问题通常需要规划多步路径γ不能太低我常用0.9到0.95。再高会导致训练初期的Q值传递非常缓慢每一步的奖励都要传递很远再低则智能体过于短视路径经常绕路。最直观的判断方式是看训练结束后导出的路径如果路径明显绕远路先检查γ是否偏低。回合数我初期设置了500回合发现10×10的迷宫能收敛但路径不稳定5000回合则能稳定输出最优路径。至于具体多少回合合适看你的迷宫大小。一个经验公式是状态数乘以动作数再乘以10到50倍。10×10的迷宫状态规模是100×44005000回合刚好在10倍左右实测比较充足。3. 实操过程与Matlab完整实现3.1 迷宫生成与边界处理迷宫生成的输入参数很简单行数N、障碍物比例p。核心逻辑是生成一个N×N的全零矩阵随机选择一定数量的格子设置为1障碍物然后强制设置起点和终点为通路。function maze create_maze(N, obstacle_ratio) maze zeros(N, N); num_obstacles round(obstacle_ratio * N * N); indices randperm(N * N, num_obstacles); maze(indices) 1; maze(1) 0; % 起点 (1,1) maze(N * N) 0; % 终点 (N,N) end这里有一个很多人忽略的坑randperm生成的障碍物索引是线性的也就是把N×N矩阵拉成一维向量后去索引。如果不做转换到后面使用状态编号时会非常混乱。所以我在生成迷宫后还会额外维护一个二维索引映射关系确保状态编号一致。连通性检查用深度优先搜索实现function connected check_connectivity(maze) N size(maze, 1); visited false(N, N); stack [1, 1]; while ~isempty(stack) [x, y] deal(stack(end, 1), stack(end, 2)); stack(end, :) []; if visited(x, y), continue; end visited(x, y) true; if x N y N connected true; return; end % 四个方向压栈 for dir 1:4 [nx, ny] move_point(x, y, dir); if nx 0 nx N ny 0 ny N ~maze(nx, ny) ~visited(nx, ny) stack(end1, :) [nx, ny]; end end end connected false; end这段代码的核心在于栈的压入和弹出操作维护了一个搜索路径能快速判定起点到终点是否“可达”而不仅仅是用随机概率去赌迷宫生成得是否合理。我实测大约有超过70%的随机生成迷宫是连通的如果不检查剩下那些失败迷宫会让整个训练过程的收敛曲线很难看。3.2 状态与动作空间映射这个维度我一开始没处理干净吃过亏。Q表要用线性索引来存迷宫的每个格子是二维坐标(x, y)必须建立一个明确的映射公式。state_index (x - 1) * N y; % 二维坐标转一维索引动作的编号我规定为1-上2-下3-左4-右。移动函数function [nx, ny, is_valid] move_point(x, y, action) switch action case 1, nx x - 1; ny y; case 2, nx x 1; ny y; case 3, nx x; ny y - 1; case 4, nx x; ny y 1; end is_valid nx 1 nx N ny 1 ny N maze(nx, ny) 0; end3.3 训练循环与Q表收敛判断训练循环的结构决定整个算法的稳定性。我来看一段核心训练代码的完整结构Q zeros(N * N, 4); alpha 0.1; gamma 0.9; epsilon 0.1; max_episodes 5000; for episode 1:max_episodes state 1; % 起点状态索引 is_done false; while ~is_done % 选择动作 if rand() epsilon action randi(4); else [~, action] max(Q(state, :)); end % 执行动作获取下一状态和奖励 q_next get_next_state(state, action); % 返回更新后的状态索引和是否有效移动 [next_state, reward, is_done] get_transition(state, action); % Q-learning更新 max_next max(Q(next_state, :)); Q(state, action) Q(state, action) alpha * (reward gamma * max_next - Q(state, action)); state next_state; end % 尝试线性衰减探索率 if mod(episode, 100) 0 epsilon max(epsilon * 0.95, 0.01); end end这里get_transition函数的逻辑要包含合法移动、撞墙惩罚、到达终点判断三个环节返回下一状态索引、回报、是否结束三项内容。将这三部分集成到一个函数里训练主体的逻辑会保持清晰方便后续调试时打印状态变化。训练结束后Q表就是策略本身。提取路径时从起点反复选择Q值最大的动作进入下一步同时记录坐标直到到达终点state 1; path [1, 1]; while state ~ N * N [~, action] max(Q(state, :)); [nx, ny] move_point(state_to_coord(state, N), action); path(end 1, :) [nx, ny]; state coord_to_state(nx, ny, N); end这段路径提取逻辑是验证算法是否学好的最直观方式。如果提取出来的路径存在撞墙或绕远路要么是训练不充分要么是参数设计有问题可以对照下面的排查章节逐项检查。3.4 可视化设计与实验观察Matlab在这方面的天然优势是可视化非常方便可视化对于调试策略的效果无与伦比。我画了两张图第一张是Q表热力图。每个格子的颜色深浅表示该状态下最大Q值的大小。训练初期只有终点及其附近的状态有点颜色训练后期颜色从终点逐渐向外扩散。通过观察这张图的扩散速度可以直观判断奖励传递是否顺畅。第二张是训练过程中的累积回报曲线。横轴是回合数纵轴是每回合获取的总回报。正常收敛的曲线应该是一个有波动但整体下降的趋势——前期因为探索多、惩罚多总回报低后期开始找到终点负惩罚减少曲线上升最后平稳策略趋于稳定。如果曲线一直在低位震荡或者突然跳变大概率是迷宫生成或奖励函数出了问题。4. 常见问题与排查技巧实录4.1 训练不收敛奖励函数的数量级不对这是我在调试过程中遇到的第一个大坑也是最典型的问题。训练了2000回合Q表还是一团乱路径提取出来完全不可用。排查后发现终点奖励是100撞墙是-10但其实每走一步的延迟惩罚只有-0.1量级太小。智能体对“绕路”这件事几乎没有感知到终点的路径越绕越远。调整的方式很简单把每步惩罚从-0.1改成-1量级和撞墙惩罚、终点奖励匹配到同一个数量级。修改后收敛速度明显加快路径质量也随之提升。奖励函数设置时一个重要的检验标准是终点奖励应该明显大于路径上的累计惩罚。假设最短路径长度为20步累计步数惩罚就是-20终点100仍然占主导。这种量级关系要认真算一遍不要凭感觉。4.2 智能体原地打转ε衰减太慢有一次测试发现智能体已经到终点附近了却开始原地转圈始终进不去终点。每次在终点周围反复选择随机动作即使进入终点会获得100的奖励它还是在附近的几个状态之间打转。定位到原因ε0.1固定不变探索率太高有10%的随机动作会打断已经形成的策略进入终点附近后又容易被随机试探拉走。而且10×10迷宫的状态空间很小高探索率的负面影响被放大了。我按100回合为单位做线性衰减后这个问题基本消失。衰减率以0.95为初始值观察着调如果500回合后ε仍然大于0.05说明衰减太慢可以加速。但这里有一个副作用需要说明ε衰减过快也不行比如100回合从0.1猛降到0.01智能体过早进入“纯利用”模式后续根本没有机会体验不同的路径策略很容易停留在次优解。衰减速度请根据迷宫大小调整10×10的迷宫线性衰减是合适的更大的迷宫建议用指数衰减。4.3 撞墙行为反复出现动作选择没有限制非法动作训练过程中需要一个很关键的判断动作是执行了越界或撞墙算无效动作还是直接禁止这个动作我一开始的做法是如果撞墙就给予-10惩罚但不改变状态直接进入下一回合。这种做法在数学上是正确的但实际操作下来容易让智能体“反复撞墙”来惩罚自己。有一种解释是当Q表还未收敛时Q值本身带有噪声撞墙后状态不变、Q值被降低反而让智能体在某次随机探索中选择同一个墙位置去“擦除”一个之前误判过高的Q值这在统计上能帮助探索但在实际运行中就是反复撞墙路径质量极其难看。解决方法一旦move_point判断为非法移动直接将奖励置为-10并结束该回合也就是说非法移动被当作一次失败的尝试而不是让智能体停留在原地重新选择。很多人可能觉得这个改动会降低到终点的训练次数但实测下来收敛反而更快因为智能体对“非法动作”形成强惩罚记忆而不是把时间浪费在原地转圈。4.4 迷宫随机性导致训练曲线抖动固定随机种子在做对比实验时我发现同样的参数换了迷宫后结果差异非常大。这不是算法的锅而是环境随机性导致的。如果每次训练都用不同的迷宫那么Q表学习的是“平均多种迷宫”的策略而不是单一迷宫下的最优策略“稳定收敛”就无从谈起。不同需求有不同的应对方案如果目的是固定一个迷宫测试最优路径那训练前要固定随机种子。如果目的是测试算法在不同迷宫的泛化能力那每个迷宫跑10次取平均结果统计不要拿单次结果下结论。Matlab里的random_seed rng(master_seed)这样管理种子可以保证每次实验重现性。这件事对调试极其重要否则你很难区分这个改进到底是因为参数调好了还是碰巧生成了一座简单迷宫。4.5 矩阵维度报错与索引越界状态映射不统一我遇到过很多次维度报错几乎都出在“二维坐标”和“一维索引”混用上。Matlab里二维索引和一维索引之间可以隐式切换很多初学者不留意就会在一个函数里传入二维坐标在另一个函数里却使用一维索引报错提示却指向不明。我的习惯是写两个工具函数coord_to_state和state_to_coord并确保训练循环里的状态变量只用一种形式。如果一定要混用至少加上明确的转换注释。另外动作数组的长度应该是4对应动作编号1-4Q表初始化时Q zeros(NN, 4)这两个维度必须匹配任何一处写成NN*4都会直接导致Q表索引计算错误。4.6 常见问题速查表故障现象产生原因解决方案训练2000回合仍未收敛奖励量级不匹配每步惩罚太小将每步惩罚调整到-1量级智能体原地打转ε值固定且偏高采用衰减策略后期ε低于0.05路径绕远路折扣因子γ偏低把γ调整为0.9以上反复撞墙非法移动后仍留在原地继续选择撞墙后直接结束回合训练曲线剧烈波动每回合迷宫重新生成环境差异大固定随机种子或多次实验取均值Q表维度错误一维索引和二维坐标混用统一状态映射函数分别封装转换逻辑障碍物堵死全部路径障碍物比例过高添加连通性检查不通过就重新生成5. 实验扩展与后续优化思路基础版的Q-learning迷宫跑通之后有很多可以继续深挖的方向这里说两个我实际尝试过的。第一个是障碍物的动态变化。基础版是固定的一张迷宫训练到最后扩展版可以让障碍物每隔几回合重新生成一次。这种设定更接近真实环境的动态性也更能体现Q-learning的适应能力。但注意迷宫变化后旧的Q表需要一定的回合数来适应新环境训练回合数要相应增加。实测结果显示障碍物重新生成频率太高Q表几乎无法收敛每100回合变化一次是勉强能接受的上限。第二个是动作空间的扩展。基础版只有上、下、左、右四个方向如果改成八方向增加斜向移动状态的转移矩阵会变得更复杂但路径规划会更灵活。可以明显感觉到智能体走出来的路径更贴近直觉路径长度也更短。不过代价是Q表的规模从N²×4变为N²×8训练回合数大概要多出40%左右。如果你还想更进一步可以考虑将Q-learning接入外部仿真环境比如用Matlab的接口连接一个机器人仿真平台将迷宫渲染成真实地图智能体控制轨迹的每一步都对应一个动作输出。这种做法的工程量主要在环境接口的对接上算法本身不用改。但也提醒一句真实环境有噪声、有延迟Q-learning的收敛可能没有仿真环境那么好必要的话可以回退到带衰减的ε策略并加大训练量。6. 一些个人经验与总结Q-learning能解的迷宫问题看似简单但它背后涉及的探索与利用、奖励建模、环境与算法关系等一系列思考可以平行迁移到很多强化学习应用场景中。我在后面接触的一些项目中比如简化版调度、游戏AI、推荐策略初始验证都沿用了这套“先建模、简算法、重调试”的思路。最后分享三个我反复强调的经验第一调参前先调bug。如果你发现不管怎么设置参数训练结果都差不多那大概率不是参数的错而是环境或代码逻辑有隐藏错误。先打印状态转移日志确认每一步的转移是否符合预期再动参数。第二Q表收敛与否不能只看有没有到终点。路径长度才是更敏感的指标。我见过一种情况智能体每次都能到终点但路径长度是最优路径的两倍因为每步-1的惩罚不够强绕远路并未被有效压制。检查路径长度的变化曲线比只看总回报直观得多。第三先把迷宫变小。不要一开始就用50×50的迷宫调试那是自找麻烦。先在6×6、8×8的小网格上跑通整个流程再逐渐加大迷宫规模。小迷宫训练速度快、可视化直观定位问题极其高效。如果你正在学习强化学习或想用一个具体项目来验证理论这个迷宫实验是一个非常不错的起点。把Q-learning的更新公式写成代码容易但把奖励设计、环境建模、参数调试这些细节真正串起来才会对算法本身产生更实际的认知。希望这篇能帮你少踩一些坑。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑