简介:MADDPG(多智能体深度确定性策略梯度)算法配套的粒子环境合集,打包为multiagent-particle-envs-master,面向强化学习研究者、算法工程师及相关专业学生,用于连续动作空间下多智能体协同与竞争任务的训练、评估和算法对比。压缩包共24个文件,以21个Python源码为主,涵盖环境定义、智能体模型、训练循环和可视化工具,另有README等说明文档,整体仅32KB,轻量且易于部署。环境内置追捕、合作搬运等多种场景,包含状态空间、连续动作空间、奖励函数与智能体实时交互等核心组件,稍作参数调整即可改变规模与难度,适合观察多智能体策略涌现。已有1397人学习下载。通过这套环境,可快速搭建MADDPG实验平台,灵活改造场景与奖励,深入对比多智能体算法的收敛行为,为自动驾驶、机器人协作等应用场景提供研究基座。 搞多智能体强化学习,绕不开MADDPG,而MADDPG绕不开“环境”。这个词在我们这行有两层意思:一层是算法跑起来依赖的工具链环境,Python、PyTorch、gym这些版本一错就是一下午;另一层是智能体要解决的仿真场景,OpenAI当年配套MPE粒子环境,后来SMAC、MaCA等场景也都被拿来跑MADDPG。大多数人看这个算法,最头疼的不是网络结构,而是环境版本和场景调度。这篇文章就把我折腾MADDPG各种环境的经验完整梳理一遍,从官方粒子场景到自定义改造,从工具链版本搭配到报错排查,争取让你照着就能把环境跑通。
1. 先盘清楚:MADDPG到底在用哪些环境
1.1 官方MPE场景一览
MADDPG(Multi-Agent Deep Deterministic Policy Gradient)论文里用的仿真环境,是OpenAI开源的Multi-Agent Particle Environment,简称MPE。它把智能体抽象成二维平面上的圆点粒子,通过控制速度、角度去完成各种任务。界面虽然简陋,但研究多智能体交互足够了。
MPE里内置了十几个场景,最常用的几个我整理成了表格:
| 场景名称 | 类型 | 核心规则 |
|---|---|---|
| simple_spread | 合作 | 多个智能体分散覆盖所有地标,且不能互相碰撞 |
| simple_tag | 竞争+合作 | 3个追捕者合作围捕1个猎物,另有1个静态障碍物 |
| simple_adversary | 对抗 | 1个对手知道真实目标,2个合作方需要识破并聚集到正确地标 |
| simple_push | 对抗 | 蓝方推球到目标,红方拦阻 |
| simple_reference | 合作+通信 | 智能体需要到达与自身颜色对应的地标,且位置信息只能通过通信获取 |
| simple_speaker_listener | 通信 | Speaker知道目标但不能移动,Listener只能靠Speaker的消息行动 |
| simple_crypto | 通信/加密 | 基于密钥与消息的通信对抗场景 |
这些场景基本覆盖了多智能体领域的经典问题分类:纯合作、纯竞争、混合博弈、通信协作。对MADDPG来说,simple_spread是最适合入门的环境,因为逻辑清晰、奖励稀疏度适中,而且所有智能体同质,方便对照实验结果。simple_tag则适合观察“竞争+合作”混合模式下策略如何演化。
1.2 除了MPE,还有哪些可替换环境
MPE最大的优点是轻量、训练快、可视化直观,但它的观测和动作空间都相对简单,离真实问题还有距离。近些年在MADDPG相关的论文里,还有两类环境出镜率很高:
第一类是SMAC(StarCraft Multi-Agent Challenge),即星际争霸多智能体挑战。它把智能体放在微观战斗场景里,每个单位都有自己的血量、攻击范围、地形位置,而且动作空间包含多种攻击技能选择。SMAC的优势是能测试算法在更复杂的序列决策和团队协作上的表现,但代价是训练速度比MPE慢一个量级。
第二类是MaCA(Multi-Agent Combat Arena),作为开源的多智能体对抗平台,它比SMAC更轻,又比MPE更接近真实博弈,适合做红蓝对抗、战术决策类实验。选MADDPG环境时,记住一个原则:先用MPE验证算法逻辑正确,再考虑换到复杂场景看泛化能力。直接一上来就跑SMAC,往往连环境都还没配好,人先被劝退了。
2. 为什么偏偏是这些环境:MADDPG的算法诉求
2.1 中心化训练去中心化执行,环境必须能给出全局信息
MADDPG最核心的设计思路,是每个智能体都有一个“中心化评论家”(Centralized Critic)。训练时,评论家能看到所有智能体的观测和动作;执行时,每个智能体的“演员”(Actor)却只用单智能体的观测。这种机制要求环境必须提供全局状态信息,或者至少能方便地拿到所有智能体的联合观测。
MPE的MultiAgentEnv在设计上天然满足这个需求。它的observation函数对每个智能体分别计算局部观测,但环境内部持有整个world状态,任何智能体的真实位置、速度、地标信息都能读出来。很多跑MADDPG失败的情况,并不是算法写错,而是环境根本没提供联合观测,导致评论家输入维度对不上,训练直接崩。选环境时,第一步就要确认:这个环境能不能拿到全局状态?拿不到,就别硬套MADDPG。
2.2 不同任务类型需要不同场景来暴露算法的特性
我在实际调试中发现,MADDPG在不同任务场景下的表现差异非常大,这不是玄学,而是环境结构决定的。
在simple_spread这样的纯合作场景里,所有智能体共享同一个团队奖励,MADDPG的评论家天然能看到全局,协调能力会被充分激发。但在simple_adversary这类对抗场景里,赢家通吃的奖励结构会让训练过程变得非常不稳定,需要把学习率调小、经验池容量加大,否则很容易出现一方突然碾压、另一方梯度直接走偏的情况。
在需要通信的场景里,比如simple_speaker_listener,智能体的观测维度是不一样的,有的智能体多出消息通道。这时候环境配置的难度又重新回到航线上:如果你的环境只支持同构智能体,那就跑不了异构通信实验。所以想彻底理解MADDPG,最好把合作、竞争、通信三类场景都跑一遍,对比收益曲线和策略行为,比单纯看算法伪代码有用得多。
3. 从零搭一套可复现的MADDPG环境
3.1 运行环境:conda + Python + PyTorch 的版本组合怎么选
老读者都知道,我不太喜欢故作高深,直接给你我这几个月自己在不同机器上验证过的一套稳定配置。MADDPG官方代码是TensorFlow 1.x的,但这个版本现在几乎装不上了,我建议直接用PyTorch重写版,社区里有很多实现,选star最多、issue最少的那种就行。
推荐环境组合如下:
conda create -n maddpg python=3.8 conda activate maddpg pip install torch==1.13.1 numpy==1.24.3 gym==0.25.2 matplotlib==3.7.5为什么选Python 3.8而不是最新版?因为太多老依赖包括MPE的setup.py里都不会声明上限,新Python上跑会莫名踩到distutils被移除、collections.Iterable被删除之类的坑。为什么gym版本要卡在0.25.2?这是兼容MPE老代码与PyTorch版训练脚本之间最稳的版本,太新的gym会把reset()的返回值改成(obs, info)元组,直接导致训练脚本报错。
装完Python环境后,接下来安装MPE。MPE推荐用pip以开发模式安装,这样以后改场景代码不用重新装包。
git clone https://github.com/openai/multiagent-particle-envs.git cd multiagent-particle-envs pip install -e .3.2 写一段随机策略脚本,验证环境通没通
很多同学环境装完第一件事就是跑训练,结果训练脚本一报错,都不知道是环境的问题还是算法的问题。这里我强烈建议,先写一段随机策略脚本,只验证环境本身是否正常:
import gym import multiagent.scenarios as scenarios from multiagent.environment import MultiAgentEnv scenario = scenarios.load("simple_spread.py").Scenario() world = scenario.make_world() env = MultiAgentEnv(world, scenario.reset_world, scenario.reward, scenario.observation) print("智能体数量:", env.n) print("观测空间:", env.observation_space) print("动作空间:", env.action_space) obs_n = env.reset() print("reset后各智能体观测形状:", [o.shape for o in obs_n]) for step in range(100): # 随机动作,MPE动作空间是离散5维:不动 + 上下左右 action_n = [env.action_space[i].sample() for i in range(env.n)] obs_n, reward_n, done_n, info_n = env.step(action_n) if step % 20 == 0: print(f"step {step}, reward: {reward_n}")如果这段代码能顺利跑完,说明MPE环境本身没问题,后面训练不收敛、奖励不上升,都可以从算法侧找原因。如果这里就报错,那问题大概率出在gym版本或依赖冲突上。
3.3 连接MADDPG训练脚本的注意事项
环境验证通过之后,连训练脚本时要注意env.step()返回值的解包方式。老版本的gym里是obs_n, reward_n, done_n, info_n四元组,但gym 0.26之后多了terminated和truncated两个布尔量,变成了五元组。如果你发现训练脚本在ValueError: too many values to unpack处报错,说明训练脚本按老接口写的,而你的gym太新了。
解决办法有两种:一是把gym降到0.25.2(我最推荐,省事);二是改训练脚本为obs_n, reward_n, terminated_n, truncated_n, info_n = env.step(action_n)。另外提醒一句,记得在训练循环里固定随机种子,MPE的reset_world逻辑每次重置都是随机的,不固定种子的话,实验复现基本无从谈起。
4. 实战中的版本与报错排查清单
4.1 gym版本引起的连环坑
我把这几年的实战经验整理成了一份速查表,照着排查至少能省出半天时间:
| 报错信息 | 常见原因 | 处理方案 |
|---|---|---|
ImportError: cannot import name 'error' from 'gym' | gym新版本删除了gym.error模块 | 固定gym为0.25.2,或改代码里的import路径 |
ValueError: too many values to unpack | gym 0.26+的step返回五元组 | 升级训练脚本适配新版API,或降级gym |
error: Cannot inherit from environment that has already been instantiated | 重复创建gym环境实例 | 训练脚本里把环境创建放到main中只执行一次 |
TypeError: reset() got an unexpected keyword argument 'seed' | 新旧gym的reset参数不一致 | 去掉seed参数,用env.seed()或手动固定全局随机种子 |
No module named 'multiagent' | MPE未安装或未在虚拟环境中激活 | 检查当前环境,重新执行pip install -e . |
在这些坑里,最隐蔽的是第二种。同一个训练脚本,在一台机器上能跑,换一台就报错,我排查了半天发现是conda环境里gym被其他依赖自动升级了。建议在项目目录下维护一个requirements.txt,把gym、numpy、torch版本全部锁死,避免依赖自动漂移。
4.2 无头服务器上的渲染问题
在本地跑MPE时,env.render()会弹出一个Matplotlib窗口,但如果你是在云服务器或Docker容器里跑,没有图形界面,渲染代码会直接报no display name and no $DISPLAY environment variable的错误。
解决方案是在训练脚本顶部加上:
import matplotlib matplotlib.use("Agg")这样Matplotlib会走非交互式后端,env.render()返回RGB数组而不是尝试弹窗。另外,如果是用MPE自带的render,强烈建议在服务器上关掉实时渲染,改成每隔N个episode保存一张当前帧图片,这样既能观察策略行为,又不拖慢训练。我习惯在代码里加一个--render_every 100的参数,只在关键节点输出画面。
4.3 训练不收敛,先别急着调网络
很多初学者训练simple_tag不收敛,第一反应是加学习率、改网络层数,但真实原因往往在环境侧。比如simple_tag里,猎物(adversary)的速度上限比追捕者略大,这是环境设定,如果追捕者默认参数下根本追不上猎物,那算法就是再优化也没用,这是环境本身的任务难度。
另一个典型的坑是奖励尺度差异过大。在simple_adversary里,adversary和cooperative agents的奖励函数完全不同,如果不对奖励做归一化或裁剪,训练时梯度更新幅度会剧烈波动。我一般会在奖赏进入经验池时做一步np.clip或标准化预处理,这个操作对收敛速度的提升非常明显。所以遇到不收敛,先打印每个episode的奖励明细,看是不是环境理解错了,再考虑动网络结构。
5. 学会改环境才算真正入门MADDPG
5.1 看懂MPE的World、Agent与Scenario结构
MPE的代码量不大,核心就三个文件:core.py定义数据结构,environment.py封装gym接口,scenario.py定义具体场景逻辑。我最初看的时候觉得抽象,直到我把core.py里的对象类比成了游戏对象:World是整个世界容器,Agent是智能体,Landmark是静态地标,Entity是这两者的公共基类,拥有位置、速度、大小这些物理属性。
理解了这个模型,改环境就有抓手了。比如想让地标移动,就去改Landmark的movable属性;想让智能体看不见某类目标,就去改scenario.observation函数里的过滤条件。MPE的设计把物理属性和智能决策分开,这也是它适合学术实验的原因之一。
5.2 快速改一个自定义场景:加地标、改奖励
以simple_spread为例,想增加一个地标,只需要在场景类的make_world方法里,复制一段Landmark创建代码并改掉颜色和位置即可。但真正影响强化学习训练的是reward函数,我以“增加对碰撞的惩罚力度”为例,给出修改点:
def reward(self, agent, world): reward = 0 # 与所有地标的距离负向求和 dists = [np.sqrt(np.sum(np.square(agent.state.p_pos - lm.state.p_pos))) for lm in world.landmarks] reward -= min(dists) # 原版只对碰撞减1,改成按相对速度加权惩罚 for other in world.agents: if other is agent: continue dist = np.sqrt(np.sum(np.square(agent.state.p_pos - other.state.p_pos))) if dist < agent.size + other.size: relative_speed = np.linalg.norm( agent.state.p_vel - other.state.p_vel) reward -= 1.0 + relative_speed * 0.5 return reward改完场景后,记得在multiagent/scenarios/目录下新建一个simple_spread_new.py,然后在__init__.py里把新场景加进scenario_list,再用前面的验证脚本里scenarios.load("simple_spread_new.py")测试。这一步跑通,说明你已经具备自定义实验环境的能力了。
关于通信类场景的扩展,我再多说一句。如果你想在自定义环境里加入消息传递机制,参考MPE里的simple_speaker_listener实现,它通过Agent的state.c字段保存通信消息,并在observation函数中把消息拼接到其他智能体的观测向量里。注意消息长度的设置会影响全局状态维度,评论家网络输入层也要对应调整。
我个人实际折腾下来的体会是:MADDPG这种算法,真正的难点不在模型结构,而在“喂给它什么样的环境信号”。环境选不对,比如全局信息缺失、奖励尺度失衡、观测向量不统一,后面所有调参都是白费功夫。所以我的建议是,从MPE的simple_spread入手,先把环境和代码跑通,再逐步换到竞争、通信场景,最后再尝试自定义环境。等你能熟练改环境了,才算真正入了多智能体强化学习的门。
本文还有配套的精品资源,点击获取