化學(xué)習(xí)二維地圖源碼實(shí)戰(zhàn):Q-Learning路徑規(guī)劃與避坑指南)
簡(jiǎn)介這份資源面向希望用MATLAB入門強(qiáng)化學(xué)習(xí)的開(kāi)發(fā)者與在校學(xué)生聚焦二維迷宮場(chǎng)景下的最優(yōu)路徑求解問(wèn)題。壓縮包共3個(gè)文件均為m腳本整體約2KB分別承擔(dān)迷宮環(huán)境反饋、動(dòng)作選擇與主流程調(diào)度等職責(zé)結(jié)構(gòu)精簡(jiǎn)便于直接閱讀與二次修改。內(nèi)容圍繞狀態(tài)、動(dòng)作、獎(jiǎng)勵(lì)與策略等核心概念展開(kāi)涉及Q-learning、SARSA及DQN等算法的選型思路并說(shuō)明小規(guī)模迷宮與大規(guī)模迷宮在算法復(fù)雜度上的取舍。已有218人學(xué)習(xí)適合作為課程實(shí)驗(yàn)或自學(xué)練手素材。讀者可借此理解如何定義狀態(tài)空間、動(dòng)作空間、狀態(tài)轉(zhuǎn)移規(guī)則與獎(jiǎng)勵(lì)函數(shù)掌握Q表迭代更新與策略優(yōu)化的基本流程并觀察智能體從試錯(cuò)到收斂、最終找到起點(diǎn)至終點(diǎn)最優(yōu)路徑的完整過(guò)程為后續(xù)遷移到更復(fù)雜環(huán)境打下基礎(chǔ)。1. 從一份 MATLAB 強(qiáng)化學(xué)習(xí)二維地圖源碼說(shuō)起它到底能跑出什么如果你手上正好有一份名為matlab源代碼強(qiáng)化學(xué)習(xí)算法二維地圖.rar的壓縮包或者你正在搜「matlab 強(qiáng)化學(xué)習(xí) 二維地圖 源代碼」想找一份能直接跑通的參考實(shí)現(xiàn)那這篇就是寫給你的。它解決的核心問(wèn)題很具體讓一個(gè)智能體Agent在一張用矩陣表示的二維柵格地圖里從起點(diǎn)走到終點(diǎn)同時(shí)繞開(kāi)障礙物并且整個(gè)過(guò)程用 MATLAB 原生代碼實(shí)現(xiàn)不依賴 Python 環(huán)境、不依賴深度學(xué)習(xí)工具箱之外的重型框架。這類源碼通常包含三塊東西地圖生成把二維場(chǎng)景離散成網(wǎng)格矩陣、強(qiáng)化學(xué)習(xí)主體Q-Learning 或 DQN 的循環(huán)、可視化把每一步的路徑畫出來(lái)。它適合兩類人一類是剛學(xué)完強(qiáng)化學(xué)習(xí)理論、想找一個(gè)能改參數(shù)、能看中間過(guò)程的最小可運(yùn)行例子另一類是做機(jī)器人路徑規(guī)劃、AGV 調(diào)度、游戲 AI 的工程師需要一個(gè)能快速驗(yàn)證獎(jiǎng)勵(lì)函數(shù)設(shè)計(jì)的沙盒。二維地圖這個(gè)設(shè)定不是偷懶而是把狀態(tài)空間壓到可控范圍讓你能把注意力放在獎(jiǎng)勵(lì)設(shè)計(jì)和收斂判斷上而不是被高維感知拖垮。我見(jiàn)過(guò)太多人拿到這類壓縮包后第一步就翻車解壓出來(lái)一堆.m文件不知道從哪個(gè)入口跑改了個(gè)學(xué)習(xí)率結(jié)果曲線直接發(fā)散。所以下面不按「源碼目錄介紹」那種流水賬寫而是按「先立住原理、再動(dòng)手復(fù)現(xiàn)、最后講坑」的順序把這份二維地圖強(qiáng)化學(xué)習(xí)源碼拆成能抄作業(yè)的步驟。2. 二維柵格地圖與強(qiáng)化學(xué)習(xí)要素怎么對(duì)應(yīng)狀態(tài)、動(dòng)作、獎(jiǎng)勵(lì)的建模選擇2.1 為什么二維地圖要用矩陣而不是坐標(biāo)系二維地圖在 MATLAB 里最常見(jiàn)的表示就是一個(gè)M×N的矩陣0 表示可通行1 表示障礙物2 和 3 分別標(biāo)記起點(diǎn)和終點(diǎn)。用矩陣而不是連續(xù)坐標(biāo)好處是狀態(tài)可以直接用行列索引(i,j)表示狀態(tài)空間大小就是M*NQ 表可以開(kāi)成一個(gè)M*N行、動(dòng)作數(shù)列的二維數(shù)組。這比連續(xù)狀態(tài)下的函數(shù)逼近簡(jiǎn)單一個(gè)量級(jí)也讓你能一眼看出 Q 表哪塊沒(méi)更新到。常見(jiàn)做法是地圖尺寸取 10×10 到 20×20。太小比如 5×5路徑太短看不出策略差異太大比如 50×50Q 表收斂需要的回合數(shù)會(huì)明顯上升調(diào)試周期變長(zhǎng)。我一般先用 10×10 跑通再換 20×20 驗(yàn)證泛化。2.2 動(dòng)作空間與狀態(tài)轉(zhuǎn)移的四種寫法動(dòng)作空間通常取 4 個(gè)上、下、左、右。也有取 8 個(gè)的加對(duì)角線但 8 動(dòng)作會(huì)讓「斜穿障礙物角落」成為一個(gè)必須額外處理的邊界問(wèn)題新手容易在這里寫出穿墻 bug。所以源碼里如果用的是 4 動(dòng)作不要覺(jué)得它簡(jiǎn)陋這是刻意規(guī)避邊界歧義。狀態(tài)轉(zhuǎn)移要處理兩件事撞墻和撞障礙。撞墻時(shí)有兩種策略一種是留在原地一種是反彈回上一格。留在原地更常用因?yàn)樗尅缸矇Α惯@個(gè)動(dòng)作的即時(shí)獎(jiǎng)勵(lì)為負(fù)智能體會(huì)自己學(xué)會(huì)避開(kāi)。撞障礙同理通常給一個(gè)較大的負(fù)獎(jiǎng)勵(lì)比如 -1而每走一步給一個(gè)小的負(fù)獎(jiǎng)勵(lì)比如 -0.01用來(lái)鼓勵(lì)最短路徑。% 4 動(dòng)作定義上 下 左 右 actions [-1 0; 1 0; 0 -1; 0 1]; function [nextState, reward, done] step(state, action, map, goal) [rows, cols] size(map); [r, c] ind2sub([rows, cols], state); nr r action(1); nc c action(2); % 邊界與障礙判斷 if nr 1 || nr rows || nc 1 || nc cols || map(nr, nc) 1 nextState state; % 撞墻或撞障礙留在原地 reward -1; % 懲罰 done false; else nextState sub2ind([rows, cols], nr, nc); if nextState goal reward 10; % 到達(dá)終點(diǎn) done true; else reward -0.01; % 每步小懲罰鼓勵(lì)最短路徑 done false; end end end這段代碼里三個(gè)參數(shù)最值得調(diào)撞墻懲罰、每步懲罰、到達(dá)獎(jiǎng)勵(lì)。撞墻懲罰絕對(duì)值太小比如 -0.1智能體會(huì)反復(fù)撞墻刷負(fù)獎(jiǎng)勵(lì)但不學(xué)太大比如 -100早期隨機(jī)探索時(shí) Q 值波動(dòng)劇烈收斂慢。到達(dá)獎(jiǎng)勵(lì)要明顯大于「最短路徑步數(shù) × 每步懲罰」否則智能體會(huì)覺(jué)得繞路更劃算。2.3 Q-Learning 更新公式里兩個(gè)容易設(shè)錯(cuò)的參數(shù)Q-Learning 的核心更新是Q(s,a) Q(s,a) α * [r γ * max(Q(s,:)) - Q(s,a)]α 是學(xué)習(xí)率γ 是折扣因子。二維地圖場(chǎng)景下α 取 0.1 到 0.3 比較穩(wěn)γ 取 0.9 到 0.95。γ 太接近 1智能體會(huì)過(guò)度看重遠(yuǎn)期獎(jiǎng)勵(lì)在障礙密集地圖里容易在局部打轉(zhuǎn)γ 太小比如 0.5它會(huì)變得短視寧可撞墻也不繞遠(yuǎn)路。還有一個(gè)隱藏參數(shù)是 ε-greedy 的 ε 衰減策略。常見(jiàn)寫法是 ε 從 1.0 線性衰減到 0.05衰減回合數(shù)取總訓(xùn)練回合的 60% 到 80%。如果衰減太快智能體還沒(méi)探索完地圖就開(kāi)始貪心容易卡在次優(yōu)路徑衰減太慢后期還在隨機(jī)走收斂曲線一直抖。3. 用 MATLAB 把 Q-Learning 在二維地圖上跑通從初始化到收斂的完整命令3.1 環(huán)境準(zhǔn)備與入口文件識(shí)別拿到壓縮包后先看目錄里有沒(méi)有main.m或run_*.m這類文件它通常就是入口。如果沒(méi)有找包含for episode 1:max_episodes循環(huán)的那個(gè)文件。MATLAB 對(duì)文件名和函數(shù)名一致性有要求如果入口是腳本script而不是函數(shù)function直接按 F5 或命令行輸入文件名即可運(yùn)行。運(yùn)行前確認(rèn)兩件事當(dāng)前文件夾已切換到源碼所在目錄且沒(méi)有同名.m文件在路徑上沖突。我習(xí)慣在命令行先執(zhí)行clear; clc; close all;避免上一次運(yùn)行的變量殘留導(dǎo)致「明明改了參數(shù)卻沒(méi)變化」這種玄學(xué)問(wèn)題。3.2 Q 表初始化與訓(xùn)練主循環(huán)下面是一個(gè)可以直接抄的最小訓(xùn)練主循環(huán)假設(shè)地圖是 10×10起點(diǎn) 1終點(diǎn) 100。clear; clc; close all; % 1. 構(gòu)建地圖0 可通行1 障礙 map zeros(10, 10); map(3, 2:8) 1; % 一堵橫墻 map(6:9, 6) 1; % 一堵豎墻 startState 1; goalState 100; % 2. 超參數(shù) alpha 0.2; % 學(xué)習(xí)率 gamma 0.95; % 折扣因子 epsilon 1.0; % 初始探索率 epsilonMin 0.05; epsilonDecay 0.995; % 每個(gè)回合衰減 maxEpisodes 500; % 3. Q 表初始化 numStates numel(map); numActions 4; Q zeros(numStates, numActions); actions [-1 0; 1 0; 0 -1; 0 1]; % 4. 訓(xùn)練 rewardsPerEpisode zeros(maxEpisodes, 1); for ep 1:maxEpisodes state startState; totalReward 0; for step 1:200 % 單回合最大步數(shù)防止死循環(huán) % ε-greedy 選動(dòng)作 if rand epsilon a randi(numActions); else [~, a] max(Q(state, :)); end [nextState, reward, done] step(state, actions(a,:), map, goalState); % Q 更新 Q(state, a) Q(state, a) alpha * ... (reward gamma * max(Q(nextState, :)) - Q(state, a)); state nextState; totalReward totalReward reward; if done break; end end rewardsPerEpisode(ep) totalReward; epsilon max(epsilonMin, epsilon * epsilonDecay); end % 5. 畫收斂曲線 figure; plot(rewardsPerEpisode); xlabel(回合); ylabel(累計(jì)獎(jiǎng)勵(lì)); title(Q-Learning 二維地圖收斂曲線); grid on;這段代碼里step函數(shù)就是 2.2 節(jié)寫的那段需要單獨(dú)存成step.m或放在同一腳本末尾MATLAB R2016b 之后支持腳本內(nèi)局部函數(shù)。maxEpisodes取 500 是經(jīng)驗(yàn)值10×10 地圖通常 300 到 800 回合能收斂。step上限 200 是后悔藥防止智能體在某個(gè)狀態(tài)反復(fù)橫跳導(dǎo)致單回合不結(jié)束。3.3 策略提取與路徑可視化訓(xùn)練完后 Q 表里每個(gè)狀態(tài)取max對(duì)應(yīng)的動(dòng)作就是貪心策略。把從起點(diǎn)開(kāi)始按貪心策略走出來(lái)的路徑畫在地圖上能直觀判斷策略是否合理。% 提取貪心路徑 path startState; state startState; for k 1:100 [~, a] max(Q(state, :)); [nextState, ~, done] step(state, actions(a,:), map, goalState); if nextState state % 撞墻說(shuō)明策略有問(wèn)題 break; end path [path; nextState]; state nextState; if done break; end end % 可視化 figure; imagesc(map); colormap(gray); hold on; [pr, pc] ind2sub(size(map), path); plot(pc, pr, r-o, LineWidth, 2); plot(ind2sub(size(map), startState, 2), ... ind2sub(size(map), startState, 1), go, MarkerSize, 10); plot(ind2sub(size(map), goalState, 2), ... ind2sub(size(map), goalState, 1), bo, MarkerSize, 10); title(Q-Learning 學(xué)到的路徑);如果路徑出現(xiàn)來(lái)回震蕩或者提前停在障礙前先別懷疑算法八成是獎(jiǎng)勵(lì)設(shè)置或 ε 衰減的問(wèn)題。把rewardsPerEpisode畫出來(lái)看如果曲線在 -50 附近長(zhǎng)期不上升說(shuō)明智能體根本沒(méi)學(xué)到有效策略這時(shí)候優(yōu)先檢查step函數(shù)里撞墻判斷的邊界條件。4. 避坑與排查二維地圖強(qiáng)化學(xué)習(xí)源碼最常見(jiàn)的 5 個(gè)翻車點(diǎn)4.1 現(xiàn)象訓(xùn)練曲線一直震蕩不收斂Q 值越來(lái)越大原因通常是 Q 更新里用了max(Q(nextState,:))但nextState是終止?fàn)顟B(tài)時(shí)沒(méi)有屏蔽后續(xù) Q 值。到達(dá)終點(diǎn)后donetrue但代碼仍然把gamma * max(Q(goal,:))加進(jìn)去導(dǎo)致終點(diǎn)的 Q 值被反復(fù)放大。解決在 Q 更新前判斷if done, target reward; else target reward gamma*max(...); end。4.2 現(xiàn)象智能體學(xué)會(huì)繞遠(yuǎn)路明明有更短路徑卻走大圈這是每步懲罰設(shè)置過(guò)小或折扣因子過(guò)高的典型癥狀。每步懲罰 -0.01 時(shí)繞 10 步只多扣 0.1而 γ0.99 會(huì)讓遠(yuǎn)期獎(jiǎng)勵(lì)幾乎不打折智能體對(duì)「多走幾步」不敏感。解決把每步懲罰調(diào)到 -0.05 到 -0.1γ 降到 0.9 到 0.95重新訓(xùn)練。4.3 現(xiàn)象換了地圖尺寸后代碼報(bào)索引越界常見(jiàn)于把地圖從 10×10 改成 20×20 后sub2ind和ind2sub的維度參數(shù)沒(méi)同步改或者動(dòng)作數(shù)組里[-1 0]在邊界行上算出nr0。解決所有涉及行列計(jì)算的地方統(tǒng)一用[rows, cols] size(map)動(dòng)態(tài)獲取不要硬編碼 10。邊界判斷里nr 1和nr rows兩個(gè)條件缺一不可。4.4 現(xiàn)象MATLAB 中文注釋亂碼尤其是 2023 之后的版本這是編碼問(wèn)題不是代碼問(wèn)題。MATLAB 2023 起默認(rèn)用 UTF-8但老源碼可能是 GBK。解決用feature(DefaultCharacterSet)查看當(dāng)前編碼或者在編輯器里「另存為」時(shí)手動(dòng)選 UTF-8。如果批量文件亂碼用 VS Code 打開(kāi)后逐個(gè)轉(zhuǎn)碼再保存別在 MATLAB 里直接改容易越改越亂。4.5 現(xiàn)象訓(xùn)練時(shí)快時(shí)慢同樣參數(shù)兩次運(yùn)行結(jié)果差很多強(qiáng)化學(xué)習(xí)本身有隨機(jī)性rand和randi沒(méi)固定種子時(shí)每次結(jié)果都不同。如果差異大到「一次收斂一次發(fā)散」先固定隨機(jī)種子rng(42)再跑確認(rèn)是參數(shù)問(wèn)題還是隨機(jī)性問(wèn)題。固定種子后仍然發(fā)散才是真的參數(shù)或邏輯有 bug。5. 把二維地圖 Q-Learning 用到自己場(chǎng)景的三個(gè)進(jìn)階技巧第一個(gè)技巧是獎(jiǎng)勵(lì)塑形Reward Shaping。原始獎(jiǎng)勵(lì)只有「到終點(diǎn) 10、撞墻 -1、每步 -0.01」在障礙密集地圖里稀疏得可憐。我一般會(huì)加一個(gè)基于曼哈頓距離的勢(shì)函數(shù)每走一步如果離終點(diǎn)更近額外給 0.02反之給 -0.02。這樣智能體早期就能感知到方向收斂回合數(shù)通常能砍掉三分之一。注意勢(shì)函數(shù)要滿足「勢(shì)能差」形式否則會(huì)改變最優(yōu)策略。第二個(gè)技巧是用containers.Map或稀疏矩陣存 Q 表。10×10 地圖 Q 表才 100×4隨便開(kāi)。但如果地圖到 100×100Q 表就是 10000×4用普通矩陣沒(méi)問(wèn)題可一旦狀態(tài)變成「位置朝向」的組合狀態(tài)數(shù)翻幾倍這時(shí)候用稀疏存儲(chǔ)能省內(nèi)存。MATLAB 里sparse對(duì) Q 表更新支持良好max操作也兼容。第三個(gè)技巧是驗(yàn)證策略時(shí)不要只看一條路徑。固定種子跑出來(lái)的貪心路徑可能只是眾多最優(yōu)路徑中的一條。我習(xí)慣把起點(diǎn)周圍 5 個(gè)狀態(tài)各跑一次貪心看是否都能到終點(diǎn)以此判斷 Q 表是否真的覆蓋了地圖大部分區(qū)域。如果某些起點(diǎn)走幾步就卡住說(shuō)明那些狀態(tài)的 Q 值還沒(méi)被充分更新需要增加訓(xùn)練回合或調(diào)整 ε 衰減。最后說(shuō)個(gè)我自己的習(xí)慣每次改完獎(jiǎng)勵(lì)函數(shù)先把maxEpisodes設(shè)成 50 跑一遍看前 50 回合的累計(jì)獎(jiǎng)勵(lì)有沒(méi)有上升趨勢(shì)。有趨勢(shì)再放開(kāi)到 500 回合沒(méi)趨勢(shì)就先查獎(jiǎng)勵(lì)邏輯別硬跑。這個(gè)習(xí)慣幫我省過(guò)很多次「跑了一晚上發(fā)現(xiàn)獎(jiǎng)勵(lì)寫反了」的時(shí)間。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取