Vector3 giratorio por un cuaternión

25

Estoy intentando rotar un vector3 por un cuaternión dado.

Se que esto es verdad

v=qvq-1

Sé que q-1 es el inverso que simplemente -qmetrounasolnorteyotturemi(q) , pero ¿cómo mapeo la multiplicación del vector al cuaternión para recuperar un vector?

He descubierto que puede tratar v como una matriz, y convertir q y q a matrices, y luego convertir v de una matriz a un vector, pero esto parece un poco exagerado solo para obtener un vector. ¿Existe una implementación más limpia que pueda usar?

gardian06
fuente

Respuestas:

36

Como lo expusieron Nathan Reed y Teodron, la receta para rotar un vector v por un cuaternión de longitud unitaria q es:

1) Crear un cuaternión puro p de v . Esto simplemente significa agregar una cuarta coordenada de 0:

pags=(vX,vy,vz,0 0)pags=(v,0 0)

2) Multiplíquelo previamente con q y multiplíquelo posteriormente con el conjugado q * :

pags=q×pags×q

3) Esto dará como resultado otro cuaternión puro que se puede volver a convertir en un vector:

v=(pX,pagsy,pagsz)

Este vector v es v girado por q .


Esto está funcionando pero lejos de ser óptimo . Las multiplicaciones de cuaterniones significan toneladas y toneladas de operaciones. Tenía curiosidad sobre varias implementaciones como esta , y decidí encontrar de dónde provenían. Aquí están mis hallazgos.

También podemos describir q como la combinación de un vector tridimensional u y un s escalar :

q=(tuX,tuy,tuz,s)q=(tu,s)

Por las reglas de multiplicación cuaternión , y como el conjugado de un cuaternión de longitud unitaria es simplemente inverso, obtenemos:

pags=qpagsq=(tu,s)(v,0 0)(-tu,s)=(sv+tu×v,-tuv)(-tu,s)=((-tuv)(-tu)+s(sv+tu× v)+(sv+tu×v)×(-tu),...)=((tuv)tu+s2v+s(tu×v)+sv×(-tu)+(tu×v)×(-tu),...)

La parte escalar (elipses) da como resultado cero, como se detalla aquí . Lo interesante es la parte del vector, también conocido como nuestro vector girado v ' . Se puede simplificar usando algunas identidades vectoriales básicas :

v=(tuv)tu+s2v+s(tu×v)+s(tu×v)+tu×(tu×v)=(tuv)tu+s2v+2s(tu×v)+(tuv)tu-(tutu)v=2(tuv)tu+(s2-tutu)v+2s(tu×v)

Esto ahora es mucho más óptimo ; dos productos de puntos, un producto cruzado y algunos extras: alrededor de la mitad de las operaciones. Lo que daría algo así en el código fuente (suponiendo una biblioteca matemática vectorial genérica):

void rotate_vector_by_quaternion(const Vector3& v, const Quaternion& q, Vector3& vprime)
{
    // Extract the vector part of the quaternion
    Vector3 u(q.x, q.y, q.z);

    // Extract the scalar part of the quaternion
    float s = q.w;

    // Do the math
    vprime = 2.0f * dot(u, v) * u
          + (s*s - dot(u, u)) * v
          + 2.0f * s * cross(u, v);
}
Laurent Couvidou
fuente
Felicitaciones a una mejor respuesta por escrito. Y teniendo en cuenta que la mayoría de los fanáticos del rendimiento tienden a usar intrínsecos para realizar operaciones vectoriales, se acelera bastante (incluso para la multiplicación de cuaterniones simples, especialmente en arquitecturas de inteligencia).
Teodron
El resultado final es similar a la fórmula de rotación de Rodrigues: de todos modos, tiene los mismos vectores de base; Tendría que buscar algunas identidades trigonométricas para ver si los coeficientes coinciden.
Nathan Reed
@NathanReed Esta parece ser otra forma de llegar al mismo resultado. También me gustaría saber si esto coincide. ¡Gracias por señalar eso!
Laurent Couvidou
1
Estaba comprobando la implementación de GLM de esto y parece implementarse de manera un poco diferente, es decir, como sigue: vprime = v + ((cross(u, v) * s) + cross(u, cross(u, v)) * 2.0f¿Es esta una optimización similar? Se ve algo similar, pero no es lo mismo: solo usa productos cruzados, no productos de punto. El código fuente original se puede encontrar en el archivo type_quat.inl del repositorio GLM oficial en el operator*que toma un cuaternión y un vector ( vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v))
j00hi
7

En primer lugar, q ^ (- 1) no es -q / magnitud (q); es q * / (magnitud (q)) ^ 2 (q * es el conjugado; eso niega todos los componentes excepto el real). Por supuesto, puede omitir la división por la magnitud si todos sus cuaterniones ya están normalizados, lo que normalmente sería en un sistema de rotación.

En cuanto a la multiplicación con un vector, simplemente extiende el vector a un cuaternión estableciendo el componente real de un quat en cero y sus componentes ijk en el xyz del vector. Luego haces las multiplicaciones de quaternion para obtener v ', y luego extraes los componentes ijk nuevamente. (La parte real de v 'siempre debe salir a cero, más o menos algún error de punto flotante).

Nathan Reed
fuente
5

Primera observación: qlo contrario de no es -q/magnitude(q), eso es completamente incorrecto. Las rotaciones con cuaterniones implican que estos equivalentes de números complejos 4D tienen una norma unitaria, por lo tanto, se encuentran en la esfera de la unidad S3 en ese espacio 4D. El hecho de que un quat sea unitario significa que su norma es norm(q)^2=q*conjugate(q)=1y eso significa que la inversa del quat es su conjugado.

Si una unidad de cuaternión se escribe como q=(w,x,y,z)= (cos (t), sin (t) v ), entonces su conjugado es conjugate(q)=(w,-x,-y,-z)= (cos (t), - sin (t) v ), donde t es la mitad del ángulo de rotación y v es el eje de rotación (como un vector unitario, por supuesto).

Cuando ese tipo de Hamilton decidió jugar con equivalentes de números complejos en dimensiones superiores, también se topó con algunas propiedades agradables. Por ejemplo, si empleas un cuaternión completamente puro q=(0,x,y,z)(¡sin parte escalar w !), Puedes considerar esa basura como un vector (¡en realidad es un quat sobre lo que la gente podría llamar el ecuador de la esfera S3, que es una esfera S2! ! - cosas alucinantes si consideramos cuán técnicamente discapacitados nos parecen las personas del siglo XIX hoy en día a los vaqueros de los teléfonos móviles. Así que Hamilton tomó ese vector en su forma quat: v=(0,x,y,z)e hizo una serie de experimentos considerando las propiedades geométricas de los quats.

INPUT: _v=(x,y,z)_ a random 3D vector to rotate about an __u__ unit axis by an angle of _theta_

OUTPUT: q*(0,_v_)*conjugate(q)

dónde

 q = (cos(theta/2), sin(theta/2)*u)
 conjugate(q) = inverse(q) = (cos(theta/2), -sin(theta/2)*u)
 norm(q)=magnitude(q)=|q|=1

Observación: q * (0, v) * conj (q) tiene que ser otro quat de la forma (0, v '). No pasaré por toda esa explicación aparentemente complicada de por qué sucede esto, pero si gira un cuaternión imaginario puro (¡o un vector en nuestro caso!) A través de este método, debe obtener un tipo de objeto similar: quat imaginario puro. y tomas su parte imaginaria como resultado. Ahí lo tienes, el maravilloso mundo de las rotaciones con cuaterniones en una cáscara de nuez (ty).

NOTA : a cualquiera que salte con esa frase usada en exceso: ¡los quats son buenos porque evitan el bloqueo de cardán ... ¡primero deben desbloquear su imaginación! Los quats son un mero aparato matemático "elegante" y se pueden evitar por completo mediante el uso de otros enfoques, el que encuentro completamente geométricamente equivalente es el enfoque de ángulo del eje.

CÓDIGO : la biblioteca de C ++ que imagino es bastante simplista, pero tiene todas las operaciones de matriz, vector y quat que un experimentador de gráficos 3D debería necesitar sin tener que perder más de 15 minutos para aprenderlo. Puede probar las cosas que escribí aquí usando eso en 15 minutos si no eres un novato en C ++. ¡Buena suerte!

teodron
fuente
+1 para tu nota. Apuesto a que la mayoría de la gente no podría lograr un bloqueo de cardán real si lo intentaran. Se ha convertido en una frase para cualquier comportamiento inesperado al realizar rotaciones.
Steve H
La mayoría de las personas no pueden construir un mecanismo de cardán adecuado y piensan que si encadenan 3 matrices de rotaciones, automáticamente terminarán con la representación de "ángulos de Euler". articulaciones que pueden experimentar redundancia al intentar realizar cinemática inversa (tiene más grados de libertad de lo que realmente necesita para producir la orientación deseada). Oh, bueno, eso es otro tema, pero pensé que era agradable estar lejos del bombo este "legendario" tema ha generado entre los programadores CG ..
teodron
Nitpickery: mientras que el ángulo del eje es equivalente en que ambas representaciones pueden representar todas las rotaciones en SO (3) de manera única (está bien, módulo de la doble cubierta habitual) y, por supuesto, hay una transformación casi trivial de un lado a otro, los cuaterniones tienen La ventaja de ser mucho más fácil de componer que todas las otras representaciones no matriciales.
Steven Stadnicki
Tienen la ventaja de ser más fáciles de componer debido a su buen comportamiento en cualquier lenguaje de programación orientado a objetos, especialmente cuando se utiliza la sobrecarga del operador. No estoy seguro, pero tal vez incluso sus propiedades de interpolación esférica se conservan para el ángulo del eje (¿excepto SQUAD tal vez?).
Teodron
2

Aquí hay una forma alternativa de transformar un vector por un cuaternión. Es la forma en que MS lo hace en el marco xna. http://pastebin.com/fAFp6NnN

Steve H
fuente
-1

Traté de resolver esto a mano y se me ocurrió la siguiente ecuación / método:

// inside quaterion class
// quaternion defined as (r, i, j, k)
Vector3 rotateVector(const Vector3 & _V)const{
    Vector3 vec();   // any constructor will do
    vec.x = 2*(r*_V.z*j + i*_V.z*k - r*_V.y*k + i*_V.y*j) + _V.x*(r*r + i*i - j*j - k*k);
    vec.y = 2*(r*_V.x*k + i*_V.x*j - r*_V.z*i + j*_V.z*k) + _V.y*(r*r - i*i + j*j - k*k);
    vec.z = 2*(r*_V.y*i - r*_V.x*j + i*_V.x*k + j*_V.y*k) + _V.z*(r*r - i*i - j*j + k*k);
    return vec;
}

Agradecería que alguien revisara mt deriviation. Utilicé http://pastebin.com/8QHQqGbv . Sugeriría copiar a un editor de texto que admita el desplazamiento lateral.

en mi notación usé q ^ (- 1) para significar conjugado, y no inverso, y diferentes identificadores, pero espero que sea seguible. Creo que la mayoría tiene razón, especialmente cuando al probar que la porción real del vector desaparecería.

gardian06
fuente