Unity + OpenCVの仮想Quadrocopter(パート1)

CPDV。平行六面体とシリンダーに囲まれた仮想クアドロコプター



こんにちは、カブラビテス!



私たちは何を家を建てるべきですか? 描く-私たちは生きます。 この一連の記事では、Unityで仮想クアドロコプターを作成(およびその方法を説明)した経験を共有したいと思います。 また、habrの集合的な心から貴重なアドバイスを得るために:)ヘリコプターのナビゲーションにおける既存のコンピュータービジョンアルゴリズムとそのアプリケーションをテストするために、仮想ドローンを計画しました。 5番目のバージョンでは、UnityにはC ++プラグインを作成する機能があります。つまり、OpenCVなどのサードパーティのC / C ++ライブラリのすべての機能に、 牛の超大国のすべての機能を使用できます。 はい、現実の世界はUnityよりもはるかに複雑です。 しかし、私はそのようなシミュレータの使用がドローンの人工知能の開発のための良い初期近似として役立つと信じたいです。 興味があれば、猫へようこそ。



パート1では、Unityで独自の仮想クアドロコプターを作成し、PIDコントローラーで安定させます。 OpenCVの接続はパート2になります。パート3では、OpenCVからの密な3D再構築のアルゴリズムをテストする予定です。 次:それがどうなるか。



Mac OSで開発していますが、ツール自体はクロスプラットフォームであるため、他のシステムでもう一度試すことができると思います。 最初に、Unityで作業を詳細に説明します。これにより、記事から離れることなく 、少なくとも記事から離れることなく 、記述されている内容を再現できるようになります。 それでは始めましょう。 Unityをインストールして実行すると、バージョン5.2.0f3があります。 空の3Dプロジェクトを作成します。 デフォルトのレイアウトを使用します:ウィンドウ->レイアウト->デフォルト。 次に、メニューGameObject-> 3D Object-> Cubeに移動します。原点に1 x 1 x 1の白い立方体があります。 そして今、私たちはそれから地球の表面を作ります。 インスペクターの右側で、Scaleキューブが表面のように見えるように設定します。 X:20 Y:0.1 Z:20(同じブロック内でオブジェクトの座標と方向を設定できます)。 次に、表面を緑色にペイントします。Assets-> Create-> Material、名前をGroundに設定します-表面のテクスチャを作成しました。 次に、AssetsフォルダーのProjectインスペクター(Project)で、作成したばかりのテクスチャ(マテリアル)を確認し、それを選択して、インスペクターの右側に色を設定します(Main Maps-> Albedo)。 階層で表面を見つけます。それがまだキューブと呼ばれている場合は、より適切な名前を付けることができます。 ドラッグアンドドロップテクスチャを表面に投げると、必要な色でペイントされました。



地表



それでは、クアドロコプターの作成に取りかかりましょう。 クアドロコプターに関する情報は素晴らしい記事から得られました。基本的な概念、操作と制御の原理を非常によく説明しています。 この記事を読むことを強くお勧めします。ここでは、クアドロコプター構造の機能については説明しませんので、不必要に繰り返さないようにしてください。 私たちのドローンは、キューブ、ベース、4xシリンダーで構成され、これらはエンジンを強化するためのロッドであり、4xカプセルはこれらがエンジンです。 [作成]ボタンを使用して階層に空のオブジェクトを作成し、Quadrocopterと呼びます。 それを右クリックして、そこに上記のプリミティブを追加します。 固体の予想される動作に似せるために、すぐに実際の寸法に近づけることをお勧めします。 Unityのドキュメントでは、1は1メートルであると書かれており、0.2 x 0.1 x 0.2の基本スケールを作成しました。 プリミティブのサイズを設定するより適切な方法をコメントに示してください。Unityインターフェースを見て、別の直感的な方法は見つかりませんでした。 位置、方向、およびサイズを操作することにより、次のような2次関数を取得します。



クワッドコプター。プリミティブ



前部エンジンは白でマークされています(quadrocopterには前部があります)。 打ち上げ、それは、当然、どこにも飛ばない。 これは正常です:)ここで、プリミティブに物理学を追加する必要があります。 また、ロッドとボディを1つの不可分なフレームに結合することも望まれます。フレームは、必要なプリミティブを転送する空のGameObjectになります。 固体物理学を実装するために、Rigidbodyコンポーネントが使用されます。 フレームなどの要素を選択します。 右側のインスペクターで、[コンポーネントの追加]-> [物理]-> [剛体]をクリックします。 リストのインスペクターに、リジッドボディが表示されます。 エンジンに追加します。 まず、すべてが崩壊し、バラバラになります-これは正常で、物理学ができました。



クワッドコプター。倒れた



すべてがバラバラにならないように、コンポーネントの追加->物理->固定ジョイントをquadrocopterのコンポーネントに追加します。 このコンポーネントはハードリンクを実装します。 Connected Bodyパラメーターは、バインドするオブジェクトを示します。 エンジンをフレームに取り付けます。 そして今、バラバラになっているものは何もありません。



クワッドコプター。倒れ、バラバラにならなかった



次に、エンジンにパワーを追加する必要があります。 このために、単純なC#スクリプトを使用します。 Assets Create-> C#Scriptに移動し、motorScriptと呼びます。



using UnityEngine; using System.Collections; public class motorScript : MonoBehaviour { public float power = 0.0f; void FixedUpdate () { GetComponent<Rigidbody> ().AddRelativeForce (0, power, 0); } }
      
      





次に、エンジンを選択し、コンポーネントの追加->スクリプト->モータースクリプトを実行します。 スクリプトにはパラメーターpowerがあります-これはモーターのpowerです。 たとえば、各モーターに2の値を設定できます。これで、2次関数がそれほど速く落ちず、エンジンが実行されていることがわかります。 今では、外部からの回転角度を設定できるようにクアドロコプターを安定させ、彼がそれらを保持することができます。 これを行うには、quadrocopterScript.csスクリプトを作成します。 Quadrocopterの安定化は、PIDコントローラーによって実行されます。 このプロセスの詳細については、 上記の記事を参照してください 。 角度ごとに独自のPIDコントローラーが必要になり、最終的には3つの独立したコントローラーが必要になります。 以下はスクリプトコードです。 しかし、最初に、1つのニュアンスに言及します。 クワドロコプターのヨーイングは、エンジンのモーメントにより実現されます。 それらはコンストラクト全体を回転できますが、GetComponent().AddRelativeTorqueをエンジンスクリプトに追加しようとしたにもかかわらず、何らかの理由でこれはUnityで発生しません。 その結果、前部エンジンと後部エンジンが互いに向き合うように、ロッドの軸に対してエンジンをわずかに(10度)回転させるだけでよいという結論に達しました。 これは、構造全体の角運動量が同じエンジン出力で消滅するために必要です。 Quadrocopterスクリプトは次のとおりです。



 using UnityEngine; using UnityEngine.UI; using System.Collections; using System; public class quadrocopterScript : MonoBehaviour { //  private double pitch; // private double roll; // private double yaw; // public double throttle; //,    ,   public //  public double targetPitch; public double targetRoll; public double targetYaw; //PID ,     //   ,  PID   //    :)    private PID pitchPID = new PID (100, 0, 20); private PID rollPID = new PID (100, 0, 20); private PID yawPID = new PID (50, 0, 50); void readRotation () { //   , //       // --,       // Vector3 rot = GameObject.Find ("Frame").GetComponent<Transform> ().rotation.eulerAngles; pitch = rot.x; yaw = rot.y; roll = rot.z; } //   //  PID    //   ,       void stabilize () { //         //      [-180, 180]   //  PID ,       350 //,     -10 double dPitch = targetPitch - pitch; double dRoll = targetRoll - roll; double dYaw = targetYaw - yaw; dPitch -= Math.Ceiling (Math.Floor (dPitch / 180.0) / 2.0) * 360.0; dRoll -= Math.Ceiling (Math.Floor (dRoll / 180.0) / 2.0) * 360.0; dYaw -= Math.Ceiling (Math.Floor (dYaw / 180.0) / 2.0) * 360.0; //1  2   //3  4   double motor1power = throttle; double motor2power = throttle; double motor3power = throttle; double motor4power = throttle; //      double powerLimit = throttle > 20 ? 20 : throttle; // : //       //    double pitchForce = - pitchPID.calc (0, dPitch / 180.0); pitchForce = pitchForce > powerLimit ? powerLimit : pitchForce; pitchForce = pitchForce < -powerLimit ? -powerLimit : pitchForce; motor1power += pitchForce; motor2power += pitchForce; motor3power += - pitchForce; motor4power += - pitchForce; // : //    ,     double rollForce = - rollPID.calc (0, dRoll / 180.0); rollForce = rollForce > powerLimit ? powerLimit : rollForce; rollForce = rollForce < -powerLimit ? -powerLimit : rollForce; motor1power += rollForce; motor2power += - rollForce; motor3power += - rollForce; motor4power += rollForce; // : double yawForce = yawPID.calc (0, dYaw / 180.0); yawForce = yawForce > powerLimit ? powerLimit : yawForce; yawForce = yawForce < -powerLimit ? -powerLimit : yawForce; motor1power += yawForce; motor2power += - yawForce; motor3power += yawForce; motor4power += - yawForce; GameObject.Find ("Motor1").GetComponent<motorScript>().power = motor1power; GameObject.Find ("Motor2").GetComponent<motorScript>().power = motor2power; GameObject.Find ("Motor3").GetComponent<motorScript>().power = motor3power; GameObject.Find ("Motor4").GetComponent<motorScript>().power = motor4power; } //     Unity    FixedUpdate,    Update void FixedUpdate () { readRotation (); stabilize (); } } public class PID { private double P; private double I; private double D; private double prevErr; private double sumErr; public PID (double P, double I, double D) { this.P = P; this.I = I; this.D = D; } public double calc (double current, double target) { double dt = Time.fixedDeltaTime; double err = target - current; this.sumErr += err; double force = this.P * err + this.I * this.sumErr * dt + this.D * (err - this.prevErr) / dt; this.prevErr = err; return force; } };
      
      





このスクリプトをQuadrocopterオブジェクトに追加し、ガスと必要な回転角度を設定する機会があります。 ガスの私では、22.3の二次曲線がゆっくりと座ります。 角度による安定化をテストするために、スクリプトのターゲット...パラメーターがゼロ角度を指定している場合、quadrocopterの変換で角度を個別に設定し、水平位置を取るのを見ることができます。



仮想ジョイスティック、美しいモデル、環境を固定するタスクは、イニシアチブの読者にお任せします。



これはどのように機能しましたか? Androidパッケージへのリンクを試すことができます



記事で行われたコードはgithubで見ることができます。



All Articles