motoh's blog

主に趣味の電子工作やプログラミングについて書いていきます

Unityでロボットアームの逆運動学

前回記事でロボットアームの順運動学をUnityで実装しましたので、今回は次のステップとして逆運動学を実装してみました。逆運動学は三角関数で解析的に解く方法と、ヤコビアンを利用して数値的に解く方法がありますが、今回は数値的に解いています。本記事ではアルゴリズムの概要を解説し、C#スクリプトソースコードも載せますが、基礎から説明するととても長くなってしまうので、ある程度書籍などで学んだ知識を前提とします。私は梶田秀司編著「ヒューマノイドロボット」を参考にしています。

前回記事をご覧いただいた方へ
前回の順運動学は、Matrix4x4で同次変換行列を作り、回転と並行移動をまとめて計算しましたが、今回は同次変換行列を用いていません。前回のソースコードを利用できないことについてご了承願います。

 

 

ロボットアームの構造

ロボットアームの構造については前回記事「Unityでロボットアームの順運動学」に同じとします。逆運動学の計算で利用するパラメータを示します。

f:id:mzmlab:20190407002009p:plain

 

pwj :ワールド座標系での位置
pj :ローカル座標系での位置(親リンク相対)
Rwj:ワールド座標系での姿勢
Rj :ローカル座標系での姿勢(親リンク相対)
anglej :関節の回転角度
aj :関節軸ベクトル(親リンク相対)

 

逆運動学(数値解法)のアルゴリズム

逆運動学ではアーム先端の位置と姿勢を指定できますが、今回は位置のみにして簡略化しました(初心者なので^^;)。逆運動学を数値計算で解くアルゴリズムは次のようになります。

①逆運動学で求める、各関節の角度を並べたベクトルをangleとし、初期値を設定する。
②アーム先端の目標位置pw4_targetを指定する。
③順運動学で、angleから各関節の位置と姿勢を計算する。
④目標位置からの誤差Δpw4を計算する。(Δpw4 が十分に小さければ終了。)
 Δpw4 = pw4_target – pw4

Δpw4を小さくする角度修正量Δangleを計算する。
Δangleを関節角度に反映する。
 angle = angle + Δangle

⑦③から繰り返す。

 角度修正量Δangleをどのように求めるかが問題となりますが、ここでヤコビアンを使います。各関節を微小に変化(Δangle)させたときのアーム先端の位置の微小変化量(Δpw4)をヤコビアンJを用いてΔpw4 = J Δangleと表現できれば、J逆行列を使ってΔangleを求めることができます。

 Δangle = J-1 Δpw4

 

ヤコビアンの計算方法

ある関節が微小に回転したとき、アーム先端の位置がどのように変化するかを考えます。例えば、第2関節の微小回転角度をΔangle2とすると、アーム先端の位置の微小変化量Δpw4は次のように計算できます(×外積)。

 Δpw4 = Rw2a2×(pw4 - pw2)Δangle2

全ての関節についてこれを計算し、足し合わせれば、Δpw4を求めることができます。それを行列で表すと、

 f:id:mzmlab:20190407002524p:plain

先に示した式Δpw4 = J Δangleと比較すると、

f:id:mzmlab:20190407002711p:plain

ということになります。(J3x3行列となる。)

 

C#スクリプト

上記のアルゴリズムを実装したソースコードを載せます。一つ面倒なこととして、Unityには4x4行列しか用意されていないため、行列演算にはMath.Net Numericsというライブラリを使っています。このライブラリは.NETFrameworkのバージョン4.0以降ですが、Unity3.5なので使うにはひと手間必要です。こちらの記事が参考になりました。

Unityで数値計算ライブラリMath.NET Numericsを使う方法 - arXiv探訪

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MathNet.Numerics.LinearAlgebra.Single;    //Unityの行列は4x4しかないため、行列演算にはこのライブラリを用いる

public class RobotControllerScript : MonoBehaviour {

    public Transform axis1_tf, axis2_tf, axis3_tf;      //各軸のTransform(逆運動学の結果を出力してロボットを動かす)
    public Transform hand_transform;                    //アーム先端のTransform(逆運動学の結果確認用)
    public Vector3 pw4_target;                          //アーム先端の目標位置(Inspectorビューから入力)

    //---リンクのパラメータ---//
    private Vector3 pw1, pw2, pw3, pw4;    //ワールド座標系での位置
    private Quaternion Rw1, Rw2, Rw3;      //ワールド座標系での姿勢
    private Vector3 p1, p2, p3, p4;        //ローカル座標系での位置(親リンク相対)
    private Quaternion R1, R2, R3;         //ローカル座標系での姿勢(親リンク相対)
    private float angle1, angle2, angle3;  //関節角度
    private Vector3 a1, a2, a3;            //関節軸ベクトル(親リンク相対)

    private float lambda = 0.1f;    //逆運動学の収束を調整する係数(0~1で設定)

    //UI
    public UnityEngine.UI.Text transform_label;

    void Start () {
        //固定値のパラメータを設定
        p1 = new Vector3(0f, 1f, 0f);   //第1軸のローカル座標(親リンク相対)
        p2 = new Vector3(0f, 1f, 0f);   //第2軸のローカル座標(親リンク相対)
        p3 = new Vector3(0f, 3f, 0f);   //第3軸のローカル座標(親リンク相対)
        p4 = new Vector3(0f, 3f, 0f);   //アーム先端のローカル座標(親リンク相対)
        a1 = Vector3.up;                //第1軸の関節軸ベクトル(親リンク相対)
        a2 = Vector3.right;             //第2軸の関節軸ベクトル(親リンク相対)
        a3 = Vector3.right;             //第3軸の関節軸ベクトル(親リンク相対)

        //関節角度の初期値を設定
        //※0度だとヤコビアンの逆行列が存在しないため少し角度を付けておく
        angle1 = 10f;                   //第1軸の回転角度
        angle2 = 10f;                   //第2軸の回転角度
        angle3 = 10f;                   //第3軸の回転角度
    }

    void FixedUpdate () {

        for (int i = 0; i < 50; i++)
        {
            //// 順運動学 ////
            pw1 = p1;
            Rw1 = Quaternion.AngleAxis(angle1, a1);

            pw2 = Rw1 * p2 + pw1;
            R2 = Quaternion.AngleAxis(angle2, a2);
            Rw2 = Rw1 * R2;

            pw3 = Rw2 * p3 + pw2;
            R3 = Quaternion.AngleAxis(angle3, a3);
            Rw3 = Rw2 * R3;

            pw4 = Rw3 * p4 + pw3;


            //// ヤコビアンを作成 ////
            Vector3 j1 = Vector3.Cross(Rw1 * a1, pw4 - pw1);
            Vector3 j2 = Vector3.Cross(Rw2 * a2, pw4 - pw2);
            Vector3 j3 = Vector3.Cross(Rw3 * a3, pw4 - pw3);

            var J = DenseMatrix.OfArray(new float[,]     // ※Math.Netを使用
            {
                { j1.x, j2.x, j3.x },
                { j1.y, j2.y, j3.y },
                { j1.z, j2.z, j3.z }
            });

            //// 逆運動学 ////

            //アーム先端の目標位置からの誤差を計算
            Vector3 err = pw4_target - pw4;
            float err_norm = err.sqrMagnitude;

            //err_norm < 1E-5なら計算終了
            if (err_norm < 1E-5) break;

            // ※Jの逆行列と掛け算するためにerrをMath.Netの行列に入れなおす
            var err2 = DenseMatrix.OfArray(new float[,]
            {
                { err.x },
                { err.y },
                { err.z }
            });

            var d_angle = lambda * J.Inverse() * err2;

            //逆運動学で得た角度修正量を各軸に反映する(d_angleはラジアンなので度に変換)
            angle1 += d_angle[0, 0] * 180f / 3.14159f;
            angle2 += d_angle[1, 0] * 180f / 3.14159f;
            angle3 += d_angle[2, 0] * 180f / 3.14159f;
        }

        //逆運動学で計算した関節角度をロボットに反映
        Quaternion axis1_rot = Quaternion.AngleAxis(angle1, Vector3.up);
        Quaternion axis2_rot = Quaternion.AngleAxis(angle2, Vector3.right);
        Quaternion axis3_rot = Quaternion.AngleAxis(angle3, Vector3.right);

        axis1_tf.localRotation = axis1_rot;
        axis2_tf.localRotation = axis2_rot;
        axis3_tf.localRotation = axis3_rot;

        //UIにアーム先端の位置を表示
        transform_label.text = "Transform X:" + hand_transform.position.x.ToString("F2") +
                                " Y:" + hand_transform.position.y.ToString("F2") +
                                " Z:" + hand_transform.position.z.ToString("F2");


    }
}

 

実行結果

アーム先端の目標位置(pw4_target)をx = 3y = 3z = 3 として逆運動学を実行した結果を示します。初期状態は各関節の角度を10度としています。0度だとヤコビアン逆行列が存在しないためです。画面にはアーム先端のTransform.positionを表示しており、指定した通りになっています。

f:id:mzmlab:20190407165810p:plain


また、関節角度がどのように収束しているのか気になったため、グラフ化してみました。ヤコビアンのおかげでうまく収束していることがわかります(第2関節(angle2)が一旦大きくマイナスに振れているのが気になりますが)。

f:id:mzmlab:20190407003909p:plain



追記(2020.01.24)

当ブログをtwitterで参照してくださり、6軸の位置・姿勢制御に拡張までされている方を発見したので紹介させていただきます。twitter活動はしていないのでこのような形で恐縮ですが、参照してくださりありがとうございました。とても励みになりました(^^)

 

 

Unityでロボットアームの順運動学

Unityでロボットアームを作り、順運動学の計算をしてみました(最終的には逆運動学をやりたいので、その前段階として)。Matrix4x4クラスで各関節の同次変換行列を作り、チェーンルールを利用してシンプルに計算することができました。

 

ロボットアームの構造

f:id:mzmlab:20190227224315p:plain

なるべく簡単なモデルで試したかったので、自由度は3としました。リンクは入れ子構造にすることで、各軸のローカル座標系の回転に合わせてその先のリンクが動きます。

f:id:mzmlab:20190227224402p:plain

初期姿勢は、各軸のローカル座標系がワールド座標系と平行となるようにしました。

f:id:mzmlab:20190227224534p:plain

 

C#スクリプト

各軸の角度をpublicで宣言して、UnityInspectorビューから入力できるようにしました。処理の流れは、

1.入力された角度で各軸のTransformを更新し、アームを動かす。
2
.入力された角度で順運動学の計算を行う。
3
Transformの値と順運動学の計算結果を表示する(Transformと計算結果が一致すれば成功)。

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RobotControllerScript : MonoBehaviour {
    //ロボット制御
    public Transform axis1_tf, axis2_tf, axis3_tf;      //各軸のTransform
    public float axis1_angle, axis2_angle, axis3_angle; //各軸の角度(スクリプトの入力)
    private Quaternion axis1_rot, axis2_rot, axis3_rot; //Transform更新用のクオータニオン

    //順運動学
    private Matrix4x4 T01, T12, T23;    //同次変換行列
    private Vector4 hand_pos_world;     //アーム先端のワールド座標(順運動学で算出する)
    private Vector4 hand_pos_local;     //最後の関節の座標系での、アーム先端のローカル座標

    //UI
    public UnityEngine.UI.Text transform_label;
    public UnityEngine.UI.Text forward_K_label;
    public Transform hand_transform;

    void Start () {
        //同次変換行列を初期化
        T01 = Matrix4x4.identity;
        T12 = Matrix4x4.identity;
        T23 = Matrix4x4.identity;

        //最後の関節の座標系での、アーム先端のローカル座標を設定(これは変化しない)
        hand_pos_local = new Vector4(0f, 3f, 0f, 1f);
    }

    void FixedUpdate () {
        //////ロボット制御//////
        
        //スクリプトに入力された関節角度をロボットに反映
        axis1_rot = Quaternion.AngleAxis(axis1_angle, Vector3.up);
        axis2_rot = Quaternion.AngleAxis(axis2_angle, Vector3.right);
        axis3_rot = Quaternion.AngleAxis(axis3_angle, Vector3.right);

        axis1_tf.localRotation = axis1_rot;
        axis2_tf.localRotation = axis2_rot;
        axis3_tf.localRotation = axis3_rot;

        //////順運動学//////

        //スクリプトに入力された関節角度で、同次変換行列を更新
        T01.SetTRS(new Vector3(0f, 1f, 0f),
                    Quaternion.AngleAxis(axis1_angle, Vector3.up),
                    new Vector3(1f, 1f, 1f));

        T12.SetTRS(new Vector3(0f, 1f, 0f),
                    Quaternion.AngleAxis(axis2_angle, Vector3.right),
                    new Vector3(1f, 1f, 1f));

        T23.SetTRS(new Vector3(0f, 3f, 0f),
                    Quaternion.AngleAxis(axis3_angle, Vector3.right),
                    new Vector3(1f, 1f, 1f));

        //順運動学の計算
        hand_pos_world = T01 * T12 * T23 * hand_pos_local;

        //UIに表示
        transform_label.text = "Transform X:" + hand_transform.position.x.ToString("F2") +
                        " Y:" + hand_transform.position.y.ToString("F2") +
                        " Z:" + hand_transform.position.z.ToString("F2");

        forward_K_label.text = "Forward K X:" + hand_pos_world.x.ToString("F2") +
                        " Y:" + hand_pos_world.y.ToString("F2") +
                        " Z:" + hand_pos_world.z.ToString("F2");

    }
}
  

実行結果

f:id:mzmlab:20190227224804p:plain

表示の意味は、
Transform:アーム先端のTransform.positionを表示
Forward K:順運動学の計算結果を表示

両者は一致しており、順運動学の計算を正しく行えていることがわかります。

【Unity】Spring Joint のMIN/MAXの意味

バネのMINMAXって何だ?と、初めて使ったときに悩んだのでメモしておきます。

     f:id:mzmlab:20190217010428p:plain

 

1. MAXよりも伸ばすと、縮む方向に復元力が発生する。

     f:id:mzmlab:20190217010701p:plain

 

2. MINよりも縮むと、伸びる方向に復元力が発生する。

     f:id:mzmlab:20190217010758p:plain

 

3. MINMAXの間は不感帯となり、物体は等速運動をする。

    f:id:mzmlab:20190217011104p:plain

 

ふつう、バネには不感帯はないので、MINMAX(=バネの自然長)としてよいと思います。

 

Unityでペンシルロケットの運動を確認

高校で習った物理で、式の上では理解していても、実際に確かめたことはないものが多いですよね。そのような物理現象を、Unity物理エンジンで確かめたらおもしろいのではないかと思い立ちました。(物理を習ったのは大昔で、Unityも初心者なので、いろいろ突っ込みどころがあるかもしれません。)

今回はペンシルロケットの運動を確認します(ペンシルロケットについては、高校物理の問題集でたまたま知っただけなので深くは語れません)。ロケットは物体を水平後方に瞬間的に放出することで、反作用を受けて前方に加速されます。このとき、放出の前後で運動量が保存されます。これらについて、Unityで確認してみたいと思います。

※運動量の保存は過去記事 Unityで運動量保存の法則を確認 - mizu-mi blogでも確認しましたが、そのとき扱ったのは物体同士の衝突でした。今回は新たに放出における運動量保存を確認します。

 

ペンシルロケットを飛ばしてみる

まず、物体の放出をどのように実現するかです。ロケットが反作用を受ける必要があるので、ここではバネ(Spring Joint)を利用しました。初期状態をバネが縮んだ状態にしておけば、スタートと同時に物体が放出されます。

■初期状態

f:id:mzmlab:20190213234110p:plain

■放出

f:id:mzmlab:20190213234127g:plain

飛びました(^^)

この絵だとあまり迫力はありませんが、瞬間的におよそ時速70kmに達したことになります。

 

放出前後の運動量保存を確認

ロケットの質量m12kg、物体の質量m25kgとしました。放出後の運動量を計算すると、

    m1v1m2v2 = 2×19.0992 + 5×(-7.6397)
                          = 38.1984 – 38.1985
                          ≒ 0

放出前の運動量は、静止しているため0です。よって、運動量が保存されていることになります。

 

[付録] ペンシルロケットの作り方

構成

ロケットは4つのオブジェクトで構成されています。

 ①ロケットの胴体(Cylinder
 ②
バネの可動部(Cylinder
 ③
放出物(Sphere
 ④
ロケットの先端(飾り。blenderで円錐を作成)

f:id:mzmlab:20190217161749p:plain


Hierarchy

 ①ロケットの胴体(Rigidbody、Spring Joint付加
  +
④ロケットの先端
 +
②バネの可動部(Rigidbody付加 )
 +
③放出物(Rigidbody付加 )

 

[ポイント]

・①ロケットの胴体と②バネの可動部はSpring Jointを付けるためにRigidbodyを付ける必要があります。

・①ロケットの胴体はColliderを外し、②バネの可動部と③放出物がすり抜けるようにしています(本当は胴体を筒状にできれば本物っぽいのですが)。

・④ロケットの先端は①ロケットの胴体の子オブジェクトにします。そうしないと、ロケット発射した際、先端だけ動かずに置いて行かれます(親オブジェクトにRigidbodyが付いてないと、子オブジェクトは一緒に動いてくれないようです)。

 

バネの取り付け

f:id:mzmlab:20190217160408p:plain

Spring Jointは胴体側に付け、Anchorは胴体の端に設定します。Connected Bodyに可動部を割り当て、Connected Anchorを可動部の端に設定します。

Spring JointMIN/MAXにバネの自然長を設定します(ここでは3mとした)。
MIN/MAX
の意味はこちらにまとめました。

・可動部の初期位置は適当にバネが縮んだ状態にします(ここでは0.5mとした)。

・胴体や可動部のサイズは適当に調整します。

 

ロケットの先端(円錐)の作成

Unityに円錐がなかったので、Blenderで作成しました。以下のサイトを参考にして、円錐の作成からUnityへの取り込みまで簡単にできました(サイト内ではテクスチャを割り当てていますが、今回は不要)。

ピラミッドを作る - socialakiba wiki

 

Unityで物理実験ができるようになるまで

今までUnityで物理実験を行ってきましたが、Unityの使い方については一切触れませんでした。そこで今回は、Unityで物理実験ができるようになるまでのステップを紹介したいと思います。

過去記事のいくつか
Unityで万有引力による運動の確認 - mizu-mi blog
Unityでモンキーハンティングの確認 - mizu-mi blog
Unityで運動量保存の法則を確認 - mizu-mi blog

 

 

Step1:Unityの基本を学ぶ

Unity公式HPチュートリアル「はじめてのUnityで、玉転がしゲームを作りながら以下のような基本を学ぶことができます。これだけで、物理実験に必要な知識のほとんどが揃います。

・プロジェクトの作成
・玉や壁の作成
・玉の移動
・カメラの移動
・UIの作成(スコアなどを画面に表示する)

2020.01.30 追記
玉転がしゲームを作成するチュートリアルはいつの間にか無くなっており、近い内容で以下のチュートリアルが用意されています(英語です)。

learn.unity.com

幸いなことに、チュートリアルの内容を日本語で丁寧に説明してくださっているブログがありましたのでこちらも紹介します。

baba-s.hatenablog.com

 

Step2:コンポーネントの扱いに慣れる

シミュレーションの仕様は、物体のオブジェクトに付加する各種コンポーネントの設定で決まります。これは、Step1で体験することができるので、その後は自分でいろいろ作ってみるとすぐに慣れてきます。

Transform
物体の位置、姿勢を決めるコンポーネント

Rigidbody
物体に物理特性を与えるコンポーネント。重力を加えるかどうかを設定したり、力(加速度)を加えたり、速度を与えたりできる。

Colider
物体同士の衝突をシミュレーションするためのコンポーネント

Phisic Material
物体の摩擦係数(静止摩擦係数、動摩擦係数)、及び反発係数を設定する。これはColiderに紐付けて使う。

ScriptC#):
Rigidbody
に力や速度を設定するには、スクリプトを書く必要がある(というか、スクリプトのおかげで、条件に応じて力や速度を変化させることができる)。

 

Step3:公開する(したい人だけ)

GIFアニメーション

私はUnity Recorderを使っています。アセットストアからダウンロードできます。
こちらの記事が参考になります。

qiita.com

軌跡描画

物体の軌跡を描画したほうがわかりやすい場合があります(惑星の軌道とか)。
こちらの記事が参考になります。

note.mu

 

リンク集のようになってしまいましたが、まとめると、Step1のチュートリアルだけでかなり遊べるようになりました。ゲームを作ろうとすると、もっとたくさんのことを学ぶ必要がありそうですが^^;

Unityで万有引力による運動の確認

高校で習った物理で、式の上では理解していても、実際に確かめたことはないものが多いですよね。そのような物理現象を、Unity物理エンジンで確かめたらおもしろいのではないかと思い立ちました(物理を習ったのは大昔で、Unityも初心者なので、いろいろ突っ込みどころがあるかもしれません)。

今回は万有引力による運動を確認します。惑星は、太陽と万有引力で引き合うことで、太陽の周りを楕円起動で周回します(ケプラーの法則)。パラメータとC#スクリプトも公開します。

→さらに月の運動を追加した場合はこちら

mzmlab.hatenablog.com

 

万有引力の大きさ:

質量がm1, m22物体間の距離がrのとき

    f = \displaystyle G\frac{m_1m_2}{r^2}        (G : 万有引力定数=6.67×10^{-11} N・m^2/kg^2 )

ケプラーの法則

(ⅰ) 惑星は太陽を1つの焦点とする楕円上を運動する。
(ⅱ) 惑星と太陽とを結ぶ線分が一定時間に通過する面積は一定である。
(ⅲ) 公転周期T2乗は、楕円の半長軸a3乗に比例する(T^2 = ka^3

 

 

万有引力による運動を確認

パラメータは以下の設定としました(スケールはでたらめです)。あとは、C#スクリプト(最後に示す)で惑星に初速と万有引力を与えることで、惑星の動きを再現できました(本当は太陽にも同じ大きさの万有引力がかかりますが、たぶんほとんど影響がないので、ここでは省略)。【A】 

    万有引力定数G = 6.67E-05 Nm^2/kg^2 (実際の100万分の1)
    太陽の質量M = 1E+07 kg
    惑星の質量m = 1 kg
    太陽と惑星の距離(初期値)r0 = 5 m
    惑星の速度(初期値)v0 = 15 m/s (太陽へ向かう方向に対して垂直方向に与える)

 

f:id:mzmlab:20190209214952g:plain

 

ある速度では等速円運動となる

惑星にかかる向心力F=万有引力)が、

    \displaystyle F=\frac{mv^2}{r}

となるとき、惑星は等速円運動をします。
よって、等速円運動となるときの速度vは、向心力 = 万有引力より、

    \displaystyle \frac{mv^2}{r}=G\frac{Mm}{r^2}

    \displaystyle v=\sqrt{\frac{GM}{r}}≒11.5498 m/s
となります。vをこの値に変えてみましょう。【B】 

 

f:id:mzmlab:20190209215154g:plain

 

速度が上がり過ぎると無限遠方に飛び去る

力学的エネルギー \displaystyle E=\frac{mv^2}{2}+(-G\frac{Mm}{r})が、E ≧ 0 なら、無限遠方に飛び去ります。
V について解くと、

    \displaystyle v\geq\sqrt{\frac{2GM}{r}} ≒ 16.334 m/s

が飛び去る条件となります。では、v17m/sに変えてみましょう。【C】 

 

f:id:mzmlab:20190209215311g:plain

※視点変えました。 

この後しばらく待っても戻ってきませんでした。

 

[付録] Unityオブジェクト設定とC#スクリプト

今回作成した主なオブジェクトの設定を記載します。 

■太陽(Sphere)のコンポーネント設定(記載なしのものはデフォルト)

コンポーネント 設定 備考
A B C
Transform Position X 0 m  
Y 0 m  
Z 0 m  
Rigidbody Mass 1E+07 kg  
Use Gravity チェックなし  

 

■惑星(Sphere)のコンポーネント設定(記載なしのものはデフォルト)

コンポーネント 設定 備考
A B C
Transform Position X 5 m  
Y 0 m  
Z 0 m  
Rigidbody Mass 1 kg  
Use Gravity チェックなし  
Script 初速(Z方向) 15 m/s 11.55 m/s 17 m/s C#スクリプトのStart()で設定する。
力(太陽に向かう方向) 万有引力を計算して設定 C#スクリプトのFixed Update()で設定する。

 

■惑星のC#スクリプト

万有引力の与え方はこちらが参考になりました。
[Unity] 惑星に向かって物体が落ちるようにする | ftvlog

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlanetBehaviourScript : MonoBehaviour {

    public GameObject sun;
    public float initVelocityZ;
    private float f;    //万有引力
    private float m;    //惑星の質量
    private float M;    //太陽の質量
    private float r;    //太陽と惑星の距離
    private float G = 6.67E-5f;    //万有引力定数(通常の1000000倍)

    void Start () {
        //惑星に初速を与える
        Vector3 initVelocity  = new Vector3(0f, 0f, initVelocityZ); ;
        GetComponent<Rigidbody>().velocity = initVelocity;
        //惑星と太陽の質量を取得しておく
        m = GetComponent<Rigidbody>().mass;
        M = sun.GetComponent<Rigidbody>().mass;
    }

    void FixedUpdate()
    {
        //惑星から太陽に向かうベクトルを計算
        Vector3 direction = sun.transform.position - GetComponent<Transform>().position;
        //太陽と惑星の距離rを計算
        r = direction.magnitude;
        //単位ベクトルに変換(後で万有引力の方向として使う)
        direction.Normalize();
        //万有引力を計算
        f = G * m * M / (r * r);
        //万有引力を与える
        GetComponent<Rigidbody>().AddForce(f * direction, ForceMode.Force);
    }
}

Unityでモンキーハンティングの確認

高校で習った物理で、式の上では理解していても、実際に確かめたことはないものが多いですよね。そのような物理現象を、Unity物理エンジンで確かめたらおもしろいのではないかと思い立ちました。(物理を習ったのは大昔で、Unityも初心者なので、いろいろ突っ込みどころがあるかもしれません。)

今回はモンキーハンティングです。木の枝にぶら下がったサルに銃の照準を合わせ、引き金を引くと、銃声に驚いたサルが枝から手を放して落下するとします。このとき、銃弾は落下中のサルに必ず命中するという話です。物理の授業で教わりましたが、実験したことはありません。では、さっそくやってみましょう。【A

 

f:id:mzmlab:20190203223354g:plain

 

当たった!

ちなみに、弾丸はどんなに遅くても当たります。(地面に落ちるより前に当たる必要はある。)【B

 

f:id:mzmlab:20190203223448g:plain

 

モンキーハンティングを実際に試そうとすると、かなり大がかりな装置が必要ではないでしょうか。Unityなら数十分でできました。次は何を試そうかな~

 

[付録] Unityオブジェクト設定

今回作成した主なオブジェクトの設定を記載します。

■サル(Sphere)のコンポーネント設定(記載なしのものはデフォルト)

コンポーネント 設定 備考
A B
Transform Position X 30 m  
Y 30 m  
Z 0 m  
Rigidbody Mass 1 kg  
Use Gravity チェックあり  

 

■弾丸(Sphere)のコンポーネント設定(記載なしのものはデフォルト)

コンポーネント 設定 備考
A B
Transform Position X 30 m  
Y 30 m  
Z 0 m  
Rigidbody Mass 1 kg  
Use Gravity チェックあり  
Script 初速 25 m/s 15 m/s C#スクリプトのStart()で設定する。

 

■弾丸のC#スクリプト

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletScript : MonoBehaviour {

    public Transform monkeyTransform;    //サルのTransform
    public float v0;    //弾丸の初速

    // Use this for initialization
    void Start () {
        // 銃からサルに向かうベクトルを計算
        Vector3 monkeyDir = monkeyTransform.position - GetComponent<Transform>().position;
        monkeyDir.Normalize();    //ベクトルを正規化(大きさを1にする)

        // 弾丸の初速を設定
        GetComponent<Rigidbody>().velocity = v0 * monkeyDir;		
    }
}