Wie wird das Gimbal Locked-Problem mithilfe von akkumulativen Matrixtransformationen gelöst?

12

Ich lese gerade das Online-Buch "Learning Modern 3D Graphics Programming" von Jason L. McKesson

Ab sofort bin ich mit dem Gimbal-Lock-Problem beschäftigt und weiß, wie es mit Hilfe von Quaternionen gelöst werden kann.

Allerdings gleich hier auf der Quaternions-Seite .

Ein Teil des Problems ist, dass wir versuchen, eine Orientierung als eine Reihe von 3 akkumulierten axialen Rotationen zu speichern. Orientierungen sind Orientierungen, keine Rotationen. Und Orientierungen sind mit Sicherheit keine Rotationsserie. Wir müssen also die Ausrichtung des Schiffes als eine Ausrichtung behandeln, als eine bestimmte Größe.

Ich denke, dies ist der erste Punkt, an dem ich anfange, verwirrt zu sein. Der Grund dafür ist, dass ich den dramatischen Unterschied zwischen Orientierungen und Rotationen nicht sehe. Ich verstehe auch nicht, warum eine Orientierung nicht durch eine Reihe von Rotationen dargestellt werden kann ...

Ebenfalls:

Der erste Gedanke zu diesem Zweck wäre, die Orientierung als Matrix beizubehalten. Wenn die Zeit gekommen ist, um die Ausrichtung zu ändern, wenden wir einfach eine Transformation auf diese Matrix an und speichern das Ergebnis als neue aktuelle Ausrichtung.

Dies bedeutet, dass jedes Gieren, Nicken und Rollen, das auf die aktuelle Ausrichtung angewendet wird, relativ zu dieser aktuellen Ausrichtung ist. Welches ist genau das, was wir brauchen. Wenn der Benutzer ein positives Gieren anwendet, soll dieses Gieren sie relativ zu der Position drehen, auf die sie gerade zeigen, und nicht relativ zu einem festen Koordinatensystem.

Das Konzept verstehe ich, aber ich verstehe nicht, wie, wenn das Akkumulieren von Matrixtransformationen eine Lösung für dieses Problem ist, der Code auf der vorherigen Seite nicht genau das ist.

Hier ist der Code:

void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack currMatrix;
    currMatrix.Translate(glm::vec3(0.0f, 0.0f, -200.0f));
    currMatrix.RotateX(g_angles.fAngleX);
    DrawGimbal(currMatrix, GIMBAL_X_AXIS, glm::vec4(0.4f, 0.4f, 1.0f, 1.0f));
    currMatrix.RotateY(g_angles.fAngleY);
    DrawGimbal(currMatrix, GIMBAL_Y_AXIS, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
    currMatrix.RotateZ(g_angles.fAngleZ);
    DrawGimbal(currMatrix, GIMBAL_Z_AXIS, glm::vec4(1.0f, 0.3f, 0.3f, 1.0f));

    glUseProgram(theProgram);
    currMatrix.Scale(3.0, 3.0, 3.0);
    currMatrix.RotateX(-90);
    //Set the base color for this object.
    glUniform4f(baseColorUnif, 1.0, 1.0, 1.0, 1.0);
    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));

    g_pObject->Render("tint");

    glUseProgram(0);

    glutSwapBuffers();
}

Nach meinem Verständnis wird das, was er tut (Ändern einer Matrix auf einem Stapel), nicht als Akkumulieren von Matrizen angesehen, da der Autor alle einzelnen Rotationstransformationen zu einer Matrix zusammengefasst hat, die oben auf dem Stapel gespeichert wird.

Ich verstehe eine Matrix so, dass sie verwendet wird, um einen Punkt, der relativ zu einem Ursprung ist (sagen wir ... dem Modell), und ihn relativ zu einem anderen Ursprung (der Kamera) zu machen. Ich bin mir ziemlich sicher, dass dies eine sichere Definition ist, aber ich habe das Gefühl, dass etwas fehlt, was mich daran hindert, dieses Gimbal-Lock-Problem zu verstehen.

Eine Sache, die für mich keinen Sinn macht, ist: Wenn eine Matrix den Unterschied zwischen zwei "Räumen" bestimmt, wie kommt es, dass eine Drehung um die Y-Achse zum Beispiel "rollen" den Punkt nicht in den "Rollenraum" setzt "die dann noch einmal in bezug auf diese Rolle transformiert werden können ... Mit anderen Worten, sollten keine weiteren Transformationen bis zu diesem Punkt in bezug auf diesen neuen" Rollenraum "erfolgen und daher darf die Rotation nicht in bezug auf die vorherige sein" Modellraum ", der die kardanische Verriegelung verursacht.

Das ist der Grund, warum die Kardansperre auftritt, richtig? Das liegt daran, dass wir das Objekt um die X-, Y- und Z-Achse drehen, anstatt es um seine eigenen relativen Achsen zu drehen . Oder liege ich falsch?

Da dieser Code, den ich verlinkt habe, anscheinend keine Ansammlung von Matrixtransformationen ist, können Sie bitte ein Beispiel für eine Lösung mit dieser Methode geben.

Also zusammenfassend:

  • Was ist der Unterschied zwischen einer Drehung und einer Orientierung?
  • Warum ist der Code in kein Beispiel für die Anhäufung von Matrixtransformationen eingebunden?
  • Was ist der eigentliche, spezifische Zweck einer Matrix, wenn ich falsch lag?
  • Wie könnte eine Lösung des Gimbal-Lock-Problems durch Akkumulation von Matrixtransformationen implementiert werden?
  • Als Bonus: Warum sind die Transformationen nach der Rotation noch relativ zum "Modellraum"?
  • Ein weiterer Bonus: Bin ich falsch in der Annahme, dass nach einer Transformation weitere Transformationen relativ zum Strom stattfinden werden?

Auch wenn es nicht impliziert wurde, verwende ich OpenGL, GLSL, C ++ und GLM, daher sind Beispiele und Erklärungen in Bezug auf diese sehr willkommen, falls dies nicht erforderlich ist.

Je mehr das Detail desto besser!

Danke im Voraus.

Luke San Antonio Bialecki
quelle

Antworten:

11

Ich bin mir nicht sicher, wie ich das vorwegnehmen kann, außer ich hoffe, dass es am Ende gut zusammenpasst. Das heißt, lassen Sie uns eintauchen in:

Eine Rotation und eine Orientierung sind unterschiedlich, weil die erstere eine Transformation beschreibt und die letztere einen Zustand beschreibt. Eine Drehung ist, wie ein Objekt in eine Ausrichtung gelangt , und eine Ausrichtung ist der lokal gedrehte Raum des Objekts . Dies kann direkt damit zusammenhängen, wie die beiden mathematisch dargestellt werden: Eine Matrix speichert Transformationen von einem Koordinatenraum in einen anderen (das war korrekt), und ein Quaternion beschreibt direkt eine Ausrichtung. Die Matrix kann daher nur beschreiben, wie das Objekt durch eine Reihe von Rotationen in eine Orientierung gelangt. Das Problem dabei ist jedoch Gimbal Lock.

Die kardanische Verriegelung demonstriert die Schwierigkeit, ein Objekt durch eine Reihe von Drehungen in eine Ausrichtung zu bringen. Das Problem tritt auf, wenn mindestens zwei der Rotationsachsen ausgerichtet sind:

Bild mit freundlicher Genehmigung von deepmesh3d.com
Im linken Bild oben drehen sich die blaue und die orange Achse gleich! Dies ist ein Problem, da dies bedeutet, dass einer der drei Freiheitsgrade verloren gegangen ist und zusätzliche Rotationen von diesem Punkt aus zu unerwarteten Ergebnissen führen können. Die Verwendung von Quaternionen behebt dieses Problem, da durch das Anwenden einer Quaternion zum Transformieren der Ausrichtung eines Objekts das Objekt direkt in eine neue Ausrichtung versetzt wird (so kann ich es am besten ausdrücken), anstatt die Transformation in Roll-, Nick- und Gieroperationen zu unterteilen.

Jetzt bin ich tatsächlich skeptisch, ob das Akkumulieren von Matrizen eine vollständige Lösung für dieses Problem ist, da das Akkumulieren von Matrizen (also das Akkumulieren von Rotationen) genau das Problem ist, das das Gimbal-Lock-Problem verursachen kann. Der richtige Weg, mit der Transformation durch eine Quaternion umzugehen, besteht darin, entweder eine Quaternionsmultiplikation für einen Punkt durchzuführen:

pTransformed = q * pAsQuaternion * qConjugate

oder durch Konvertieren des Quaternions in eine Matrix und Transformieren des Punkts unter Verwendung dieser Matrix.

Im globalen Raum wird immer eine einfache Matrixrotation (z. B. ein 45-Grad-Gierwinkel) definiert. Wenn Sie die Umwandlung im lokalen Raum anwenden möchten, müssen Sie die Umwandlung in den lokalen Raum des Objekts umwandeln. Es klingt seltsam, also werde ich näher darauf eingehen. Hier kommt die Wichtigkeit der Reihenfolge der Rotationen zum Tragen. Ich empfehle, hier ein Buch zu lesen, damit Sie die Transformationen mitverfolgen können.

Beginnen Sie mit dem flachen Buch, dessen Umschlag zur Decke zeigt und so ausgerichtet ist, als wollten Sie es öffnen und mit dem Lesen beginnen. Kippen Sie nun die Vorderseite des Buches um 45 Grad nach oben (der vordere Einband sollte ungefähr zu Ihnen zeigen):

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(45);

Angenommen, Sie möchten das Gierwinkel des Buches um 45 Grad einstellen (ich gehe von einem rechtshändigen Koordinatensystem aus, sodass sich die Überschrift nach links ändert) und möchten, dass dies auf die lokale Position des Buches zutrifft Koordiniere den Raum, so dass der Einband des Buches noch zu dir zeigt:

bookMatrix.RotateY(45);

Das Problem ist, dass diese Drehung im globalen Koordinatenraum stattfindet, sodass der Buchdeckel über Ihre rechte Schulter zeigt. Damit diese Änderung der Überschrift im lokalen Koordinatenraum auftritt, sollten Sie sie zuerst anwenden!

glutil::MatrixStack bookMatrix;
bookMatrix.RotateY(45);
bookMatrix.RotateX(45);

Versuch es! Legen Sie das Buch wieder mit der Vorderseite nach oben an die Decke. Ändern Sie das Gierwinkel um 45 Grad und neigen Sie es dann um 45 Grad entlang der globalen X-Achse (von links nach rechts). Dies ist die Ausrichtung, die Sie mit einer Teilung von 45 und einem Gieren von 45 im lokalen Raum des Buches erwartet haben.

Was bedeutet das? Alles, worauf es wirklich ankommt, ist, dass die Reihenfolge der Operationen wichtig ist. Transformationen, die zuerst durchgeführt werden, werden zu lokalen Transformationen im Kontext von Transformationen, die anschließend durchgeführt werden. Es wird eine Menge, den Kopf herumzuwickeln, und so sparen Quaternionen eine Menge Ärger. Die überspringen alle auftragsabhängigen Sachen.

Der andere große Vorteil, den Quaternionen bieten, besteht darin, dass sie die Interpolation von Orientierungen ermöglichen. Der Versuch, zwischen Eulerwinkeln zu interpolieren, ist aufgrund der Ordnungsabhängigkeiten nahezu unmöglich. Die mathematischen Eigenschaften des Quaternions ermöglichen eine genau definierte sphärische lineare Interpolation zwischen ihnen.

Um alles zusammenzufassen und Ihre ursprüngliche Frage zu beantworten: Akkumulative Matrixtransformationen lösen das Gimbal-Lock-Problem nicht wirklich, es sei denn, die Transformationen werden sorgfältig ausgewählt und in einer genauen Reihenfolge angewendet. Verwenden Sie daher immer Quaternionen und wenden Sie Quaternionen mithilfe der Quaternionsmultiplikation auf Punkte an.

Hoffe das hilft :)

kevintodisco
quelle
4
Nur zur Veranschaulichung: Quaternionen können immer noch eine Kardansperre einführen, wenn dies über Euler-Winkel beschrieben wird. da Sie die gleiche Berechnung auf andere Weise durchführen (Quaternionen statt Matrizen)
concept3d
1
@ concept3d - herzlichen Glückwunsch zu dieser Erwähnung! Es ist wichtig zu verstehen, warum der Kardanmechanismus dazu neigt, einen Freiheitsgrad zu verlieren: Es ist wie ein Robotergelenk, das von Natur aus ein überbestimmtes Gleichungssystem beschreibt. Wenn Sie diesen Mechanismus mit Quaternionen, Matrizen oder Magie aufbauen, werden Sie dennoch mit Mehrdeutigkeiten konfrontiert - es ist eine echte Lösung, es sei denn, Sie müssen ihn für einen demonstrativen oder technischen Zweck verwenden. .
Teodron
Quaternionen sind schwer vorstellbar, ich denke immer, dass sie (Einheitsquaternionen) einen 3-Sphären-Raum repräsentieren, also jede Orientierung repräsentieren können, während ich verstehe, dass Euler-Winkel jeweils Kreise / Turos repräsentieren, also keine vollständige Sphäre sind ist nicht sehr genau, um die Orientierung darzustellen (3 Kreise / Torus können nicht wirklich jede mögliche Orientierung erzeugen, es sei denn, sie drehen sich unabhängig voneinander, was bei Eulerwinkeln nicht möglich ist), nicht sicher, ob ich es genau erklärt habe :)
concept3d
1

Matrixakkumulationen können in der Tat die Gimbal-Sperre lösen. Durch das Akkumulieren von Rotationen fügen Sie Kardanringe hinzu, die jede beliebige Rotation zulassen. Das von ktodisco bereitgestellte Diagramm zeigt eine Kardansperre im linken Diagramm. Die Matrix für diese Ausrichtung kann wie folgt definiert werden:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);

Aufgrund der Drehung des y-Kardans sind die X- und Z-Kardans jetzt gesperrt, sodass wir einen Bewegungsgrad verloren haben. Zu diesem Zeitpunkt haben wir kein Gieren (lokales y, globales z) unter Verwendung dieser drei Kardanringe. Durch Hinzufügen eines weiteren Kardanrahmens kann ich mich lokal um das y drehen:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);
bookMatrix.RotateY(90);

Fügen Sie für jeden neuen Wurf, jede Neigung und jedes Gieren einen weiteren Kardanring hinzu, der sie in einer Matrix akkumuliert. Jedes Mal, wenn eine weitere lokale Drehung benötigt wird, wird eine Drehung erstellt und mit der Akkumulationsmatrix multipliziert. Wie im Kapitel erwähnt, gibt es immer noch Probleme, aber Gimbal Lock gehört nicht dazu.

Justin Ehrlich
quelle