
簡介一份專為多智能體強化學習MARL設計的環境工具包面向強化學習研究者、算法工程師與學生用于在統一平臺中開發、訓練和對比多智能體協作與競爭策略。環境覆蓋滅火、找寶藏、抓豬、足球、移動箱子、無人機、清潔、倉庫等典型任務場景支持多個智能體并行學習并通過環境反饋與其他智能體行為共同演化策略。壓縮包共包含71個文件其中36個Python源碼文件提供各場景的環境實現與測試入口13個PDF文檔說明任務設計與算法原理13個GIF演示直觀展示運行效果另有若干PNG圖片與依賴庫文件整體體積僅3.65MB輕量易部署。目前已有671人學習使用。通過閱讀源碼和實驗文檔讀者可快速理解MARL環境構建邏輯并基于這些場景開展課程實驗、算法對比或論文復現各模塊均提供獨立環境與測試腳本便于按需擴展是入門與進階多智能體強化學習的實用參考。1. 為什么我會把這個 multi-agent 強化學習環境倉庫留下來做多智能體強化學習時最讓人頭疼的往往不是算法本身的公式而是一組能讓多個智能體穩定交互的環境。論文里描述的場景要么交互復雜要么沒有開源實現自己從頭寫又要處理地圖渲染、回合重置、獎勵平衡一堆瑣事。這個倉庫把 13 個多智能體場景打包在一起每個場景都有 env_xxx.py 和配套的 test_xxx.py還有對應的 PDF 設計說明和運行演示 GIF覆蓋了救火、尋寶、抓豬、足球、無人機避讓、倉庫調度、救援等典型任務。對想快速驗證 DQN、PPO 或 Actor-Critic 類算法在 multi-agent 環境上效果的研究者和工程師來說這是一個直接可用的多智能體強化學習環境不需要再重復造地圖輪子。倉庫里每個環境都保持相對獨立你既可以把整個項目當作算法評測基準也可以只挑其中一個環境做深入改造。比如想研究協作機制就直接打開 env_Cleaner.py 和 env_FireFighter.py想對比競爭場景就去看 env_Soccer.py 和 env_OppositeV2.py。下面我會從環境接口設計、場景分類、訓練腳本接入三個層面拆解這個資源最后一個部分給出自定義環境和驗證環境的完整思路。2. 環境接口設計從 env_xxx.py 看懂 state-action 與 reward 規范2.1 reset/step 是多智能體環境的統一入口打開任意一個 env_xxx.py都會發現它不是嚴格意義上的 OpenAI Gym Environment而是一組更松散的 Python 類。拿 env_Cleaner.py 來說類里通常包含 init(world_size, obstacles, n_agents) 這類構造參數以及 reset() 和 step(actions)。與單智能體環境不同step 里的 actions 是一個列表長度等于智能體數量每個智能體對應一個動作編號。reset 返回的是所有智能體的初始位置和一個地圖狀態。我一般拿到新環境的第一件事是打開同目錄的 test_xxx.py看它怎么調用環境。幾乎所有 test 腳本都遵循同一個模板先實例化環境再循環 reset、step最后把渲染結果打印或保存。下面是一個根據倉庫中常見 test_FireFighter.py 風格整理的調用框架# test_FireFighter.py 的簡化調用結構 import numpy as np from env_FireFighter import FireFighterEnv # 具體類名以文件為準 env FireFighterEnv(size7, fires3, agents2) obs env.reset() # obs 里通常包含各智能體位置、火點位置、時間步 for step in range(100): actions [env.action_space_sample() for _ in range(env.n_agents)] next_obs, rewards, done, info env.step(actions) env.render() # 有的環境用字符畫有的用 matplotlib 畫網格 if done: break這段代碼說明三件事環境的動作必須一次性傳入所有智能體的決策step 返回的 rewards 也是一個列表與智能體一一對應done 表示整個回合是否結束而不是單個智能體是否完成。參數 size、fires、agents 是這個倉庫環境常見的構造參數實際名字以各環境的 PDF 為準。了解這些之后接算法時只需要把actions換成策略網絡的輸出即可。2.2 狀態空間與動作空間在代碼里怎么表示這個倉庫的環境基本都是網格世界動作空間通常是離散的 4 個方向上、下、左、右少數環境會有“停留”或“推箱子”等額外動作。以 env_MoveBox.py 為例智能體不僅可以選擇移動方向還可能有一種“推動”動作這會讓動作空間變成 5 或 6 維。而 env_OppositeV2.py 這類對抗環境動作空間也會保持對稱保證兩個智能體有相同的決策自由度。狀態空間則分為兩類一類是相對位置例如 env_GoTogether 中兩個智能體需要匯合觀測可能直接給兩者的相對坐標另一類是完整地圖例如 env_Drones 和 env_Warehouse 會把整張柵格地圖拍平成向量網絡輸入維度等于grid_width * grid_height * channelschannels 對應障礙物、智能體、目標點等圖層。下表是我根據倉庫內 PDF 說明整理的常見動作定義習慣動作編號含義典型環境0向上移動所有網格環境1向下移動所有網格環境2向左移動所有網格環境3向右移動所有網格環境4保持不動/抓取CatchPigs、Soccer5推動箱子MoveBox這個表只是通用約定具體到每個環境我建議直接打印env.action_space或閱讀對應 PDF因為倉庫中部分環境允許地形交互未必完全遵循這張表。比如 Soccer 里的動作可能還包含“射門”方向不能想當然地當成四方向導航。2.3 reward 與 done 的設計差異以 FireFighter 和 Cleaner 為例多智能體環境里 reward 設計決定了協作還是競爭。FireFighter 的典型設定是 n 個消防員撲滅 n 處火點每個時間步所有智能體共享一個整體獎勵比如每撲滅一處火給 10 分步進懲罰為 -0.1。這樣設計會讓兩個智能體天然傾向于分工因為火點被任意一個撲滅全隊都受益。對應到 Q 學習里每個智能體看到的reward是相同的但各自狀態不同所以要小心多智能體 credit assignment 問題——當全場只有總獎勵時某個智能體很難知道自己這一步對團隊成功貢獻了多少。Cleaner 環境則不太一樣。它的目錄下還有 maze.py 和 disjointSet.pymaze 負責生成連通迷宮disjointSet 用于判斷清掃區域是否被完全覆蓋。這個環境的 reward 往往按被清掃格子的數量計算每個格子第一次被掃到才有正獎勵重復清掃只有很小的負獎勵。此時多個清潔員如果共享同一張地圖相互之間是“隱性競爭”關系——誰先掃到某格誰拿到獎勵。我在調試這種環境時會先把累計 reward 打出來如果總獎勵一直不變多半是 done 條件或者 map 存儲更新出了問題。注意判斷一個環境是協作還是競爭不能只看名字要去看 step 函數里 reward 是怎么求和或分配的。同類任務改一行 reward 就能讓性質反轉。2.4 一個通用的交互循環模板為了把任意一個 env_xxx.py 接到強化學習算法上我會寫一個獨立的 runner而不是直接改環境源碼。下面這個模板兼容前面提到的所有環境它假設環境有n_agents屬性動作按批次傳入def rollout_episode(env, policy, max_steps200): obs env.reset() total_rewards [0.0] * env.n_agents for _ in range(max_steps): actions policy.select_actions(obs) # 返回長度等于 n_agents 的離散動作 obs, rewards, done, info env.step(actions) for i, r in enumerate(rewards): total_rewards[i] r if done: break return total_rewards, info其中policy.select_actions是通用接口可以替換成隨機策略、DQN 的 epsilon-greedy 或 PPO 的 actor 網絡輸出。注意 rewards 是以 list 返回的不能直接當標量累加。這個模板解決了我接入新環境時 90% 的重復代碼問題剩下 10% 是不同環境對 done 和 info 定義不同需要在測試腳本里專門對齊。3. 場景分類解析協作、競爭與混合任務怎么在多智能體環境里建模3.1 協作型場景CatchPigs、GoTogether 和 RescueCatchPigs 字面意思是“抓豬”從倉庫里的 gif 看兩個智能體需要把豬趕到某個區域。這種任務必須協作因為單邊驅趕很難把豬逼到拐角。實現層面豬的位置一般也維護在地圖數組里智能體走到豬相鄰格時會觸發“驚嚇”機制讓豬往反方向跑。因此 reward 不應該只按“是否抓住”給通常會加“接近豬”的中間獎勵避免智能體學成互相亂轉。在代碼里你會在 step 函數中看到對豬位置的條件判斷這里容易出的 bug 是豬被多個智能體同時驚嚇時移動方向沖突我一般會用方向疊加后再歸一化的做法處理。GoTogether 則是一個匯合任務兩個智能體從不同起點出發目標是同時站到同一格。這個環境的經典陷阱是提前匯合所以 done 條件往往是“兩者同格且時間步大于某個閾值”而不是第一次同格就停止。Reward 設計上可以給“距離縮小”的稠密獎勵也可以只給最終成功 1、不成功 0 的稀疏獎勵用來對比不同算法對 reward 密度的敏感度。如果訓練時發現兩個智能體一開始就往對方起點沖說明它們沒有學到“等一等再匯合”這個時序約束此時檢查 done 條件是否過于寬松。Rescue 環境在倉庫里同時存在 Python2 和 Python3 兩套代碼說明它經歷過跨版本遷移。救援場景一般是有若干被困者分布在危險區域智能體把它們搬運到安全區。這個任務的狀態空間相對較大因為要同時表征被困者位置、智能體位置和危險區擴散范圍。如果在舊版 Python2 代碼上訓練建議先遷移到 Python3重點檢查xrange、字典迭代和 matplotlib 渲染接口的變化。我在跑這個環境時會先固定隨機種子確保兩次 reset 的地圖一致否則后續調參時很難判斷算法進步來自策略優化還是地圖難度波動。3.2 競爭型場景Soccer 和 Opposite 的對抗關系Soccer 是典型的零和競爭。倉庫內附了 redbot.png 和 bluebot.png 兩張機器人圖標說明它用 pygame 或 matplotlib 做可視化較多。足球比賽里一個智能體控球時另一個要搶斷reward 本質上是對稱的進球方 1失球方 -1。如果兩個智能體使用同一套參數初始化很容易出現“互相抵消”的學習信號我一般會在訓練時引入自博弈或者給雙方使用不同的探索率。比如紅色方使用 epsilon0.1藍色方使用 epsilon0.3這樣至少一方能產生較豐富的經驗避免兩個策略同時停滯。Opposite 從名字就能看出是“相對”任務兩個智能體很可能被放置在對稱位置執行相反的導航路徑。這種環境最適合測試競爭型 multi-agent 算法的穩定性。它的難點在于單純擴大經驗回放池會混入大量對手不同策略下的數據導致策略震蕩。常見做法是使用 league 式的多對手采樣但我手頭實驗時發現先用固定規則對手做預訓練再切換到自博弈收斂會快得多。另外注意讀取 env_OppositeV2.py 時V2 可能已經修正了 V1 的獎勵對稱性問題如果你的實驗對公平性敏感優先用新版。3.3 單智能體與多智能體的邊界SingleCatchPigs 和 MoveBox倉庫里還有 SingleCatchPigs與 CatchPigs 形成對比。Single 版本只有一個智能體去抓豬豬的行為如果是固定規則那它本質上是一個單智能體問題完全可以用 DQN 直接解決不需要 MARL 算法。但這里把它也收進來好處是可以直接對比“單智能體訓練”和“雙智能體訓練”在同一場景下的表現差異。我在課程設計中經常讓學生先跑 SingleCatchPigs再切換到 CatchPigs觀察協作帶來的收益和訓練難度的上升。這種對比實驗只需要修改環境參數不需要改動算法代碼非常適合寫進實驗報告。MoveBox 則處在單/多智能體的模糊地帶如果只有一個箱子且只有一個智能體推那它是單智能體但倉庫中 gif 顯示 MoveBox 似乎存在兩個智能體合作把箱子推向目標。這種“可調智能體數量”的環境很適合做 scalability 實驗——同樣一個算法智能體數量從 1 變到 3學習曲線會明顯變差這本身就是一個值得寫進報告里的結論。需要注意的是MoveBox 的觀測里通常包含箱子的相對坐標如果多個智能體同時推箱子步長和碰撞檢測都會影響訓練穩定性跑之前先單步調試確認獎勵方向一致。3.4 環境橫向對比與選型建議下面這張表匯總了我對主要環境的理解方便根據實驗目的快速選擇環境環境智能體數任務性質適合驗證的算法特性FireFighter2~3協作公共 reward 下的 credit assignmentGoTogether2協作稀疏 reward 下的延時匯合Rescue2~4協作動態障礙與多目標調度CatchPigs2協作多智能體圍捕策略SingleCatchPigs1單智能體作為 MARL 的基線對比Soccer2競爭自博弈與對手建模Opposite2競爭對稱策略與算法穩定性MoveBox1~2混合智能體數量擴展性Drones3混合碰撞避免與路徑規劃Warehouse2~5協作多智能體資源分配Cleaner2混合公共 vs 個體 reward 對比需要注意我并不建議直接把表格里的特點當作定論因為倉庫里很多環境同時支持修改參數例如 Cleaner 可以通過改變 reward 模式切換協作和競爭選型時必須結合測試腳本里的默認參數。選環境時優先看 PDF 說明里的重點章節比如 Cleaner.pdf 會寫明迷宮生成算法這比讀源碼更快。4. 訓練腳本的接入與調試把 test_xxx.py 接到 DQN/PPO 上4.1 先跑通測試腳本拿到壓縮包后進入 master 目錄對每個環境直接跑一條命令就行。比如cd Multi-Agent-Reinforcement-Learning-Environment-master python test_FireFighter.py注意環境依賴。倉庫里大量使用 numpy、matplotlib少數用到 pygame。如果提示 ModuleNotFoundError: No module named pygame用 pip install pygame 安裝即可。運行 test_Soccer.py 前要保證當前目錄下有 redbot.png、bluebot.png 這兩張圖否則渲染函數會報 IOError。同樣其他 test 腳本也會隱式依賴當前目錄所以不要從別的目錄運行腳本盡量cd到對應環境目錄。跑通之后觀察輸出圖像是否符合預期比如 FireFighter 里火點是否被正確渲染、智能體是否能在有限步內完成任務。4.2 把環境接到 DQN / PPO 算法上由于環境不是 gym 標準接口接入算法時我會寫一個 adapter。以 PPO 為例用 gym 一樣的格式封裝 env# make_env.py 適配器把 test 腳本里用到的環境包裝成 gym-like 接口 import gym from gym import spaces import numpy as np class MARLWrapper(gym.Env): def __init__(self, env, n_agents): super().__init__() self.env env self.n_agents n_agents # 假設所有智能體觀測量是固定長度向量動作為 0~4 的離散值 self.observation_space spaces.Box(low0, high1, shape(128,), dtypenp.float32) self.action_space spaces.Discrete(5) def reset(self): obs self.env.reset() return np.asarray(obs, dtypenp.float32).flatten()[None, :] def step(self, actions): # actions 可以是 shape(n_agents,) 的整數數組 obs, rewards, done, info self.env.step(actions.tolist()) obs_flat np.asarray(obs, dtypenp.float32).flatten()[None, :] return obs_flat, np.mean(rewards), done, info這段代碼有幾個值得注意的地方多智能體環境的 obs 通常是 n_agents 個獨立觀測這里簡單拼接后當作單個 obs 使用reward 用np.mean(rewards)做平均這只適用于公共 reward 的協作任務競爭環境下不能這么處理否則兩個智能體的 reward 直接抵消訓練不出來。觀測空間固定為 128 維只是一個示例實際要以環境里的狀態向量長度為準否則 DQN 網絡輸入層維度會錯。如果用 stable-baselines3只需要把上面的 wrapper 傳給PPO(MlpPolicy, env)即可。但要記住 SB3 默認只處理單智能體MARL 場景需要自己維護多個策略副本或者把多智能體問題轉成 centralized training這部分超出環境本身的范疇需要另外設計。我一般在調試階段用這個 wrapper 確認算法能跑真正做實驗時再換成獨立的 multi-agent rollout 模塊。4.3 多智能體 rollout 中的數據格式在寫 collect rollout 代碼時我一般會保存每個時間步的完整 transition所有智能體的 obs、actions、rewards、next_obs、done。使用 numpy 數組一次性存儲而不是 Python list否則在 1e5 步規模上內存會打爆。def collect_trajectory(env, policy, max_steps1000): obs env.reset() obs_dim 128 # 以實際環境為準 trajectory { obs: np.zeros((max_steps, env.n_agents, obs_dim), dtypenp.float32), actions: np.zeros((max_steps, env.n_agents), dtypenp.int32), rewards: np.zeros((max_steps, env.n_agents), dtypenp.float32), } for t in range(max_steps): actions policy(obs) next_obs, rewards, done, _ env.step(actions) trajectory[obs][t] obs trajectory[actions][t] actions trajectory[rewards][t] rewards obs next_obs if done: break return trajectory這個結構可以直接用于后續 computing returns 或 advantage。注意done是全局回合結束不是每個 agent 自身的 done所以存儲時不需要按 agent 拆分 done只用判斷是否退出循環即可。在多智能體 PPO 中計算 advantage 時要小心如果多個 agent 各自估算 value價值函數的輸入是全局 obs 拼接或共享狀態不能獨立地用每個 agent 的 reward 去算否則會忽略隊友影響。4.4 常見運行問題和排查方法結合我跑這些環境時遇到的坑列幾個常見問題屏幕一閃而過很多 test 腳本是即時渲染循環沒有 sleep加上 matplotlib 的 interactive mode 未開啟。改成plt.pause(0.05)即可。Python2 代碼Rescue 環境有 Python2 目錄在 Python3 下會報print語法錯誤使用 2to3 工具轉換后再跑。渲染模式報錯部分環境在無顯示器的服務器上調用matplotlib.pyplot會報_tkinter.TclError在腳本頭部添加import matplotlib; matplotlib.use(Agg)可以解決。步數過多不結束檢查環境沒有設計最大步數測試腳本里的for step in range(100)是唯一限制算法訓練時需要自己按 max_steps 截斷。獎勵維度不匹配如果 rewards 返回的是標量而代碼按 list 處理會報floatobject is not subscriptable。先打印 type(rewards) 再解析。5. 擴展自己的 MARL 環境接口驗證與 rollout 檢查5.1 參照 Cleaner 新建一個簡單環境如果想把自定義任務包裝成同風格環境最簡單的方式是復制 env_Cleaner.py 作為模板。Cleaner 的代碼已經把地圖從文件加載、智能體移動、格子清掃、reward 計算、回合終止這幾件事分開了。你只需要替換地圖生成邏輯和 agent 行為規則。具體來說保留 reset 和 step 兩個方法在 step 里循環每個 agent 執行移動再統一更新全局狀態reward 計算放在所有 agent 移動完之后而不是每移動一個就加一次這樣可以保持和前文一致的“全局獎勵”。自定義環境時還要定義自己的動作映射。建議沿用倉庫的 0~4 四方向約定未來做橫向對比時會省去很多麻煩。狀態表示上不要把智能體位置直接放進二維坐標容易讓網絡對地圖尺寸過擬合。我一般會生成一個 one-hot 圖層把每個智能體、目標、障礙物分別疊在獨立的 channel 上這樣再接 CNN 或 MLP 都有較好的泛化性。5.2 用隨機策略做環境自檢在訓練任何算法之前我都會先跑一個隨機策略自檢。腳本很簡單python -c from env_Drones import DronesEnv; env DronesEnv(); obs env.reset(); [env.step([env.action_space.sample() for _ in range(env.n_agents)]) for _ in range(50)]; print(random rollout ok)如果隨機策略都能穩定跑完 50 步不出異常說明環境接口層面沒問題。之后再做 reward 范圍檢查記錄 100 個回合的累計 reward確認沒有 NaN、沒有絕對值爆炸。若 reward 一直為 0回去檢查 done 條件是否在第一步就被觸發若某個 agent 的 reward 明顯大于其他 agent要先確認是不是 reward 分配邏輯寫錯。最后把每步的 rendered frame 存成 gif倉庫里那些 gif 就是用類似方法合成的這一步能直觀判斷任務是否可學。我還會加一個“單步斷言”在 reset 之后連續調用 step 兩次對比前后 obs 是否有變化。如果兩次 step 返回一模一樣的 obs說明環境狀態沒有更新多半是 actions 沒有被真正應用。這種小檢查在新增地圖或修改移動邏輯時能快速暴露低級錯誤。拿到完整的軌跡后把每個 agent 的累計 reward 畫成曲線如果曲線在某個步數突然下降往往不是算法問題而是環境給了大額懲罰這時候回看 PDF 里的獎勵設計說明最有效。本文還有配套的精品資源點擊獲取