1、ML-Agents库介绍
ML-Agents库,训练用于2D、3D、VR/AR游戏的智能agent,经过训练的agent可用于多种目的,包括:控制NPC行为(采用各种设置,例如多个agent和对抗)、对游戏内部版本进行自动化测试、以及评估不同游戏设计决策的预发布版本
2、Unity中创建立方体、球和地面
球——玩家移动和AI控制
立方体——需要球移动到的位置,当移动到距离达到指定阈值时为移动成功
成功目标:将球移动到立方体位置
失败条件:球出界(落出地面)
3、创建脚本
创建RollerAgent脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 导入ML-Agents库
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
public class RollerAgent : Agent
{
// 要移动到目标立方体的位置
public Transform target;
// 刚体力的倍数
public float forceMultiplier = 10.0f;
private Rigidbody rigidbody;
// Start is called before the first frame update
void Start()
{
rigidbody = this.GetComponent<Rigidbody>();
}
// 每次训练开始时调用,可以用来初始化 AI和环境
// 调用场景中每个Agent,开始函数
public override void OnEpisodeBegin()
{
//base.OnEpisodeBegin();
// 当球出界,重置球的位置到原点
if(this.transform.localPosition.y < 0.5)
{
this.rigidbody.velocity = Vector3.zero;
this.rigidbody.angularVelocity = Vector3.zero;
this.transform.localPosition = new Vector3(0.0f, 0.5f, 0.0f);
}
// 目标立方体随机出现在场地某个位置
target.localPosition = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4); // [-4, 4 之间随机生成立方体] Random.value值为[0~1]
}
// 每走一步收集AI所需要的矢量信息,AI通过这些信息了解现在的环境
// sensor.AddObservation() 方法收集所需数据
// 通过调用场景中每个Agent的CollectObservations(VectorSensor sensor)函数,收集有关场景的信息,更新他们的Sensor并收集 观测向量
public override void CollectObservations(VectorSensor sensor)
{
//base.CollectObservations(sensor);
// sensor.AddObservation() 方法收集所需数据
// 收集目标正方体 和 球的位置
sensor.AddObservation(target.localPosition);
sensor.AddObservation(this.transform.localPosition);
// 收集球的速度
sensor.AddObservation(this.rigidbody.velocity.x);
sensor.AddObservation(this.rigidbody.velocity.z);
}
/// <summary>
/// 指定每一步的AI的行为
/// ML-Agents支持两种类型的操作:连续和离散。通过actions.ContinuousActions数组来记录所有连续的操作
/// 需要在BehaviorParameters组件中设置连续型操作个数和离散型操作个数,以及每个离散型操作的可能数
/// 在ppo算法中数组内的每一个值都在[-1, 1]之间
///
/// actions.DiscreteActions数组记录所有离散 的操作,每个值时0~x中的整数,x表示该操作的所有可能性的总数(例如 :实现跳跃 0代表不跳 ,1代表跳)
/// 调用每个Agent的OnActionReceived()函数,传递Agent根据策略选择的动作
/// </summary>
/// <param name="actions"></param>
public override void OnActionReceived(ActionBuffers actions)
{
//base.OnActionReceived(actions);
Vector3 controlSignal = Vector3.zero;
controlSignal.x = actions.ContinuousActions[0];
controlSignal.z = actions.ContinuousActions[1];
// 将当前动作赋值给球
this.rigidbody.AddForce(controlSignal * forceMultiplier);
// rewards
// 判断球和目标立方体之间 的距离
float distanceToTarget = Vector3.Distance(this.transform.localPosition, target.localPosition);
// 如果距离靠近
if (distanceToTarget < 1.42f)
{
// SetReward() 本次决策的奖励值设为多少 AddReward() 本次决策的奖励值加多少
this.SetReward(1.0f);
// 结束本次训练,进入初始化状态再次进行训练
this.EndEpisode();
}
// 当掉出场地时,结束本次训练,进入初始化状态再次进行训练
if(this.transform.localPosition.y < 0)
{
// 用EndEpisode()结束这次训练,然后进入初始化状态再次进行训练
// 已经到达设定的Max Step或Agent将自身标记为EndEpisode(),调用 Agent的OnEpisodeBegin()函数,结束
this.EndEpisode();
}
}
// 启发函数,增加手动测试功能,实现玩家可以通过自己的输入控制AI,方便排查环境中是否有bug,是否可以正常运行
public override void Heuristic(in ActionBuffers actionsOut)
{
//base.Heuristic(actionsOut);
// 人工操作
ActionSegment<float> continuousActionsOut = actionsOut.ContinuousActions;
// 按 a/d 左右 w/s 上下
continuousActionsOut[0] = Input.GetAxis("Horizontal");
continuousActionsOut[1] = Input.GetAxis("Vertical");
}
}
将脚本加入到球体上,此时球体会增加脚本
Behavior Parameters(行为参数)——每个代理必须有一个行为。行为决定了代理如何做出决策
Decision Requester——用于AI从模型申请动作。设置决策间隔太短的话会占很多计算资源,在不影响AI效果的情况下越大越好。
4、对整体流程的总结
OnEpisodeBegin——训练初始化AI和环境,训练开始时调用
CollectObservations——收集每一步AI的矢量信息
使用 sensor.AddObservation,收集所需数据
OnActionReceived——行为有连续和离散之分,将行为赋值给AI,并判断结束本次训练的条件,
设置奖励值SetReward()
EndEpisode——结束本次训练,进入初始化后再次进行训练
Heuristic——实现玩家通过自己的输入控制AI,方便排查环境是否有bug