Unityを使った磁気浮上シミュレーション

Unityで、磁気浮上を模擬するプログラムを作成しました。

#1. 要求事項
[1]　磁気浮上アクチュエータを２個とする。
[2]　磁気浮上アクチュエータは浮上体の下面につける。
[3]　ある位置に停止させる（定位置制御）
#2. 実施事項
[1]　磁気浮上アクチュエータを子オブジェクトとする。
[2]　浮上体は親オブジェクトとして、磁気浮上アクチュエータである子オブジェクトをFixed Jointで固定する。

#3. 具体的な実施事項
・親オブジェクトに追加するスクリプトから、子オブジェクトを読み出し、子オブジェクトに重力に反する力を発生させる。
・定位置制御として、位置P速度PI制御及び重力補償を加える。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305726/46c4ca5c-579c-8d36-3bd4-c1508858cda4.png)

動画が埋め込められないので、状況は分かりにくいですが、スクリプトのみを添付しますので、
参考になればと思います。

#4. スクリプト
###4.1 コード
```C#
using UnityEngine;

public class Carrier_Control : MonoBehaviour
{
    public Rigidbody carrier;
    private double f1;
    private double f2;
    private double posY_old1 = 0;
    private double posY_old2 = 0;
    private double posY_old = 0;
    private double Kp = 20;
    private double Kv = 20;
    private double Ts = 1;
    private double Ki = 0.02;
    private double M = 10;
    private double G = 9.8;
    private double Mg;
    private double velY_old = 0;
    private double velY = 0;
    private double refV = 0;
    private double refV_old = 0;

    // Start is called before the first frame update
    void Start()
    {
        Mg = M/2 * G;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        Transform myTransform = this.transform;

        Vector3 pos = myTransform.position;

        Transform mag1Transform = transform.GetChild(0).gameObject.GetComponent<Transform>();
        Transform mag2Transform = transform.GetChild(1).gameObject.GetComponent<Transform>();

        Vector3 magPos1 = mag1Transform.position;
        Vector3 magPos2 = mag2Transform.position;

        velY = (pos.y - posY_old)/Ts;
        refV = (0.45 - pos.y) * Kp;          
        f1 =  (refV-velY_old) * Kv + (refV + refV_old - velY_old) * Ki * Kv;
        f2 =  (refV-velY_old) * Kv + (refV + refV_old - velY_old) * Ki * Kv;

        Vector3 force1 = new Vector3(0.0f, (float)(f1*M/2+Mg), 0.0f);
        Vector3 force2 = new Vector3(0.0f, (float)(f2*M/2+Mg), 0.0f);

        Rigidbody mag1 = transform.GetChild(0).gameObject.GetComponent<Rigidbody>();
        Rigidbody mag2 = transform.GetChild(1).gameObject.GetComponent<Rigidbody>();

        mag1.AddForce(force1);
        mag2.AddForce(force2);

        posY_old1 = magPos1.y;
        posY_old2 = magPos2.y;
        posY_old = pos.y;
        velY_old = velY;
        refV_old = refV - velY_old;
    }
}
```
##4.2 コードのポイント
Qiitaの[参考記事](https://qiita.com/tilyakuda/items/0c86e5b9741083219862)にもありますが、Unityの基本はgameObjectを利用することであり、Transformクラスをコンポーネントとして取得し、さらに、位置情報を取得する点。

```C#
        Transform mag1Transform = transform.GetChild(0).gameObject.GetComponent<Transform>();
        Transform mag2Transform = transform.GetChild(1).gameObject.GetComponent<Transform>();

        Vector3 magPos1 = mag1Transform.position;
        Vector3 magPos2 = mag2Transform.position;
``` 

Rigidbodyクラスをコンポーネントとして取得し、子オブジェクトに力を発生させる点。

```C#
        Rigidbody mag1 = transform.GetChild(0).gameObject.GetComponent<Rigidbody>();
        Rigidbody mag2 = transform.GetChild(1).gameObject.GetComponent<Rigidbody>();

        mag1.AddForce(force1);
        mag2.AddForce(force2);
```

#5. 感想
　Unityをうまく使いこなすには、GameObjectの理解が重要ですね。[参考記事1](https://qiita.com/megane42/items/9709d696e8f2561dbb1d)
　ちなみに、データの逐次計算部分は、FixedUpdate()を利用してください。
　Update()では浮上させることができませんでした。