27 KiB
Aimbot-ParallelEnv
これはUnity ML-Agentsに基づいたFPSゲームのマルチエージェントトレーニング環境で、特定の目標と対応する難易度を生成し、エージェントのアクションに基づいて報酬をフィードバックして強化学習エージェントをトレーニングするための環境です。
Description
プロジェクトはML-Agents 2.0.1を基にして、Unity 2021.3.14f1を使用して開発されています。 Python側では、mlagents-envs 0.30.0を使用してゲーム環境と通信を行います。
Quick start
プロジェクトAimbot-PPOでは、この環境を使用してPPOアルゴリズムのトレーニングが行われています。具体的な使用例はAimbotEnv.pyで記載されています。
ゲーム環境の概要
本環境は初代Doomの基本モードを模したFPSゲームで、エージェントには異なる目標が与えられます。エージェントは目標の種類と位置、自身の状態に基づいて、撃つかどうか、撃つ方向、移動方向を決定します。この環境には2つのモードと3種類の目標があります:訓練モードとテストモード、そしてFreeTarget
、GotoTarget
、AttackTarget
です。FreeTarget
では、エージェントは武器を使ってエリア内のすべての敵を倒す必要があります。GotoTarget
とAttackTarget
では、エリア内に目標ブロックが生成され、エージェントはそれに応じた異なる行動を取る必要があります。さらに、GotoTarget
とAttackTarget
では、生成される目標ブロックの構造に応じて5つの異なる難易度に分けられています。
ゲームの概要
この環境は初代Doomの基本モードを模したFPSゲームで、エージェントには異なる目標が与えられます。エージェントは目標の種類と位置、自身の状態に基づいて、撃つかどうか、撃つ方向、移動方向を決定します。この環境には2つのモードと3種類の目標があります:訓練モードとテストモード、そしてFreeTarget
、GotoTarget
、AttackTarget
です。FreeTarget
では、エージェントは武器を使ってエリア内のすべての敵を倒す必要があります。GotoTarget
とAttackTarget
では、エリア内に目標ブロックが生成され、エージェントはそれに応じた異なる行動を取る必要があります。さらに、GotoTarget
とAttackTarget
では、生成される目標ブロックの構造によって、5つの異なる難易度に分けられています。
モード
訓練モードとテストモードは、Python側と通信が必要で、この環境から観測されたObservationに基づいてActionを生成し、環境にフィードバックします。環境を起動する際、ゲーム内でユーザーが操作してモードを選択する必要があります。ML-Agentsにはタイムリミットが存在するため、45秒以内にモードを選択する必要があります。45秒を超えると、自動的にゲーム環境から退出します。
訓練モード
訓練モードに入る前に、手動でスタート画面からTrain-Free
、Train-Goto
、Train-Attack
(単一モードの訓練)またはTrain-Mix
(全モードの訓練)の中から選択する必要があります。選択後、訓練モードに入りますが、この選択は後で変更できません。複数の難易度がある目標ブロックTrain-Goto
とTrain-Attack
のモードでは、スタート画面に異なる難易度の目標ブロックを生成する確率を調整するパネルが画面の左上にあります。デフォルトでは、各難易度の生成確率は均等となっています。このパネルでは、各モードの難易度の合計確率が1になるように、一つの難易度の確率を調整すると他の難易度の確率が自動的に調整されます。ユーザーは右側のロックボタンをクリックして、特定の難易度の確率をロックまたは解除することができます。このパネルは訓練モードの実行中にも表示され、ユーザーが訓練中に確率パネルを調整すると、次の目標ブロックの生成時に適用されます。
テストモード
テストモードでは、ユーザーが手動でエージェントに命令を指定する必要があります。画面右上のメニューをクリックすることで、目標ブロックや敵を生成したり、目標モードを切り替えたりすることができます。このモードでは、訓練モードにはない新しいターゲットStayTarget
が追加されています。ユーザーが特定の目標を指定しない場合やゲームをクリアした場合、StayTarget
がエージェントに割り当てられます。右上隅のメニューをクリックすると、マウスモードを切り替えて敵(Enemy)を生成するモードや、特定の難易度のターゲットブロックを生成するモードに変更できます。マウスがブロック生成モードで、マウスをゲームエリアに移動すると、生成するオブジェクトのプレビューが表示され、マウスの右クリックで指定した位置に対応する敵やブロックを生成できます。ターゲットブロックを生成すると、対応するターゲットが自動的にエージェントに割り当てられます。FreeTarget
を割り当てる必要がある場合は、FreeMode
ボタンをクリックします。ClearGame
をクリックすると、すべての敵とブロックがクリアされ、エージェントのターゲットがStayTarget
に設定されます。StayMode
をクリックすると、エージェントのターゲットを強制的にStayTarget
に設定できます。
Target
FreeTarget
エージェントがFreeTarget
に割り当てられた場合、エージェントはエリア内のすべての敵を武器で倒す必要があります。このモードでは、ラウンド開始時にエリア内にランダムな数の敵が生成されます(デフォルトは6体)。エージェントはエリアのランダムな位置に生成され、向きは前回のラウンドを引き継ぎます(最初のラウンドの場合はデフォルトで角度0)。エージェントがすべての敵を倒せば勝利と判断され、30秒以内にすべての敵を倒せない場合は失敗と判断されます。
GotoTarget
エージェントがGotoTarget
に割り当てられた場合、指定された目標エリアの特定の位置に移動することが目標です。このモードでは、ラウンドが始まるとエージェントの位置は前のラウンドを引き継ぎます(最初のラウンドではマップの左下隅にデフォルトで生成されます)。Gotoの目標ブロックは、難易度の確率に応じてエリア内にランダムに生成され、異なる難易度には複数のプリセットブロックが用意されています。これらのプリセットブロックはSceneBlocksSet
によってPrefabフォルダに保存されています。すべての目標ブロックの合計サイズは10x10で、ブロックが生成される際にはエージェントの位置を避けます。指定された目標エリアはFireBase
と呼ばれる円形のエリアで、その範囲は目標ブロックを超えることはありませんが、位置とサイズはプリセットブロックによって異なります。エージェントが目標ブロックの中心位置に正常に移動すると勝利と判断され、30秒以内に中心位置に移動できない場合は失敗と判断されます。
AttackTarget
エージェントがAttackTarget
に割り当てられた場合、目標エリアに火力を集中し、主な目標はブロック内のすべての敵を倒すことです。このモードでのエージェントと目標ブロックの生成戦略、およびFireBase
の構成はGotoTarget
と同様です。しかし、AttackTarget
のFireBase
エリア内には必ず敵が生成されます。エージェントがブロック内のすべての敵を倒せば勝利と判断され、30秒以内にすべての敵を倒せない場合は失敗と判断されます。
Environment
Observation Space
本強化学習訓練設定では、状態(State)は環境を理解し、分析する上での鍵となります。観測環境は3つの主要な部分で構成されています:TargetState
(ターゲット状態)、AgentState
(自機状態)、そしてRaycastState
(レイキャスト状態)。これらの観測データはML-AgentsのVectorSensor
クラスを通じて収集され、Python側に送信され、意思決定に必要な情報を提供します。ObservationのソースコードはMLAgentsCustomController.cs
にあるoverrideされたCollectObservations
関数内に格納されています。
TargetState(ターゲット状態)
TargetState
は、エージェントが受け取ったTargetとその関連情報を含んで、合計六つあります。ターゲットの種類、x,y,z座標、TargetAreaの直径、そしてエージェントがTargetArea内にいるか、およびそのラウンドの残り時間などの情報を含みます。
番号 | 観測項目 | サイズ | 状態空間 | 説明 |
---|---|---|---|---|
0 | Targetの種類 | 1 | 0,1,2,3,4 | 割り当てられたTargetの種類を記述:0=FreeTarget、1=GotoTarget、2=AttackTarget、3=DefenceTarget(未使用)、4=StayTarget |
1~3 | Targetの座標 | 3 | 0~47 | Target中のFirebaseの連続空間座標を記述、範囲は0から47の連続値 |
4 | FireBaseの直径 | 1 | 1~10 | FireBaseの直径を記述、範囲は1から10の連続値 |
5 | InFireBase状態 | 1 | 0,1 | エージェントがFireBase内にいるかどうかを記述、0=False、1=True |
6 | 残り時間 | 1 | 0~30 | ラウンドの残り時間を記述、範囲は0から30の連続値 |
AgentState(自機の状態)
AgentState
は、エージェントの武器が攻撃可能な状態かどうか、エージェントのx, y, z座標、およびエージェントの向きの角度を含んでいます。エージェントのGameObjectの向きの角度を直接使用すると、0から360度の変化時に値が大幅に変化してしまいます。より良い周期的な表現を実現するために、エージェントのGameObjectの回転角度のコサインとサインの値を使用してエージェントの向きを表しています。
番号 | 観測項目 | サイズ | 状態空間 | 説明 |
---|---|---|---|---|
7 | GunState | 1 | 0,1 | エージェントの武器が使用可能な状態かどうかを記述。0=False、1=True |
8~10 | エージェントの座標 | 3 | 0~47 | エージェントの連続空間座標を記述、範囲は0から47の連続値 |
11~12 | エージェントの向き | 2 | -1~1 | エージェントのGameObjectの回転角度のcosとsin値を計算してエージェントの正面の向きを記述。 |
RaycastState(視野状態)
RaycastState
はエージェント視界内で発射されたレイキャストが検出したオブジェクトのタグとその距離を記録します。タグはラベルエンコーディングとワンホットエンコーディングの2つの方法で記録することができ、デフォルトではラベルエンコーディングが使用されます。これはCommonParameterContainer
オブジェクトで調整することが可能です。RaycastState
はRaySensors
クラスによって管理されており、非均一なレイキャストの分布が実装されています。デフォルトでは、視界の中央15%のエリアに射線がより密集して分布し、両側はより希薄です。中央の密集エリアからは5本の射線が、両側の希薄エリアからはそれぞれ7本の射線が発射されます。各レイキャストが検出可能な最大距離は100ユニットで、その距離を超えると0が返されます。視界の中央エリアのカバー範囲、エリア内のレイキャスト数、検出可能な距離は、各Agent
のGameObjectのInspectorで調整できます。
番号 | 観測項目 | サイズ | 状態空間 | 説明 |
---|---|---|---|---|
11~Raynum+11 | TagType(Label) | Raycast数 | 0, 1, 2 | レイキャストが検出した物体のタグ。0=Wall, 1=Enemy, 2=Player, -1=Nothing |
11~Raynum * 3+11 | TagType(Onehot) | Raycast数 * 3 | 0, 1 | レイキャストが検出した物体のタグ。ワンホットエンコーディングで記録 |
Raynum+12~2* Raynum+12(ラベル), 3* Raynum+12~4* Raynum+12(ワンホット) |
距離 | Raycast数 | 0~MaxDistance | レイキャストが検出した物体までの距離。範囲は0〜MaxDistance。MaxDistanceのデフォルトは100 |
注:nは各エージェントのRaycast数、MaxDistanceは各エージェントのレイキャストの最大探知距離を指します。
Action Space
FPSゲームにおけるプレイヤーのキーボードとマウスの同時操作をシミュレートするため、本環境のActionSpaceはDiscrete Action
(離散動作)とContinuous Action
(連続動作)の2つの部分に分かれています。Discrete Action
はプレイヤーのキーボードによる離散的な操作を模擬し、Continuous Action
はマウスによる連続的な操作を模擬します。両方のActionSpaceに対応する操作はMLAgentsCustomController.cs
内のoverrideされたHeuristic
関数で参照できます。具体的な実装はAgentController.cs
内のMoveAgent
関数とCameraControl
関数にあります。
Discrete Action(離散動作)
Discrete Action
では、エージェントは各方向の移動と攻撃を離散的に操作できます。移動操作には垂直移動と水平移動が含まれ、攻撃操作では武器の操作が可能です。
番号 | 動作 | ActionSpace | 説明 |
---|---|---|---|
0 | 垂直移動 | 0, 1, -1 | エージェントをその正面方向の垂直軸で制御。0=停止、1=前進、-1=後退 |
1 | 水平移動 | 0, 1, -1 | エージェントをその正面方向の水平軸で制御。0=停止、1=右移動、-1=左移動 |
2 | 攻撃 | 0, 1 | エージェントの攻撃操作を制御。0=攻撃しない、1=攻撃する |
Continuous Action(連続動作)
Continuous Action
では、エージェントの視点を制御できます。本環境は初期のDoomのFPS環境を模擬しているため、エージェントの視点は水平方向のみで回転可能と設定されています。
番号 | 動作 | ActionSpace | 説明 |
---|---|---|---|
0 | 水平回転 | -Inf ~ Inf | エージェントの水平方向(左右)の視角回転を制御。正の値で右に、負の値で左に回転する。 |
Reward
与えられた各ターゲットにおいて、エージェントが異なるタスクを達成することに注力するため、リワードの設計もそれぞれ異なります。訓練モードでは、リワードは共通リワード「Common Reward」と各ターゲット専用のリワードで構成されています。
Common Reward(共通リワード)
エージェントが訓練中に目標達成に役立つ行動を取るように導き、無意味な動作を避けるために、Common Reward
では以下のリワードとペナルティが設計されています。ML-AgentsはPythonに直接ラウンド終了の結果を伝えることができないため、他のリワードの値が小さすぎたり大きすぎたりして勝利または失敗を判断できない場合に備えて、ラウンド終了時のリワードには極大値と極小値が追加されています。
リワード/ペナルティ | 値/シンボル | 説明 |
---|---|---|
非ターゲット命中 | 3 | エージェントがターゲットではない敵を撃つか倒すと得られるリワード |
ターゲット命中 | 25 | エージェントがターゲットとなる敵を撃つか倒すと得られるリワード |
移動ペナルティ | -0.5 | エージェントがフィールド上を移動するときに与えられるペナルティ |
回転ペナルティ | P_s |
エージェントが視点を回転するときに与えられるペナルティ |
勝利リワード | 999 | ラウンドが終了し、そのラウンドでエージェントが目標を達成したときに与えられるリワード |
失敗リワード | 999 | ラウンドが終了し、そのラウンドでエージェントが失敗したときに与えられるリワード |
エージェントが訓練中に無意味な回転を行わないように、また回転速度が速すぎないように、以下の関数を使用してSpinPenalty
を与えます。40個のマウス操作の回転記録が閾値に達していない場合、小さなペナルティが与えられます。閾値を超えると、エージェントが同じ方向に持続的に回転していることがわかるので、大きなペナルティが与えられます。ここでのPstは時刻t
の回転ペナルティ、mouseXtは時刻t
のエージェントのContunuouse Action
内の回転アクションの値を指します。
P_{st} =
\begin{cases}
-|mouseX_t| \cdot 0.06 &, & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| < 10 \\
-\left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| & , & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| \geq 10
\end{cases}
FreeTarget
FreeTarget
では、主な目標は全ての敵を消滅させることです。そのため、敵を追跡する能力が非常に重要です。このモードでは、2種類の報酬メカニズムが設定されています:視界の中心で敵を検出した場合、エージェントには2の報酬が与えられます。敵を検出しなかった場合、視界の中心から検出された敵までの距離 D
に基づいて2未満の報酬が与えられます。距離の計算は、Raycastで検出された敵にのみ考慮され、距離が0.5に近づくほど、つまり敵の半径がRewardに近づくほど、報酬は2に近づきます。たとえば、下図ではEnemy1
とEnemy3
との距離のみが計算され、最短距離 D_1
が報酬の計算に使用されます。検出されなかったEnemy2
とEnemy4
は除外されます。このモードでは、すべての敵がターゲットと見なされ、敵が倒されたり、ヒットした場合、エージェントはCommon Reward
のTargetHit Reward
を獲得します。
報酬/ペナルティ | 値/記号 | 説明 |
---|---|---|
FacingReward | R_f |
視界の中心と検出された最も近い敵との距離に基づく報酬 |
R_{f} =
\begin{cases}
2 &, &min(D) \leq 0.5 \\
\frac{1}{\sqrt{\frac{min(D)}{2}}} &,& min(D) \gt 0.5
\end{cases}
GotoTarget
GotoTarget
では、主な目標は指定されたFireBase
への移動です。時刻 t
と t-1
でのプレイヤーとターゲット間の距離 D_t
と D_{t-1}
の差に基づいて、各ステップで獲得できる距離報酬 R_d
が設定されています。エージェントが目標に近づくと正の報酬が与えられ、目標から離れると0未満のペナルティが与えられます。プレイヤーがFireBase
内にいる場合、より高い固定報酬を得ることができますが、この場合DistanceReward
は効果を発揮しません。このモードでは、すべての敵は攻撃対象とは見なされませんが、敵をヒットまたは倒した場合、Common Reward
のNonTargetHit
を獲得できます。
報酬/ペナルティ | 値/記号 | 説明 |
---|---|---|
DistanceReward | R_d |
エージェントが目標外にいる際の目標との距離差に基づく報酬 |
InAreaReward | 5 | エージェントが目標内にいる際の持続報酬 |
R_{d} = (D_{t-1} - D_{t}) \cdot 20
AttackTarget
Attack Mode
では、主な目的は目標地点の敵を排除し、エージェントが目標地点に対して行動をとることを奨励することです。したがって、FreeTarget
と同様に、目標地点への視線の移動も重要です。エージェントが目標地点に面して攻撃する場合、それは抑圧行動と見なされ、より低いSuppressiveReward
が与えられます。目標地点に面しているが攻撃していない場合、さらに低いFacingAreaReward
が継続して与えられます。FireBase
内の敵は目標と見なされ、敵が倒されたりヒットした場合、エージェントはCommon Reward
のTargetHit Reward
を獲得します。一方、FireBase
外の敵は目標とは見なされず、敵が倒されたりヒットした場合、エージェントはCommon Reward
のNonTargetHit Reward
のみを獲得します。
報酬/ペナルティ | 値/記号 | 説明 |
---|---|---|
SuppressiveReward | 5 | エージェントがFireBase に対して抑圧攻撃を行った際の報酬 |
FacingAreaReward | 2 | エージェントが目標に向かっている間の継続報酬 |
Side Channel
環境のデバッグやUnityとPython間の非リアルタイム通信を実現するため、この環境ではML-Agentsが提供するSide Channelを使用しています。ここではhuggingfaceのCustom Side Channelsをベースで開発を進みました。Unity側では、Side Channelの実装はAimbotSideChannel.cs
とAimBotSideChannelController.cs
に位置しています。各ログ情報は|
でフィールドを区切り、最初のフィールドはログのタイプで、次のフィールドはカスタムログ情報です。この環境では、UnityのSide ChannelはすべてのLogType.Warning
とLogType.Error
タイプのログをPython側に送信します。LogType.Warning
はラウンド終了後の勝敗情報やUnityからPythonへの指示を伝えます。勝敗情報はWarning|Result|Win
やWarning|Result|Lose
のようになります。一方、指令は次のトレーニング終了後にモデルを保存するコマンドで、その内容はWarning|Command
となります。