累積マトリックス変換を使用してジンバルロック問題を解決する方法


12

ジェイソンL.マッケソンのオンライン「Learning Modern 3D Graphics Programming」本を読んでいます

今のところ、ジンバルロックの問題と、クォータニオンを使用してそれを解決する方法について説明しています。

ただし、ここ、クォータニオンページで

問題の一部は、方向を一連の3つの累積軸回転として保存しようとしていることです。方向は回転ではなく方向です。そして、向きは確かに一連の回転ではありません。そのため、船の向きを特定の量としての向きとして扱う必要があります。

これは私が混乱し始める最初の場所だと思います。理由は、向きと回転の劇的な違いが見えないからです。また、方向が一連の回転で表現できない理由もわかりません...

また:

この目的に向けた最初の考えは、マトリックスとしての方向を維持することです。向きを変更するときが来たら、この行列に変換を適用し、結果を新しい現在の向きとして保存します。

これは、現在の方向に適用されるすべてのヨー、ピッチ、およびロールが、現在の方向に対して相対的であることを意味します。まさにそれが私たちに必要なものです。ユーザーが正のヨーを適用する場合、そのヨーは、ある固定座標系ではなく、現在のポインティングの位置を基準にして回転させます。

概念は理解していますが、行列変換の累積がこの問題の解決策である場合、前ページで示したコードがそれだけではないことを理解していません。

コードは次のとおりです。

void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack currMatrix;
    currMatrix.Translate(glm::vec3(0.0f, 0.0f, -200.0f));
    currMatrix.RotateX(g_angles.fAngleX);
    DrawGimbal(currMatrix, GIMBAL_X_AXIS, glm::vec4(0.4f, 0.4f, 1.0f, 1.0f));
    currMatrix.RotateY(g_angles.fAngleY);
    DrawGimbal(currMatrix, GIMBAL_Y_AXIS, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
    currMatrix.RotateZ(g_angles.fAngleZ);
    DrawGimbal(currMatrix, GIMBAL_Z_AXIS, glm::vec4(1.0f, 0.3f, 0.3f, 1.0f));

    glUseProgram(theProgram);
    currMatrix.Scale(3.0, 3.0, 3.0);
    currMatrix.RotateX(-90);
    //Set the base color for this object.
    glUniform4f(baseColorUnif, 1.0, 1.0, 1.0, 1.0);
    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));

    g_pObject->Render("tint");

    glUseProgram(0);

    glutSwapBuffers();
}

私の理解では、著者は個々の回転変換をすべてスタックの一番上に格納されている1つのマトリックスに結合したため、彼がしていること(スタック上のマトリックスの変更)はマトリックスの累積とは考えていません。

マトリックスの私の理解は、それらが原点(たとえば...モデル)に相対的なポイントを取得し、別の原点(カメラ)に相対的にするために使用されるということです。これは安全な定義であると確信していますが、このジンバルロックの問題を理解するのを妨げている何かが足りないと感じています。

私にとって意味のないことの1つは、マトリックスが2つの「空間」の相対的な差を決定する場合、Y軸の周りの回転がどのように来るのか、たとえば、ロールはポイントを「ロール空間」に入れない「このロールに関連してもう一度変換できます...つまり、このポイントへのそれ以上の変換は、この新しい「ロールスペース」に関連してはならず、したがって、回転は前の「ジンバルロックを引き起こしているモデル空間」。

それがジンバルロックが正しく発生する理由です。これは、オブジェクトを独自の相対軸の周りに回転させるのではなく、X、Y、およびZ軸のセットの周りにオブジェクトを回転させるためです。それとも私は間違っていますか?

どうやら私がリンクしたこのコードはマトリックス変換の蓄積ではないので、この方法を使用したソリューションの例を教えてください。

要約すると:

  • 回転と向きの違いは何ですか?
  • コードがマトリックス変換の累積の例ではなくリンクされているのはなぜですか?
  • 私が間違っていた場合、マトリックスの実際の特定の目的は何ですか?
  • マトリックス変換の累積を使用してジンバルロック問題の解決策を実装するにはどうすればよいですか?
  • また、ボーナスとして:回転後の変換が「モデル空間」に対して相対的なのはなぜですか?
  • 別のボーナス:変換後に、現在の変換に関連してさらに変換が行われるという仮定は間違っていますか?

また、暗示されていない場合は、OpenGL、GLSL、C ++、およびGLMを使用しているため、これらの用語の例と説明は、必要でない場合は大歓迎です。

詳細が多ければ多いほど良い!

前もって感謝します。

回答:


11

これを序文にする良い方法は定かではありませんが、最後にうまく結びつくことを望んでいます。とはいえ、次のことに飛び込みましょう。

前者は変換を記述し、後者は状態を記述するため、回転と方向は異なります。回転はオブジェクトがどのようにオリエンテーションなるかであり、オリエンテーションはオブジェクトのローカル回転スペースです。これは、2つが数学的にどのように表現されるかに直接関係する可能性があります。マトリックスは、ある座標空間から別の座標空間への変換を保存します。したがって、マトリックスは、一連の回転を介して、オブジェクトがどのように方向を取得するかのみを記述できます。ただし、これに関する問題はジンバルロックです。

ジンバルロックは、一連の回転を使用してオブジェクトを方向に合わせるのが難しいことを示しています。この問題は、少なくとも2つの回転軸が整列している場合に発生します。

画像提供:deepmesh3d.com
上の左の画像では、青とオレンジの軸が同じ回転をしています!これは問題です。これは、3つの自由度のいずれかが失われたことを意味し、この点からの追加の回転により予期しない結果が生じる可能性があるためです。四元数を使用してこれを解決するのは、四元数を適用してオブジェクトの方向を変換すると、変換をロール、ピッチ、ヨーの操作に分割するのではなく、オブジェクトを新しい方向に直接配置するためです(これが最善の方法です)。

今、私は実際に行列を累積することはこれに対する完全な解決策であることに懐疑的です。なぜなら、行列を累積する(したがって回転を累積する)ことは、そもそもジンバルロックの問題を引き起こす可能性があるからです。クォータニオンによる変換を処理する適切な方法は、ポイントでクォータニオンの乗算を実行することです。

pTransformed = q * pAsQuaternion * qConjugate

または、クォータニオンを行列に変換し、その行列を使用してポイントを変換します。

単純なマトリックス回転(45度のヨーなど)は、常にグローバル空間で定義されます。ローカル空間で変換を適用する場合は、変換をそのオブジェクトのローカル空間に変換する必要があります。奇妙に聞こえるので、詳しく説明します。ここで、回転の順序の重要性が出てきます。変換を追跡できるように、ここで本をつかむことをお勧めします。

本を平らにして、表紙を天井に向けて開き、まるで本を開けて読み始めようとします。次に、本の前面を45度上に傾けます(前面カバーがおおよそあなたに面しているはずです)。

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(45);

今、あなたが本のヨーを45度調整したいとしましょう(私は右手座標系を仮定していると思うので、これは見出しを左に変更します)、そしてこれを本のローカルに適用したいです本の表紙がまだあなたに面するように、座標空間。

bookMatrix.RotateY(45);

問題は、この回転がグローバル座標空間で発生するため、本の表紙が右肩の方を向いて終わることです。このヘディングの変更をローカル座標空間で発生させるには、最初に適用する必要があります!

glutil::MatrixStack bookMatrix;
bookMatrix.RotateY(45);
bookMatrix.RotateX(45);

やってみよう!天井を上に向けて再び本を開始します。ヨーを45度変更し、グローバルX軸に沿って45度ピッチします(左から右に実行)。これは、本のローカルスペースで45のピッチと45のヨーで予想した方向です。

これは何を意味するのでしょうか?結局のところ、操作の順序が重要なのです。最初に行われた変換は、その後行われた変換のコンテキストでローカル変換になります。頭を包むのは大変なことであり、これがクォータニオンが多くのトラブルを救う方法です。すべての順序依存のものをスキップします。

クォータニオンが提供するもう1つの大きな利点は、方向の補間が可能なことです。オイラー角間を補間しようとすることは、順序の依存関係のためほとんど不可能です。四元数の数学的特性により、それらの間の明確な球面線形補間が可能になります。

物事をまとめて元の質問に対処するには、変換が慎重に選択され、正確な順序で適用されない限り、累積マトリックス変換はジンバルロックの問題を実際に解決しません。したがって、常にクォータニオンを使用し、クォータニオン乗算を使用してポイントにクォータニオンを適用します。

お役に立てれば :)


4
記録のために、四元数はオイラー角で記述されている場合でもジンバルロックを導入できます。異なる方法で同じ計算を行うため(マトリックスではなく四元数)
concept3d

1
@ concept3d-これに言及しておめでとうございます!ジンバルメカニズムが自由度を失いやすい原因を理解することが重要です。これは、過剰に決定された方程式系を本質的に記述するロボットジョイントのようなものです。四元数、行列、または魔法を使用してこのメ​​カニズムを構築すると、あいまいさが残ります-それを理解し、実際のソリューションである最初の場所では使用しません(実証または技術的な目的で使用する必要がない限り) 。
テオドロン

四元数は想像するのが難しいです、私がいつも考えるのは、それら(単位四元数)は3球空間を表し、したがって任意の方向を表すことができますが、オイラー角はそれぞれ円/チューロを表し、したがって完全な球ではないことを理解しています私は正確に説明したかどうかわからなく、方向性を表現するための非常に正確な方法ではありません(彼らは独立して回転しない限り、3円/トーラスが本当に可能なすべての向きを生成することはできませんオイラーの場合には不可能である角度):)
concept3d

1

実際、マトリックスの蓄積はジンバルロックを解決できます。回転を累積することにより、ジンバルを追加し、任意の回転を許可します。ktodiscoが提供した図は、左の図にジンバルロックを示しています。この方向のマトリックスは、次のように定義できます。

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);

yジンバルの回転により、XジンバルとZジンバルがロックされ、1度の動きが失われました。この時点では、これら3つのジンバルを使用したヨーイング(ローカルy、グローバルz)はありません。しかし、別のジンバルを追加することにより、yを中心にローカルに回転できます。

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);
bookMatrix.RotateY(90);

新しいロール、ピッチ、ヨーごとに、別のジンバルを追加して、1つのマトリックスに追加します。そのため、別のローカル回転が必要になるたびに、回転が作成され、累積行列に乗算されます。この章で言及しているように、まだ問題はありますが、ジンバルロックはそれらの1つではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.