Pregunta

¿Existe un algoritmo existente para convertir una representación de cuaternión de una rotación en una representación de ángulo de Euler? El orden de rotación para la representación de Euler es conocido y puede ser cualquiera de las seis permutaciones (es decir, xyz, xzy, yxz, yzx, zxy, zyx). He visto algoritmos para un orden de rotación fijo (generalmente la convención de rumbo, banco, giro de la NASA) pero no para un orden de rotación arbitrario.

Además, debido a que hay múltiples representaciones de ángulo de Euler de una sola orientación, este resultado será ambiguo. Esto es aceptable (debido a que la orientación sigue siendo válida , puede que no sea la que el usuario espera ver), sin embargo, sería aún mejor si hubiera un algoritmo que tuviera límites de rotación (es decir, el número de grados de libertad y los límites de cada grado de libertad) en cuenta y dieron la representación de Euler "más sensata" dadas esas limitaciones.

Tengo la sensación de que este problema (o algo similar) puede existir en IK o en los dominios de dinámica corporal rígida.


Resuelto: Me acabo de dar cuenta de que podría no estar claro que resolví este problema siguiendo los algoritmos de Ken Shoemake de Graphics Gems. Respondí mi propia pregunta en ese momento, pero se me ocurre que puede que no esté claro que lo hice. Vea la respuesta a continuación para obtener más detalles.


Solo para aclarar: sé cómo convertir de un cuaternión al llamado ' Tait- Bryan 'representación - lo que yo llamaba la convención de' NASA '. Este es un orden de rotación (asumiendo la convención de que el eje 'Z' está arriba) de zxy. Necesito un algoritmo para todas órdenes de rotación.

Posiblemente la solución, entonces, es tomar la conversión de orden zxy y derivar de ella otras cinco conversiones para las otras órdenes de rotación. Supongo que esperaba que hubiera una solución más 'global'. En cualquier caso, me sorprende no haber podido encontrar soluciones existentes por ahí.

Además, y esta quizás debería ser una pregunta separada por completo, cualquier conversión (suponiendo un orden de rotación conocido, por supuesto) va a seleccionar la representación de one Euler, pero de hecho hay muchas. Por ejemplo, dado un orden de rotación de yxz, las dos representaciones (0,0,180) y (180,180,0) son equivalentes (y producirían el mismo cuaternión). ¿Hay alguna manera de restringir la solución usando límites en los grados de libertad? ¿Como lo haces en IK y dinámica de cuerpo rígido? es decir, en el ejemplo anterior, si solo hubiera un grado de libertad sobre el eje Z, entonces la segunda representación se puede ignorar.


He rastreado un artículo que podría ser un algoritmo en este pdf pero debo confesar que encuentro la lógica y las matemáticas un poco difíciles de seguir. ¿Seguramente hay otras soluciones por ahí? ¿Es el orden de rotación arbitrario realmente tan raro? ¿Seguramente todos los paquetes 3D principales que permiten la animación esquelética junto con la interpolación de cuaterniones (es decir, Maya, Max, Blender, etc.) deben haber resuelto exactamente este problema?

¿Fue útil?

Solución

Esto parece un caso clásico de tecnología antigua que se pasa por alto: logré sacar una copia de Graphics Gems IV del garaje y parece que Ken Shoemake no solo tiene un algoritmo para convertir desde ángulos de Euler de arbitrario orden de rotación, pero también responde a la mayoría de mis otras preguntas sobre el tema. ¡Hurra por los libros! Si tan solo pudiera votar la respuesta del Sr. Shoemake y recompensarlo con puntos de reputación.

Supongo que una recomendación de que cualquiera que trabaje con los ángulos de Euler debería obtener una copia de Graphics Gems IV de su biblioteca local y leer la sección que comienza en la página 222 tendrá que hacer. Tiene que ser la explicación más clara y concisa del problema que he leído hasta ahora.


Aquí hay un enlace útil que he encontrado desde - http://www.cgafaq.info/wiki/Euler_angles_from_matrix : sigue el mismo sistema que Shoemake; las 24 permutaciones diferentes del orden de rotación están codificadas como cuatro parámetros separados: eje interno, paridad, repetición y marco, lo que le permite reducir el algoritmo de 24 casos a 2. Podría ser un wiki útil en general: no había venido a través de ella antes.

Al enlace anterior proporcionado parece estar roto aquí es otra copia de `` Calcular ángulos de Euler desde una matriz de rotación " ;.

Otros consejos

En un sistema de coordenadas cartesianas diestro con el eje Z apuntando hacia arriba, haga esto:

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

¡He estado buscando durante varios días una solución similar, y finalmente encontré este sitio web que tiene un algoritmo para convertir los cuaterniones en rotaciones arbitrarias de Euler y Tait-Bryan!

Aquí está el enlace: http://bediyap.com/programming/convert -cuaternion-to-euler-rotations /

Y aquí está el código:

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

He publicado mi artículo titulado "Conversión de ángulo de Quaternion a Euler para secuencia de rotación arbitraria utilizando métodos geométricos". en mi sitio web en noelhughes.net. También tengo algoritmos para convertir cualquier conjunto de ángulos de Euler a un cuaternión y un cuaternión a / desde la matriz de coseno de dirección que publicaré este fin de semana. Estos también están en el sitio web de Martin Bakers, aunque son un poco difíciles de encontrar. Busca en Google mi nombre, Noel Hughes, y quaternions y deberías encontrarlo.

Lo resuelvo de esta manera:

paso 1 : asegúrese de qué convención para la rotación de Euler desea, por ejemplo, zyx .

paso 2 : Calcule la matriz de rotación analítica para la rotación. Por ejemplo, si desea R ( zyx ),

** R *** zyx * = ** R *** x * ( phi ) * ** R *** y * ( theta ) * ** R *** z * ( psi ), donde los elementos se convierten

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) 

paso 3 : mediante inspección, puede encontrar el pecado o el bronceado de los tres ángulos utilizando los elementos anteriores. En este ejemplo,

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

paso 4 : Calcule la matriz de rotación desde su cuaternión (consulte wikipedia ), para los elementos que necesita para calcular los ángulos como en 3) arriba.

Se pueden calcular otras convenciones utilizando el mismo procedimiento.

Aquí hay un artículo que escribí sobre la conversión de un cuaternión a los ángulos de Euler.

Enlace 1

También he puesto una serie de documentos en esta ubicación que discuten varios aspectos de los cuaterniones, los ángulos de Euler y las matrices de rotación (DCM).

Enlace 2

Para aquellos que se topan con esta página mientras buscan en Google, recientemente encontré derivaciones para estas conversiones para los 12 Tait-Bryan intrínsecos (1-2-3, 3-2-1, etc.) y Euler adecuado (1-2 -1, 3-1-3, etc.) secuencias de rotación en las siguientes dos referencias:

Gracias a frodo2975 por el segundo enlace.

Wikipedia muestra cómo puede usar las partes del cuaternión y calcular los ángulos de Euler.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top