Rotationen und Quaternionen



  • Hallo zusammen,

    ich habe folgendes Problem: Ich habe einige Rotationen von Gliedern, diese Rotationen kann ich in meinem Programm abfragen und bekomme Quaternionen, die diese Rotationen darstellen, zurück.

    Ich brauche für meine Zwecke jedoch Winkel in Grad und zwar, naja ich denke mal in Euler Winkeln.

    Wieso ich hier schreibe "Ich denke mal" ?
    Ich hab mich jetzt die letzten 2 Tage damit auseinandergesetzt und ich zweifel ein wenig an mir selbst.

    Bisher dachte ich immer (und nichts anderes wurde mir gelehrt), dass die Rotationen eines Objektes durch 3 Winkel beschrieben werden können: Rotation um die X-, Y- und Z-Achse (diese brauche ich auch).

    Ok, kein Ding dachte ich mir, googlest du einfach mal nach der Umwandlung von Quaternionen zu Euler'schen Winkeln. Kackpunkt dabei ist, dass jede Website was anderes angibt.
    Wiki1
    Wiki2
    Euclidean Space.com
    Online Umwandler

    Ich benutze in meinem (C#-)Programm das XNA Framework, dort sind Quaternionen schon implementiert (ansonsten würde ich sie gar nicht verwenden).
    Ich habe mir also mit dieser Klasse ein paar simple Tests geschrieben, damit ich sehe ob die entsprechende Umwandlungsmethode richtig ist.

    SO nach folgendem Pseudocode:

    rotationsVector = (90, 0, 0)
    
    rotationsMatrix = createYawPitchRoll(rotationsVector.x, rotationsVector.y, rotationsVector.z)
    
    rotationsQuaternion = rotationsMatrix.getRotationsQuaternion() 
    
    resultVector = rotationsQuaternion.toEulerAngles()
    
    assert( resultVector == rotationsVector )
    

    Alle Methoden (bis auf die "toEulerAngles") sind von XNA schon bereitgestellt.

    Der Test klappte natürlich... Nicht!

    Da meistens nur die Zahlen vertauscht waren (z.B. kam öfters dann anstelle von (90, 0, 0) sowas raus (0, 90, 0), dachte ich, Ok, die Kanäle sind vertauscht und ich hab angefangen, diese zu tauschen. Das habe ich jedoch nicht allgemeingültig geschafft (also so, dass es alle Test besteht).

    Ich habe dann angefangen, meinen Test zu reduzieren:

    rotationsVector = (90, 0, 0)
    
    rotationsMatrix = createYawPitchRoll(rotationsVector.x, rotationsVector.y, rotationsVector.z)
    
    resultVector = rotationsMatrix.getAngles();
    

    mit

    public static Vector3 getAngles(this Matrix r)
            {
                float beta = (float)Math.Atan2(-r.M31, Math.Sqrt(r.M11 * r.M11 + r.M21 * r.M21)); ;
                float alpha = (float)Math.Atan2(r.M21 / Math.Cos(beta), r.M11 / Math.Cos(beta));
                float gamma = (float)Math.Atan2(r.M32 / Math.Cos(beta), r.M33 / Math.Cos(beta));
    
                if (equalFloat(beta, MathHelper.PiOver2)) // if (beta == MathHelper.PiOver2)
                {
                    alpha = 0.0f;
                    gamma = (float)Math.Atan2(r.M12, r.M22);
                }
                else if (equalFloat(beta, -MathHelper.PiOver2)) //(beta == -MathHelper.PiOver2)
                {
                    alpha = 0.0f;
                    gamma = (float)-Math.Atan2(r.M12, r.M22);
                }
                else
                {
                    alpha = (float)Math.Atan2(r.M21 / Math.Cos(beta), r.M11 / Math.Cos(beta));
                    gamma = (float)Math.Atan2(r.M32 / Math.Cos(beta), r.M33 / Math.Cos(beta));
                }
    
                return new Vector3((float)alpha, (float)beta, (float)gamma);
            }
    

    Und hier sind schon Fehler drin.

    Daher meine Fragen:
    - In welcher Reihenfolge müssen die Winkel in sowas wie "CreateMatrixFromYawPitchRoll" reingesteckt werden? xyz ist falsch, laut Wikipedia würde ich dann zyx versuchen, aber das geht auch schief...

    - Die Komponenten eines Quaternions sind wie zu interpretieren? xyzw, wobei x der Realteil und die anderen die Imaginärteile sind? Bei dem Online-Umrechner (der Link ist oben schon drin) ist u0 der w Teil, u1 z, u2 y und u3 der x Teil...

    Ich bin mittlerweile total durcheinander, vielleicht kann jemand mir helfen meine Gedanken zu ordnen...

    PS: Wie ich hörte gibt es Rotationen um die Roll-Nick-Gier Achse, Eulersche Winkel, Rotationsmatrizen und auch Achsen Winkel (Vector3 axis, float angle) neben Quaternionen...
    Hä?!?



  • Skym0sh0 schrieb:

    Ich brauche für meine Zwecke jedoch Winkel in Grad und zwar, naja ich denke mal in Euler Winkeln.

    Warum? Down that path lies madness...



  • Ich wusste, dass solche Antowrten kommen würde:

    Mein Programm exportiert einige Daten (unter anderem die Rotationswinkel von Gliedmaßen) und die Programm, die diese Daten importieren erwarten Winkelangaben.

    Zumal Winkel etwas einfacher zu lesen und vorzustellen sind (ich bin jedenfalls nicht darauf trainiert mir unter Quaternionen was vorzustellen).

    Jaja, Nachteile hin oder her, Glimbal-Lock auch. Ich kann jedenfalls 3D-Studio nicht mal eben so ändern...



  • Skym0sh0 schrieb:

    Ich wusste, dass solche Antowrten kommen würde:

    Mein Programm exportiert einige Daten (unter anderem die Rotationswinkel von Gliedmaßen) und die Programm, die diese Daten importieren erwarten Winkelangaben.

    Wenn es sich wirklich nicht vermeiden lässt, dann schau z.B. hier:
    http://www.flipcode.com/documents/matrfaq.html#Q54
    http://www.flipcode.com/documents/matrfaq.html#Q37

    Die Kompliziertheit obiger Operationen reflektiert lediglich das Böse, das allein im Gedanken steckt, sowas zu tun...

    Skym0sh0 schrieb:

    Zumal Winkel etwas einfacher zu lesen und vorzustellen sind (ich bin jedenfalls nicht darauf trainiert mir unter Quaternionen was vorzustellen).

    Nach Eulers Rotationstheorem lässt sich jede beliebige Orientierung in 3D durch eine einzelne Rotation um eine bestimmte (frei im Raum liegende) Achse erreichen. Auch wenn du 100 Rotationen nacheinander ausführst, so gibt es am Ende immer noch genau eine Rotation um irgendeine Achse die effektiv zur gleichen Ausrichtung führt, wenn auch der Weg dort hin ein anderer ist. (Ich weiß, kaum zu glauben, aber genauso leicht auszuprobieren... ;))

    Eine Einheitsquaternion (Quaternion mit Länge 1, nur solche repräsentieren Orientierungen) beschreibt im Prinzip nichts anderes als eine Achse und den entsprechenden Winkel. Die drei Imaginärteile sind Koordinaten der Richtung der Achse und der Realteil entspricht dem Rotationswinkel um diese Achse (genaugenommen dem cos des halben Winkels)...



  • dot schrieb:

    Skym0sh0 schrieb:

    Zumal Winkel etwas einfacher zu lesen und vorzustellen sind (ich bin jedenfalls nicht darauf trainiert mir unter Quaternionen was vorzustellen).

    Nach Eulers Rotationstheorem lässt sich jede beliebige Orientierung in 3D durch eine einzelne Rotation um eine bestimmte (frei im Raum liegende) Achse erreichen. Auch wenn du 100 Rotationen nacheinander ausführst, so gibt es am Ende immer noch genau eine Rotation um irgendeine Achse die effektiv zur gleichen Ausrichtung führt, wenn auch der Weg dort hin ein anderer ist. (Ich weiß, kaum zu glauben, aber genauso leicht auszuprobieren... ;))

    Eine Einheitsquaternion (Quaternion mit Länge 1, nur solche repräsentieren Orientierungen) beschreibt im Prinzip nichts anderes als eine Achse und den entsprechenden Winkel. Die drei Imaginärteile sind Koordinaten der Richtung der Achse und der Realteil entspricht dem Rotationswinkel um diese Achse (genaugenommen dem cos des halben Winkels)...

    Saubere Arbeit, damit kann ich was anfangen. Jetzt kann ich es endlich in meiner Vorstellung begreifen.

    Ok, zum ersten Teil, ja da gucke ich dann auch mal rein. (Die Seite kenne ich sogar und auch da hab ich ein paar mal recherchiert)

    Wie sieht denn das mit dem Rollen-Nicken-Gieren (in XNA) aus?
    Rollen -> Drehung um die X-Achse
    Nicken -> Drehung um die Y-Achse
    Gieren -> Drehung um die Z-Achse

    Stimmt das so?
    Müsste ich dann eine Yaw-Pitch-Roll Matrix in der Reihenfolge erstellen?

    Xna.Framework.Vector3 rotational = new Xna.Framework.Vector3(90.0f, 0.0f, 0.0f);
    
    rotational = rotational.ToRadians(); // optional
    
    Matrix rotationsMatrix = Matrix.CreateFromYawPitchRoll(rotational.Z, rotational.Y, rotational.X);
    


  • YawPitchRoll entspricht normalerweise der Sequenz ZXY. Hängt aber natürlich von deiner Achsenkonvention ab...



  • Ok, ich hab hier von einem Kollegen(der da selbst schonmal vor einiger Zeit über eine Woche dransaß) seine Umrechnung bekommen. ich poste sie einfach mal für alle nachfolgenden Problemkinder.

    //In a 2D grid, returns the angle to a specified point from the +X axis
    private static float ArcTanAngle(float X, float Y)
    {
    	if (X == 0)
    	{
    		if (Y == 1)
    			return (float)Microsoft.Xna.Framework.MathHelper.PiOver2;
    		else
    			return (float)-Microsoft.Xna.Framework.MathHelper.PiOver2;
    	}
    	else if (X > 0)
    		return (float)Math.Atan(Y / X);
    	else if (X < 0)
    	{
    		if (Y > 0)
    			return (float)Math.Atan(Y / X) + Microsoft.Xna.Framework.MathHelper.Pi;
    		else
    			return (float)Math.Atan(Y / X) - Microsoft.Xna.Framework.MathHelper.Pi;
    	}
    	else
    		return 0;
    }
    
    //returns Euler angles that point from one point to another
    private static Microsoft.Xna.Framework.Vector3 AngleTo(Microsoft.Xna.Framework.Vector3 from, Microsoft.Xna.Framework.Vector3 location)
    {
    	Microsoft.Xna.Framework.Vector3 angle = new Microsoft.Xna.Framework.Vector3();
    	Microsoft.Xna.Framework.Vector3 v3 = Microsoft.Xna.Framework.Vector3.Normalize(location - from);
    	angle.X = (float)Math.Asin(v3.Y);
    	angle.Y = ArcTanAngle(-v3.Z, -v3.X);
    	return angle;
    }
    
    //converts a Quaternion to Euler angles (X = pitch, Y = yaw, Z= roll)
    public static Microsoft.Xna.Framework.Vector3 QuaternionToEuler(Microsoft.Xna.Framework.Quaternion rotation)
    {
    	Microsoft.Xna.Framework.Vector3 rotationaxes = new Microsoft.Xna.Framework.Vector3();
    
    	Microsoft.Xna.Framework.Vector3 forward = Microsoft.Xna.Framework.Vector3.Transform(Microsoft.Xna.Framework.Vector3.Forward, rotation);
    	Microsoft.Xna.Framework.Vector3 up = Microsoft.Xna.Framework.Vector3.Transform(Microsoft.Xna.Framework.Vector3.Up, rotation);
    	rotationaxes = AngleTo(new Microsoft.Xna.Framework.Vector3(), forward);
    	if (rotationaxes.X == Microsoft.Xna.Framework.MathHelper.PiOver2)
    	{
    		rotationaxes.Y = ArcTanAngle(up.Z, up.X);
    		rotationaxes.Z = 0;
    	}
    	else if (rotationaxes.X == -Microsoft.Xna.Framework.MathHelper.PiOver2)
    	{
    		rotationaxes.Y = ArcTanAngle(-up.Z, -up.X);
    		rotationaxes.Z = 0;
    	}
    	else
    	{
    		up = Microsoft.Xna.Framework.Vector3.Transform(up,
    		Microsoft.Xna.Framework.Matrix.CreateRotationY(-rotationaxes.Y));
    		up = Microsoft.Xna.Framework.Vector3.Transform(up,
    		Microsoft.Xna.Framework.Matrix.CreateRotationX(-rotationaxes.X));
    		rotationaxes.Z = ArcTanAngle(up.Y, -up.X);
    	}
    
    	float tmp = rotationaxes.X;
    	rotationaxes.X = rotationaxes.Y;
    	rotationaxes.Y = tmp;
    
    	return rotationaxes;
    }
    

Log in to reply