30

回転の四元数表現をオイラー角表現に変換するための既存のアルゴリズムはありますか? オイラー表現の回転順序は既知であり、6 つの順列 (xyz、xzy、yxz、yzx、zxy、zyx) のいずれかになります。固定の回転順序 (通常は NASA の見出し、バンク、ロール規則) のアルゴリズムを見てきましたが、任意の回転順序のアルゴリズムは見たことがありません。

さらに、1 つの方向に対して複数のオイラー角表現が存在するため、この結果はあいまいになります。これは許容されます (向きがまだ有効であるため、ユーザーが期待するものとは異なる可能性があります)。ただし、回転制限 (つまり、自由度の数と各自由度の制限) を考慮し、これらの制約を考慮して「最も賢明な」オイラー表現を生成しました。

この問題 (または類似の問題) が IK または剛体ダイナミクス ドメインに存在する可能性があると感じています。


明確にするために、四元数からいわゆる「テイト・ブライアン」表現に変換する方法を知っています。これを「NASA​​」規則と呼んでいました。これは、zxy の回転順序です (「Z」軸が上であるという慣例を想定しています)。すべての回転順序のアルゴリズムが必要です。

おそらく解決策は、zxy 順序変換を取得し、そこから他の回転順序の 5 つの変換を導出することです。もっと「包括的な」解決策があることを望んでいたと思います。いずれにせよ、既存のソリューションを見つけることができなかったことに驚いています。

さらに、これはおそらく完全に別の問題であるはずですが、変換 (もちろん既知の回転順序を前提として) は1 つのオイラー表現を選択しますが、実際には多くのオイラー表現があります。たとえば、回転順序が yxz の場合、(0,0,180) と (180,180,0) の 2 つの表現は同等です (同じ四元数が生成されます)。自由度の制限を使用して解を制約する方法はありますか? IK やリジッド ボディ ダイナミクスと同じように?つまり、上記の例で、Z 軸に関する自由度が 1 つしかない場合、2 番目の表現は無視できます。


このpdfのアルゴリズムである可能性のある1つの論文を追跡しましたが、論理と数学を理解するのが少し難しいと告白しなければなりません. 確かに他の解決策はありますか?任意の回転順序は本当に珍しいですか? 骨格アニメーションとクォータニオン補間を可能にする主要な 3D パッケージ (Maya、Max、Blender など) はすべて、この問題を正確に解決しているのでしょうか?

4

8 に答える 8

14

これは、見落とされている古い技術の典型的なケースのように見えます - ガレージからGraphics Gems IVのコピーをなんとか掘り出すことができました.Ken Shoemakeは、任意の回転順序のオイラー角から変換するためのアルゴリズムだけでなく、答えも持っているようです.この件に関する私の他の質問のほとんど。本万歳。Shoemake氏の回答に投票して、彼に評判ポイントを与えることができれば.

オイラー角を扱っている人は、ローカル ライブラリから Graphics Gems IV のコピーを入手し、222 ページから始まるセクションを読むことをお勧めします。これは、私がこれまでに読んだ問題の最も明確で簡潔な説明でなければなりません。


これ以降に見つけた便利なリンクを次に示します - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - これは Shoemake と同じシステムに従います。回転順序の 24 の異なる順列は、4 つの個別のパラメーター (内部軸、パリティ、繰り返し、フレーム) としてエンコードされます。これにより、アルゴリズムを 24 ケースから 2 に減らすことができます。一般的に有用な wiki になる可能性があります。前に渡ります。

提供された古いリンクはここで壊れているようで、「回転行列からのオイラー角の計算」の別のコピーです。

于 2009-07-15T01:14:21.833 に答える
10

Z 軸が上向きの右手デカルト座標系では、次の操作を行います。

struct Quaternion
{
    double w, x, y, z;
};

void GetEulerAngles(Quaternion q, double& yaw, double& pitch, double& roll)
{
    const double w2 = q.w*q.w;
    const double x2 = q.x*q.x;
    const double y2 = q.y*q.y;
    const double z2 = q.z*q.z;
    const double unitLength = w2 + x2 + y2 + z2;    // Normalised == 1, otherwise correction divisor.
    const double abcd = q.w*q.x + q.y*q.z;
    const double eps = 1e-7;    // TODO: pick from your math lib instead of hardcoding.
    const double pi = 3.14159265358979323846;   // TODO: pick from your math lib instead of hardcoding.
    if (abcd > (0.5-eps)*unitLength)
    {
        yaw = 2 * atan2(q.y, q.w);
        pitch = pi;
        roll = 0;
    }
    else if (abcd < (-0.5+eps)*unitLength)
    {
        yaw = -2 * ::atan2(q.y, q.w);
        pitch = -pi;
        roll = 0;
    }
    else
    {
        const double adbc = q.w*q.z - q.x*q.y;
        const double acbd = q.w*q.y - q.x*q.z;
        yaw = ::atan2(2*adbc, 1 - 2*(z2+x2));
        pitch = ::asin(2*abcd/unitLength);
        roll = ::atan2(2*acbd, 1 - 2*(y2+x2));
    }
}
于 2009-06-23T08:05:48.440 に答える
9

私は同様の解決策を数日間探していましたが、最終的に四元数を任意のオイラー回転とテイト・ブライアン回転に変換するアルゴリズムを持つこの Web サイトに出くわしました!

リンクは次のとおりです。http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

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

///////////////////////////////
// Quaternion to Euler
///////////////////////////////
enum RotSeq{zyx, zyz, zxy, zxz, yxz, yxy, yzx, yzy, xyz, xyx, xzy,xzx};

void twoaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r11, r12 );
  res[1] = acos ( r21 );
  res[2] = atan2( r31, r32 );
}

void threeaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r31, r32 );
  res[1] = asin ( r21 );
  res[2] = atan2( r11, r12 );
}

void quaternion2Euler(const Quaternion& q, double res[], RotSeq rotSeq)
{
    switch(rotSeq){
    case zyx:
      threeaxisrot( 2*(q.x*q.y + q.w*q.z),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    -2*(q.x*q.z - q.w*q.y),
                     2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                     res);
      break;

    case zyz:
      twoaxisrot( 2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.z + q.w*q.y),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.z - q.w*q.y),
                  res);
      break;

    case zxy:
      threeaxisrot( -2*(q.x*q.y - q.w*q.z),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      2*(q.y*q.z + q.w*q.x),
                     -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                      res);
      break;

    case zxz:
      twoaxisrot( 2*(q.x*q.z + q.w*q.y),
                  -2*(q.y*q.z - q.w*q.x),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.x*q.z - q.w*q.y),
                   2*(q.y*q.z + q.w*q.x),
                   res);
      break;

    case yxz:
      threeaxisrot( 2*(q.x*q.z + q.w*q.y),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    -2*(q.y*q.z - q.w*q.x),
                     2*(q.x*q.y + q.w*q.z),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                     res);
      break;

    case yxy:
      twoaxisrot( 2*(q.x*q.y - q.w*q.z),
                   2*(q.y*q.z + q.w*q.x),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.x*q.y + q.w*q.z),
                  -2*(q.y*q.z - q.w*q.x),
                  res);
      break;

    case yzx:
      threeaxisrot( -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                      2*(q.x*q.y + q.w*q.z),
                     -2*(q.y*q.z - q.w*q.x),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      res);
      break;

    case yzy:
      twoaxisrot( 2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.y - q.w*q.z),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.y + q.w*q.z),
                   res);
      break;

    case xyz:
      threeaxisrot( -2*(q.y*q.z - q.w*q.x),
                    q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    2*(q.x*q.z + q.w*q.y),
                   -2*(q.x*q.y - q.w*q.z),
                    q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    res);
      break;

    case xyx:
      twoaxisrot( 2*(q.x*q.y + q.w*q.z),
                  -2*(q.x*q.z - q.w*q.y),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.y - q.w*q.z),
                   2*(q.x*q.z + q.w*q.y),
                   res);
      break;

    case xzy:
      threeaxisrot( 2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                    -2*(q.x*q.y - q.w*q.z),
                     2*(q.x*q.z + q.w*q.y),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                     res);
      break;

    case xzx:
      twoaxisrot( 2*(q.x*q.z - q.w*q.y),
                   2*(q.x*q.y + q.w*q.z),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.z + q.w*q.y),
                  -2*(q.x*q.y - q.w*q.z),
                  res);
      break;
    default:
      std::cout << "Unknown rotation sequence" << std::endl;
      break;
   }
}
于 2014-12-16T03:19:33.333 に答える
3

私はこのように解決します:

ステップ 1 : オイラー回転の規則、たとえばzyxを確認します。

ステップ 2 : 回転の解析回転行列を計算します。たとえば、R( zyx ) が必要な場合は、

**R***zyx* = **R***x*(ファイ) * **R***y*(シータ) * **R***z*( psi )、要素は次のようになります。

R11 =  cos(theta)*cos(psi)
R12 = -cos(theta)*sin(psi)
R13 =  sin(theta)
R21 =  sin(psi)*cos(phi) + sin(theta)*cos(psi)*sin(phi)
R22 =  cos(psi)*cos(phi) - sin(theta)*sin(psi)*sin(phi)
R23 = -cos(theta)*sin(phi)
R31 =  sin(psi)*sin(phi) - sin(theta)*cos(psi)*cos(phi)
R32 =  cos(psi)sin(phi) + sin(theta)*sin(psi)*cos(phi)
R33 =  cos(theta)*cos(phi) 

ステップ 3 : 調べてみると、上記の要素を使用して 3 つの角度のサインまたはタンを見つけることができます。この例では、

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

ステップ 4 :上記の 3) のように角度を計算する必要がある要素について、クォータニオン (ウィキペディアを参照) から回転行列を計算します。

他の規則は、同じ手順を使用して計算できます。

于 2010-01-15T10:47:56.407 に答える
3

これは、四元数をオイラー角に変換することについて書いた論文です。

リンク 1

また、この場所には、四元数、オイラー角、回転行列 (DCM) のさまざまな側面について説明した多数のドキュメントを置いています。

リンク 2

于 2010-03-05T22:32:28.773 に答える
3

私のウェブサイト noelhughes.net に「幾何学的手法を使用した任意の回転シーケンスのクォータニオンからオイラー角への変換」というタイトルの論文を投稿しました。また、オイラー角の任意のセットを四元数および四元数から方向余弦行列に変換するアルゴリズムもあり、今週末に投稿します。これらは Martin Bakers の Web サイトにもありますが、見つけるのは少し難しいです。私の名前、Noel Hughes、および四元数をグーグルで検索すると、それが見つかるはずです。

于 2009-10-27T03:38:15.967 に答える
3

グーグル中にこのページに出くわした人のために、私は最近、12 の固有の Tait-Bryan (1-2-3、3-2-1 など) および適切な Euler (1-2-1、 3-1-3 など) 次の 2 つの参考文献のローテーション シーケンス:

2 番目のリンクを提供してくれたfrodo2975に感謝します。

于 2017-04-17T04:41:34.847 に答える