LoginSignup
10
11

More than 3 years have passed since last update.

自作シンプル物理演算ゲームを強化学習で攻略させる【Unity ML-Agents v3.0】

Last updated at Posted at 2018-04-25

自作シンプル物理演算ゲームを強化学習で攻略させる【Unity ML-Agents v3.0】

以前から機械学習などに興味があったものの、難しそうでためらっていたのですが、Unityで強化学習ができるML-Agentsというものがあると知り、理論についてほぼ何も知識のない自分でも、なんだかいけそうだと思って試してみました。

スクリーンショット 2018-04-25 23.17.07.png

まずは公式サンプルゲームを動かしてみたのですが、何か自作ゲームを学習させてみないと理解が進まないと思い、なるべくシンプルで最小限の構成になるようなゲームを考えました。そして、せっかくUnityを使うので物理演算も入れたい。

bounce-player.mov.gif

そこで考えたのが、上から降ってくる物体をヘディングのようにバウンドさせるゲームです。人間が手動でプレイするとこんな感じになります。これを強化学習させて攻略させてみました。

これ以下の内容はML-Agentsの公式サンプルを動かすところまで出来る方が対象です。Qiitaにも他の方が書いた記事があるので参考にして下さい。

ゲームを作る

スクリーンショット 2018-04-25 10.18.07.png

左右の壁とボール(小)、Player(ボール大)のみです。ボールは反発係数1にしてあるのでよく弾みます。というか、弾むたびに微妙に動きが大きくなります。なぜ?でも割と面白いのでそのままにしています。何度も弾ませたり、壁を使って連続で弾ませると、壁を超えてしまいます。それをクリア条件にすればよかった、と後から思いつきました、が本題ではないので省いてます。

このゲームのセットを1つのAgentとして、8つのAgentsを配置し学習を開始します。

シーンをスタートすると、Playerの上からボールが降ってきて、下の大きいボール(Player)で跳ね返します。操作は左右キーのみです。
→MoveAgentに記述

ボールをPlayerのy座標よりも下に落とすと、Done()が呼ばれてリセットされます。
→AgentActionに記述

取得する状態数は1つだけ。ボールとPlayerのx座標の相対位置を取得します。
→CollectObservations()に記述

報酬はボールを跳ね返したら1点追加します。
→OnCollisionEnter()にSetReward(1f)を記述

ボールを落とした場合に報酬を減らす方法も試しましたが、うまくいかなかったのでコメントアウトしています。

BounceAgent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BounceAgent : Agent {

    public float speed;
    public GameObject ball;

    public override void CollectObservations()
    {
        AddVectorObs(ball.transform.position.x - gameObject.transform.position.x);

        //状態数は少なくても上手くいったため、削除
        // AddVectorObs(gameObject.transform.position);
        // AddVectorObs(ball.transform.GetComponent<Rigidbody>().velocity);
    }

    void OnCollisionEnter(Collision col)
    {
        if (col.gameObject.tag == "ball")
        {
            //衝突する度に報酬を与える
            SetReward(1f);
        }
    }
    public override void AgentAction(float[] vectorAction, string textAction)
    {
        MoveAgent(vectorAction);

        //以下のような報酬の与え方でもよいが、単に衝突時に報酬を与えてもうまくいくため削除
        // if (Mathf.Abs(ball.transform.position.x - gameObject.transform.position.x) < 0.2f)
        // {
        //     SetReward(0.1f);
        // }
        // else{
        //     SetReward(-0.1f);
        // }

        if ((ball.transform.position.y - gameObject.transform.position.y) < 0)
        {
            //ゲームオーバー時に報酬を引く方法だとうまくいかなかった
            // SetReward(-10f);
            Done();
        }

        Monitor.Log ("Reward", reward);
    }

    public override void AgentReset()
    {
        ball.transform.position = new Vector3(Random.Range(-0.2f,0.2f),1.5f,0) + gameObject.transform.position;
        ball.GetComponent<Rigidbody>().velocity = new Vector3(0f, 0f, 0f);
    }

    public void MoveAgent(float[] act)
    {
        int action = Mathf.FloorToInt(act[0]);

        //アクション
        if (action == 1)
        {
            transform.Translate(-speed, 0, 0);
            // AddReward(-0.01f);
        }
        if (action == 2)
        {
            transform.Translate(speed, 0, 0);
            // AddReward(-0.01f);
        }

        //移動制限
        if (transform.localPosition.x < -1.5f)
        {
            transform.localPosition = new Vector3(-1.5f, 0, 0);
        }
        if (1.5f < transform.localPosition.x)
        {
            transform.localPosition = new Vector3(1.5f, 0, 0);
        }
    }
}

学習結果

スクリーンショット 2018-04-25 8.56.12.png

10万回学習させたところ、7回ぐらい跳ね返せるようになりました。

Bounce_learned_trim.mov.gif

スクリーンショット 2018-04-25 8.57.02.png

その他の値はこのようになっています。

改良点と課題

今回の学習結果では、ボールのx座標に追従するような動きになっています。このような動きは、わざわざ強化学習でなく、コードを書くだけでも似たような動きが実現できると思います。

より人間的な動きとしては、落下地点を予測してあらかじめ移動しておくものになるはずです。そのために、状態数や報酬の与え方を工夫する必要がありそうです。

制作物

macでとりあえず動きを見たい時用。
http://mixsandwich.info/Bounce.zip

ソースコードです。
https://github.com/mixsandwich/Bounce

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11