基于DQN的栅格地图路径规划:Matlab完整实现与调参实战

📅 发布时间:2026/10/20 22:29:43
基于DQN的栅格地图路径规划:Matlab完整实现与调参实战
做二维栅格地图路径规划时很多人其实都经历过这个转变一开始用A*、Dijkstra、RRT这类经典搜索算法静态地图下表现确实不错路径短、速度也快。可一旦地图换了或者想要智能体在相似的栅格环境里直接复用经验就得重新全局搜索这就有点别扭了。直到我认真接触了DQNDeep Q Learning才确定另一条思路是可行的——用深度学习神经网络去拟合Q值函数让智能体在二维栅格地图上通过不断试错自己学会看到什么样的状态该往哪个动作走。这篇文章就是把我在Matlab里的完整实现过程、踩过的坑、以及调参的实战经验整理出来给同样想用DQN做栅格路径规划的研究者和工程师一个可以直接参考的复现路径。1. 为什么栅格路径规划需要DQN经典方法的痛点与强化学习的切入点1.1 经典路径规划算法在静态地图里的表现与局限栅格地图路径规划本质上是在一个由连续方格组成的地图里找一条从起点到目标点、且不经过障碍物的可行路径。经典做法里A算法用启发式函数引导搜索在静态地图中能很快找到较短路径Dijkstra是A的特例保证最优但搜索范围更大RRT类方法适合高维连续空间但在地格离散场景下优势不明显。这些方法有一个共同特征每一次规划都是一次独立的计算过程。地图更新后哪怕只新增了一个障碍物整个全局搜索也得重新跑一遍。如果你的场景是同一类地图但每次细节不同或者需要智能体在线快速决策而算力又受限经典搜索算法就会显得笨重——你不是在训练一个会举一反三的策略而是在反复执行一种从零开始的计算。我在做一个模拟项目时遇到过非常现实的问题地图尺寸是15×15的栅格A*跑一次路径只要几十毫秒但因为环境里的障碍是随机生成的每换一个障碍布局都要重新调用规划模块高频场景下整个系统就被搜索开销拖住了。那时候我就意识到真正需要的不是每次重新搜索而是让智能体面对相似环境时靠经验直接输出动作序列。1.2 强化学习把路径规划变成决策序列问题强化学习路径规划的视角和搜索算法完全不同。它不把路径当作一次性搜索结果而是当作智能体与环境交互得到的一组动作序列。在这套框架里智能体位于某个栅格它观察自己当前的状态选择一个动作比如向上走环境返回新的状态和一个奖励值如果撞到障碍物得到一个负奖励如果到达目标点得到一个正奖励每一步还会有微小的时间成本惩罚。智能体的任务就是最大化累积折扣奖励也就是学会一条尽量短、尽量不撞墙、最终能抵达终点的路径。这个过程的训练逻辑可以用一个递推关系来理解Q(s,a) ← Q(s,a) α * [r γ * max_a′ Q(s′,a′) − Q(s,a)]其中Q(s,a)表示在状态s下执行动作a的价值r是即时奖励γ是折扣因子α是学习率。这个式子说明当前状态动作的价值等于即时奖励加上未来最优动作价值的折扣和。强化学习的路径规划核心就是让这个价值函数不断逼近真实值——一旦收敛智能体在每个状态只需选择Q值最大的动作就能走出一条策略意义上的最优路径。1.3 DQN的核心思想用神经网络给每个动作打分经典Q-learning在状态空间比较小的时候很管用比如迷宫只有几个格子Q表可以精确记录每个状态动作的价值。可栅格地图一旦放大到20×20状态就包含了智能体坐标、目标点坐标、局部障碍物信息组合起来状态数量会快速膨胀Q表会变得不可维护——这就是所谓的维度灾难。DQN做的事情本质上是用一个神经网络来替代Q表的位置网络输入的是状态描述输出的是各个动作的Q值预测值。网络通过不断拟合目标Q值来更新权重矩阵格子再多也不怕因为状态被压缩成了低维特征向量。而Matlab里搭建这种全连接网络非常直接fullyConnectedLayer和reluLayer堆叠即可这也是我选择Matlab来实现的重要原因之一——配合工具箱你甚至可以在很短的时间内从一个想法变成可训练的工程原型。2. 栅格环境建模状态空间、动作空间和奖励函数的权衡2.1 栅格地图的矩阵表示与可行区域判断栅格地图在Matlab中通常用一个二维整数矩阵表示维度就是地图的长和宽。我用map(row, col)来存储0代表可通行1代表障碍物。定义一个模拟项目X时我常这样创建地图mapSize 20; map zeros(mapSize, mapSize); % 随机放置障碍物比例约为 20% map(rand(mapSize, mapSize) 0.2) 1; % 确保起点和目标点不是障碍 startPos [2, 2]; goalPos [18, 18]; map(startPos(1), startPos(2)) 0; map(goalPos(1), goalPos(2)) 0;判断智能体能否走到某个格子核心就是检查这个格子的坐标是否在地图边界内并且对应的矩阵值不是1。这一步非常关键因为很多训练异常都出在撞墙检测的边界条件上比如把智能体推出矩阵范围或者对角运动时直接斜穿障碍体的角点。2.2 状态向量设计绝对坐标、相对坐标和局部视野状态表示是DQN路径规划中容易被低估的设计点。你可能第一反应是直接把智能体的(x, y)、目标的(x, y)拼成四维向量喂给网络。这在固定地图、单一目标下可以跑通但如果换一张地图绝对坐标分布完全不同网络学到的知识就很难迁移。我在演进方案时采用了相对坐标 局部障碍视野的组合相对目标坐标Δx goalX − agentXΔy goalY − agentY刻画距离目标还有多远、在哪个方位代数距离abs(Δx) abs(Δy)给网络一个变化趋势信号局部视野以智能体为中心的3×3窗口里的障碍标记拉平成8个邻域值中心是智能体本身不输入告诉网络上下左右分别有没有墙。这样设计之后状态维度大约是2 1 8 11维。小地图和大一点的地图都可以复用状态空间已经和地图尺寸解耦泛化能力就好了不少。值得说明一点绝对坐标不是不能用我最初就是直接绝对坐标训练结果也能收敛但把地图尺寸从15×15换到20×20后需要重新训练这启发我改成相对表达。对固定地图的复现研究两种方案均可对想验证迁移能力的读者极力推荐相对坐标方案。2.3 动作空间四方向与八方向的取舍栅格地图里动作空间可以定义成四方向上、下、左、右也可以定义成八方向加入四个斜向。从代码角度四方向只需要一个[−1,0; 1,0; 0,−1; 0,1]的偏移表八方向再追加[−1,−1; −1,1; 1,−1; 1,1]。但是八方向的看起来更灵活往往伴随陷阱斜向运动如果直接判定穿过相邻两个格子就会形成斜穿障碍角点的非法路径。严格的栅格路径规划通常要求路径上的每一段都符合可通行规则这需要在碰撞检测时额外判断被切过的两个正格子是否都是空闲。算法复杂度上去了训练稳定性也会下降。我的建议是做DQN路径规划第一版用四方向就够。四方向虽然动作跨度小但学起来快、路径也不会出现穿墙错觉等基线稳定之后再考虑扩展成八方向并补齐斜穿检查。2.4 奖励函数设计稠密奖励、稀疏奖励与绕路的博弈奖励函数是DQN路径规划中最影响训练质量的部分没有之一。最天然的设计是稀疏奖励到达目标点给正奖励撞墙给负奖励其他情况奖励为0。但纯稀疏奖励在栅格地图中会让探索效率很低——智能体在迷宫一样的环境里很难偶遇到终点回报信号太微弱训练几百轮都可能学不到任何东西。我在实际调参里采用了一种适度的稠密奖励方案到达目标点50每一步−0.05引导智能体尽量用较短步数完成任务撞墙−10避免无意义探索靠近目标奖励如果这一步的相对距离比上一步缩短了额外给0.2注意第4条要慎用。初版我把距离缩小就给奖励设得过大结果智能体学会了先绕着墙走很远再直线逼近终点因为绕路过程中每靠近一点都能拿正向激励反而整体路径变得非常难看。后来的方案是把步长惩罚和靠近奖励放在一个量级上才达到了既往前走、又不绕大远路的效果。奖励值也不宜过大。我一开始设置到达奖励为500撞墙惩罚为−200网络输出层很容易出现梯度爆炸损失直接变为NaN。后来把奖励控制在±50以内、损失输出正常训练稳定性有了明显提升。记住一个经验奖励数值大小要和神经网络的初始化输出范围匹配宁可小一点不要贪大。3. 网络结构与训练机制经验回放、目标网络和超参数表3.1 用Matlab搭一个深度Q网络在Matlab里搭建DQN的神经网络有两种路线一种是直接用Reinforcement Learning Toolbox定义环境后调用现成训练接口几行代码就能跑起来适合快速验证另一种是自己构建网络、写训练循环完全掌控每一步。后者对理解原理帮助更大也只需要Deep Learning Toolbox即可。我的网络结构很朴素输入层对应当前状态维度11后面接两个全连接隐藏层numStates 11; numActions 4; hiddenSize1 64; hiddenSize2 64; layers [ featureInputLayer(numStates, Normalization, none) fullyConnectedLayer(hiddenSize1) reluLayer fullyConnectedLayer(hiddenSize2) reluLayer fullyConnectedLayer(numActions) regressionLayer ]; qNetwork assembleNetwork(layers);如果你是自己推导梯度更新可以用dlnetwork配合dlgradient如果更想快速跑通可以把目标Q值当成回归标签用trainNetwork做有监督式的更新。两种思路我都试过手写更新更灵活适合做研究trainNetwork写法更直观适合复现基线。第一种在连续在线更新场景里更常用下面讨论的也是这种方式。网络结构不需要追求大而深栅格路径规划的状态维度并不高64个神经元的单隐藏层往往就够了。我试过128128的两个隐藏层在小地图上训练时间翻倍收敛效果却没有显著提升。对这份研究来说6464是一个稳定的中间配置。3.2 DQN训练为什么必须配经验回放经验回放Experience Replay是DQN相对传统Q-learning的关键改进。在线交互得到的样本是高度相关的连续两个时刻的状态只差一个格子如果用相邻样本来更新网络梯度方向会发生严重偏置训练会震荡甚至发散。经验回放的做法是维护一个容量固定的缓存池把每次交互得到的(s, a, r, s′, done)存进去训练时从缓存池里随机抽样一批数据来更新网络。这样做有两个好处打破样本的时间相关性让更新所需要的样本尽量独立同分布提高样本利用率一条经验可以被多个批次重复使用。在Matlab里我通常这样管理缓存bufferSize 20000; bufferIdx 1; buffer.state zeros(bufferSize, numStates); buffer.action zeros(bufferSize, 1); buffer.reward zeros(bufferSize, 1); buffer.nextState zeros(bufferSize, numStates); buffer.done zeros(bufferSize, 1);bufferIdx在写入时循环覆盖超过bufferSize后从头再写。采样时等缓存容量超过训练所需的最小batch后就可以用randperm(bufferCount, batch)随机选取一批下标取出对应数据做训练。不建议缓存池太小时就开训否则本质还在用强相关样本更新优势发挥不出来。3.3 目标网络的作用与更新方式如果只用当前网络自己生成目标Q值会有一个隐患每次网络权重更新所有动作的Q值预测也会变化导致学习目标一直在移动——就像射箭时靶子不停晃动很难瞄得准。DQN引入目标网络Target Network来解决这个问题。目标网络的结构与主网络相同参数隔一段时间才同步一次主网络的权重。在两步之间目标网络保持不变于是主网络的回归目标就相对稳定训练的收敛性大大改善。我在实现中用硬更新方式每targetUpdateInterval步直接把主网络参数复制给目标网络targetNet copyNet(qNet); % 或直接复制权重硬更新简单直接适合栅格路径规划这种训练节奏已知的场景。软更新每次用较小系数融合权重会更平滑但要多调一个tau参数如果不嫌麻烦也可以尝试。对快速复现而言硬更新足够。3.4 超参数敏感性一组能直接跑通的参数超参数新手最容易忽略但往往决定训练成败。下面这组参数是我在15×15到20×20的地图上多次验证过的初始设置不需要太精细先把流程跑通再针对性调整。参数名推荐值说明折扣因子 gamma0.95略小可以让智能体更重视近期奖励训练更快学习率0.001再大容易发散再小收敛太慢epsilon 初始1.0一开始全随机探索epsilon 最小0.05保留一点探索能力epsilon 衰减0.995 每episode从1.0衰减到0.05大约需要600个episode经验池容量20000太大占内存太小样本多样性不够batch大小32常用配置16也可以跑但方差更大目标网络更新间隔500步与episode长度匹配通常1~2个episode更新一次每episode最大步数200防止死循环超过判定失败并终止这些参数没有标准答案但它是很好的起点。实际运行时我建议你把每个episode的累积奖励、步数和成功标记都存到数组里训练结束后画出来观察趋势比盯着控制台数字更容易判断问题出在哪。4. 核心代码框架从环境交互到训练循环的Matlab实现4.1 环境模拟器的编写思路DQN的数据来源是智能体与环境的交互所以第一步是写一个轻量的环境模拟函数。我把它拆成三部分重置函数设定起点和目标点返回初始状态步进函数接收动作计算下一状态、奖励和是否结束状态生成函数从地图、智能体坐标、目标坐标拼接出状态向量。核心步进逻辑大致如下function [nextState, reward, done] step(map, agentPos, goalPos, action) [dx, dy] actionOffset(action); % 动作映射到坐标偏移 newPos agentPos [dx, dy]; % 边界和障碍判断 if newPos(1) 1 || newPos(1) size(map,1) || ... newPos(2) 1 || newPos(2) size(map,2) || ... map(newPos(1), newPos(2)) 1 nextState buildState(agentPos, goalPos, map); % 状态不变 reward -10; done false; elseif newPos(1) goalPos(1) newPos(2) goalPos(2) agentPos newPos; nextState buildState(agentPos, goalPos, map); reward 50; done true; else agentPos newPos; nextState buildState(agentPos, goalPos, map); reward -0.05; done false; end end关于状态不变这个设计撞墙后智能体留在原地但状态向量完全不变可能会让智能体形成撞一次后再撞一次的惯性动作。我在入口处加了轻微随机扰动避免它总在实际操作中反复尝试同一个非法动作这个小改动显著减少了训练中后期的抖动。4.2 经验缓存与批量采样的实现经验缓存的写入操作很简单就是按循环下标覆盖写入。真正要注意的是批量采样时奖励和目标值不能直接混用。我会把条件转移做成矩阵形式来加速计算。% 从缓存随机采样 batch 个样本 idx randperm(bufferCount, batch); batchS buffer.state(idx, :); batchA buffer.action(idx, :); batchR buffer.reward(idx, :); batchS2 buffer.nextState(idx, :); batchDone buffer.done(idx, :); % 计算目标Q值 targetQ batchR; futureQ max(predict(targetNet, dlarray(batchS2, CB)), [], 1); targetQ(~batchDone) targetQ(~batchDone) gamma * futureQ(~batchDone);这里的关键是把done处理成掩码只有当不是终止状态时未来Q值才被加进目标如果已经到达终点就不能再去想象终点的下一跳。很多初版代码的错误就出在忘记对终止样本做特殊处理导致智能体在终点附近反复横跳。采样方面batch设成32在多数情况下足够。如果地图较大、状态特征较复杂可以提到64但训练时间长也会相应上升。实践经验是先调batch如果损失曲线波动剧烈再加如果损失下降太慢优先检查学习率和奖励比例而不是盲目加大batch。4.3 训练主循环一次episode的完整生命周期训练主循环是整个程序的核心结构上分为三层外层的for episode控制训练轮次中层的while控制单个episode内的探索内层的if判断当前是否达到训练更新间隔。每个episode结束时按epsilonDecay衰减探索率并按步数累计统计结果。for episode 1:maxEpisodes [agentPos, goalPos, state] envReset(map); done false; stepCnt 0; epReward 0; while ~done stepCnt maxSteps % epsilon-greedy 选择动作 if rand epsilon action randi(numActions); else qValues predict(qNet, dlarray(state, CB)); [~, action] max(qValues); end [nextState, reward, done] envStep(map, state, action); % 写入经验缓存 addBuffer(buffer, state, action, reward, nextState, done); state nextState; stepCnt stepCnt 1; epReward epReward reward; % 当经验数量足够时每4步训练一次 if bufferCount 1000 mod(stepCnt, 4) 0 trainStep(); % 内部完成采样、计算目标、调用优化器 end % 定期同步目标网络 if mod(globalStep, 500) 0 targetNet copyNet(qNet); end globalStep globalStep 1; end epsilon max(epsilon * epsilonDecay, epsilonMin); % 记录本episode的epReward、stepCnt、done等 end这个循环里有一个容易忽略的细节训练更新不是每步都做而是隔几步做一次。原因是相邻步的样本相关性太强连续更新很容易原地打转。我通常每4个环境交互步骤训练一次网络这样既不会漏掉重要的经验梯度又能保证更新的样本具备一定多样性。4.4 训练过程可视化与模型保存训练过程中除了看累积奖励更推荐同时记录两个指标当前episode的步数以及最近100个episode的成功到达率。步数下降说明智能体在学着走近路成功到达率上升说明它真正学会了完成任务。Matlab里可视化很方便每50个episode画一次图就行但不要每轮都调用plot绘图开销在训练循环里会被放大很多导致本来几分钟的训练拖到十几分钟。我一般把指标存到数组里全部训练结束后一次性画图最多在关键节点每200轮刷新一次曲线。模型保存用save(dqn_model.mat, qNetwork)即可。保存时要一并保存超参数、地图信息、归一化参数如果有否则下次加载时状态向量维度对不上又得重新适配。这个细节吃过亏之后我才养成了把训练配置 网络一起打包的习惯。5. 训练结果怎么看收敛曲线、成功率与路径质量5.1 累积奖励曲线和平均步数曲线DQN在路径规划任务中训练曲线不会像损失曲线那样平滑单看一次episode的累积奖励意义不大。你必须看滑动平均我通常取窗口长度为50或100。以15×15地图为例训练800个episode后累积奖励曲线会呈现三个阶段的特征第一阶段前100个episode奖励普遍为负甚至稳定在-30到-60之间这是智能体在随机探索阶段频繁撞墙、走很远才结束的正常表现第二阶段100~400个episode奖励缓慢上升偶尔出现正奖励说明智能体已经隐约摸到某些状态选择某些动作更划算的规律第三阶段400~800个episode奖励趋于稳定成功到达目标点时出现明显的峰值曲线整体位于正区间。平均步数曲线的下降趋势更直观。一开始平均步数被maxSteps卡在200因为探不到目标走到步数上限才终止之后逐步下降到接近最短路径的水平。我在模拟项目X里的实际数据是从平均127步下降到33步左右而A*在相同地图上的最短路径是27步差距已经相当小。5.2 成功率衡量能不能找到路最直接的指标如果你只用一条曲线判断模型好坏我建议选择最近100个episode的成功率而不是平均奖励。成功率含义很直接过去100轮里智能体有多少次成功抵达目标点。比如successRate 0.9意味着这100次里90次是成功走完路径的。成功率会和epsilon衰减策略强相关。前期epsilon较高智能体随机走动成功率基本为零中期随着epsilon下降成功率开始攀升后期如果成功率卡在0.7左右上不去常见原因是epsilon保持在0.05有5%的概率随机乱动导致偶发失败——这是正常的不必强行把epsilon降到0保持少量探索反而有助于应对环境变化。如果要追求展示最优路径的效果可以在训练完成后关闭epsilon随机探索直接用纯贪心策略让智能体走一条路径并输出轨迹。我在展示结果时一般会保留两种统计训练过程中历史成功率曲线和最终贪心策略的实际路径图。5.3 和A*算法对比最优性与泛化性的取舍把DQN结果和A对比是绝大多数研究汇报里的必要环节。这里要把话说清楚**A给的是在已知全局地图上的最短路径假设启发函数一致且四方向搜索而DQN给出的是策略意义上的近似最优路径两者不能单比路径长度**。在15×15的简单地图上DQN学习到的路径长度通常能达到A的1.1到1.5倍少数随机种子下甚至能逼近1倍。这不是DQN比A差而是两者的定位不同A是离线已知全图搜索一次最优解DQN是在线局部感知训练出可复用的策略。如果地图是静态且全知的A永远是更快更准的选择但如果同一类环境要重复决策、地图约束还可能微调训练一次DQN的收益远高于反复调用搜索算法。我在固定地图上的实验结果是A*求解时间平均0.03秒但每次换地图都要重算DQN在训练2000个episode后单次决策时间约几个毫秒也就是推理时不需要遍历全图。两者各有适用场景选型时想清楚自己的真实约束是最短路径还是快速决策。5.4 典型失败模式局部震荡、绕远路与假死循环即使训练稳定DQN还是可能出现一些模式化的失败早知道这些现象可以少走弯路。局部震荡最典型智能体已经看到目标却在一个障碍物角点附近来回进退反复做左-右-左的动作。原因是Q值在局部区域非常接近微小噪声就会让动作切换。解决办法是在奖励函数里加大对原地折返动作的负反馈或者采用动作平滑机制连续两次选择相反方向动作时额外罚分。绕远路是另一种常见失败智能体绕了一个大圈才到终点路径明显不如A*简洁。这往往是因为靠近奖励设置得过于慷慨智能体发现绕远但每一步都能获得正向奖励的总收益反而高于走短路但每一步惩罚较小。我处理这个问题时把靠近奖励从0.5降到了0.1并把步长惩罚提高到了−0.1绕路现象明显收敛。假死循环是指智能体走到某个格子后永远在两步之间反复跳动直到maxSteps耗尽。这一般是动作空间设计不完善导致的比如没有原地不动选项就可能在两个相邻格之间来回跳。兜底策略就是设置maxSteps为地图面积的两倍左右超过即强制终止避免训练被无用样本拖慢。6. 调参实录让DQN从不收敛到稳定出路径的实操心得6.1 不收敛的排查顺序如果训练了500个episode累积奖励曲线还是一路走低不要急着改网络结构按下面顺序排查第一先检查奖励函数。是不是所有奖励都是负值如果只有到达目标给50、撞墙给-10、每一步-0.05理论上智能体应该首先学会尽量完成目标来抵消步数惩罚。如果地图太大、目标太远50不够覆盖步数惩罚智能体会发现什么都不做等到超时比积极探索更划算这就会表现为奖励持续低迷。第二检查epsilon衰减速度。如果衰减太慢1000个episode后epsilon还在0.6以上智能体会一直随机乱撞很难系统性地逼近目标。我习惯让epsilon在前300~500个episode内从1.0降到0.1附近之后再缓慢衰减到0.05。第三检查目标网络是否更新过于频繁。如果每50步就同步一次目标网络基本等于主网络的影子去相关性被削弱训练很容易震荡。硬更新间隔建议在300~1000步之间。第四检查batch是否为零。很多新手在缓存池还没攒够batch数量时就开始训练造成采样错误或数据不足排查时先看缓存池计数是否大于batch。6.2 奖励数值范围的锚定效应奖励数值的大小不止影响总奖励曲线还会直接干扰神经网络梯度的绝对值。网络参数更新的方向取决于损失函数而损失函数里target和预测值的差异如果动辄是几百的量级梯度就会爆炸如果奖励全是0.01这种极小值梯度又会萎缩到学不动。我的经验是把最大正奖励控制在50以内最大负奖励控制在−20附近这样目标Q值的数值范围大致在−50到50之间和网络输出层经过relu后的量级是匹配的。如果你发现训练中loss突然跳了几千甚至变成NaN第一反应就应该是检查奖励里有没有超大的值。另外到达目标奖励和步数惩罚的比例至少要拉开一个数量级。我常用的组合是到达50每步−0.1撞墙−10这样智能体会明确地知道多走几步代价很小但必须到达终点从而优先学会找路而不是原地观望。6.3 训练提速的两个小技巧第一个技巧是从小地图开始验证奖励配置。我最初直接在25×25地图上训练每轮还要绘制地图和路径效率极低调参也慢。后来改成先用6×6或8×8的极简地图验证奖励函数不会让智能体乱绕再逐步扩展地图尺寸。小地图几分钟就能看出收敛趋势一次失败的奖励设计不用浪费几小时。第二个技巧是减少训练过程中的非必要计算开销。规划化训练时不必要的plot、disp、pause都会拖慢训练特别在Matlab这种解释型环境里绘图开销可能占整个训练时间的50%以上。把可视化挪到训练结束之后训练过程中的监控只需要每50个episode记录一次数据画面感稍微损失一点但速度提升非常明显。我在测试一个12×12的地图时优化前是每1000个episode花15分钟优化后只需要5分钟差别全在去掉高频绘图和结算函数中的冗余计算。6.4 从固定地图走向随机地图泛化训练的进阶方向如果固定地图的DQN已经稳定收敛下一步值得尝试的就是随机化训练。方法很简单每个episode开始之前重新随机生成一张地图、随机设置起点和目标点让智能体接触更多环境状态。这个技巧在深度学习里叫域随机化domain randomization在路径规划场景里非常实用。随机化训练下状态向量的价值立刻体现出来——如果状态用的是绝对坐标网络很难适应不同起点如果用的是相对坐标和局部视野智能体就能把靠近目标走、避障走的规律迁移到新地图上。我做了个简单对比固定地图训练的模型在原地图成功率95%换一张相同规格新地图后成功率骤降到40%而随机地图训练的模型在新地图上依然能保持85%以上的成功率。当然随机化的代价是训练时间变长收敛难度也更大。我的建议是先把固定地图跑到满意再逐步引入随机地图最后阶段可以调整障碍比例和地图尺寸做真正的泛化训练。这正是DQN在路径规划方向最值得深入研究的价值所在——不是你训练出一个能走某张图的模型而是训练出一个会走路的策略这是经典搜索算法给不了的。