0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AIテニスプレイヤーとVRで試合しよう(MlAgents)-2

Last updated at Posted at 2023-03-06

前回記事の続きになります

MlAgentを用いたテニスのAI学習において報酬値の設定について解説します(MlAgent Pluginのサンプルシーンの解説になります)

報酬値の設定概要

報酬を設定するコンポーネントはballにアタッチします
ボールがラケット、壁に当たることが報酬に直結するからです

Header

TennisAgent型変数m_AgentA,BにラケットA,BのTennisAgentsコンポーネントを入れます。→ これにより報酬値,スコアをラケットに入れることができます。
列挙型変数FloorHitに最後にballが触れた床(かサービスショット)を記録します。
int型(bool型で良い気がする) lastAgentHit に最後に触れたラケット格納 0 → A ,1 → B

Header.cs
    public GameObject areaObject;//Agent A,Bのスクリプト取得のため
    public int lastAgentHit;//0はA 1はB  なぜboolにしない?
    public bool net;//サービスショットでネットに当たったか

    //
    public enum FloorHit
    {
        Service,
        FloorHitUnset,
        FloorAHit,
        FloorBHit
    }

    public FloorHit lastFloorHit;

    TennisArea m_Area;
    TennisAgent m_AgentA;//エピソードの開始終了を操作する
    TennisAgent m_AgentB;//エピソードの開始終了を操作する
    
    //ラケット内の関数を呼び出せるようにする
    void Start()
    {
        m_Area = areaObject.GetComponent<TennisArea>();
        m_AgentA = m_Area.agentA.GetComponent<TennisAgent>();
        m_AgentB = m_Area.agentB.GetComponent<TennisAgent>();
    }

報酬設定

Reset() :エピソードを終了する 
AgentAWins() 
ラケットAに報酬1を与えラケットBにペナルティ-1与え、スコア加算しエピソード終了
AgentBWins() 
ラケットBに報酬1を与えラケットAにペナルティ-1与え、スコア加算しエピソード終了

AgentWin.cs
    //得点が入ったもしくは 目的を達成しなかった場合エピソードの強制終了
    void Reset()
    {
        m_AgentA.EndEpisode();
        m_AgentB.EndEpisode();
        m_Area.MatchReset();
        lastFloorHit = FloorHit.Service;
        net = false;
    }
    //ラケットAに得点が入ったとき Aに報酬与えBにペナルティ与える その後エピソード終了
    void AgentAWins()
    {
        m_AgentA.SetReward(1);
        m_AgentB.SetReward(-1);
        m_AgentA.score += 1;
        Reset();

    }
    //ラケットBに得点が入ったとき Aに報酬与えBにペナルティ与える その後エピソード終了
    void AgentBWins()
    {
        m_AgentA.SetReward(-1);
        m_AgentB.SetReward(1);
        m_AgentB.score += 1;
        Reset();

    }

場外に球が出た時

Aの壁に球が当たったとき(場外)の勝敗判定
最後に触ったのがラケットA自身 → Bの勝ち
最後に触ったのがBでAのエリアでワンバンした → Bの勝ち
それ以外(ノーバウンドでBの球が場外に出た時)→ Aの勝ち

AgentWin.cs
    void OnCollisionEnter(Collision collision)
    {
        //壁,床に当たったとき (iWallは壁,床,ネットのTag)
        if (collision.gameObject.CompareTag("iWall"))
        {
            //Aの壁に当たったとき
            if (collision.gameObject.name == "wallA")
            {
                //Aが打った球がAの壁に当たった 又は Bが打った球がAのエリアで一回バウンドした Bの勝ち
                if (lastAgentHit == 0 || lastFloorHit == FloorHit.FloorAHit)
                {
                    //報酬,ペナルティ与えてエピソード終了
                    AgentBWins();
                }
                //ノーバウンドでBがAのエリア外に出したとき Aの勝ち
                else
                {
                    //報酬,ペナルティ与えてエピソード終了
                    AgentAWins();
                }
            }
        }
    }

自陣でパウンドしたとき

ラケットAが打った球が自陣でワンバウンドしたとき 
ラケットBからの球が自陣で二回バウンドしたとき
サービスショットが打てず(ラケットに触れず)自陣でバウンドしたとき
→ ラケットBの勝ち

最後に当たった床を更新 
サービスショットが成功したときnetをtrueに更新

AgentWin.cs
void OnCollisionEnter(Collision collision)
    {
        
        else if (collision.gameObject.name == "floorA")
        { 
             //自身が打った球が自陣でワンバウンドしたとき 相手からの球が自陣で二回バウンドしたとき サービスショットが自陣でバウンドしたとき
            if (lastAgentHit == 0 || lastFloorHit == FloorHit.FloorAHit || lastFloorHit == FloorHit.Service)
                {
                    AgentBWins();
                }
                //球を打ち返えせた時 サービスショットが成功したとき
                else
                {
                    //最後に当たった床を更新 service, B → A
                    lastFloorHit = FloorHit.FloorAHit;
                    //ネットに引っかからず通過した判定(サービスショット成功したか否か)
                    if (!net)
                    {
                        net = true;
                    }
                }
            }
        }
    

サービスショット失敗

Aがサービスショット時にネットに引っかかった → Bの勝ち
Bがサービスショット時にネットに引っかかった → Aの勝ち

net.cs
    else if (collision.gameObject.name == "net" && !net)
    {
        //サービスショット失敗した人判別
        if (lastAgentHit == 0)
        {
            AgentBWins();
        }
        else if (lastAgentHit == 1)
        {
            AgentAWins();
        }
    }

ラケットに触れた時

Aのラケットに連続して当たったとき → Bの勝ち

net.cs
    else if (collision.gameObject.name == "AgentA")
            {
                //ラケットに2回当たったとき
                if (lastAgentHit == 0)
                {
                    AgentBWins();
                }
                else
                {
                    //agent can return serve in the air
                    if (lastFloorHit != FloorHit.Service && !net)
                    {
                        net = true;
                    }
                //最後に触れたのがAと記録
                lastAgentHit = 0;
                //サービスショット,floorB → floorHitUnsetに変更(最後にラケットAかBに触れたこと記憶)
                lastFloorHit = FloorHit.FloorHitUnset;
                }
            }

まとめ

報酬を与えるパターンとして
自身依存
自身が打った球が相手エリアでバウンドし場外に出た → +1
相手の球を1バウンドで返せなかった → -1
サービスショットの球に触れず地面と接触した → -1
自身の球がネットに引っかかった → -1
球がラケットに二回触れた → -1
相手依存
相手が打った球が相手エリアの場外に出た → +1
相手が打った球が相手エリアでバウンドした → +1

得点のみが報酬につながっている
ボールの速度、ラケットとボールの距離などによる報酬は考慮されていない
動きはx,y及びラケットのz軸の回転のみ

今後

ラケットの動きにZ方向を追加(横方向)
ラケットの回転にY軸を追加
続きは以下の記事

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?