ML-Agents案例之双人足球

2024-04-03 18:32
文章标签 案例 足球 ml 双人 agents

本文主要是介绍ML-Agents案例之双人足球,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本案例源自ML-Agents官方的示例,Github地址:https://github.com/Unity-Technologies/ml-agents,本文是详细的配套讲解。

本文基于我前面发的两篇文章,需要对ML-Agents有一定的了解,详情请见:Unity强化学习之ML-Agents的使用、ML-Agents命令及配置大全。

我前面的相关文章有:

ML-Agents案例之Crawler

ML-Agents案例之推箱子游戏

ML-Agents案例之跳墙游戏

ML-Agents案例之食物收集者

对称环境博弈

环境说明

在这里插入图片描述

这是一场对称性的2对2的足球比赛,双方人员配置一致,目标是在防止球进入己方球门的同时,把球送进对方的球门。

奖励设置:当把球踢到对方的球门时,分数+1,但是还要减去和花费时间成正比的惩罚。当求踢进己方球门时,分数-1。

输入维度:

在这里插入图片描述

可以看到,每个智能体身上都有14根射线传感器,前面120读角均匀分布11根,后面均匀分布3根,探测的标签有球,己方球门,对方球门,己方队友,对手,墙壁。关于射线传感器Ray Perception Sensor 3D,请参考ML-Agents案例之推箱子游戏。

输出维度:

三个离散输出,分别控制:前后移动,左右移动,旋转。

代码分析

环境控制

环境控制器,挂载在空物体上:

using System.Collections.Generic;
using Unity.MLAgents;
using UnityEngine;public class SoccerEnvController : MonoBehaviour
{// 存储单个智能体关键信息的类[System.Serializable]public class PlayerInfo{public AgentSoccer Agent;[HideInInspector]public Vector3 StartingPos;[HideInInspector]public Quaternion StartingRot;[HideInInspector]public Rigidbody Rb;}/// 每一个episode的最大步数/// Max Academy steps before this platform resets[Tooltip("Max Environment Steps")] public int MaxEnvironmentSteps = 25000;public GameObject ball;[HideInInspector]public Rigidbody ballRb;Vector3 m_BallStartingPos;// 关于多个智能体信息的列表public List<PlayerInfo> AgentsList = new List<PlayerInfo>();private SoccerSettings m_SoccerSettings;// 两个多智能体组,代表两个队private SimpleMultiAgentGroup m_BlueAgentGroup;private SimpleMultiAgentGroup m_PurpleAgentGroup;private int m_ResetTimer;// 初始化环境void Start(){// 寻找一个唯一的脚本,其中只包含了两种智能体的材质,是否随机队伍,移动速度的变量m_SoccerSettings = FindObjectOfType<SoccerSettings>();// 实例化m_BlueAgentGroup = new SimpleMultiAgentGroup();m_PurpleAgentGroup = new SimpleMultiAgentGroup();ballRb = ball.GetComponent<Rigidbody>();m_BallStartingPos = new Vector3(ball.transform.position.x, ball.transform.position.y, ball.transform.position.z);// 组队,构成两组多智能体foreach (var item in AgentsList){item.StartingPos = item.Agent.transform.position;item.StartingRot = item.Agent.transform.rotation;item.Rb = item.Agent.GetComponent<Rigidbody>();if (item.Agent.team == Team.Blue){m_BlueAgentGroup.RegisterAgent(item.Agent);}else{m_PurpleAgentGroup.RegisterAgent(item.Agent);}}// 重置场景ResetScene();}void FixedUpdate(){// 达到时间后停止训练,进入下一个episode,重置场景m_ResetTimer += 1;if (m_ResetTimer >= MaxEnvironmentSteps && MaxEnvironmentSteps > 0){m_BlueAgentGroup.GroupEpisodeInterrupted();m_PurpleAgentGroup.GroupEpisodeInterrupted();ResetScene();}}// 重置球的位置public void ResetBall(){var randomPosX = Random.Range(-2.5f, 2.5f);var randomPosZ = Random.Range(-2.5f, 2.5f);ball.transform.position = m_BallStartingPos + new Vector3(randomPosX, 0f, randomPosZ);ballRb.velocity = Vector3.zero;ballRb.angularVelocity = Vector3.zero;}// 进球后的处理,包括处理加减分,开启新的episode,重置环境public void GoalTouched(Team scoredTeam){if (scoredTeam == Team.Blue){m_BlueAgentGroup.AddGroupReward(1 - (float)m_ResetTimer / MaxEnvironmentSteps);m_PurpleAgentGroup.AddGroupReward(-1);}else{m_PurpleAgentGroup.AddGroupReward(1 - (float)m_ResetTimer / MaxEnvironmentSteps);m_BlueAgentGroup.AddGroupReward(-1);}m_PurpleAgentGroup.EndGroupEpisode();m_BlueAgentGroup.EndGroupEpisode();ResetScene();}// 重置整个场景,带一定随机public void ResetScene(){m_ResetTimer = 0;//Reset Agentsforeach (var item in AgentsList){var randomPosX = Random.Range(-5f, 5f);var newStartPos = item.Agent.initialPos + new Vector3(randomPosX, 0f, 0f);var rot = item.Agent.rotSign * Random.Range(80.0f, 100.0f);var newRot = Quaternion.Euler(0, rot, 0);item.Agent.transform.SetPositionAndRotation(newStartPos, newRot);item.Rb.velocity = Vector3.zero;item.Rb.angularVelocity = Vector3.zero;}//Reset BallResetBall();}
}

智能体控制

AgentSoccer.cs文件:

这里有两个枚举变量,分别代表所处的队和所处位置:

public enum Team
{Blue = 0,Purple = 1
}
public enum Position
{Striker,     // 前锋Goalie,      // 守门员Generic      // 通用
}

初始化:

public override void Initialize()
{// 确定最大训练步数SoccerEnvController envController = GetComponentInParent<SoccerEnvController>();if (envController != null){m_Existential = 1f / envController.MaxEnvironmentSteps;}else{m_Existential = 1f / MaxStep;}// 获取BehaviorParameters组件中的队伍ID,初始化对应属性m_BehaviorParameters = gameObject.GetComponent<BehaviorParameters>();if (m_BehaviorParameters.TeamId == (int)Team.Blue){// dteam = Team.Blue;// 计算初始位置initialPos = new Vector3(transform.position.x - 5f, .5f, transform.position.z);// 用来计算旋转rotSign = 1f;}else{team = Team.Purple;initialPos = new Vector3(transform.position.x + 5f, .5f, transform.position.z);rotSign = -1f;}// 守门员给一个较高的横向速度if (position == Position.Goalie){m_LateralSpeed = 1.0f;m_ForwardSpeed = 1.0f;}// 前锋横向速度较低,但拥有较高的前进速度else if (position == Position.Striker){m_LateralSpeed = 0.3f;m_ForwardSpeed = 1.3f;}// 通用速度else{m_LateralSpeed = 0.3f;m_ForwardSpeed = 1.0f;}m_SoccerSettings = FindObjectOfType<SoccerSettings>();agentRb = GetComponent<Rigidbody>();agentRb.maxAngularVelocity = 500;// 获取配置文件参数m_ResetParams = Academy.Instance.EnvironmentParameters;
}

输出动作:

// 接收神经网络输出,驱动智能体
public override void OnActionReceived(ActionBuffers actionBuffers)
{// 前锋有时间惩罚,如果是守门员是时间奖励,这样就能训练出不同的逻辑if (position == Position.Goalie){// Existential bonus for Goalies.AddReward(m_Existential);}else if (position == Position.Striker){// Existential penalty for StrikersAddReward(-m_Existential);}// 封装了移动的逻辑MoveAgent(actionBuffers.DiscreteActions);
}public void MoveAgent(ActionSegment<int> act)
{var dirToGo = Vector3.zero;var rotateDir = Vector3.zero;m_KickPower = 0f;// 接收三个输出var forwardAxis = act[0];var rightAxis = act[1];var rotateAxis = act[2];// 下面把三个输出分别赋给前后移动,左右移动,旋转switch (forwardAxis){case 1:dirToGo = transform.forward * m_ForwardSpeed;// 只有向前移动时才有踢力m_KickPower = 1f;break;case 2:dirToGo = transform.forward * -m_ForwardSpeed;break;}switch (rightAxis){case 1:dirToGo = transform.right * m_LateralSpeed;break;case 2:dirToGo = transform.right * -m_LateralSpeed;break;}switch (rotateAxis){case 1:rotateDir = transform.up * -1f;break;case 2:rotateDir = transform.up * 1f;break;}// 执行动作transform.Rotate(rotateDir, Time.deltaTime * 100f);agentRb.AddForce(dirToGo * m_SoccerSettings.agentRunSpeed,ForceMode.VelocityChange);
}

每个episode开始时的处理:

// 从配置文件中获取数据,与球碰撞的奖励系数,课程学习会使用
public override void OnEpisodeBegin()
{m_BallTouch = m_ResetParams.GetWithDefault("ball_touch", 0);
}

碰撞处理:

void OnCollisionEnter(Collision c)
{// 计算踢力var force = k_Power * m_KickPower;if (position == Position.Goalie){force = k_Power;}// 和球碰撞会获得奖励if (c.gameObject.CompareTag("ball")){AddReward(.2f * m_BallTouch);// 计算自身到接触点的向量,然后标准化var dir = c.contacts[0].point - transform.position;dir = dir.normalized;// 给球添加力c.gameObject.GetComponent<Rigidbody>().AddForce(dir * force);}
}

挂载在足球上的脚本SoccerBallContraller.cs:

using UnityEngine;public class SoccerBallController : MonoBehaviour
{public GameObject area;[HideInInspector]public SoccerEnvController envController;public string purpleGoalTag; //will be used to check if collided with purple goalpublic string blueGoalTag; //will be used to check if collided with blue goalvoid Start(){envController = area.GetComponent<SoccerEnvController>();}// 碰撞检测,是否和球门产生碰撞,碰撞了就调用环境控制器的方法void OnCollisionEnter(Collision col){if (col.gameObject.CompareTag(purpleGoalTag)) //ball touched purple goal{envController.GoalTouched(Team.Blue);}if (col.gameObject.CompareTag(blueGoalTag)) //ball touched blue goal{envController.GoalTouched(Team.Purple);}}
}

配置文件

本案例使用了多智能体算法poca,上一个使用多智能体算法的案例是ML-Agents案例之推箱子游戏

对称环境的好处就是双方都共用一套模型。

配置如下:

behaviors:SoccerTwos:trainer_type: pocahyperparameters:batch_size: 2048buffer_size: 20480learning_rate: 0.0003beta: 0.005epsilon: 0.2lambd: 0.95num_epoch: 3learning_rate_schedule: constantnetwork_settings:normalize: falsehidden_units: 512num_layers: 2vis_encode_type: simplereward_signals:extrinsic:gamma: 0.99strength: 1.0keep_checkpoints: 5max_steps: 50000000time_horizon: 1000summary_freq: 10000self_play:save_steps: 50000team_change: 200000swap_steps: 2000window: 10play_against_latest_model_ratio: 0.5initial_elo: 1200.0

可以看到,poca除了可以和ppo具有相同的配置参数外,此处还展示了一个新功能,那就是Self Play

    self_play:save_steps: 50000team_change: 200000swap_steps: 2000window: 10play_against_latest_model_ratio: 0.5initial_elo: 1200.0

Self Play

Self Play是计算机的“左右互搏术”,通过自己随机的一个个智能体相互战斗,共同进步,从而共同实现策略的优化。

参考文章:左右互搏,self-play,《Emergent Complexity via Multi-Agent Competition》

Self Play为强化学习面临的常见问题增加了额外的混杂因素。一般在技能水平、最终策略通用性、学习的稳定性之间进行权衡。和低多样性对手进行训练比和高多样性对手进行训练会使学习过程更稳定。在此背景下,本指南讨论了可以调整的self play超参数。

如果环境包含多个分成多个团队的智能体,您可以通过为每个行为提供以下配置来使用Self Play:

环境描述
save_steps(默认 = 20000)存储智能体策略的训练步数。例如,如果save_steps=10000当前策略的模型每个10000step保存一次,保存的叫做快照(snapshots)。请注意,训练步数是按每个智能体单独计算的。有关更多信息,请参阅v0.13 之后的迁移文档。 较大的save_steps会使智能体接受更多的训练,因此将产生一组涵盖更广泛比赛风格的对手。智能体针对更广泛的对手进行训练。学习一个策略来击败更多样化的对手是一个更难的问题,因此可能需要更多的整体训练步骤,实现更通用和更强大的策略。该值还取决于环境对智能体的困难程度。 典型范围:10000-100000
team_change(默认 = 5 * save_steps)切换学习团队的训练步数。每个团体学习到一定步数后,将切换给另一个团体进行学习。在不对称的比赛中,对方团体可能需要较少的训练步骤来获得类似的性能提升。与更简单的智能体团队相比,较高的team_change使用户能够训练更复杂的智能体团队。 较大的值team-change将允许智能体针对其对手进行更长时间的训练。智能体针对同一组对手训练的时间越长,击败他们的能力就越大。然而,针对同样的训练时间过长可能会导致对特定对手策略的过度拟合,智能体可能会在对抗下一批对手时失败。 team-change还决定保存多少智能体的训练快照用作其他团队的对手。因此,我们建议将此值设置为save_steps的倍数。 典型范围:4x-10x,其中 x=save_steps
swap_steps(默认 = 10000)切换对手快照之间的步数。此时对手遵循固定策略而不学习。在非对称游戏中,我们可能有智能体数量不同的团队。例如两个智能体的团队每个step收集的智能体数据是一个智能体的团队的两倍。因此,这两个值应该不同,以确保相同数量的训练步数。swap_steps的公式为:(num_agents / num_opponent_agents)*(team_change / x),x是交换次数。详细解释在下方。
play_against_latest_model_ratio(默认 = 0.5)智能体与对手的最新策略对抗的概率。也就是说有 1 - play_against_latest_model_ratio概率智能体将与过去迭代中对手的快照进行对抗。 较大的值play_against_latest_model_ratio表示智能体将更频繁地与当前对手对战。由于智能体正在更新其策略,如果每次迭代的对手都会有所不同,可能会导致学习环境的不稳定,但会给智能体带来更具挑战性的情况的自动课程,这可能最终使其更强大。 典型范围:0.0-1.0
window(默认 = 10)储存快照的容量大小,智能体的对手从中采样。例如,window大小为 5 将保存最近拍摄的 5 个快照。每次拍摄新快照时,最老的快照将被丢弃。较大的值window意味着智能体的对手池将包含更多的行为多样性,因为它将包含训练早期的策略。就像在save_steps超参数中一样,智能体针对更广泛的对手进行训练。学习一个策略来击败更多样化的对手是一个更难的问题,因此需要更多的整体训练步骤,但也会在使得自身策略训练得更加强大。 典型范围:5-30

ELO机制

ELO等级分制度是衡量选手水平的评分方法,在训练智能体的过程中,各个智能体团队通过相互博弈共同进步,会使得ELO分数持续升高。一般来说,初始分为1200,赢了奖励一定分数,输了会扣除一定分数。详细规则请查看链接:

ELO算法的原理及应用

ELO评分算法

交换步骤的注意事项

例如,在 2v1 场景中,如果我们希望在 team-change=200000 步期间交换发生 x=4 次,则一个智能体的团队的 swap_steps 为:

swap_steps = (1 / 2) * (200000 / 4) = 25000

两个智能体的团队的 swap_steps 为:

swap_steps = (2 / 1) * (200000 / 4) = 100000 注意,在团队规模相同的情况下,第一项等于 1,swap_steps 可以通过将总步数除以所需的交换次数来计算。

较大的 swap_steps 值意味着智能体将在更长的训练迭代次数中与相同的固定对手对战。使训练环境更稳定,但让智能体面临针对该特定对手过度拟合其行为的风险。

效果

在这里插入图片描述

非对称环境博弈

环境说明

在这里插入图片描述

如图所示,在非对称的环境博弈中,守门员只有一人,任务是防守两个前锋的进攻,把球保持在球门之外,而两个前锋的任务就是把球踢进球门。

在对称性的环境博弈中,模型都是可以相互通用的,例如在上面2V2的例子中,四个球员都是共享一个网络模型,就能实现对抗。

而在1个守门员对2个前锋的环境中,两者的逻辑必然存在巨大的不同,因此,我们需要给双方设定不同的网络进行训练。

代码依然采用的是我们在对称性环境所用的代码,守门员和前锋的不同之处在于给其设定了不同的Position参数,如图:

在这里插入图片描述

Position是个公有变量,守门员设定为Goalie,前锋设定为Striker。这样我们回头查看初始化函数Initialize()的代码,就会发现守门员和前锋所拥有的前向移动速度和侧向移动速度是不同的。守门员横向速度高,而前锋前向速度高。

配置文件

behaviors:Goalie:trainer_type: pocahyperparameters:batch_size: 2048buffer_size: 20480learning_rate: 0.0003beta: 0.005epsilon: 0.2lambd: 0.95num_epoch: 3learning_rate_schedule: constantnetwork_settings:normalize: falsehidden_units: 512num_layers: 2vis_encode_type: simplereward_signals:extrinsic:gamma: 0.99strength: 1.0keep_checkpoints: 5max_steps: 30000000time_horizon: 1000summary_freq: 10000self_play:save_steps: 50000team_change: 200000swap_steps: 1000window: 10play_against_latest_model_ratio: 0.5initial_elo: 1200.0Striker:trainer_type: pocahyperparameters:batch_size: 2048buffer_size: 20480learning_rate: 0.0003beta: 0.005epsilon: 0.2lambd: 0.95num_epoch: 3learning_rate_schedule: constantnetwork_settings:normalize: falsehidden_units: 512num_layers: 2vis_encode_type: simplereward_signals:extrinsic:gamma: 0.99strength: 1.0keep_checkpoints: 5max_steps: 30000000time_horizon: 1000summary_freq: 10000self_play:save_steps: 50000team_change: 200000swap_steps: 4000window: 10play_against_latest_model_ratio: 0.5initial_elo: 1200.0

可以看到,于2v2的对称性环境博弈相比,这个环境需要的模型多了一个(Behavior Name分别为Goalie和Striker),因此需要配置的量也多了一倍。两个网络参数唯一的不同就在于Self Play中的swap_steps。具体原因可以查看上面的Self Play章节。

在非对称性的环境中,双方都会随机出许多团队进行相互对抗,并且相互学习共同进步。每个团队不仅与不同的对手战斗,还会与对手以前的模型进行战斗,最终能训练出一个适应性,通用性较强的智能体团队。

效果

在这里插入图片描述

后记

本文针对ML-Agents的足球案例进行了讲解,包括对称环境的2V2双人足球,还有非对称性的 1守门员 VS 2前锋。主要的知识点是Self Play。也就是如何保存不同的模型让其相互对决,来实现整体的进化。

在对称环境中,我们在每个智能体团队中的智能体需要达成任务一致的情况下,我们只需要一个模型,就能完成所有智能体的训练。

而在非对称环境中,我们则需要给团队双方不同的模型,进行训练,如果队内智能体需要完成的任务有差异,队内不同智能体的模型可能也会有所不同。

这篇关于ML-Agents案例之双人足球的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/873665

相关文章

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插

django学习入门系列之第三点《案例 小米商城头标》

文章目录 阴影案例 小米商城头标往期回顾 阴影 设置阴影 box-shadow:水平方向 垂直方向 模糊距离 颜色 box-shadow: 5px 5px 5px #aaa; 案例 小米商城头标 目标样式: CSS中的代码 /*使外边距等于0,即让边框与界面贴合*/body{margin: 0;}/*控制父级边框*/.header{backgroun

MATLAB算法实战应用案例精讲-【数模应用】三因素方差

目录 算法原理 SPSSAU 三因素方差案例 1、背景 2、理论 3、操作 4、SPSSAU输出结果 5、文字分析 6、剖析 疑难解惑 均方平方和类型? 事后多重比较的类型选择说明? 事后多重比较与‘单独进行事后多重比较’结果不一致? 简单效应是指什么? 边际估计均值EMMEANS是什么? 简单简单效应? 关于方差分析时的效应量? SPSSAU-案例 一、案例

Retrofit介绍案例

Retrofit这东西我就不多做解释了,反正最近应用很广,基本都快和OkHttp一起成为安卓的事实网络访问标准框架了。   这么好的一个东西,官网文档实在是不算太好,说的不太清晰。按官网的经常会有“Could not locate ResponseBody converter for”问题。 反正折腾了一番,终于跑出来了一个例子。这里把正确的例子写出来,方便大家参考。 首先要注意

Kimichat使用案例026:AI翻译英语PDF文档的3种方法

文章目录 一、介绍二、腾讯交互翻译TranSmart https://transmart.qq.com/三、沉浸式翻译三、谷歌网页翻译 一、介绍 短的文章,直接丢进kimichat、ChatGPT里面很快就可以翻译完成,而且效果很佳。但是,很长的PDF文档整篇需要翻译,怎么办呢? 二、腾讯交互翻译TranSmart https://transmart.qq.com/ 软件

用python写一个AI Agent对接企业微信上下游协同的案例

要实现一个AI Agent对接企业微信上下游协同,我们可以使用Python编写一个企业微信机器人,用于接收和处理来自企业微信的消息。在此示例中,我们将使用`wechatpy`库来实现企业微信机器人,并使用`requests`库实现与上下游系统的通信。 首先,确保安装了`wechatpy`和`requests`库: ```bash pip install wechatpy requests ``

[案例解析]山东首单跨境数据资产入表案例解析

“ 该案例实现了数据资产跨境的突破” 众所周知,自从我国《个护法》出台,加上后来对于数据出海的各种规定陆续出台,数据出海面临更加严格的监管,能够出海已经不容易,再能够在出海的基础上实现数据资产入表更是意义重大。 01   案例简介 —————————————————— 近日,在济南市大数据局、中国(山东)自贸试验区济南片区的指导下,山东产权交易集团旗下山

React18中各种Hooks用法总结( 内附案例讲解)

React中各种Hooks用法总结 内附案例讲解 一、useState useState 是一个 React Hook,它允许你向组件添加一个 状态变量。 import React, { FC, memo, useState } from 'react'import { MainContainer } from './style'interface IProps {children?:

《三国:谋定天下》成为了SLG游戏现象级的成功案例

原标题:《三国:谋定天下》引领SLG游戏新潮流,B站股价五个飙升了30%   易采游戏网6月23日:B站作为年轻人喜爱的文化社区和视频平台,再次用一款新的游戏证明了其在游戏发行领域的独到眼光与强大实力。最近大火的策略角色扮演游戏《三国:谋定天下》成为了现象级的成功案例,不仅游戏本身质量受到认可,而且在竞争激烈的iOS畅销榜上勇夺第三的位置,仅排在了资深巨头DNF手游和《王者荣耀》之后。更加引人注

JUnit最简单的测试案例

需要测试的类: package cn.edu.junit;public class Calculate {//加public int add(int a,int b){return a+b;}//减public int subtract(int a,int b){return a-b;}//乘public int multiply(int a,int b){return a*b;}//除pu