前回記事の続きになります
MlAgentを用いたテニスのAI学習において報酬値の設定について解説します(MlAgent Pluginのサンプルシーンの解説になります)
報酬値の設定概要
報酬を設定するコンポーネントはballにアタッチします
ボールがラケット、壁に当たることが報酬に直結するからです
Header
TennisAgent型変数m_AgentA,BにラケットA,BのTennisAgentsコンポーネントを入れます。→ これにより報酬値,スコアをラケットに入れることができます。
列挙型変数FloorHitに最後にballが触れた床(かサービスショット)を記録します。
int型(bool型で良い気がする) lastAgentHit に最後に触れたラケット格納 0 → A ,1 → B
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与え、スコア加算しエピソード終了
//得点が入ったもしくは 目的を達成しなかった場合エピソードの強制終了
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の勝ち
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に更新
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の勝ち
else if (collision.gameObject.name == "net" && !net)
{
//サービスショット失敗した人判別
if (lastAgentHit == 0)
{
AgentBWins();
}
else if (lastAgentHit == 1)
{
AgentAWins();
}
}
ラケットに触れた時
Aのラケットに連続して当たったとき → Bの勝ち
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軸を追加
続きは以下の記事