2009-06-23 8 views
26

Gibt es einen Algorithmus zur Umwandlung einer Quaternion-Darstellung einer Rotation in eine Euler-Winkel-Darstellung? Die Rotationsordnung für die Euler-Darstellung ist bekannt und kann eine der sechs Permutationen sein (d. H. Xyz, xzy, yxz, yzx, zxy, zyx). Ich habe Algorithmen für eine feste Rotationsordnung gesehen (normalerweise NASA-Kurs, Bank, Rollkonvention), aber nicht für eine willkürliche Rotationsordnung.Gibt es einen Algorithmus zur Umwandlung von Quaternion-Rotationen in Euler-Winkel-Rotationen?

Darüber hinaus wird dieses Ergebnis mehrdeutig sein, da es mehrere Euler-Winkel-Darstellungen einer einzelnen Orientierung gibt. Dies ist akzeptabel (weil die Orientierung immer noch ist gültig, es ist vielleicht nicht die, die der Benutzer erwartet zu sehen), aber es wäre noch besser, wenn es einen Algorithmus gäbe, der Rotationsgrenzen (dh die Anzahl der Grade von Freiheit und die Grenzen für jeden Freiheitsgrad) berücksichtigt und die "sinnvollste" Euler-Repräsentation unter Berücksichtigung dieser Zwänge ergeben.

Ich habe das Gefühl, dass dieses Problem (oder etwas Ähnliches) in den IK- oder Starrkörperdynamikdomänen existieren kann.


Gelöst: Ich habe erkannt, dass es nicht klar sein könnte, dass ich dieses Problem durch die folgenden Ken Shoemake Algorithmen aus Graphics Gems gelöst. Ich habe zu der Zeit meine eigene Frage beantwortet, aber es fällt mir auf, dass es nicht klar ist, dass ich das getan habe. Weitere Informationen finden Sie in der Antwort unten.


Nur um zu klären - ich weiß, wie von einer Quaternion auf die so genannten ‚Tait-Bryan‘ Darstellung zu konvertieren - was ich die Konvention ‚NASA‘ rufe. Dies ist eine Rotationsreihenfolge (unter der Annahme, dass die 'Z'-Achse oben ist) von zxy. Ich brauche einen Algorithmus für alle Rotationsaufträge.

Möglicherweise besteht die Lösung darin, die Umwandlung der zxy-Reihenfolge vorzunehmen und daraus fünf weitere Umwandlungen für die anderen Rotationsreihenfolgen abzuleiten. Ich glaube, ich hatte gehofft, dass es eine "übergreifende" Lösung gibt. Auf jeden Fall bin ich überrascht, dass ich dort keine bestehenden Lösungen finden konnte.

Zusätzlich, und das sollte vielleicht eine separate Frage insgesamt sein, wird jede Konvertierung (unter der Annahme einer bekannten Reihenfolge, natürlich) eine Euler-Darstellung wählen, aber es gibt tatsächlich viele. Zum Beispiel sind die beiden Darstellungen (0, 0, 180) und (180, 180, 0) bei einer Rotationsreihenfolge von yxz äquivalent (und würden die gleiche Quaternion ergeben). Gibt es eine Möglichkeit, die Lösung mit Einschränkungen der Freiheitsgrade zu beschränken? Wie bei IK und Starrkörperdynamik? in dem obigen Beispiel, wenn es nur einen Freiheitsgrad um die Z-Achse gäbe, könnte die zweite Darstellung vernachlässigt werden.


Ich habe ein Papier aufgespürt, die ein Algorithmus in this pdf sein könnte, aber ich muss zugeben, dass ich die Logik und Mathematik ein wenig schwer zu folgen finden. Sicherlich gibt es andere Lösungen da draußen? Ist eine willkürliche Reihenfolge der Drehungen wirklich so selten? Sicherlich muss jedes größere 3D-Paket, das Skelettanimation zusammen mit Quaternion-Interpolation ermöglicht (d. H. Maya, Max, Blender usw.), genau dieses Problem gelöst haben.

+0

Die Leute sind normalerweise faul genug zu versuchen, sich an irgendeine Art von Standard anzupassen. Meine unausgesprochene Vermutung wäre daher, dass Sie kaum eine Quelle für mehr als zwei oder drei der verschiedenen Permutationen finden könnten. –

+0

Möglicherweise suchen Sie nach etwas anderem, da Euler-Winkel für jede Lösung eine unendliche Anzahl von Repräsentationen haben. –

+0

Ich würde _love_ nicht auf die Verwendung von Euler-Winkeln für die Darstellung beschränkt sein, aber für die Domäne, in der ich arbeite (3D-Animation), sind sie die Standardmethode, um dem Benutzer Drehungen zu präsentieren. Und wegen des ihnen innewohnenden Problems (Gimbal Lock, etc.) ist es notwendig, dass die Rotationsreihenfolge ebenfalls editierbar ist. –

Antwort

13

Dies sieht aus wie ein klassischer Fall von alten Technik übersehen - ich es geschafft, eine Kopie Graphics Gems ausgraben IV aus der Garage und es sieht aus wie Ken Shoemake hat nicht nur einen Algorithmus zur Konvertierung von Euler-Winkel von arbiträren Rotationsreihenfolge, sondern auch die meisten meiner anderen Fragen zu dem Thema beantwortet. Hurra für Bücher. Wenn ich nur die Antwort von Herrn Shoemake abstimmen und ihn mit Reputationspunkten belohnen könnte.

Ich schätze eine Empfehlung, dass jeder, der mit Euler-Winkeln arbeitet, eine Kopie von Graphics Gems IV von seiner lokalen Bibliothek bekommen soll und den Abschnitt ab Seite 222 lesen muss. Es muss die klarste und prägnanteste Erklärung für das Problem sein, das ich bisher gelesen habe.


Hier ist eine nützliche Verbindung, die ich da gefunden habe - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - das ist das gleiche System wie Shoemake folgt; die 24 verschiedenen Permutationen der Rotationsordnung werden als vier separate Parameter - innere Achse, Parität, Wiederholung und Rahmen - kodiert, die es dann erlauben, den Algorithmus von 24 Fällen auf 2 zu reduzieren. Könnte ein nützliches Wiki im Allgemeinen sein - ich war nicht gekommen darüber hinweg.

Zum alten Link zur Verfügung gestellt scheint gebrochen here ist eine weitere Kopie von "Computing Euler Winkel aus einer Rotationsmatrix ".

+1

(zum Zeitpunkt der Erstellung dieses Artikels) ist der Link cgafaq.info tot. [webarchive] (http://web.archive.org/web/20110722193627/http://cgafaq.info/wiki/Euler_angles_from_matrix) ist das Beste, was ich finden konnte. –

+1

Ich kann das nicht genug ausrichten. Ich benutze Shoemakes Algorithmus seit 5 Jahren, es ist wirklich großartig. Es ist effizient und vollständig flexibel, implementiert alle 24 möglichen Euler/Tait-Bryan-Kombinationen und verarbeitet sowohl rotierende als auch stationäre Referenzrahmen. –

+0

Ich erfand unabhängig, was sich als etwas sehr ähnlich zu Shoemakes Algorithmus erwies. Mein Vorgesetzter wollte, dass ich es veröffentliche. Ich habe etwas recherchiert, und dann habe ich Shoemakes Algorithmus gefunden. Kein Papier. Jemand hat meinen Code nach Java portiert; Sie können es immer noch unter http://uahuntsville-siso-smackdown.googlecode.com/svn-history/r3/trunk/ez/siso/smackdown/utilities/EulerAngles.java finden (bis Google Code im Januar 2016 heruntergefahren wird). –

2

Wikipedia zeigt, wie Sie die Teile der Quaternion verwenden und die Euler-Winkel berechnen können.

+1

Es gilt für sogenannte Tait-Bryan-Winkel [http://en.wikipedia.org/wiki/Tait-Bryan_angles], aber es wird nicht wirklich erwähnt, dass Euler-Winkel andere Rotationsordnungen haben können. –

+0

Lediglich das Posten eines Links ist kein signifikanter Beitrag – bobobobo

10

In einem rechtshändigen kartesischen Koordinatensystem mit der oben zeigt Z-Achse, dies zu tun:

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)); 
    } 
} 
+1

Ja, dies verwendet die NASA-Konvention - Gieren, Pitch and Roll (oder Kopf-, Nick- und Wurfbewegungen, wenn Sie möchten). Dies wäre (unter der Annahme, dass Ihr Rechtshänder Cartesian mit Z up ist) eine Rotationsordnung von zxy. Ich bin nach einem Algorithmus, der xyz, xzy, yxz, yzx, zxy und zyx behandelt. Vielleicht besteht die einzige Möglichkeit darin, im Wesentlichen sechs verschiedene Konvertierungen zu liefern, die von der von Ihnen angegebenen abgeleitet sind? Und würde es einen Weg geben, diesen Ansatz zu erweitern, so dass gemeinsame Grenzen und Freiheitsgrade verwendet werden können, um eine eindeutige Euler-Repräsentation zu erhalten? –

+0

Mit meiner Interpretation von "non-ambigous" ist die kurze Antwort "Nein". :) –

+0

Was ist, wenn Sie gemeinsame Grenzen berücksichtigen? Wenn Sie zum Beispiel zwei mögliche Euler-Darstellungen haben, kann eine von ihnen eliminiert werden, weil sie außerhalb des Bewegungsbereichs eines bestimmten Gelenks liegt. Müssten Sie dieses Problem nicht lösen, wenn Sie Skelettanimation aus Motion Capture-Daten erzeugen? –

3

Ich habe meinen Artikel mit dem Titel "Quaternion to Euler Angle Conversion für Arbitrary Rotation Sequence mit geometrischen Methoden" auf meiner Website bei noelhughes.net veröffentlicht. Ich habe auch Algorithmen, um jeden Satz von Euler-Winkeln in eine Quaternion- und Quaternion-Kosinus-Matrix umzuwandeln, die ich dieses Wochenende veröffentlichen werde. Diese sind auch auf Martin Bakers Website, obwohl ein wenig schwer zu finden. Google mein Name, Noel Hughes und Quaternions und Sie sollten es finden.

3

Ich löse es so aus:

Schritt 1: Stellen Sie sicher, die Konvention für Euler Rotation Sie wollen, sagen wir, zyx.

Schritt 2: Berechnen Sie die analytische Rotationsmatrix für die Rotation. Zum Beispiel, wenn Sie wollen, R (zyx),

** R *** zyx * = ** R *** x * (phi) * ** R *** y * (Theta) * ** R *** z * (psi), wo die Elemente

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) 

Schritt 3 werden: Durch die Kontrolle, können Sie die Sünde oder tan für die drei Winkel finden mit die Elemente oben. In diesem Beispiel

tan(phi) = -R23/R33 

sin(theta) = -R13 

tan(psi) = -R12/R11 

Schritt 4: Berechnen Sie die Rotationsmatrix von Ihrer quaternion (siehe wikipedia), für die Elemente Sie die Winkel, wie in 3) oben berechnen müssen.

Andere Konventionen können mit dem gleichen Verfahren berechnet werden.

3

Hier ist ein Papier, das ich über die Umwandlung eines Quaternions in Euler-Winkel geschrieben habe.

Link 1

Ich habe stellen auch eine Reihe von Dokumenten an dieser Stelle verschiedene Aspekte der Quaternionen diskutieren, Euler-Winkel und Drehmatrizen (DCM).

Link 2

4

Ich habe für eine ähnliche Lösung mehrere Tage lange gesucht, und ich lief schließlich auf dieser Website, die einen Algorithmus zur Umwandlung von Quaternionen auf beliebige Euler und Tait-Bryan Umdrehungen hat!

Hier ist der Link: http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

Und hier ist der Code:

/////////////////////////////// 
// 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; 
    } 
} 
2

Für diejenigen, die auf dieser Seite stolpern, während Googeln fand ich vor kurzem Ableitungen für diese Umrechnungen für alle 12 intrinsische Tait-Bryan (1-2-3, 3-2-1, etc.) und Proper Euler (1-2-1, 3-1-3, etc.) Rotations Sequenzen in den folgenden zwei Referenzen:

Dank frodo2975 für den zweiten Link.

Verwandte Themen