El uso de cuaterniones para representar la rotación no es difícil desde un punto de vista algebraico. Personalmente, me resulta difícil razonar visualmente sobre cuaterniones, pero las fórmulas involucradas en su uso para las rotaciones son bastante simples. Proporcionaré un conjunto básico de funciones de referencia aquí. (Ver también esta respuesta preciosa por hosolmaz, en el que los paquetes de estos juntos para crear una clase Quaternion práctico.)
Se puede pensar en cuaterniones (para nuestros propósitos) como un escalar más un vector de 3-d - - abstractly, w + xi + yj + zk
, aquí representado por una simple tupla (w, x, y, z)
. El espacio de rotaciones de 3-d está representado en su totalidad por un subespacio de los cuaterniones, el espacio de unidades de los cuaterniones de la unidad, por lo que debe asegurarse de que sus cuaterniones estén normalizados. Puede hacerlo de la manera que normalizaría cualquier 4-vector (es decir, la magnitud debe ser cercano a 1, y si no es así, reducir la escala de los valores de la magnitud):
def normalize(v, tolerance=0.00001):
mag2 = sum(n * n for n in v)
if abs(mag2 - 1.0) > tolerance:
mag = sqrt(mag2)
v = tuple(n/mag for n in v)
return v
Tenga en cuenta que para simplicidad, las siguientes funciones asumen que los valores de quaternion son ya normalizados. En la práctica, tendrá que renormalizarlos de vez en cuando, pero la mejor manera de lidiar con eso dependerá del dominio del problema. Estas funciones proporcionan solo lo básico, solo como referencia.
Cada rotación está representada por una unidad quaternion, y las concatenaciones de rotaciones corresponden a multiplicaciones de unidades quaternions. La fórmula de esto es la siguiente:
def q_mult(q1, q2):
w1, x1, y1, z1 = q1
w2, x2, y2, z2 = q2
w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
y = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2
z = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2
return w, x, y, z
Para rotar un vectorpor un cuaternión, necesita conjugado del cuaternión también.Eso es fácil:
def q_conjugate(q):
w, x, y, z = q
return (w, -x, -y, -z)
Ahora cuaternión-vector de multiplicación es tan simple como convertir un vector en un cuaternión (mediante el establecimiento de w = 0
y dejando x
, y
y z
la misma) y luego multiplicando q * v * q_conjugate(q)
:
def qv_mult(q1, v1):
q2 = (0.0,) + v1
return q_mult(q_mult(q1, q2), q_conjugate(q1))[1:]
Finalmente, debe saber cómo convertir las rotaciones de eje-ángulo en cuaterniones. ¡También es fácil! Tiene sentido "desinfectar" la entrada y la salida aquí llamando al normalize
.
def axisangle_to_q(v, theta):
v = normalize(v)
x, y, z = v
theta /= 2
w = cos(theta)
x = x * sin(theta)
y = y * sin(theta)
z = z * sin(theta)
return w, x, y, z
y Fondo:
def q_to_axisangle(q):
w, v = q[0], q[1:]
theta = acos(w) * 2.0
return normalize(v), theta
Aquí está un ejemplo de uso rápido. Una secuencia de rotaciones de 90 grados sobre los ejes x, y y z devolverá un vector en el eje y a su posición original. Este código realiza esas rotaciones:
x_axis_unit = (1, 0, 0)
y_axis_unit = (0, 1, 0)
z_axis_unit = (0, 0, 1)
r1 = axisangle_to_q(x_axis_unit, numpy.pi/2)
r2 = axisangle_to_q(y_axis_unit, numpy.pi/2)
r3 = axisangle_to_q(z_axis_unit, numpy.pi/2)
v = qv_mult(r1, y_axis_unit)
v = qv_mult(r2, v)
v = qv_mult(r3, v)
print v
# output: (0.0, 1.0, 2.220446049250313e-16)
tener en cuenta que esta secuencia de rotaciones no volverá todos vectores a la misma posición; por ejemplo, para un vector en el eje x, corresponderá a una rotación de 90 grados alrededor del eje y. (Mantenga la derecha en reglas en mente aquí, una rotación positiva sobre el eje y empuja un vector en el eje x en la región z negativa .)
v = qv_mult(r1, x_axis_unit)
v = qv_mult(r2, v)
v = qv_mult(r3, v)
print v
# output: (4.930380657631324e-32, 2.220446049250313e-16, -1.0)
Como siempre, por favor, hágamelo saber si usted encuentra algún problema aquí.
1. Estos están adaptados a partir de un tutorial OpenGL archived here.
2. La fórmula de multiplicación del cuaternión parece un loco nido de ratas, pero la derivación es simple (si es tediosa). Solo tenga en cuenta primero que ii = jj = kk = -1
; luego que ij = k
, jk = i
, ki = j
; y finalmente que ji = -k
, kj = -i
, ik = -j
. Luego, multiplica los dos cuaterniones, distribuyendo los términos y reorganizándolos en función de los resultados de cada una de las 16 multiplicaciones. Esto también ayuda a ilustrar por qué puede usar cuaterniones para representar la rotación; las últimas seis identidades siguen la regla de la mano derecha, creando biyecciones entre rotaciones dei
a j
y rotaciones alrededor dek
, y así sucesivamente.
solo para aclarar, ¿quieres una transformación lineal de un 3-espacio euclidiano a otro 3-espacio euclidiano? – ThomasMcLeod
Aquí hay una pista: ¿A qué se traducirían los vectores '(0, 0, 1)', '(0, 1, 0)' y '(1, 0, 0)'? –
La matriz de rotación es la mejor opción aquí. La matriz de rotación que relaciona los cuadros de coordenadas es fácil de obtener y eficiente de aplicar. Obtener y aplicar un cuaternión aquí requeriría esencialmente convertir de la matriz de rotación y luego convertir nuevamente a la matriz de rotación. Obviamente, es mejor usar la matriz de rotación. Los cuaterniones tienen sus fortalezas en otros lugares. Sus puntos fuertes son que consisten en solo 4 números, pueden componerse y aplicarse sin trigonometría, tienen restricciones simples (magnitud de la unidad) y son muy adecuados para la interpolación. – Praxeolitic