B站视频: 年轻人的第一个游戏AI:Unity强化学习工具MLAgents全流程实例教程.
ml-agents,选择版本0.14.0。
下载完后,进入ml-agents-envs目录,例如我的路径为:D:\simws\lib\ml-agents-0.14.0\ml-agents-envs>。
cmd后,运行pip install -e .(-e后有一个点)。
再进入ml-agents目录,再执行pip install -e .
执行mlagents-learn --help测试是否安装成功。
如图所示,即安装成功!
https://www.jianshu.com/p/e21a32e67b70.
选择的版本是2019.4.16https://unity.cn/releases/lts.
打开Unity Hub: 选择项目——>新建:
选择3D,输入名称,选择路径。
在项目中,选择Window——>package manager——>左上角+号——>add package from disk。
选择下载的ML-agents包里com.unity.ml-agents里的package.json文件。
2.2.1 新建空的游戏对象
在Hierarchy空白区域右键鼠标,选择Create Empty。在右侧改名字为JuggleArea
2.2.2 在空对象里新建一个平面
在JuggleArea上右键,选择3D Object——>Plane.改名字为Ground,x轴和y轴都设置为2倍。
2.2.3 新建一个球,3D Object——>Sphere,改名字为Ball。给它Y轴坐标一个5。
2.2.4 新建一个Capsule,3D Object——>Capsule,表示球员,改名字为Player。给它Y轴坐标一个1。
2.3.1 在Assets下新建文件夹Materials存放材质文件。
2.3.2 在Materials目录下右键——>Create,新建一个Material,修改名字为Ball,修改颜色为红色。
将材质拖动到球上,即可给球添加上材质。
2.3.3
Ctrl+D复制材质。改名字为Player,选一个颜色,再复制一个材质,改名字为Ground选择一个颜色。
2.3.4 给Ball和Player分别设置刚体(Rigibody).
选择Ball,选择Add Component,
选择RigidBody.
此时点击Play按钮,发现弹性不是很好。需要添加物理材质。
2.3.5 添加物理材质。
再Materials区域,右键——>Create——>Physic Materials,修改名字为BallPhy,弹性修改为0.9。
Ctrl+D复制一个物理材质,修改名字为PlayPhy,弹性设置为0.5.将材质给物体都添加上即可。
为了防止Player出现奇怪的姿势,将它的X轴和Y轴的角度冻结。
第一步:CollectObservations()收集游戏的各种环境。例如球的位置,球的速度,球员的位置,球员的速度等,将这些信息给到MLAgents。
第二步:MLAgents根据环境信息计算一个操作数组。
第三步:将操作数组发送给AgentAction()。它实现整个游戏的一个操作。在本例中主要实现移动球员,判断颠球,以及判断游戏是否结束。如果结束了,就调用AgentRest()重置环境。没有结束就从第一步循环下去。
在Assets下新建Scripts文件夹,在文件夹下新建一个C#脚本JuggleAgent,并将JuggleAgent拖到Player上。在Assets下新建Prefabs文件夹。将JuggleArea拖进去。这样就成了一个预设。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MLAgents;
using TMPro;
public class JuggleAgent : Agent
{
public Rigidbody ball;
Rigidbody player;
public float speed = 30.0f;
float diff = 0.0f; // 球员当前的y轴与上一步的的差异
float previousDiff = 0.0f; // 保留了上一个diff
float previousY = 5.0f; // 球员上一步的y轴的值
bool collied = false;
private void OnCollisionEnter(Collision collision)
{
if(collision.rigidbody == ball)
{
collied = true;
}
}
public override void InitializeAgent()
{
player = this.GetComponent<Rigidbody>();
}
// 观察到的场景信息
public override void CollectObservations()
{
AddVectorObs(ball.transform.localPosition); // 球的位置 3维
AddVectorObs(ball.velocity); // 球的速度, 3维
AddVectorObs(ball.rotation); // 球的角度, 4维
AddVectorObs(ball.angularVelocity); // 球的角速度,3维
// 球的信息 3+3+4+3 = 13
AddVectorObs(player.transform.localPosition);
AddVectorObs(player.velocity);
AddVectorObs(player.rotation);
AddVectorObs(player.angularVelocity);
// 球+球员信息:13 * 2 =26 维数据
}
public override void AgentAction(float[] vectorAction)
{
Vector3 controlSignal = Vector3.zero;
controlSignal.x = vectorAction[0];
controlSignal.z = vectorAction[1];
// 先判断Y轴是不是等于1(在地面,因为设置的地面高度为1),只有在地面的时候才能起跳,不在地面不能跳。
if(player.transform.localPosition.y == 1.0f)
{
controlSignal.y = vectorAction[2] * 10.0f;
}
player.AddForce(controlSignal * speed);
// 下面是判断球员是否颠球成功。球从上往下掉,Y轴减小,颠球成功,则Y轴变大,所以在Y先变小,后变大的时候,表示颠球成功,及previousDiff<0,diff>0的时候。
diff = ball.transform.localPosition.y - previousY;
// 为了防止球员拿着球上下移动,所以加了一个碰撞检测collied由OnCollisionEnter()方法得出。
if(diff > 0.0f && previousDiff < 0.0f && collied)
{
AddReward(0.1f);
}
collied = false;
previousDiff = diff;
previousY = ball.transform.localPosition.y;
// 如果球低于1.5,即接不到球,或者球员跑出了边界(场地为20*20,以中心点为(0,0),则位置绝对值大于10表示出街),判定游戏结束。
if(ball.transform.localPosition.y < 1.5f || Mathf.Abs(player.transform.localPosition.x) > 10.0f || Mathf.Abs(player.transform.localPosition.z) > 10.0f)
{
Done();
}
}
// 重置方法,重置时,让球在球员头顶Y=5处,以(0,0)为圆心,5-10处随机掉落。
public override void AgentReset()
{
ball.transform.localPosition = new Vector3(Random.value * 10 - 5, 5.0f, Random.value * 10 - 5);
ball.velocity = Vector3.zero;
ball.rotation = Quaternion.Euler(Vector3.zero);
ball.angularVelocity = Vector3.zero;
player.transform.localPosition = Vector3.up;
player.velocity = Vector3.zero;
player.rotation = Quaternion.Euler(Vector3.zero);
player.angularVelocity = Vector3.zero;
diff = 0.0f;
previousDiff = 0.0f;
previousY = 5.0f;
collied = false;
}
public override float[] Heuristic()
{
float[] vectorAction = new float[3];
vectorAction[0] = Input.GetAxis("Horizontal");
vectorAction[1] = Input.GetAxis("Vertical");
vectorAction[2] = Input.GetAxis("Jump");
return vectorAction;
}
}
在CollectObservations()方法中,环境信息是26维的。在Prefabs中双击JuggleArea。选择Player——>Behavior Parameters——>Vector Observation-——>Space Size设置为26。将名字改为JuggleBrain,将动作改为连续Continuous,动作空间改为3。AgentAction()方法即配置的3维操作数据。
在Assets目录下新建config文件夹,新建文件trainer_config.yaml
default:
trainer: ppo
batch_size: 16
beta: 5.0e-3
buffer_size: 256
epsilon: 0.2
hidden_units: 128
lambd: 0.95
learning_rate: 3.0e-4
learning_rate_schedule: linear
max_steps: 100.0e5
memory_size: 256
normalize: false
num_epoch: 3
num_layers: 4
time_horizon: 64
sequence_length: 64
summary_freq: 10000
use_recurrent: false
vis_encode_type: simple
reward_signals:
extrinsic:
strength: 1.0
gamma: 0.99
在Assets下cmd运行mlagents-learn config\trainer_config.yaml --run-id=playball --train
在unity中点击开始按钮。
日志文件在Assets/summaries文件夹。
cd 到Assets
tensorboard --logdir=summaries
输出
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.0.2 at http://localhost:6006/ (Press CTRL+C to quit)
可在http://localhost:6006/查看训练数据。