MATLABで設計したサーボシステムをUnityで実装&動かしてみる
前回はMATLABで台車型倒立振子のサーボシステムを設計して、Simulinkでシミュレーションしてみた。
今回は以前状態フィードバック制御のときにつくったUnityの台車型倒立振子のモデルを使って、サーボシステムをUnity上で実装して動かしてみる。Unityの台車型倒立振子のモデルのつくり方は以下を参照。
開発環境
MATLAB online(MATLAB R2022a)
Simulink
Control System Toolbox
Unity 2021.3.7f1
Windows 10
サーボシステムのゲイン
前回のおさらいだが、以下のようなサーボシステムを想定して、MATLABでフィードバックゲインを求めた。状態変数は、台車の変位(x)、速度(dx/dt)、振子の角度(θ)、角速度(dθ/dt)で、入力:台車に加える力(u)になっており、変位xを目標値に追従させるようなサーボシステムになっている。
Matlabで計算したフィードバックゲインK、Gは以下の通り。
K = -89.2517 -93.5492 -677.8023 -247.7323 G = 41.8367
このゲインを使って、Unity上でC#のスクリプトを実装し、台車型倒立振子を目標値に追従させる。
台車を目標値に追従させるサーボ制御器をC#で実装する
以前Unityで状態フィードバック制御をおこなった時にMoveCart.csというスクリプトをつくったが、これを少しいじってMoveCartServo.csというスクリプトをつくる。以下が作成したスクリプト。
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; public class MoveCartServo : MonoBehaviour { private GameObject refObj; [SerializeField] private float x; [SerializeField] private float deltaX; [SerializeField] private float theta; [SerializeField] private float deltaTheta; public TextMeshProUGUI xRefText; public TextMeshProUGUI xText; private float xRef = 0.0f; private float x1 = 0.0f; private float x2 = 0.0f; private float x3 = 0.0f; private float ku = 0.0f; private float u = 0.0f; private float k1 = -89.2517f; private float k2 = -93.5492f; private float k3 = -677.8023f; private float k4 = -247.7323f; private float g = 41.8367f; // Start is called before the first frame update void Start() { refObj = GameObject.Find( "Pole" ); xRefText.text = "xRef = 0"; xText.text = "x = 0"; } // Update is called once per frame void FixedUpdate() { Rigidbody2D rb = this.GetComponent(); Vector2 force = new Vector2(0.0f, 0.0f); x = rb.position.x; deltaX = rb.velocity.x; theta = refObj.GetComponent().rotation * Mathf.Deg2Rad; deltaTheta = refObj.GetComponent().angularVelocity * Mathf.Deg2Rad; x1 = xRef - x; x2 = x2 + x1 * Time.deltaTime; x3 = -g * x2; ku = k1 * x + k2 * deltaX + k3 * (-theta) + k4 * (-deltaTheta); u = x3 - ku; xRefText.text = "xRef = " + xRef.ToString(); xText.text = "x = " + x.ToString(); force = new Vector2(u, 0.0f); rb.AddForce (force); } public void OnClickLeft() { xRef = xRef - 1.0f; } public void OnClickRight() { xRef = xRef + 1.0f; } }
前回のスクリプトと被る部分も多いので詳細な説明は割愛するが、前回の状態フィードバック制御から積分器が追加されているため、C#のスクリプト内で積分をしてやる必要がある。
積分の実装方法は色々あると思うが、今回はシンプルにオイラー法で実装した。Time.deltaTimeは 直前のフレームと今のフレーム間で経過した時間を返すので、これに目標値と変位の差分をとったx1をかけて加算している。
x2 = x2 + x1 * Time.deltaTime;
また、Unityの実行画面でボタンにより目標値を±1ずつ変えられるように、以下の処理を追加している。Unity上でボタンを押して処理を実行させる方法は後で書く。
public void OnClickLeft() { xRef = xRef - 1.0f; } public void OnClickRight() { xRef = xRef + 1.0f; }
さらに、Unityの画面上に変位xの目標値xRefと、倒立振子の実際の変位xをテキストで表示している。Unity上で文字を表示させる方法についても後で書く。
xRefText.text = "xRef = " + xRef.ToString(); xText.text = "x = " + x.ToString();
Unityで倒立振子にスクリプトを追加する
さきほどつくったモデルをUnity上に設定していく。基本的には以前つくったモデルと変わらないが、ボタンとテキストの内容を変えて
- ButtonLeft:変位の目標値を-1するボタン
- ButtonRight:変位の目標値を+1するボタン
- TextRefX:変位の目標値を表示するテキスト
- TextX:現在の倒立振子の変位を表示するテキスト
を追加している。まずは、Cartオブジェクトを選択してMoveCartServo.csを追加する。
次にButtonLeftを選択し、以下の画像のようにOnclickにCartをドラッグ&ドロップしたあと、MoveCartServo.OnClickLeftを設定する。これでボタンを押すことで、MoveCartServoのOnClickLeft関数が実行される。ButtonRightについても同様に設定する。
最後に、再びCartを選択して、Move Cart ServoのX Ref Text、X Textに、TextRefオブジェクト、Textオブジェクトをそれぞれドラッグ&ドロップする。これで画面上に目標値と実際の変位が表示される。
これで準備は完了。
実行して動かしてみる
後はUnityで動かすだけ。再生ボタンを押して、ボタンを適当に押して目標値を変えてみた様子が以下の動画。ボタンで設定した目標値xRefの値に対して、台車が動いて追従していることがわかる。
やはり波形だけでなく、アニメーションで動いている様子が見えると楽しい。
次回は、いったんMATLABに戻って台車型倒立振子の最適レギュレータを設計してみたいと思う。
参考文献
この本のMATLAB/Simulink 6か月ライセンスを使ってやってます↓
Interface 2022年 9月号
MATLAB/Simulink記事まとめ
MATLABとSimulinkの記事は以下にまとめてます。