手把手带你用机器学习写unity AI

版权声明:本文为博主原创文章,未经博主允许不得转载。超级无敌帅哥陈 https://blog.csdn.net/ILYPL/article/details/78895217

2017unity机器学习社区挑战赛参加地址:https://connect.unity.com/challenges/ml-agents-1
win 10 上搭建Unity 机器学习环境:https://unity3d.college/2017/10/25/machine-learning-in-unity3d-setting-up-the-environment-tensorflow-for-agentml-on-windows-10/
中文版win 10上快速搭建GPU版本tensorflow:http://blog.csdn.net/ilypl/article/details/78165118
unity机器学习插件:https://github.com/Unity-Technologies/ml-agents

=====================正文分割=========================
直接打开上面下载的unity机器学习环境
这里写图片描述
里面有一些现成的demo,可以看一下这个插件到底能做什么。
这次皮皮陈带你做一个依靠角色视觉来寻找特定目标的demo这里写图片描述
我们先新建一个Plane作为地板:
地板大小各位随意,我设为10:这里写图片描述
然后新建玩家角色和不同的目标对象,我用胶囊来代表角色,红色球体代表目标,绿色球体作为干扰目标。
新建3个空对象,分别作为随机生成ball的管理器,Academy和Brain

这里写图片描述
====================角色对象的设置=======================
先把环境中的Directional Light调暗
这里写图片描述
然后新建一个Spotlight作为角色虚拟视线范围
这里写图片描述
调好Spotlight的方向,然后挂到角色上
这里写图片描述
同时把角色的tag改为Player,再f给角色添加Rigidbody
========================两个ball的设置======================
给两个ball添加颜色材质,同时分别设置两个ball 的layer和Tag
比如红球
这里写图片描述
===================激动人心的代码部分========================
1,在地板上随机生成两个ball:

times -= Time.deltaTime;
        if (times < 0)
        {
            int red_i = Random.Range(-50, 50);
            int red_j = Random.Range(-50, 50);
            int green_i = Random.Range(-50, 50);
            int green_j = Random.Range(-50, 50);

            GameObject red = (GameObject)Instantiate(red_ball);
            GameObject green = (GameObject)Instantiate(green_ball);

            red.transform.position = new Vector3(red_i,1, red_j);
            green.transform.position = new Vector3(green_i,1, green_j);

            times = 2f;

用Random.Range()随机出特定区间的数值。
2,两个ball与角色碰撞后消失,然后把reward传回到agent

private void OnCollisionEnter(Collision collision)
    {
        FindAgent player = GameObject.Find("player").GetComponent<FindAgent>();
        if (transform.gameObject.layer == LayerMask.NameToLayer("Green_ball"))
        {
            player.reward = -1f;
        }
        else
        {
            player.reward = +5f;
        }
        if (collision.gameObject.tag == "Player")
        {
            Destroy(ball);
        }
    }

3,生成角色虚拟视线

void DrawFieldOfView()
    {
        // 获得最左边那条射线的向量,相对正前方,角度是-45
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * eye_distance;
        for (int i = 0; i <= eye_angle; i++)
        {// 每条射线都在forward_left的基础上偏转一点,最后一个正好偏转90度到视线最右侧
            Vector3 v = Quaternion.Euler(0, (90.0f / eye_angle) * i, 0) * forward_left; ;

            // 创建射线
            Ray ray = new Ray(transform.position, v);
            RaycastHit hitt = new RaycastHit();
            // 射线只与两种层碰撞,注意名字和你添加的layer一致,其他层忽略
            int mask = LayerMask.GetMask("Wall", "Red_ball", "Green_ball");
            Physics.Raycast(ray, out hitt, eye_distance, mask);

            // Player位置加v,就是射线终点pos
            Vector3 pos = transform.position + v;
            if (hitt.transform != null)
            {
                // 如果碰撞到什么东西,射线终点就变为碰撞的点了
                pos = hitt.point;
            }
            // 从玩家位置到pos画线段,只会在编辑器里看到
            Debug.DrawLine(transform.position, pos, Color.red); ;

            // 如果真的碰撞到敌人,进一步处理
            if (hitt.transform != null && hitt.transform.gameObject.layer == LayerMask.NameToLayer("Red_ball"))
            {
                red_ball = hitt.transform.position;
            }
            if(hitt.transform != null && hitt.transform.gameObject.layer == LayerMask.NameToLayer("Green_ball"))
            {
                green_ball = hitt.transform.position;
            }
        }
    }

这里写图片描述
4,重点!agent的编写
InitializeAgent()类似Start()的初始化方法

public override void InitializeAgent()
    {
        red_ball = get_from_PlayerMove.red_ball;
        green_ball = get_from_PlayerMove.green_ball;        
    }

List CollectState()每一个训练相关的数值都要在这里,比如在这个demo中,我们要角色自动寻找目标同时还要区分干扰目标,那我们就要让Brain知道,角色的移动,朝向,所以添加了角色的position和rotation,还有两个ball的坐标。另外需要理解一点,不管你什么机器学习,一定是数值到算法层,比如图像识别,最后都是把图像降维成数值再处理。所以这里CollectState()内传到Brain的都是单个数值,比如要传角色的位置,传Vector3类型是不行的,因为Vector3是有3个值得向量(x,y,z),所以你要传角色位置,你把3个方向的数值一个个传。

public override List<float> CollectState()
    {
        List<float> state = new List<float>();
        state.Add(player.transform.rotation.x);
        state.Add(player.transform.position.z);

        state.Add(red_ball.x);
        state.Add(red_ball.z);

        state.Add(green_ball.x);
        state.Add(green_ball.z);

        return state;
    }

AgentStep(float[] act)每一步的行动,比如在这个demo中,我们要角色一直移动,所以在AgentStep(float[] act)中添加角色移动,另外这里的act参数是怎么回事呢。act有两种模式,一种是离散,一种是连续。比如在这个demo中我把act设置为离散,因为角色的行为无非是左转右转和向前,那么我们把act为0时作为角色向前的信号,1和2作为角色向左和向右,用离散就合适了,离散和连续在Brain中的action设置即可。

public override void AgentStep(float[] act)
    {
        reward = -0.01f;
        int action = Mathf.FloorToInt(act[0]);
        if (action == 0)
        {
            player.transform.Translate(0, 0, 1, Space.Self);
        }
        if (action == 1)
        {
            player.transform.Rotate(0, -10, 0, Space.Self);
        }
        if (action == 2)
        {
            player.transform.Rotate(0, 10, 0, Space.Self);
        }
        if (player.transform.position.x >= 50 || player.transform.position.x <= -50 || player.transform.position.z >= 50 || player.transform.position.z <= -50)
        {
            reward = -10f;
            done = true;
        }
    }

这里写图片描述
AgentReset()重置方法,就是一次训练结束了,环境怎么重置。比如我们这个demo,什么时候需要重置?当然是角色跑到地板意外的地方啦,一旦角色离开地板,那么我们就重置环境,把角色重置在地板的中心位置,并给予角色惩罚,让它知道它的活动范围只能在地板范围上。

public override void AgentReset()
    {
        transform.position = new Vector3(0,1,0);
    }

**项目说明:**Findagent,Player_Move,AgentMonitor脚本挂在角色上
Destroy_ball挂在两个ball上
RandomSpawn挂在随机生成ball的管理器上
FindAcademy挂在FindAcademy上
Brain挂在FindBrain上
Academy和Brain的参数设置:
这里写图片描述

这里写图片描述

具体参数的意思请参考:http://blog.csdn.net/ilypl/article/details/78387390
具体打包设置请参考:http://blog.csdn.net/ilypl/article/details/78309220

本文项目代码:https://gitee.com/CCHChenChangHong/unityDeAI/tree/master/unity强化学习基础环境/Assets/Scripts
日常深推一波公众号:
这里写图片描述

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页