图解强化学习 |MADDPG
欢迎来到图解强化学习的世界博客主页卿云阁欢迎关注点赞收藏⭐️留言首发时间2026年4月24日✉️希望可以和大家一起完成进阶之路目录环境介绍核心维度参数表环境任务与奖惩机制环境初始化参数对照表MADDPG 适配关键要点总结一下网络定义演员网络策略网络评论家网络价值网络DDPG算法定义环境介绍智能体需要控制3个蓝色小球到达3个黑点标记的目标点。 左边是通过OpenRL框架训练出来的智能体右边是随机动作的智能体。核心维度参数表指标参数详情数值 / 维度智能体总数Agent Number3动作空间维度Action Shape(5,)动作空间类型Action Space离散 Discrete (5) / 连续 Box(0.0,1.0,(5,))单智能体观测维度Observation Shape(18,)观测值范围Observation Range(−∞,∞)全局联合状态维度Global State Shape(54,)全局状态范围State Range(−∞,∞)环境任务与奖惩机制任务目标环境默认包含 N 个智能体与 N 个地标默认 N3。智能体需协同完成地标全覆盖任务同时规避智能体之间的碰撞。奖励设计全局奖励依据各地标到最近智能体的最小距离之和进行全局奖赏约束局部惩罚智能体发生碰撞时施加惩罚单次碰撞奖励值为 −1权重调节通过 local_ratio 平衡局部奖励与全局奖励权重。单智能体观测特征组成自身速度、自身位置、地标相对位置、其余智能体相对位置、通信特征向量。离散动作定义无动作、左移、右移、下移、上移。环境初始化参数对照表参数名专业释义作用说明N智能体与地标数量同步设定智能体个数 目标地标个数local_ratio局部奖励权重系数控制局部碰撞惩罚权重全局奖励权重 1−local_ratiomax_cycles最大迭代步数限制单回合最大交互步数超时自动截断continuous_actions连续动作开关False默认离散动作True启用连续动作适配 MADDPGdynamic_rescaling动态尺寸缩放是否根据渲染窗口大小自适应缩放智能体、地标尺寸MADDPG 适配关键要点MADDPG 为连续动作算法启动环境必须设置continuous_actionsTrue训练配置Actor 输入单智能体观测 18 维Actor 输出连续动作 5 维Critic 输入全局联合状态 54 维 所有智能体联合动作奖励优化可降低 local_ratio弱化碰撞惩罚、强化协同覆盖地标训练效果。总结一下游戏基础设定玩家3 个智能体目标3 个固定地标点核心任务大家分工散开每个地标都被就近覆盖三个智能体不能互相撞车观测 动作MADDPG 关键每个智能体自己看到的信息18 维局部观测包含自己速度、自己位置、地标相对坐标、其他队友相对位置、通信向量动作5 维离散不动 / 上下左右连续模式MADDPG 必用输出连续运动控制量奖惩规则核心机制全局大奖励所有地标 到最近智能体的距离之和越小 → 奖励越高 倒逼智能体散开、分头占点局部惩罚两个智能体一碰面直接扣分-1 / 次 倒逼智能体互相避让、保持距离local_ratio平衡「防撞惩罚」和「占地标奖励」权重回合规则每局最多max_cycles步走完直接结束全程协作类多智能体任务无对抗、纯合作完美适配 MADDPG 的原因连续动作可控开启 continuous_actionsTrue单智能体局部观测去中心化执行有全局联合状态中心化 Critic 训练环境简单、收敛快是 MADDPG 最经典入门测试环境项目值用于 MADDPG智能体数量33 个 Actor / 3 个 Critic单智能体观测维度18Actor 输入维度动作维度5Actor 输出维度全局状态维度54Critic 输入动作空间连续MADDPG 天然适配奖励类型全局共享合作任务结束条件25 步超时训练回合长度网络定义演员网络策略网络项目游戏维度你的网络维度含义输入维度18state_dim18智能体局部观测输出维度5action_dim5连续动作5 维输入范围(-∞, ∞)自动处理观测无限制输出范围[0,1]tanh*0.50.5符合环境要求输入[ -0.12203537, -0.15697908, # 自身速度 (2) -0.34329745, 0.37129653, # 自身位置 (2) 0.59056230, -0.13607581, # 地标1相对位置 (2) 0.62283367, -0.63547640, # 地标2相对位置 (2) -0.31667632, -0.38397613, # 地标3相对位置 (2) -0.07715695, -0.41063723, # 相对于agent_1 (2) 0.08004795, -1.21909710, # 相对于agent_2 (2) 0.0, 0.0, 0.0, 0.0 # 通信 (4) ]输出[0.12, # 不动力度 0.88, # 左移力度 0.45, # 右移力度 0.23, # 下移力度 0.91] # 上移力度评论家网络价值网络单智能体观测 18 维全局联合状态 3×18 54 维Critic 必须看全局单智能体动作 5 维3 个智能体总动作 3×5 15 维Actor 只看自己18 维→ 做决策Critic 看所有人全局54 维 所有人动作15 维→ 打分这就是 MADDPG 的核心Critic 输入 全局状态 (54) 联合动作 (15) 总共 69 维输入输入 1全局状态 state54 维[ # agent_0 (18) -0.122, -0.156, -0.343, 0.371, 0.590, -0.136, 0.622, -0.635, -0.316, -0.383, -0.077, -0.410, 0.080, -1.219, 0,0,0,0, # agent_1 (18) 0.175, 0.145, -0.420, -0.039, 0.667, 0.274, ...共18个数字, # agent_2 (18) -0.028, 0.555, -0.263, -0.847, ...共18个数字 ] # 总长度 54输入 2联合动作 action15 维agent_0 动作(5) [0.21, 0.77, 0.34, 0.11, 0.89] agent_1 动作(5) [0.44, 0.12, 0.88, 0.56, 0.23] agent_2 动作(5) [0.65, 0.33, 0.21, 0.90, 0.10][0.21,0.77,0.34,0.11,0.89, 0.44,0.12,0.88,0.56,0.23, 0.65,0.33,0.21,0.90,0.10]全局状态(54) 联合动作(15) 69 维Critic 的输出Q 值1 个数字tensor([[1.324]])Critic 网络输入全局局面54 所有人动作15输出一个分数评价好坏。DDPG算法定义定义环境的state和action的维度self.state_dim env.observation_space(env.agents[0]).shape[0] self.action_dim env.action_space(env.agents[0]).shape[0]self.state_dim 18self.action_dim 5初始化DDPG算法的超参数和网络模型self.gamma gamma # 折扣因子 self.tau tau # 软更新系数 self.action_noise action_noise # 动作噪声幅度参数名数值作用大白话对游戏的影响gamma0.99重视未来奖励学会长期协作、占地标tau0.005目标网络缓慢更新训练稳定、不崩溃action_noise0.05轻微探索智能体不乱撞也不发呆agent的id和数量self.agent_ids env.agents # 拿到所有智能体的名字 self.n_agents len(env.agents) # 数一共有几个智能体 self.agent_ids [agent_0, agent_1, agent_2] self.n_agents 3创建actor网络每个agent都需要创建一个对应的网络网络数量输入维度输出维度作用Actor318自己观测5自己动作智能体做决策Target Actor3185稳定训练Critic15415691Q 值全局打分Target Critic1691稳定训练Replay Buffer-状态 54 / 动作 15-存训练数据更新目标网络参数新目标网络 0.005 × 主网络 0.995 × 旧目标网络def update_network_parameters(self, tauNone): if tau is None: tau self.tau # 遍历每个target_actor并更新参数 for actor, target_actor in zip(self.actors, self.target_actors): for actor_params, target_actor_params in zip(actor.parameters(), target_actor.parameters()): target_actor_params.data.copy_(tau * actor_params (1 - tau) * target_actor_params) # 更新target_critic网络参数 for critic_params, target_critic_params in zip(self.critic.parameters(), self.target_critic.parameters()): target_critic_params.data.copy_(tau * critic_params (1 - tau) * target_critic_params)定义存储dict把 3 个智能体各自的观测 / 动作拼接成 1 组全局数据存入回放池输入3 个智能体分开的数据输出1 组全局拼接好的数据数据维度来源state54 维18×3action15 维5×3reward1 维3 个奖励求和next_state54 维18×3done1 个布尔值只要一个结束即结束根据当前状态选择动作# 根据当前状态选择动作 def choose_action(self, observation, trainTrue): actions {} with torch.no_grad(): for actor, agent_id in zip(self.actors, observation): # 将状态转化为tensor并传递给actor生成动作 state torch.FloatTensor(np.array([observation[agent_id]])).to(self.device) action actor(state).squeeze() # 训练阶段添加一些噪声提高探索性 if train: noise torch.tensor(np.random.normal(loc0.0, scaleself.action_noise, size(action.shape)), dtypetorch.float).to(self.device) action torch.clamp(action noise, 0, 1) # 拼接动作 actions[agent_id] action.cpu().detach().numpy() return actions手动例子agent_0输入观测18 维[ -0.122, -0.156, -0.343, 0.371, 0.590, -0.136, 0.622, -0.635, -0.316, -0.383, -0.077, -0.410, 0.080, -1.219, 0,0,0,0 ]Actor 生成动作[0.21, 0.77, 0.34, 0.11, 0.89]手动计算噪声scale0.05噪声 [0.02, -0.03, 0.01, -0.02, 0.04]动作 噪声0.21 0.02 0.230.77 - 0.03 0.740.34 0.01 0.350.11 - 0.02 0.090.89 0.04 0.93最终动作clamp 确保不会超出 0~1。[0.23, 0.74, 0.35, 0.09, 0.93]把动作存入字典{ agent_0: [0.23,0.74,0.35,0.09,0.93],agent_1: [5个数字],agent_2: [5个数字] }步骤输入输出维度观测智能体自己看到的18 个数字18Actor 网络18 维观测5 维连续动作5加噪声动作 小随机更有探索性5限制范围动作0~1 之间5输出动作字典3×5 维15使用经验回放进行训练states 全局状态 54 维3 个智能体观测拼在一起states [ # agent_0 (18维) -0.122, -0.156, -0.343, 0.371, 0.590, -0.136, 0.622, -0.635, -0.316, -0.383, -0.077, -0.410, 0.080, -1.219, 0, 0, 0, 0, # agent_1 (18维) 0.175, 0.145, -0.420, -0.039, 0.667, 0.274, 0.699, -0.224, -0.239, 0.026, 0.077, 0.410, 0.157, -0.808, 0, 0, 0, 0, # agent_2 (18维) -0.028, 0.555, -0.263, -0.847, 0.510, 1.083, 0.542, 0.583, -0.396, 0.835, -0.080, 1.219, -0.157, 0.808, 0, 0, 0, 0 ]actions 全局动作 15 维 3 个智能体动作拼在一起actions [ # agent_0 (5维) 0.23, 0.74, 0.35, 0.09, 0.93, # agent_1 (5维) 0.44, 0.12, 0.88, 0.56, 0.23, # agent_2 (5维) 0.65, 0.33, 0.21, 0.90, 0.10 ]rewards 1 个数字全局奖励rewards [-0.791]next_states 下一个全局状态 54 维和 states 结构一样也是 54 维。next_states[:, self.state_dim * i : self.state_dim * (i1)]next_states [agent0 的 18 维观测 → [0:18]agent1 的 18 维观测 → [18:36]agent2 的 18 维观测 → [36:54]]next_act [0.23, 0.74, 0.35, 0.09, 0.93]next_act [0.44, 0.12, 0.88, 0.56, 0.23]next_act [0.65, 0.33, 0.21, 0.90, 0.10]next_actions [0.23,0.74,0.35,0.09, 0.93, 0.44,0.12,0.88,0.56, 0.23, 0.65,0.33,0.21,0.90,0.10]全局状态 (1,54) ↓ 拆分 agent0(18) | agent1(18) | agent2(18) ↓ ↓ ↓ Actor0 Actor1 Actor2 ↓ ↓ ↓ act0(5) act1(5) act2(5) ↓ ↓ ↓ 拼接 → 全局动作 (1,15)q_ self.target_critic(next_states, next_actions).view(-1) q_[dones] 0.0 target rewards self.gamma * q_下一步全局局面54 下一步全局动作15 → 送入 Target Critic → 得到未来奖励 q_ → 如果结束q_0 → 最终标准答案 target 当前奖励 0.99×q_target -0.79 0.99 * 0.0 target -0.79使用当前评论家网络计算当前状态和动作对应的 Q 值q self.critic(states, actions).view(-1)让当前评论家网络给【当前全局局面 当前动作】打分输出一个评分。第一步送入 Critic 的两个输入① states 全局状态54 维[ -0.122, -0.156, -0.343, 0.371, 0.590, -0.136,0.622, -0.635, -0.316, -0.383, -0.077, -0.410,0.080, -1.219, 0, 0, 0, 0, # agent0 (18)0.175, 0.145, -0.420, -0.039, 0.667, 0.274,0.699, -0.224, -0.239, 0.026, 0.077, 0.410,0.157, -0.808, 0, 0, 0, 0, # agent1 (18)-0.028, 0.555, -0.263, -0.847, 0.510, 1.083,0.542, 0.583, -0.396, 0.835, -0.080, 1.219,-0.157, 0.808, 0, 0, 0, 0 ] # agent2 (18)shape(1, 54)② actions 全局动作15 维[ 0.23, 0.74, 0.35, 0.09, 0.93, # agent00.44, 0.12, 0.88, 0.56, 0.23, # agent10.65, 0.33, 0.21, 0.90, 0.10 ] # agent2最后的输出tensor([0.42])计算评论家网络的损失critic_loss F.mse_loss(q, target)MSE (预测值 - 标准答案)² 的平均值预测分数 q 0.42标准答案 target 0.398误差 0.022误差平方 0.000484critic_loss 0.000484# 生成当前状态对应的新动作并计算演员网络的损失 new_actions [] # 遍历每个agent对应的actor for i in range(len(self.agent_ids)): # 将拼接后的state分段送入各个actor new_act self.actors[i](states[:,self.state_dim * i:self.state_dim * (i 1)]) new_actions.append(new_act) # 拼接生成的动作 new_actions torch.cat(new_actions, dim 1) actor_loss -torch.mean(self.critic(states, new_actions))new_actions[ tensor([[0.23, 0.74, 0.35, 0.09, 0.93]]), # agent0 tensor([[0.44, 0.12, 0.88, 0.56, 0.23]]), # agent1 tensor([[0.65, 0.33, 0.21, 0.90, 0.10]]) # agent2 ]全局状态 (1,54) ↓ 拆分 agent0(18) | agent1(18) | agent2(18) ↓ ↓ ↓ Actor0 Actor1 Actor2 ↓ ↓ ↓ act0(5) act1(5) act2(5)new_actions torch.cat(new_actions, dim 1) actor_loss -torch.mean(self.critic(states, new_actions))拼接后new_actionstensor([[ 0.23, 0.74, 0.35, 0.09, 0.93, # agent0 0.44, 0.12, 0.88, 0.56, 0.23, # agent1 0.65, 0.33, 0.21, 0.90, 0.10 # agent2 ]])actor_lossactor_loss -torch.mean(self.critic(states, new_actions))new_actions最终形状(1, 15)tensor([[ 0.23, 0.74, 0.35, 0.09, 0.93, # agent0 0.44, 0.12, 0.88, 0.56, 0.23, # agent1 0.65, 0.33, 0.21, 0.90, 0.10 # agent2 ]])Actor 训练让动作获得更高的分数所以 loss 是负的 Q 值actor_loss -torch.mean( 评论家对当前动作的打分 Q )假设有5个样本的话5个Q值[0.85, 1.20, 0.45, 0.90, 1.10]求和0.851.20.450.91.1 4.5平均值4.5 / 5 0.9 actor_loss -0.9