Q表必须用三维数组初始化,即np.zeros((rows, cols, 4)),前两维对应坐标,第三维对应上下左右四个动作;一维ID映射易导致状态混乱和边界失效。

Q表初始化必须用三维数组,不能 flatten 状态
很多初学者直接把 (row, col) 坐标转成单个整数 ID(比如 row * cols + col),再初始化一维 Q 表,看似省事,但实际会导致状态映射混乱、边界判断失效。尤其当迷宫尺寸变化或加入动态障碍时,ID 重叠风险陡增。
正确做法是用 np.zeros((rows, cols, 4)) 初始化三维 Q 表:前两维对应网格坐标,第三维对应动作(0=上,1=下,2=左,3=右)。这样访问 Q[2][3][1] 就明确表示“第2行第3列向下走”的预估价值。
- 避免手动维护状态 ID 到坐标的双向映射表
- 边界检查可直接用
0 ,无需额外解码 - 可视化调试时能直接用行列索引定位问题单元格
reward 设计必须为负步长,否则路径会发散
如果把空地奖励设为 0 或正数(如 +1),智能体会倾向于绕路刷分,哪怕已靠近终点也不愿终止——因为多走一步就多拿一分。这不是“找最短路径”,而是“无限兜圈”。
典型且有效的 reward 配置是:
立即学习“Python免费学习笔记(深入)”;
- 到达终点:
+10.0(强正向激励) - 撞墙或越界:
-0.5(惩罚,但不过重,避免完全拒斥探索) - 普通移动:
-0.1(温和负奖励,驱动收敛) - 超时未达终点:
-5.0(防止死循环)
注意:所有 reward 值需与 gamma(折扣因子)配合。若 gamma=0.9,连续走 10 步的累计折损约 0.35,所以 -0.1 是合理量级;若误用 -1.0,智能体可能因惧怕惩罚而根本不敢动。
ε-greedy 衰减必须线性且带下限
固定 epsilon = 0.1 是常见错误。训练初期需要高探索(比如 epsilon=1.0),否则智能体永远困在局部;但后期若不压低 epsilon,它仍会以 10% 概率乱选动作,导致最优策略无法稳定执行。
推荐写法:epsilon = max(0.01, epsilon * 0.995) 或 epsilon = 1.0 - episode / total_episodes * 0.99。
- 必须设下限(如
0.01),否则浮点误差可能导致epsilon归零,彻底关闭探索 - 衰减太慢(如每轮只减
0.001)会让前期收敛慢;太快(如每轮 ×0.9)则后期易震荡 - 不要在每次 step 内衰减,只在每个 episode 结束后更新一次
Q值更新时 max(Q[next_state]) 必须过滤非法动作
直接写 np.max(Q[next_row][next_col]) 很危险:如果 next_state 是墙或越界位置,该位置的 Q 值本应无效,但取最大值会把它拉进更新公式,污染上游状态。
正确做法是在计算 best_next_q 前,先获取 next_state 的合法动作集合:
valid_actions = []
if next_row > 0: valid_actions.append(0) # 可以上
if next_row < rows-1: valid_actions.append(1) # 可以下
if next_col > 0: valid_actions.append(2) # 可以左
if next_col < cols-1: valid_actions.append(3) # 可以右
if valid_actions:
best_next_q = np.max(Q[next_row][next_col][valid_actions])
else:
best_next_q = 0更稳妥的做法是:在环境 step() 函数中,对非法动作直接返回原状态 + 惩罚,并确保该状态的 Q 值不参与后续 max 计算——否则训练过程会出现“明明撞墙了却学出高 Q 值”的诡异现象。
最容易被忽略的是:Q 表更新必须严格按“当前状态→动作→奖励→下一状态”四元组顺序执行,任何提前赋值或状态覆盖都会让时序差分失效。尤其在多线程或异步渲染场景下,务必确认 state 和 next_state 的快照是原子性的。


















