¿Cómo se resuelve el problema de cardán bloqueado usando transformaciones de matriz acumulativa

12

Estoy leyendo el libro en línea "Aprendizaje de la programación moderna de gráficos 3D" de Jason L. McKesson

A partir de ahora, estoy preparado para el problema del bloqueo del cardán y cómo resolverlo usando cuaterniones.

Sin embargo, aquí mismo, en la página Quaternions .

Parte del problema es que estamos tratando de almacenar una orientación como una serie de 3 rotaciones axiales acumuladas. Las orientaciones son orientaciones, no rotaciones. Y las orientaciones ciertamente no son una serie de rotaciones. Por lo tanto, debemos tratar la orientación del barco como una orientación, como una cantidad específica.

Supongo que este es el primer punto en el que empiezo a confundirme, la razón es porque no veo la diferencia dramática entre orientaciones y rotaciones. Tampoco entiendo por qué una orientación no puede ser representada por una serie de rotaciones ...

También:

El primer pensamiento hacia este fin sería mantener la orientación como una matriz. Cuando llega el momento de modificar la orientación, simplemente aplicamos una transformación a esta matriz, almacenando el resultado como la nueva orientación actual.

Esto significa que cada guiñada, inclinación y balance aplicado a la orientación actual será relativa a esa orientación actual. Que es precisamente lo que necesitamos. Si el usuario aplica un guiñada positivo, desea que ese guiñada los rote en relación con el lugar donde apuntan actualmente, no en relación con algún sistema de coordenadas fijo.

Entiendo el concepto, sin embargo, no entiendo cómo si la acumulación de transformaciones matriciales es una solución a este problema, cómo el código dado en la página anterior no es solo eso.

Aquí está el código:

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();
}

Según tengo entendido, no es lo que está haciendo (modificar una matriz en una pila) considerar la acumulación de matrices, ya que el autor combinó todas las transformaciones de rotación individuales en una matriz que se almacena en la parte superior de la pila.

Mi comprensión de una matriz es que se utilizan para tomar un punto que es relativo a un origen (digamos ... el modelo), y hacerlo relativo a otro origen (la cámara). Estoy bastante seguro de que esta es una definición segura, sin embargo, siento que falta algo que me impide comprender este problema de bloqueo de cardán.

Una cosa que no tiene sentido para mí es: si una matriz determina la diferencia relativa entre dos "espacios", ¿cómo es que una rotación alrededor del eje Y para, digamos, rodar, no pone el punto en "rodar espacio"? "que luego se puede transformar una vez más en relación con este rollo ... En otras palabras, no debería haber más transformaciones a este punto en relación con este nuevo" espacio de rollo "y, por lo tanto, no tener la rotación en relación con el anterior" espacio modelo "que está causando el bloqueo del cardán.

Es por eso que se produce el bloqueo del cardán ¿verdad? Es porque estamos girando el objeto alrededor de los ejes X, Y y Z en lugar de girar el objeto alrededor de sus propios ejes relativos . ¿O estoy equivocado?

Dado que aparentemente este código que he vinculado no es una acumulación de transformaciones matriciales, ¿puede dar un ejemplo de una solución con este método?

Entonces en resumen:

  • ¿Cuál es la diferencia entre una rotación y una orientación?
  • ¿Por qué el código vinculado no es un ejemplo de acumulación de transformaciones matriciales?
  • ¿Cuál es el propósito real y específico de una matriz, si me equivoqué?
  • ¿Cómo podría implementarse una solución al problema del bloqueo del cardán utilizando la acumulación de transformaciones matriciales?
  • Además, como beneficio adicional: ¿por qué las transformaciones después de la rotación siguen siendo relativas al "espacio modelo"?
  • Otra ventaja: ¿me equivoco al suponer que después de una transformación, se producirán más transformaciones en relación con la corriente?

Además, si no estaba implícito, estoy usando OpenGL, GLSL, C ++ y GLM, por lo que los ejemplos y explicaciones en términos de estos son muy apreciados, si no es necesario.

¡Cuanto más detalle, mejor!

Gracias por adelantado.

Luke San Antonio Bialecki
fuente

Respuestas:

11

No estoy seguro de una buena manera de prefacio esto, aparte de que espero que se una muy bien al final. Dicho esto, vamos a sumergirnos en:

Una rotación y una orientación son diferentes porque la primera describe una transformación y la segunda describe un estado. Una rotación es cómo un objeto entra en una orientación , y una orientación es el espacio rotado local del objeto . Esto puede estar directamente relacionado con la forma en que los dos están representados matemáticamente: una matriz almacena transformaciones de un espacio de coordenadas a otro (usted tenía eso correcto), y un cuaternión describe directamente una orientación. La matriz, por lo tanto, solo puede describir cómo el objeto se orienta , a través de una serie de rotaciones. Sin embargo, el problema con esto es Gimbal Lock.

El bloqueo de cardán demuestra la dificultad de orientar un objeto mediante una serie de rotaciones. El problema ocurre cuando al menos dos de los ejes de rotación se alinean:

Imagen cortesía de deepmesh3d.com
En la imagen izquierda de arriba, los ejes azul y naranja hacen la misma rotación. Esto es un problema, porque significa que se ha perdido uno de los tres grados de libertad, y las rotaciones adicionales a partir de este punto pueden producir resultados inesperados. El uso de quaternions resuelve esto porque aplicar un quaternion para transformar la orientación de un objeto colocará directamente el objeto en una nueva orientación (esa es la mejor forma en que puedo decirlo), en lugar de dividir la transformación en operaciones de balanceo, cabeceo y guiñada.

Ahora, en realidad soy escéptico sobre la acumulación de matrices como una solución completa a esto, porque la acumulación de matrices (por lo tanto, la acumulación de rotaciones) es exactamente lo que puede causar el problema de Gimbal Lock en primer lugar. La forma correcta de manejar la transformación por un cuaternión es realizar la multiplicación del cuaternión en un punto:

pTransformed = q * pAsQuaternion * qConjugate

o convirtiendo el cuaternión en una matriz y transformando el punto usando esa matriz.

Una rotación de matriz simple (como un guiñada de 45 grados) siempre se definirá en el espacio global. Si desea aplicar la transformación en el espacio local, tendría que transformar su transformación en ese espacio local de objetos. Suena extraño, así que elaboraré. Aquí es donde entra en juego la importancia del orden de las rotaciones. Recomiendo tomar un libro aquí para que pueda seguir las transformaciones.

Comience con el libro plano, con la tapa hacia arriba en el techo, orientado como si estuviera a punto de abrirlo y comenzar a leer. Ahora incline el frente del libro 45 grados hacia arriba (la portada debería estar orientada hacia usted):

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

Ahora, supongamos que desea ajustar el guiñada del libro 45 grados (creo que estoy asumiendo un sistema de coordenadas derecho, por lo que esto cambiará de rumbo a la izquierda), y desea que esto se aplique al local del libro. coordine el espacio, de modo que la portada del libro aún esté frente a usted:

bookMatrix.RotateY(45);

El problema es que esta rotación ocurre en el espacio de coordenadas global, por lo que la portada del libro terminará mirando por encima de su hombro derecho. Para que este cambio de rumbo ocurra en el espacio de coordenadas local, ¡debería haberlo aplicado primero!

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

¡Pruébalo! Comience el libro mirando hacia el techo nuevamente. Cambie su guiñada 45 grados y luego inclínelo 45 grados a lo largo del eje X global (de izquierda a derecha). Esta es la orientación que esperaba con un tono de 45 y un guiñada de 45 en el espacio local del libro.

¿Qué significa esto? Todo se reduce a que el orden de las operaciones es importante. Las transformaciones realizadas primero se convierten en transformaciones locales en el contexto de las transformaciones realizadas después. Se vuelve mucho más difícil de entender, y así es como los cuaterniones ahorran muchos problemas. Se saltan todas las cosas dependientes del orden.

La otra gran ventaja que proporcionan los cuaterniones es que permiten la interpolación de orientaciones. Intentar interpolar entre ángulos de Euler es casi imposible debido a las dependencias de orden. Las propiedades matemáticas del cuaternión permiten una interpolación lineal esférica bien definida entre ellos.

Para concluir y abordar su pregunta original: las transformaciones de matriz acumulativa realmente no resolverán el problema del bloqueo del cardán, a menos que las transformaciones se elijan cuidadosamente y se apliquen en un orden preciso. Por lo tanto, use siempre cuaterniones y aplique cuaterniones a los puntos usando la multiplicación de cuaterniones.

Espero que esto ayude :)

kevintodisco
fuente
44
solo para que conste, los cuaterniones aún pueden introducir el bloqueo de cardán si se describe a través de los ángulos de Euler; ya que harás el mismo cálculo de una manera diferente (cuaterniones en lugar de matrices)
concept3d
1
@ concept3d - ¡felicidades por mencionar esto! Es importante comprender qué hace que el mecanismo del cardán sea propenso a perder un cierto grado de libertad: es como una articulación robótica que describe inherentemente un sistema de ecuaciones sobredeterminado. Si construyes este mecanismo con cuaterniones, matrices o magia, aún terminas con ambigüedades: entenderlo y no usarlo en primer lugar es una solución real (a menos que tengas que usarlo para algún propósito técnico o demostrativo) .
teodron el
los cuaterniones son difíciles de imaginar, la forma en que siempre pienso es que ellos (cuaterniones unitarios) representan un espacio de 3 Esferas, por lo tanto, pueden representar cualquier orientación, mientras entiendo que los ángulos de Euler representan círculos / turos, por lo tanto, no es una esfera completa. no es una forma muy precisa de representar la orientación (3 círculos / toro realmente no pueden generar todas las orientaciones posibles a menos que giren independientemente, lo que no es posible en el caso de los ángulos de euler), no estoy seguro si expliqué con precisión :)
concept3d
1

De hecho, las acumulaciones de matrices pueden resolver el bloqueo de cardán. Al acumular rotaciones, agrega gimbals, lo que permite cualquier rotación arbitraria. El diagrama que proporcionó ktodisco muestra un bloqueo de cardán en el diagrama de la izquierda. La matriz para esta orientación se puede definir como:

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

Debido a la rotación del cardán y, los cardán X y Z ahora están bloqueados, por lo que hemos perdido un grado de movimiento. En este punto no tenemos guiñada (local y, global z) usando estos tres cardán. Pero al agregar otro cardán, puedo rotar localmente alrededor de la y:

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

Por cada nuevo rollo, cabeceo y guiñada, simplemente agregue otro cardán, ACUMULANDO en una matriz. Entonces, cada vez que se necesita otra rotación local, se crea una rotación y se multiplica a la matriz de acumulación. Como se menciona en el capítulo, todavía hay problemas, pero el bloqueo de cardán no es uno de ellos.

Justin Ehrlich
fuente