物理ベースのキャラ(アクティブラグドール)を、強化学習向けに調整するための手順です。
関連
- ◯
- 3Dのキャラクタを物理ベースのキャラにする:Unity (ConfigurableJoint) , UnityChan, VRM (VRoid) , DAZ
- ◯
- コンテナに強化学習のフレームワークを設置する:Docker, ML-Agents
手順:必要なオブジェクト群をコピーする
まず物理ベースのキャラに、次の強化学習フレームワーク(ML-Agents )の3Dモデルから:
- ・
- ${DIR_ML_AGENTS}/ml-agents/Project/Assets/ML-Agents/Examples/Walker/Prefabs/Ragdoll/WalkerRagdoll.prefab
次のオブジェクト群をコピーします:
- ◯
- ルートノードの直下
- ・
- オブジェクト:OrientationCube(Clone) …… スタビライザ(キャラクタの傾きをモジュール化する)
- ・
- オブジェクト:DirectionIndicator(Clone) …… キャラクタの進行方向を示すアイコン(必須ではない)
- ◯
- ルートノード(共通)
- ・
- コンポーネント:BehaviorParameters …… 強化学習の共通コンポーネント
- ・
- コンポーネント:DecisionRequester …… 行動の決定の仕様
- ◯
- ルートノード(個別:Walker)
- ・
- コンポーネント:WalkerAgent …… サンプルWalkerのためのスクリプト
- ・
- コンポーネント:JointDriveController …… 多関節のサンプルのためのコンポーネント
- ・
- コンポーネント:ModelOverrider
- ◯
- 各ボーン
- ・
- タグ:agent
- ・
- コンポーネント:Rgigidbody
- ・
- コンポーネント:GroundContact …… その箇所が接地したかどうかを判定する
手順:既存のスクリプトを修正する
既存のスクリプト(WalkerAgent.cs)にも、すこし手をくわえる必要があります。
修正が必要なのは、リセット時の姿勢と、目標に対する体の向きです:
リセット時の姿勢ーーこのスクリプトはひとつのエピソードを終えるたびに、キャラクタを最初の位置に立たせています。このときの姿勢は体の中心のボーン(hips)の向きから決めているので、このまま適用するとボーンの向きがワールドの向きとは違うUnityChan / SD_UnityChanなどはおかしな姿勢になってしまいますーー次の記述を追加/変更します:
public class WalkerAgent: Agent { private Quaternion hipsInitialRatation; /*MOD*/ // 変数:体の中心の初期回転 public override void Initialize() { hipsInitialRatation = hips.rotation; /*MOD*/ // 体の中心の初期回転を、変数に納める public override void Initialize() { /*hips.rotation = Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0);*/ /*MOD*/ // コメントアウト hips.rotation = Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0) * hipsInitialRatation; /*MOD*/ // 初期回転分を加える(クォータニオン型なので積をとる)
目標に対する体の向きーーこれが報酬のひとつになっているので、既定のままだと、UnityChan / SD_UnityChanは、体をおかしな方向に向けたまま目標に向かうことになります。ただしスクリプトのこの箇所は、報酬を計算するたびに呼ばれます。なので各キャラクタごとの専用のスクリプトにし修正を直接記述するか、余分な計算が入っても汎用的な記述にするか、いずれかのトレードオフになりますーー以下は汎用的な修正です:
public class WalkerAgent: Agent { private Vector3 hipsInitialDirection; /*MOD*/ // 体の中心の回転の差 private Vector3 headInitialDirection; /*MOD*/ // 頭の回転の差 public override void Initialize() { hipsInitialDirection = (m_OrientationCube.transform.rotation * Quaternion.Inverse(hips.rotation)) * m_OrientationCube.transform.forward; /*MOD*/ // スタビライザの方向と、体の中心の向きのズレ(単位ベクトル、クォータニオン型なので逆数を掛ける) headInitialDirection = (m_OrientationCube.transform.rotation * Quaternion.Inverse(head.rotation)) * m_OrientationCube.transform.forward; /*MOD*/ // スタビライザの方向と、頭の向きのズレ(単位ベクトル) public override void OnEpisodeBegin() { /*hips.rotation = Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0);*/ /*MOD*/ hips.rotation = Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0) * hipsInitialRatation; /*MOD*/ public override void CollectObservations(VectorSensor sensor) { /*sensor.AddObservation(Quaternion.FromToRotation(hips.forward, cubeForward));*/ /*MOD*/ /*sensor.AddObservation(Quaternion.FromToRotation(head.forward, cubeForward));*/ /*MOD*/ sensor.AddObservation(Quaternion.FromToRotation(hips.rotation * hipsInitialDirection, cubeForward)); /*MOD*/ // 回転の差分を加える(クォータニオン型なので積をとる) sensor.AddObservation(Quaternion.FromToRotation(head.rotation * headInitialDirection, cubeForward)); /*MOD*/