Probleme mit Quaternionen

Hallo, ich hab folgendes Problem:
Ich habe ein Programm geschrieben, in dem ein Objekt willkürlich um einen Punkt rotiert werden kann. Die Rotation erfolgt immer anhand der x, y und z-Achse.

Beispiel: Rotation um: 30x, 20y, 20x, 10z, 10y
Mges = MR10y * MR10z * MR20x * MR20y * MR30x //Gesamtmatrix

Das klappt wunderbar, kann mich nicht beschweren. Nur will ich am Ende aller Rotationen eine Repräsentation aller Einzelrotation haben in der Form, das ich in jeweile EINMAL anhand der x, y, und z Achse drehe.

Idee. Quaternionen. Ich baue aus jeder Teilrotation ein normiertes Quaternion und multipliziere diese zu einem GesamtQuaternion:

Qges = ((((Q30x*Q20y)*Q20x)*Q10z)*Q10y)

Um jetzt eine Umrechnung von dem GesamtQuaternion in Eulerwinkel durchzuführen, habe ich mich an http://www.euclideanspace.com/maths/geometry/rotatio… gehalten.

Es funktioniert aber nicht. Als Beispiel:

Rotiert wird um -10y, dann um 60z und zuletzt um 80x: Rotation(XZY): (80 | 60 ] -10)
Über die Quaternionen erhalte ich „Rotation(XZY): (149.765 | 60 | -10)“

Hier ist der X-Wert falsch, in anderen Fällen andere Werte.

Wo liegt denn nun da der Denkfehler? Ist die Reihenfolge der Quaternionmultiplikation korrekt? Für Hilfe wäre ich schwer dankbar!

Gruß!
Kai

Hallo an das neue Mitglied
http://mitglied.lycos.de/schachspielen/welcome.gif

Hallo, ich hab folgendes Problem:
Ich habe ein Programm geschrieben, in dem ein Objekt
willkürlich um einen Punkt rotiert werden kann. Die Rotation
erfolgt immer anhand der x, y und z-Achse.

Welche Programmiersprache ? Und mit welcher 3D-Bibliothek ?

Beispiel: Rotation um: 30x, 20y, 20x, 10z, 10y
Mges = MR10y * MR10z * MR20x * MR20y * MR30x //Gesamtmatrix

Das klappt wunderbar, kann mich nicht beschweren. Nur will ich
am Ende aller Rotationen eine Repräsentation aller
Einzelrotation haben in der Form, das ich in jeweile EINMAL
anhand der x, y, und z Achse drehe.

Idee. Quaternionen. Ich baue aus jeder Teilrotation ein
normiertes Quaternion und multipliziere diese zu einem
GesamtQuaternion:

Qges = ((((Q30x*Q20y)*Q20x)*Q10z)*Q10y)

Kurz zur Lektüre: http://de.wikipedia.org/wiki/Quaternionen & http://de.wikipedia.org/wiki/Homogene_Koordinaten
Was passiert beim Einsatz von homogenen Koordinaten ? Immerhin könnte bei der komplexen Rechnung die Multiplikation mit i ein Problem verursachen…

Um jetzt eine Umrechnung von dem GesamtQuaternion in
Eulerwinkel durchzuführen, habe ich mich an
http://www.euclideanspace.com/maths/geometry/rotatio…
gehalten.

Es funktioniert aber nicht. Als Beispiel:

Rotiert wird um -10y, dann um 60z und zuletzt um 80x:
Rotation(XZY): (80 | 60 ] -10)
Über die Quaternionen erhalte ich „Rotation(XZY): (149.765 |
60 | -10)“

Hier ist der X-Wert falsch, in anderen Fällen andere Werte.

Wo liegt denn nun da der Denkfehler? Ist die Reihenfolge der
Quaternionmultiplikation korrekt? Für Hilfe wäre ich schwer
dankbar!

Dazu müsste man den Programmcode kennen (sofern selbstgebaut). Einfacher wäre aber wohl die Verwendung von OpenGL, Java3D, DirectX,…

mfg M.L.

Hallo Markus, vielen Dank für Deine fixe Antwort. Ich beschreibe die Umgebung und Code nun genauer:

Hallo an das neue Mitglied
http://mitglied.lycos.de/schachspielen/welcome.gif

Hallo, ich hab folgendes Problem:
Ich habe ein Programm geschrieben, in dem ein Objekt
willkürlich um einen Punkt rotiert werden kann. Die Rotation
erfolgt immer anhand der x, y und z-Achse.

Welche Programmiersprache ? Und mit welcher 3D-Bibliothek ?

Es handelt sich um ein in c++ geschriebenes Programm, dass zusammem mit qt und vtk arbeitet. vtk arbeitet letzlich mit openGL. ich bekomme für die Gesamtrotation vieler Teilrotationen lediglich eine Gesamtmatrix aus vtk. Hier treten bei der Faktorisierung der Matrix unter Umständen Gimbal Locks auf, die ich mit Quaternionen zu beheben versuche.

Beispiel: Rotation um: 30x, 20y, 20x, 10z, 10y
Mges = MR10y * MR10z * MR20x * MR20y * MR30x //Gesamtmatrix

Das klappt wunderbar, kann mich nicht beschweren. Nur will ich
am Ende aller Rotationen eine Repräsentation aller
Einzelrotation haben in der Form, das ich in jeweile EINMAL
anhand der x, y, und z Achse drehe.

Idee. Quaternionen. Ich baue aus jeder Teilrotation ein
normiertes Quaternion und multipliziere diese zu einem
GesamtQuaternion:

Qges = ((((Q30x*Q20y)*Q20x)*Q10z)*Q10y)

Kurz zur Lektüre: http://de.wikipedia.org/wiki/Quaternionen &
http://de.wikipedia.org/wiki/Homogene_Koordinaten
Was passiert beim Einsatz von homogenen Koordinaten ? Immerhin
könnte bei der komplexen Rechnung die Multiplikation mit
i ein Problem verursachen…

Mit homogenen Koordinaten komme ich nicht weiter. Eben wegen der Gimbal Locks. Die Multiplikation von Quaternionen, bei dem auch die imaginären Teil i, j und k mit einbezogen werden, ist doch wohldefiniert. Denke die ist sauber, kann mich aber auch täuschen, hier werde ich als nächstes weiter Infos sammeln…

Um jetzt eine Umrechnung von dem GesamtQuaternion in
Eulerwinkel durchzuführen, habe ich mich an
http://www.euclideanspace.com/maths/geometry/rotatio…
gehalten.

Es funktioniert aber nicht. Als Beispiel:

Rotiert wird um -10y, dann um 60z und zuletzt um 80x:
Rotation(XZY): (80 | 60 ] -10)
Über die Quaternionen erhalte ich „Rotation(XZY): (149.765 |
60 | -10)“

Hier ist der X-Wert falsch, in anderen Fällen andere Werte.

Wo liegt denn nun da der Denkfehler? Ist die Reihenfolge der
Quaternionmultiplikation korrekt? Für Hilfe wäre ich schwer
dankbar!

Dazu müsste man den Programmcode kennen (sofern selbstgebaut).
Einfacher wäre aber wohl die Verwendung von OpenGL, Java3D,
DirectX,…

Okay der Code. Nicht schön aber selten. Zuerst definiere ich 5 normalisierte Quaternionen für veschiedene Rotationen. Die Rotationswerte und Achsen (x,y, oder z) können für Tests verändert werden. Dem Vector qrow werden die Quaternionen in der Reihenfolge zugefügt, wie die Rotation geschehen soll. Anschließend multipliziere ich die Quaternionen aus qrow, wobei nach jeder Multiplikation normiert wird. Und letztlich versuche ich die Eulerwinkel zurück zu gewinnen…
Er ist fix in eine main eingebaut, so dass er schnell auszuprobieren ist:

#include 
#include 
#include 

int main (int argc, char \* const argv[]) 
{

 //vec for all rotations in quat form
 std::vector \> qrow;
 double grad = M\_PI/180;

 //q = (w,x,y,z)
 //q = cos(a/2) + i ( x \* sin(a/2)) + j (y \* sin(a/2)) + k ( z \* sin(a/2)) 
 std::vector q1; // +10x
 double wq1 = 80 \*grad;
 q1.push\_back(cos(wq1));
 q1.push\_back(1 \* sin(wq1/2)); 
 q1.push\_back(0 \* sin(wq1/2)); 
 q1.push\_back(0 \* sin(wq1/2)); 
 double q1Mag=sqrt(q1[0]\*q1[0]+q1[1]\*q1[1]+q1[2]\*q1[2]+q1[3]\*q1[3]);
 for(int i=0;i q2; // +10x
 double wq2=10\*grad;
 q2.push\_back(cos(wq2/2));
 q2.push\_back(1 \* sin(wq2/2)); 
 q2.push\_back(0 \* sin(wq2/2)); 
 q2.push\_back(0 \* sin(wq2/2)); 
 double q2Mag=sqrt(q2[0]\*q2[0]+q2[1]\*q2[1]+q2[2]\*q2[2]+q2[3]\*q2[3]);
 for(int i=0;i q3; // +10x
 double wq3=10\*grad;
 q3.push\_back(cos(wq3/2));
 q3.push\_back(1 \* sin(wq3/2)); 
 q3.push\_back(0 \* sin(wq3/2)); 
 q3.push\_back(0 \* sin(wq3/2)); 
 double q3Mag=sqrt(q3[0]\*q3[0]+q3[1]\*q3[1]+q3[2]\*q3[2]+q3[3]\*q3[3]);
 for(int i=0;i q4; // -10y
 double wq4=-10\*grad;
 q4.push\_back(cos(wq4/2));
 q4.push\_back(0 \* sin(wq4/2)); 
 q4.push\_back(1 \* sin(wq4/2)); 
 q4.push\_back(0 \* sin(wq4/2)); 
 double q4Mag=sqrt(q4[0]\*q4[0]+q4[1]\*q4[1]+q4[2]\*q4[2]+q4[3]\*q4[3]);
 for(int i=0;i q5; // -10y
 double wq5=-10\*grad;
 q5.push\_back(cos(wq5/2));
 q5.push\_back(0 \* sin(wq5/2)); 
 q5.push\_back(1 \* sin(wq5/2)); 
 q5.push\_back(0 \* sin(wq5/2)); 
 double q5Mag=sqrt(q5[0]\*q5[0]+q5[1]\*q5[1]+q5[2]\*q5[2]+q5[3]\*q5[3]);
 for(int i=0;i q6; // 10z
 double wq6=60\*grad;
 q6.push\_back(cos(wq6/2));
 q6.push\_back(0 \* sin(wq6/2)); 
 q6.push\_back(0 \* sin(wq6/2)); 
 q6.push\_back(1 \* sin(wq6/2)); 
 double q6Mag=sqrt(q6[0]\*q6[0]+q6[1]\*q6[1]+q6[2]\*q6[2]+q6[3]\*q6[3]);
 for(int i=0;i qtmp;
 for (int i=0;i qs;
 for(int j=0;j qt= qrow[i];
 qtmp[0] = ( qs[0]\*qt[0] - qs[1]\*qt[1] - qs[2]\*qt[2] - qs[3]\*qt[3] ); // w
 qtmp[1] = ( qs[0]\*qt[1] + qs[1]\*qt[0] + qs[2]\*qt[3] - qs[3]\*qt[2] ); // x
 qtmp[2] = ( qs[0]\*qt[2] - qs[1]\*qt[3] + qs[2]\*qt[0] + qs[3]\*qt[1] ); // y
 qtmp[3] = ( qs[0]\*qt[3] + qs[1]\*qt[2] - qs[2]\*qt[1] + qs[3]\*qt[0] ); // z
 //normalize...
 qtmpMag=sqrt(qtmp[0]\*qtmp[0]+qtmp[1]\*qtmp[1]+qtmp[2]\*qtmp[2]+qtmp[3]\*qtmp[3]);
 for(int i=0;i 0.499\*unit) { // singularity at north pole
 heading = 2 \* atan2(qtmp[1],qtmp[0]);
 attitude = M\_PI/2;
 bank = 0;
 }
 else if (test 

Also nochmals Dank und beste Grüße,
Kai

Code-Korrektur! Bei der Erstellung des ersten Quats fehlte bei der Erstellung des Realanteils das „/2“ im Kosinus

 std::vector q1; // +10x
 double wq1 = 80 \*grad;
 q1.push\_back(cos(wq1/2)); //!!! das /2 fehlte...
 q1.push\_back(1 \* sin(wq1/2));
 q1.push\_back(0 \* sin(wq1/2));
 q1.push\_back(0 \* sin(wq1/2));
 double q1Mag=sqrt(q1[0]\*q1[0]+q1[1]\*q1[1]+q1[2]\*q1[2]+q1[3]\*q1[3]);
 for(int i=0;i
cuKai

Hallo nochmal.

vielen Dank für Deine fixe Antwort.

Bitte. Auch wenn 3D-Programmierung hier schon etwas länger her war…

Es handelt sich um ein in c++ geschriebenes Programm, dass
zusammem mit qt und vtk arbeitet. vtk arbeitet letzlich mit
openGL. ich bekomme für die Gesamtrotation vieler
Teilrotationen lediglich eine Gesamtmatrix aus vtk. Hier
treten bei der Faktorisierung der Matrix unter Umständen
Gimbal Locks auf, die ich mit Quaternionen zu beheben
versuche.

Soweit korrekt. Nur hat nicht jeder mal nebenbei qt oder vtk zur Verfügung. Aber eine schlaue Resource (unterliegt dem Urheberrecht, daher kein Verweis) hier schlägt bei Quaternionen auch die Verwendung von Projektionen vor. So sieht die Projektionsmatrix im allg. Fall aus

( 1 0 0 0 )
( 0 1 0 0 )
( 0 0 1 0 ) = M<sub>proj</sub>
(1/x0 1/y0 1/z0 1 )

Okay der Code.

Visual C++ 6.0: 9 Fehler. Klar: die Bibliotheken fehlen…

Nicht schön aber selten. Zuerst definiere ich 5
normalisierte Quaternionen für verschiedene Rotationen. Die
Rotationswerte und Achsen (x,y, oder z) können für Tests
verändert werden. Dem Vector qrow werden die Quaternionen in
der Reihenfolge zugefügt, wie die Rotation geschehen soll.
Anschließend multipliziere ich die Quaternionen aus qrow,
wobei nach jeder Multiplikation normiert wird. Und letztlich
versuche ich die Eulerwinkel zurück zu gewinnen…

Hm, bieten qt oder vtk keinen Zugriff auf fertige Algorithmen an ?
Aber vielleicht kann diese englischsprachige FAQ bereits weiterhelfen: http://www.j3d.org/matrix_faq/matrfaq_latest.html

HTH
mfg M.L.

Ahoj,
ich bin schon weiter. Drei Rotationen mit einer festen Reihenfolge (XZY) und beliebigen Rotationswerten kann ich rückrechnen, egal wie schräg die Werte sind. Das ist schon mal SEHR cool. Jetzt muss ich noch das Problem meiner willkürlichen Rotationsreihenfolgem hinbekommen…

Was haben wir gelernt: Bei der Rückrechnung von Quaternionen in Euler ist die Reihenfolge der Rotationen wichtig! Rotiere ich erst um 10y, 20z, -5x bekomme ich mit dem angegebenen Code korrekte Werte. Bei anderen Reihenfolgen nicht. :smile:

Es handelt sich um ein in c++ geschriebenes Programm, dass
zusammem mit qt und vtk arbeitet. vtk arbeitet letzlich mit
openGL. ich bekomme für die Gesamtrotation vieler
Teilrotationen lediglich eine Gesamtmatrix aus vtk. Hier
treten bei der Faktorisierung der Matrix unter Umständen
Gimbal Locks auf, die ich mit Quaternionen zu beheben
versuche.

Soweit korrekt. Nur hat nicht jeder mal nebenbei qt oder vtk
zur Verfügung. Aber eine schlaue Resource (unterliegt dem
Urheberrecht, daher kein Verweis) hier schlägt bei
Quaternionen auch die Verwendung von Projektionen vor. So
sieht die Projektionsmatrix im allg. Fall aus

( 1 0 0 0 )
( 0 1 0 0 )
( 0 0 1 0 ) = Mproj
(1/x0 1/y0 1/z0 1 )

Das Beispielproblem ist allgemein, qt und vtk sind daher nich nötig.

Hm, bieten qt oder vtk keinen Zugriff auf fertige Algorithmen
an ?

Leider nein. vtk bietet mit über 800 Klassen wahrscheinlich auch Sushi-Zubereitung an aber für mich finde ich da nix.

Aber vielleicht kann diese englischsprachige FAQ bereits
weiterhelfen:
http://www.j3d.org/matrix_faq/matrfaq_latest.html

Der Link ist hervorragend, kann ich nur empfehlen. Dazu noch die ganzen Seiten in http://www.euclideanspace.com/maths/algebra/realNorm… und es sollte beim Umgang mit Quaternionen eigentlich nix passieren…eigentlich :wink:. Mein Erfolg oder Niederlage mit der willkürlichen Reihenfolge der Rotationen werde ich hier dokumentieren.

Beste Grüße,
Kai

Hallo Kai,

Ich habe ein Programm geschrieben, in dem ein Objekt
willkürlich um einen Punkt rotiert werden kann. Die Rotation
erfolgt immer anhand der x, y und z-Achse.

OK, das kann man entweder mit Quaternion- oder
simpler Matrixalgebra lösen. Ich denke, hier
sind Quaternions (imo) völliger Overkill, simple
Matrixmultiplikation tuts auch.

Es handelt sich um ein in c++ geschriebenes Programm, dass
zusammem mit qt und vtk arbeitet. vtk arbeitet letzlich mit
openGL. ich bekomme für die Gesamtrotation vieler
Teilrotationen lediglich eine Gesamtmatrix aus vtk. Hier
treten bei der Faktorisierung der Matrix unter Umständen
Gimbal Locks auf, die ich mit Quaternionen zu beheben
versuche.

Aha. Du willst also Folgendes haben:

  1. einen Punkt im Raum, den es gilt, um
  2. eine freie Rotationsachse, die im Ursprung liegt,
  3. mit einer Rotationsmatrix „zu drehen“ sowie die
  4. Verkettung dieser Rotationen merken, um
  5. am Ende in Eulerwinkel zu zerlegen

Okay der Code. Nicht schön aber selten. Zuerst definiere ich 5
normalisierte Quaternionen für veschiedene Rotationen. Die
Rotationswerte und Achsen (x,y, oder z) können für Tests
verändert werden. Dem Vector qrow werden die Quaternionen in
[…]

Boahh! Das ist ja auch ganz schön kompliziert :wink:
(Ist wohl durch Deine Anwendung bedingt)

Ich würde vorschlagen, die ganze Rotationsgeschichte
herauszunehmen und so simpel wie möglich zu gestalten.

Der Pseudocode für Dein Problem wäre etwa sowas:

 int main(void)
{
 MAT3 m1, m2, m3;
 VEC3 axis = { 0, 0, 1 }; // rotiere um (normierte!) Achse
 VEC3 point = { 0.7, 1.2, 1.6 }; // dieser Punkt wir rotiert
 VEC3 eu; // Euler-Komponenten

 setrotmatrix(m1, axis, deg2rad(30)); // 30 grad -\> rad
 rotate(point, m1); 

 setrotmatrix(m2, axis, deg2rad(50)); // 50 grad -\> rad
 rotate(point, m2); 

 matcopy(m3, m1); // 30 Grad-Rotation (m1) nach m3 sichern
 matmult(m3, m2); // Rotationen verketten 30 + 50 grad

 eulerdecompose(eu, m3); // Eulerwinkel bestimmen
 eulershow("m3", eu); // zeigt jetzt 80(!)Grad "psi"
 ...

Jetzt müsstest Du dir „nur noch“ die Funktionen
schreiben, die das alles bewerkstelligen, ein Startpunkt
könnte sein:

 void matcopy(MAT3 dest, MAT3 src) 
{ 
 memcpy(dest, src, sizeof(MAT3)); 
}

 void setrotmatrix(MAT3 mat, VEC3 axis, double angle) 
{
 double x = axis[X], y = axis[Y], z = axis[Z];
 double c = cos(angle), s = sin(angle), t = 1.0-c;

 mat[0][0] = t\*x\*x + c;
 mat[0][1] = t\*x\*y + s\*z;
 mat[0][2] = t\*x\*z - s\*y;
 mat[1][0] = t\*x\*y - s\*z;
 mat[1][1] = t\*y\*y + c;
 mat[1][2] = t\*y\*z + s\*x;
 mat[2][0] = t\*x\*z + s\*y;
 mat[2][1] = t\*y\*z - s\*x;
 mat[2][2] = t\*z\*z + c;
}

 void rotate(VEC3 p, MAT3 mat) 
{ 
 double x = p[X], y = p[Y], z = p[Z];

 p[X] = x\*mat[0][0] + y\*mat[0][1] + z\*mat[0][2];
 p[Y] = x\*mat[1][0] + y\*mat[1][1] + z\*mat[1][2];
 p[Z] = x\*mat[2][0] + y\*mat[2][1] + z\*mat[2][2]; 
}

 void eulershow(char desc[], VEC3 eu) 
{ 
 printf("%s: THETA=%g, PHI=%g, PSI=%g\n", desc,
 rad2deg(eu[THETA]), rad2deg(eu[PHI]), rad2deg(eu[PSI]));
}

 void eulerdecompose(VEC3 euler, MAT3 mat) 
{
 euler[PHI] = asin( -mat[0][2] ); 
 euler[PSI] = atan2( mat[0][1], mat[0][0] );
 euler[THETA] = atan2( mat[1][2], mat[2][2] );
}

Den Rest wirst Du selber hinbekommen :wink:

Hoffentlich habe ich nicht mehr Verwirrung
als Nutzen angerichtet :wink:

(Quelle für Sourcen: meine „Handbibliothek“, über
die Jahre aus verschiedenen Quellen zusammengestoppelt)
Interessant: http://peds.oxfordjournals.org/cgi/content/abstract/…

Grüße

CMБ

1 „Gefällt mir“

Hallo Semjon!
Dank Dir für die umfangreichen Tipps! Lass mich nochmal kurz Deine Lsg beschreiben, so wie ich sie verstanden habe:

* Du erzeugst pro Rotation eine enstrechende RotMatrix. Die multiplizierst Du und anschließend holst Du per Faktorisierung (eulerdecompose) die Werte aus der Gesamtmatrix.
Das klappt für Dein Beispiel. Was ist aber mit einer Matrizenverkettung von Werten +/- 90 Grad und bei willkürlicher Reihenfolge?

Zeitliche Abfolge der Rotationen: 10x, 45z, -90y, 60z, 90x, -20x,

Dürfte nich klappen. Die Faktorisierung verlangt eine statische Reihenfolge der Rotationen, so wie ich das bis jetzt verstanden habe. Ich probiers nachher nochmal aus, muss jetzt los. Bis später.
Besten Gruß!
Kai

Hallo Kai,

Ich habe ein Programm geschrieben, in dem ein Objekt
willkürlich um einen Punkt rotiert werden kann. Die Rotation
erfolgt immer anhand der x, y und z-Achse.

OK, das kann man entweder mit Quaternion- oder
simpler Matrixalgebra lösen. Ich denke, hier
sind Quaternions (imo) völliger Overkill, simple
Matrixmultiplikation tuts auch.

Es handelt sich um ein in c++ geschriebenes Programm, dass
zusammem mit qt und vtk arbeitet. vtk arbeitet letzlich mit
openGL. ich bekomme für die Gesamtrotation vieler
Teilrotationen lediglich eine Gesamtmatrix aus vtk. Hier
treten bei der Faktorisierung der Matrix unter Umständen
Gimbal Locks auf, die ich mit Quaternionen zu beheben
versuche.

Aha. Du willst also Folgendes haben:

  1. einen Punkt im Raum, den es gilt, um
  2. eine freie Rotationsachse, die im Ursprung liegt,
  3. mit einer Rotationsmatrix „zu drehen“ sowie die
  4. Verkettung dieser Rotationen merken, um
  5. am Ende in Eulerwinkel zu zerlegen

Okay der Code. Nicht schön aber selten. Zuerst definiere ich 5
normalisierte Quaternionen für veschiedene Rotationen. Die
Rotationswerte und Achsen (x,y, oder z) können für Tests
verändert werden. Dem Vector qrow werden die Quaternionen in
[…]

Boahh! Das ist ja auch ganz schön kompliziert :wink:
(Ist wohl durch Deine Anwendung bedingt)

Ich würde vorschlagen, die ganze Rotationsgeschichte
herauszunehmen und so simpel wie möglich zu gestalten.

Der Pseudocode für Dein Problem wäre etwa sowas:

int
main(void)
{
MAT3 m1, m2, m3;
VEC3 axis = { 0, 0, 1 }; // rotiere um (normierte!)
Achse
VEC3 point = { 0.7, 1.2, 1.6 }; // dieser Punkt wir rotiert
VEC3 eu; // Euler-Komponenten

setrotmatrix(m1, axis, deg2rad(30)); // 30 grad -> rad
rotate(point, m1);

setrotmatrix(m2, axis, deg2rad(50)); // 50 grad -> rad
rotate(point, m2);

matcopy(m3, m1); // 30 Grad-Rotation (m1) nach m3
sichern
matmult(m3, m2); // Rotationen verketten 30 + 50 grad

eulerdecompose(eu, m3); // Eulerwinkel bestimmen
eulershow(„m3“, eu); // zeigt jetzt 80(!)Grad „psi“

Jetzt müsstest Du dir „nur noch“ die Funktionen
schreiben, die das alles bewerkstelligen, ein Startpunkt
könnte sein:

void matcopy(MAT3 dest, MAT3 src)
{
memcpy(dest, src, sizeof(MAT3));
}

void setrotmatrix(MAT3 mat, VEC3 axis, double angle)
{
double x = axis[X], y = axis[Y], z = axis[Z];
double c = cos(angle), s = sin(angle), t = 1.0-c;

mat[0][0] = t*x*x + c;
mat[0][1] = t*x*y + s*z;
mat[0][2] = t*x*z - s*y;
mat[1][0] = t*x*y - s*z;
mat[1][1] = t*y*y + c;
mat[1][2] = t*y*z + s*x;
mat[2][0] = t*x*z + s*y;
mat[2][1] = t*y*z - s*x;
mat[2][2] = t*z*z + c;
}

void rotate(VEC3 p, MAT3 mat)
{
double x = p[X], y = p[Y], z = p[Z];

p[X] = x*mat[0][0] + y*mat[0][1] + z*mat[0][2];
p[Y] = x*mat[1][0] + y*mat[1][1] + z*mat[1][2];
p[Z] = x*mat[2][0] + y*mat[2][1] + z*mat[2][2];
}

void eulershow(char desc[], VEC3 eu)
{
printf("%s: THETA=%g, PHI=%g, PSI=%g\n", desc,
rad2deg(eu[THETA]), rad2deg(eu[PHI]), rad2deg(eu[PSI]));
}

void eulerdecompose(VEC3 euler, MAT3 mat)
{
euler[PHI] = asin( -mat[0][2] );
euler[PSI] = atan2( mat[0][1], mat[0][0] );
euler[THETA] = atan2( mat[1][2], mat[2][2] );
}

Den Rest wirst Du selber hinbekommen :wink:

Hoffentlich habe ich nicht mehr Verwirrung
als Nutzen angerichtet :wink:

(Quelle für Sourcen: meine „Handbibliothek“, über
die Jahre aus verschiedenen Quellen zusammengestoppelt)
Interessant:
http://peds.oxfordjournals.org/cgi/content/abstract/…

Hallo Kai,

[…] Das klappt für Dein Beispiel. Was ist aber mit
einer Matrizenverkettung von Werten +/- 90 Grad und bei
willkürlicher Reihenfolge?

Zeitliche Abfolge der Rotationen:
10x, 45z, -90y, 60z, 90x, -20x,

Dürfte nich klappen. Die Faktorisierung verlangt eine
statische Reihenfolge der Rotationen, so wie ich das bis jetzt
verstanden habe.

Ich verstehe Dein Problem nicht ganz. Die Euler-Zerlegung
liefert Dir ja *einen* Winkelsatz, der die Rotation
zum Zielpunkt liefert.

Und wenn ich die Rotationsmatrizen aufmultipliziere,
so wie die „Rotationen ankommen“, kann ich doch gar
nichts falschmachen?

Dein Beispiel habe ich gerade mal probiert
(‚Grundachse(x,yz)‘, Winkel):

 ...
 typedef struct { VEC3 axis; double angle; } ROTDSC;

 ROTDSC rot[] = {
 { { 1, 0, 0 }, 10 },
 { { 0, 0, 1 }, 45 }, 
 { { 0, 1, 0 }, -90 },
 { { 0, 0, 1 }, 60 }, 
 { { 1, 0, 0 }, 90 },
 { { 1, 0, 0 }, -20 } };

 int n = sizeof(rot) / sizeof(\*rot);
 subsequent\_rotations(n, rot); // verkette die Rotationen

Das sind, wenn ich es richtig verstanden habe,
Deine Rotationen. Diese sind jeweils auf
eine der Hauptachsen bezogen. Wenn ich
jetzt die Rotationen verkette, liefert mir
die Routine aus dem letzten Posting folgendes
für die Eulerwinkel:

 10 : THETA=10, PHI=0, PSI=0
 45 : THETA=10, PHI=0, PSI=45
-90 : THETA=-80, PHI=-45, PSI=90
 60 : THETA=-80, PHI=-45, PSI=150
 90 : THETA=167.792, PHI=-20.7048, PSI=-130.893
-20 : THETA=-176.338, PHI=-35.035, PSI=-138.408

Das Aufmultiplizieren hab ich einfach in einer
Schleife gemacht:

 ...
 void subsequent\_rotations(int n, ROTDSC rot[])
{
 MAT3 mat, mresult;
 VEC3 point = { 0, 0, 1 };
 VEC3 eu;
 
 identity(mresult); // Resultat-Rotation initialisieren

 for(int i=0; iAber möglicherweise habe ich nicht richtig
verstanden, was Du eigentlich bezweckst?

Grüße

CMБ

Hallo Semjon,
nochmals vielen Dank! Du bist zu schnell für mich :wink:

Ich denke ich kann Dir jetzt das Problem präsentieren! Oder ich hab ein Denkfehler und Du kannst mir im nächsten Posting diesen aufzeigen. Also:

Mit Deinem Prg hast Du nun meine Rotationsreihenfolge „10x, 45z, -90y, 60z, 90x, -20x“ durchgeführt und durch Multiplikation und Eulerdecomposing die drei Alternativwerte „THETA=-176.338, PHI=-35.035, PSI=-138.408“, also „-176.338x, -35.035y, -138.408z“ erhalten. Ich habe es nach Deinen Angaben implementiert und bekomme schon andere Werte raus:

> THETA=10, PHI=-0, PSI=0  
> THETA=7.10708, PHI=-7.05302, PSI=44.5615  
> THETA=135, PHI=-80, PSI=-90  
> THETA=-98.8185, PHI=-36.7798, PSI=139.868  
> THETA=-8.81846, PHI=-36.7798, PSI=139.868  
> THETA=-28.8185, PHI=-36.7798, PSI=139.868

Okay, die Werte sind ja auch nicht eindeutig, Transformationen sind ja auch nicht eindeutig. Könnte man also hinnehmen…

Es ist jedoch so, dass wenn ich einen Punkt p(1,2,3) einmal mit der ersten Rot.Folge transformiere und ein anderes mal den Punkt mit den Alternativwerten transformiere, der Punkt eine unterschiedliche Position einnimmt:

VEC4 point = { 1, 2, 3, 1 }; 

Ergebnis:
point --> -1.91198/-2.57146/1.93182 //nach Transformation mit gesamtmatrix, die aus multiplikation aller Rot.Matrizen stammt.
point --> 0.966327/2.02636/2.99334 //nach Transf. mit Gesamtmatrix, die aus 3 Matrizen mit den Werten aus der Eulertransf. resultieren.

Ich glaube halt, dass man per Faktorisierung von der Matrix nur dann korrekte Werte bekommt, wenn bei der Rotation stets eine feste Reihenfolge der Rotationsrichtungen benutzt wird. also Rges = Rx*Ry*Rz, zum Beispiel. Sowas wie ich angegeben habe - mit einer willkürlichen Reihenfolge - scheint nicht zu klappen, oder?
Oh mann, ich hoffe, dass kam jetzt verständlich rüber…

Hast Du noch eine Idee oder klappt das bei Dir etwa und ich habe falsch gecodet?

Gruß,
Kai

[…] Das klappt für Dein Beispiel. Was ist aber mit
einer Matrizenverkettung von Werten +/- 90 Grad und bei
willkürlicher Reihenfolge?

Zeitliche Abfolge der Rotationen:
10x, 45z, -90y, 60z, 90x, -20x,

Dürfte nich klappen. Die Faktorisierung verlangt eine
statische Reihenfolge der Rotationen, so wie ich das bis jetzt
verstanden habe.

Ich verstehe Dein Problem nicht ganz. Die Euler-Zerlegung
liefert Dir ja *einen* Winkelsatz, der die Rotation
zum Zielpunkt liefert.

Und wenn ich die Rotationsmatrizen aufmultipliziere,
so wie die „Rotationen ankommen“, kann ich doch gar
nichts falschmachen?

Dein Beispiel habe ich gerade mal probiert
(‚Grundachse(x,yz)‘, Winkel):


typedef struct { VEC3 axis; double angle; } ROTDSC;

ROTDSC rot[] = {
{ { 1, 0, 0 }, 10 },
{ { 0, 0, 1 }, 45 },
{ { 0, 1, 0 }, -90 },
{ { 0, 0, 1 }, 60 },
{ { 1, 0, 0 }, 90 },
{ { 1, 0, 0 }, -20 } };

int n = sizeof(rot) / sizeof(*rot);
subsequent_rotations(n, rot); // verkette die
Rotationen

Das sind, wenn ich es richtig verstanden habe,
Deine Rotationen. Diese sind jeweils auf
eine der Hauptachsen bezogen. Wenn ich
jetzt die Rotationen verkette, liefert mir
die Routine aus dem letzten Posting folgendes
für die Eulerwinkel:

10 : THETA=10, PHI=0, PSI=0
45 : THETA=10, PHI=0, PSI=45
-90 : THETA=-80, PHI=-45, PSI=90
60 : THETA=-80, PHI=-45, PSI=150
90 : THETA=167.792, PHI=-20.7048, PSI=-130.893
-20 : THETA=-176.338, PHI=-35.035, PSI=-138.408

Das Aufmultiplizieren hab ich einfach in einer
Schleife gemacht:


void subsequent_rotations(int n, ROTDSC rot[])
{
MAT3 mat, mresult;
VEC3 point = { 0, 0, 1 };
VEC3 eu;

identity(mresult); // Resultat-Rotation initialisieren

for(int i=0; iAber möglicherweise habe ich nicht richtig
verstanden, was Du eigentlich bezweckst?

Grüße

CMБ

Hallo Kai,

Mit Deinem Prg hast Du nun meine Rotationsreihenfolge „10x,
45z, -90y, 60z, 90x, -20x“ durchgeführt und durch
Multiplikation und Eulerdecomposing die drei Alternativwerte
„THETA=-176.338, PHI=-35.035, PSI=-138.408“, also „-176.338x,
-35.035y, -138.408z“ erhalten. Ich habe es nach Deinen Angaben
implementiert und bekomme schon andere Werte raus:

Hmmmmm …

Okay, die Werte sind ja auch nicht eindeutig, Transformationen
sind ja auch nicht eindeutig. Könnte man also hinnehmen…

Es ist jedoch so, dass wenn ich einen Punkt p(1,2,3) einmal
mit der ersten Rot.Folge transformiere und ein anderes mal den
Punkt mit den Alternativwerten transformiere, der Punkt eine
unterschiedliche Position einnimmt:

Ich glaube halt, dass man per Faktorisierung von der Matrix
nur dann korrekte Werte bekommt, wenn bei der Rotation stets
eine feste Reihenfolge der Rotationsrichtungen benutzt wird.
also Rges = Rx*Ry*Rz, zum Beispiel. Sowas wie ich angegeben
habe - mit einer willkürlichen Reihenfolge - scheint nicht zu
klappen, oder?
Oh mann, ich hoffe, dass kam jetzt verständlich rüber…

Ich habe, denke ich, Deinen Gedanken verstanden.

Es stimmt, dass die Reihenfolge der „naiven“
Rotationen *nicht egal* ist bezüglich der
Endkoordinate des rotierten Punktes - aber
bezüglich der Eulerzerlegung sollte das
egal sein, da in meiner Rotationsmatrix-
schreibweise *diese* Matrix eben den Aus-
gangs-Punkt letztlich eindeutig in den Endpunkt
transformiert *und* ja die Eulerzerlegung einfach
nur diese Matrix auswertet.

Hast Du noch eine Idee oder klappt das bei Dir etwa und ich
habe falsch gecodet?

Ob Du falsch programmiert hast, kannst Du relativ
einfach bestimmen, in dem Du einen Fall durchlaufen
lässt, dessen Resultat Dir bekannt ist.

Beispiel: man nehme einen Punkt p{x=0,y=0,z=2}
und rotiere ihn 8x um 45° um die x-Achse v[1,0,0].
Da weiss man an jedem Punkt, wie die Koordinaten
aussehen müssen und wie die Eulerwinkel sein
müssen.

Ich hab das mal eben probiert und erhalte folgende
Ausgabe (Ausgabe der Punktkoordinaten rechts):

THETA=0.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=0.000, Z=2.000]
THETA=45.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=1.414, Z=1.414]
THETA=90.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=2.000, Z=0.000]
THETA=135.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=1.414, Z=-1.414]
THETA=180.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=0.000, Z=-2.000]
THETA=-135.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=-1.414, Z=-1.414]
THETA=-90.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=-2.000, Z=-0.000]
THETA=-45.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=-1.414, Z=1.414]
THETA=-0.000, PHI=0.000, PSI=0.000 POS=[X=0.000, Y=-0.000, Z=2.000]

d.h., ich komme sowohl wieder bei der
Koordinate [0,0,2] als auch bei den
Eulerkomponenten (0,0,0) wieder an. Somit
besteht der begründete Vorverdacht, dass
meine Routinen richtig rechnen :wink:

Hier die angepasste Ausgabefunktion:

 void **eulershowplus** (char desc[], VEC3 eu, VEC3 point) 
{ 
 printf("%s: THETA=%.3f, PHI=%.3f, PSI=%.3f\tPOS=[X=%.3f, Y=%.3f, Z=%.3f]\n", 
 desc,
 rad2deg(eu[THETA]), rad2deg(eu[PHI]), rad2deg(eu[PSI]),
 point[X], point[Y], point[Z]);
}

und die Schleife zur 45°-Schritt-Rotation:

 void more\_rotations()
{
 MAT3 mat, mresult;
 VEC3 point = { 0, 0, 2 }; // dieser Punkt wird rotiert
 VEC3 x\_axis = { 1, 0, 0 }; // um die X-Achse
 int n = 0; // Zaehler 0..8

 identity( **mresult** ); // verkettete Rotationen
 setrotmatrix( mat, x\_axis, deg2rad(45) ); // \*eine\* Rotation \*um\* 45°

 do {
 VEC3 eu;
 eulerdecompose(eu, **mresult** ); // aktuelle (verkettete) 
**eulershowplus** ("", eu, point); // Matrix zerlegen \*und\* 
 // gedrehte Punktkoordinaten
 matmult( **mresult** , mat); // und verketten
 // fuer naechsten Durchlauf
 rotate(point, mat); // naechste Rotation ausfuehren 
 } while (n++Achtung: die Rotation wird \*nur\* mit der
unveränderten Matrix _mat_(R=45°) durchgeführt,
die Matrix _mresult_ dient nur der sukzessiven 
Verkettung, um die Eulerzerlegung machen zu können.

Probier doch mal, was Deine Routine
im "bekannten Fall" liefert.

Grüße

CMБ