web-dev-qa-db-ja.com

クォータニオン回転をオイラー角回転に変換するためのアルゴリズムはありますか?

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

さらに、単一の方向の複数のオイラー角表現があるため、この結果はあいまいになります。これは許容できます(方向はまだ有効であるため、ユーザーが期待しているものではない可能性があります)が、次のようなアルゴリズムがあればさらに良いでしょう。回転制限(つまり、自由度の数と各自由度の制限)を考慮に入れ、これらの制約を考慮して「最も賢明な」オイラー表現を生成しました。

この問題(または同様の問題)がIKまたは剛体力学ドメインに存在する可能性があると感じています。


解決済み:GraphicsGemsのKenShoemakeのアルゴリズムに従って、この問題を解決したかどうかが明確でない可能性があることに気づきました。当時、私は自分の質問に答えていましたが、私が答えたのかどうかはっきりしないかもしれません。詳細については、以下の回答を参照してください。


明確にするために-私はクォータニオンからいわゆる ' Tait-Bryan '表現に変換する方法を知っています-私が 'NASA'コンベンションと呼んでいたもの。これは、zxyの回転順序です(「Z」軸が上にあるという規則を前提としています)。 すべての回転順序のアルゴリズムが必要です。

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

さらに、これはおそらく完全に別の質問であるはずですが、変換(もちろん、既知の回転順序を想定)は1つのオイラー表現を選択しますが、実際にはありますたくさんの。たとえば、yxzの回転順序が与えられた場合、2つの表現(0,0,180)と(180,180,0)は同等です(同じクォータニオンを生成します)。自由度の制限を使用して解を制約する方法はありますか? IKおよび剛体力学?.


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

27
Will Baker

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

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


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

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

13
Will Baker

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));
    }
}
10
Jonas Byström

私は同様の解決策を数日探していましたが、ついにクォータニオンを任意のオイラー回転とテイトブライアン回転に変換するアルゴリズムを備えたこの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;
   }
}
7
frodo2975

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

ステップ1:オイラー回転の規則を確認します(例:zyx)。

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

** R *** zyx * = ** R *** x *(phi)* ** R *** y *(theta )* ** 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つの角度のsinまたはtanを見つけることができます。この例では、

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

ステップ4:角度を計算するために必要な要素について、クォータニオンから回転行列を計算します( wikipedia を参照)。上記の3)で。

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

3
Fredriku73

これは、クォータニオンをオイラー角に変換することについて私が書いた論文です。

リンク1

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

リンク2

3
noel hughes

私のウェブサイトnoelhughes.netに、「幾何学的手法を使用した任意の回転シーケンスのクォータニオンからオイラー角への変換」というタイトルの論文を投稿しました。また、オイラー角の任意のセットをクォータニオンに変換し、クォータニオンを方向余弦行列との間で変換するためのアルゴリズムもあります。これを今週末に投稿します。これらはマーチンベーカーズのウェブサイトにもありますが、見つけるのは少し難しいです。グーグル私の名前、ノエルヒューズ、およびクォータニオンとあなたはそれを見つける必要があります。

3
noel hughes

グーグル中にこのページに出くわした人のために、私は最近、12個の固有のテイトブライアン(1-2-3、3-2-1など)と適切なオイラー(1-2-1)すべてのこれらの変換の派生物を見つけました。 3-1-3など)次の2つの参照の回転シーケンス:

frodo2975 2番目のリンクに感謝します。

3
Cody Martin

Wikipedia は、クォータニオンのパーツを使用してオイラー角を計算する方法を示しています。

2
ralphtheninja