DEV Community

blacOOil
blacOOil

Posted on

Machine learning ขับรถใน Unity ด้วย Python


self-driving car ในเกมมีความสามารถในการเรียนรู้ปรับตัว และสามารถขับรถให้เก่งขึ้นได้จากการใช้ machine learning ถายในเกมของ Unity

ขั้นแรก ตัวรถต้องสามารถ ขยับได้ด้วยตัวเองก่อนโดยใช้โคด

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarDriver : MonoBehaviour {

    #region Fields
    private float speed;
    public float speedMax = 70f;
    private float speedMin = -50f;
    public float acceleration = 30f;
    public float brakeSpeed = 100f;
    private float reverseSpeed = 30f;
    private float idleSlowdown = 10f;

    private float turnSpeed;
    public float turnSpeedMax = 300f;
    private float turnSpeedAcceleration = 300f;
    private float turnIdleSlowdown = 500f;

    private float forwardAmount;
    private float turnAmount;

    private Rigidbody carRigidbody;
    #endregion

    private void Awake() {
        carRigidbody = GetComponent<Rigidbody>();
    }

    private void Update() {
        if (forwardAmount > 0) {
            // Accelerating
            speed += forwardAmount * acceleration * Time.deltaTime;
        }
        if (forwardAmount < 0) {
            if (speed > 0) {
                // Braking
                speed += forwardAmount * brakeSpeed * Time.deltaTime;
            } else {
                // Reversing
                speed += forwardAmount * reverseSpeed * Time.deltaTime;
            }
        }

        if (forwardAmount == 0) {
            // Not accelerating or braking
            if (speed > 0) {
                speed -= idleSlowdown * Time.deltaTime;
            }
            if (speed < 0) {
                speed += idleSlowdown * Time.deltaTime;
            }
        }

        speed = Mathf.Clamp(speed, speedMin, speedMax);

        carRigidbody.velocity = transform.forward * speed;

        if (speed < 0) {
            // Going backwards, invert wheels
            turnAmount = turnAmount * -1f;
        }

        if (turnAmount > 0 || turnAmount < 0) {
            // Turning
            if ((turnSpeed > 0 && turnAmount < 0) || (turnSpeed < 0 && turnAmount > 0)) {
                // Changing turn direction
                float minTurnAmount = 20f;
                turnSpeed = turnAmount * minTurnAmount;
            }
            turnSpeed += turnAmount * turnSpeedAcceleration * Time.deltaTime;
        } else {
            // Not turning
            if (turnSpeed > 0) {
                turnSpeed -= turnIdleSlowdown * Time.deltaTime;
            }
            if (turnSpeed < 0) {
                turnSpeed += turnIdleSlowdown * Time.deltaTime;
            }
            if (turnSpeed > -1f && turnSpeed < +1f) {
                // Stop rotating
                turnSpeed = 0f;
            }
        }

        float speedNormalized = speed / speedMax;
        float invertSpeedNormalized = Mathf.Clamp(1 - speedNormalized, .75f, 1f);

        turnSpeed = Mathf.Clamp(turnSpeed, -turnSpeedMax, turnSpeedMax);

        carRigidbody.angularVelocity = new Vector3(0, turnSpeed * (invertSpeedNormalized * 1f) * Mathf.Deg2Rad, 0);

        if (transform.eulerAngles.x > 2 || transform.eulerAngles.x < -2 || transform.eulerAngles.z > 2 || transform.eulerAngles.z < -2) {
            transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
        }
    }

    //private void OnCollisionEnter(Collision collision) {
      //  if (collision.gameObject.layer == GameHandler.SOLID_OBJECTS_LAYER) {
        //    speed = Mathf.Clamp(speed, 0f, 20f);
        //}
    //}

    public void SetInputs(float forwardAmount, float turnAmount) {
        this.forwardAmount = forwardAmount;
        this.turnAmount = turnAmount;
    }

    public void ClearTurnSpeed() {
        turnSpeed = 0f;
    }

    public float GetSpeed() {
        return speed;
    }


    public void SetSpeedMax(float speedMax) {
        this.speedMax = speedMax;
    }

    public void SetTurnSpeedMax(float turnSpeedMax) {
        this.turnSpeedMax = turnSpeedMax;
    }

    public void SetTurnSpeedAcceleration(float turnSpeedAcceleration) {
        this.turnSpeedAcceleration = turnSpeedAcceleration;
    }

    public void StopCompletely() {
        speed = 0f;
        turnSpeed = 0f;
    }

}

Enter fullscreen mode Exit fullscreen mode

และใน version ที่พึ่งได้เล่นไป จะเป็น version แรกที่รถ จะสามารถ ขับได้โดยผู้เล่นผ่านโค้ดของ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarAi : MonoBehaviour
{
    [SerializeField] private Transform targetPositionTranform;
    private CarDriver CarDriver;
    private Vector3 TargetPosition;
    // Start is called before the first frame update
    private void Awake()
    {
        CarDriver = GetComponent<CarDriver>();
    }

    // Update is called once per frame
    void Update()
    {
        SetTargetPosition(targetPositionTranform.position);
        float forwardAmount = 0;
        float turnAmount = 0f;

        float reachTargetDistance = 7f;
        float distanceToTarget = Vector3.Distance(transform.position, TargetPosition);
        if(distanceToTarget > reachTargetDistance) {
            Vector3 dirToMovePosition = (TargetPosition - transform.position).normalized;
            float dot = Vector3.Dot(transform.forward, dirToMovePosition);

            if (dot > 0)
            {
                forwardAmount = 1f;
            }
            else
            {
                float reversDistance = 25f;
                if(distanceToTarget > reversDistance)
                {
                    forwardAmount = 1f;
                }
                else
                {
                    forwardAmount = -1f;
                }

            }

            float angleToDir = Vector3.SignedAngle(transform.forward, dirToMovePosition, Vector3.up);
            if (angleToDir > 0)
            {
                turnAmount = 1f;
            }
            else
            {
                turnAmount = -1f;
            }
        }
        else
        {
            if(CarDriver.GetSpeed() > 15f)
            {
                forwardAmount = -1f;
            }
            else
            {
                forwardAmount = 0f;
            }

            turnAmount = 0f;
        }

        CarDriver.SetInputs(forwardAmount, turnAmount);
    }
    public void SetTargetPosition(Vector3 TargetPosition)
    {
        this.TargetPosition = TargetPosition;
    }
}
Enter fullscreen mode Exit fullscreen mode

นำไปใส้เป็น component ของรถให้สามารถขับได้

จากนั้นเครื่องมือต่อไปที่ใช้ คือ asset ML-agent ที่เป็น asset ที่สามารถสร้าง agent ซึ่งจะเป็น component ของ ai ที่เราต้องการผ่าน GitHub

GitHub logo Unity-Technologies / ml-agents

The Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents using deep reinforcement learning and imitation learning.

Unity ML-Agents Toolkit

docs badge

license badge

(latest release) (all releases)

The Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents. We provide implementations (based on PyTorch) of state-of-the-art algorithms to enable game developers and hobbyists to easily train intelligent agents for 2D, 3D and VR/AR games. Researchers can also use the provided simple-to-use Python API to train Agents using reinforcement learning imitation learning, neuroevolution, or any other methods. These trained agents can be used for multiple purposes, including controlling NPC behavior (in a variety of settings such as multi-agent and adversarial), automated testing of game builds and evaluating different game design decisions pre-release. The ML-Agents Toolkit is mutually beneficial for both game developers and AI researchers as it provides a central platform where advances in AI can be evaluated on Unity’s rich environments and then…

เมือทำการ install เสร็จแล้วจึงสามารถใช้ ML-agents Component ได้

โดยตัว Ai จะมีการทำ Reinforcement learning

โดยจะมีรำดับการทำงานที่

1.Observation
ทำการสังเกตุ enivromentในเกมเพื่อเก็บข้อมูลประกอบการทำงานในขั้นต่อไป

public override void CollectObservations(VectorSensor sensor)(
       sensor.AddObervation(transform.position);//ให้ ai สั่งเกตุตำแหน่งของตัวรถ
       sensor.AddObervation(target.Transform.position);//ให้ ai สังเกตุตำแหน่งของเป้าหมาย
Enter fullscreen mode Exit fullscreen mode

2.Decision
การตัดสินใจจากองค์ประกอบต้างๆที่ได้เก็บข้อมูลมา

public override void OnActionReceived(ActionBuffers action){
      float moveX = action.ContinuousActions[0];
      float moveZ = action.ContinuousActions[1];// ให้aiทำการเคลื่อนที่เข้าหาเป้าหมายมากขึ้น 
Enter fullscreen mode Exit fullscreen mode

3.Reward
รับรางวัลเพื่อพิสูจว่าสิ่งที่ Ai ทำไปนั้นเป็นสิ่งที่ถูกต้องตามที่กำหนดไว้ซึ่งหากผิดจะต้องกับไปที่กระบวนการเเรกไหม่ทั้งหมด

 private void OnTriggerEnter(Collider other){
              SetReward(1f); // เมื่อรถชนวัตถุที่เราต้องการแล้วจึงจะได้รางวัล
              EndEpisode(); 
Enter fullscreen mode Exit fullscreen mode

เมือทำการCode สำเร็จ รถจะสามารถขับได้ด้วยตัวเอง>-<

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay