Otro tutorial de Quaternions

Existen muchos tutoriales que explican qué son los quaternions, sin embargo parece que están orientados a un público que ya tiene desarrollada cierta “intuición matemática”, es decir, han tenido contacto moderado o alto con álgebra abstracta, topología, teoría de categorías, entre otros.

Están plagados de tecnicismos, tanto así que comienzan definiendo a los quaternions como el cubrimiento doble del grupo SO(3). Es decir. aún no son accesibles para la mayoría de estudiantes y/o profesionales de ciencias de la computación.

En este post voy a explicar qué son los quaternions a un nivel accesible para gente interesada en sus aplicaciones en computación gráfica.

Entonces, comencemos por el principio de todo: los números complejos.

Los números complejos representan rotaciones

El matemático Sir William Rowan Hamilton paso 20 años de su vida tratando de representar las rotaciones en 3 dimensiones de manera similar a como los números complejos pueden representar rotaciones en el plano, y lo consiguió en 1843. En la época de Hamilton no existía todavía el álgebra vectorial, es decir, no se contaba con la noción de espacio vectorial, productor interior, producto cruz, etc. Las teoría de matrices no estaba desarrollada al punto que no existía una notación estándar para describir un conjunto de m ecuaciones con n variables. Así que Hamilton tuvo que desarrollar todas estas ideas antes de poder descubrir los quaternions, sin dudas fue un ¡genio!.

Hamilton conocía que los números complejos de norma = 1 pueden ser interpretados como rotores. Por ejemplo, el número:

R = \cos \theta + i \sin \theta

Representa la rotación de \theta radianes al rededor del origen. Hay que prestar atención a que la norma de R es unitaria, es decir \|R\| = 1.

De modo que si tenemos el vector 2d v = x + i y, y queremos rotarlo \theta radianes en el origen para hallar el vector v', podemos hacerlo simplemente multiplicandolo por el rotor R:

v' = R v

Cómo funciona esto? veamos con atención:

v' = R v

v' = (\cos \theta + i \sin \theta) (x + i y)

v' = x \cos \theta + i y \cos \theta + i x \sin \theta + i^2 y \sin \theta

Teniendo en cuenta que i^2 = -1 y agrupando tenemos:

v' = x \cos \theta - y \sin \theta + i (y \cos \theta + x \sin \theta)

Este resultado es exactamente igual al que se obtiene al aplicar una matriz de rotación 2×2 al vector v. Este hecho es ampliamente usado para crear geometría plana basada en álgebra compleja.

Los quaternions generalizan a los números complejos a 3 dimensiones

Hamilton descubrió que los números complejos se pueden generalizar a 3 dimensiones, y los quaternions son justamente eso. Podemos afirmar que los números complejos son un caso particular de quaternions y el álgebra compleja es un caso particular del álgebra de quaternions. Es decir, Hamilton no sólo inventó un truco para rotar vectores, sino que descubrió una ¡álgebra completa!.

Los quaternions son números de la forma:

Q = q_0 + i q_1 + j q_2 + k q_3

donde los números i, j y k son números imaginarios, los mismos números imaginarios que caracterizan a los números complejos, y por lo tanto poseen la propiedad de tener el cuadrado negativo:

i^2 = j^2 = k^2 = -1

Tal como hicimos con los números complejos, podemos usar a los quaternions para representar rotaciones, pero esta vez rotaciones en 3D en lugar de rotaciones en el plano. Demostraré como hacer esto en las siguientes secciones.

Todo quaternion tiene dos partes, una parte escalar y una parte vectorial Q = (escalar, vector). En particular, los quaternions que representan rotaciones tienen dos características:

  • La parte vectorial del quaternion representa la dirección del eje de rotación, o en otras palabras, la normal del plano que contiene la rotación.
  • Las norma del quaternion es igual a uno: \|R\| = 1

Los quaternions y los números complejos son equivalentes en el plano

El caso ejemplo más simple de quaternion es aquel que solamente tiene su parte real y no tiene parte compleja, es decir:

Q = q_0 + i 0 + j 0 + k 0

Al igual que todos los números reales son números complejos por definición, todos los números reales son quaternions. En otras palabras, un quaternion que no tiene parte imaginaria es isomorfo a un número real.

Esto parece no ser muy interesante pero en nuestro caso eso implica que un quaterion con sólo parte real representa una transformación de escalado uniforme.

El siguiente caso es el de un quaternion que posee su parte real y además una dimensión imaginaria, por ejemplo:

Q = q_0 + i q_1 + j 0 + k 0

Para todo efecto práctico, este quaternion se comporta exactamente igual que un rotor complejo. Es decir que este quaternion representa una rotación en el plano, de la misma forma en que lo hace un número complejo de norma = 1. Por ejemplo:

R = \cos \theta + i \sin \theta + j 0 + k 0, \ \ \ \ \|R\| = 1

Si nos restringimos a las 2 dimensiones podemos definir quaternions de forma muy similar a como definimos números complejos:

Quaternion para rotar un vector en el plano YZ: R = \cos \theta + i \sin \theta + j 0 + k 0
Quaternion para rotar un vector en el plano XZ: R = \cos \theta + i 0 + j \sin \theta + k 0
Quaternion para rotar un vector en el plano XY: R = \cos \theta + i 0 + j 0 + k \sin \theta

Rotar vectores en el plano con quaterions es un caso especial y funciona igual que con los números complejos. Por ejemplo, dado un vector en el plano YZ v = 0 + i 0 + j y + k z, el cual representamos como un quaternion imaginario (que sólo posee parte vectorial y no parte escalar), aplicamos la rotación en el plano YZ:

v' = R v

v' = (\cos \theta + i \sin \theta + j 0 + k 0) (0 + i 0 + j y + k z)

v' = j y \cos \theta + k z \cos \theta + i j y \sin \theta + i k z \sin \theta

Teniendo en cuenta que i k = -j, i j = k y agrupando tenemos:

v' = j (y \cos \theta - z \sin \theta) + k (z \cos \theta + y \sin \theta)

Cuyo resultado es exactamente igual al que se obtiene al aplicar una matriz de rotación 3×3 al vector v = (0, y, z).

Para llegar a este resultado hemos utilizado un conjunto de reglas que al principio pueden parecer extrañas:

i j = k, \ \ \ \ j i = -k
j k = i, \ \ \ \ k j = -i
k i = j, \ \ \ \ i k = -j

Si analizamos bien, vemos que estas reglas no son arbitrarias, sino que en realidad son todas las combinaciones del producto cruz de los números imaginarios i, j y k. De hecho el producto cruz fue uno de los descubrimientos de Hamilton. Cuando se trata de quaternions tenemos que pensar en esos números imaginarios como vectores unitarios ortogonales si se quiere. Pero sin olvidar que el cuadrado de ellos no es igual a 1, sino que es igual a -1:

i^2 = j^2 = k^2 = -1

Estas reglas nos van a llevar a la siguiente conclusión: El álgebra de quaternions es NO CONMUTATIVA.

Quaternions y Rotaciones en 3D

Supongamos que quiero rotar un vector v = (v_0, v_1, v_2) un angulo \theta alrededor de un eje b = (b_0, b_1, b_2). Construimos dos quaternions usando el vector b y el angulo \theta:

Q = \cos(\theta/2) + \sin(\theta/2) \ ( i b_0 + j b_1 + k b_2 )

Q^{-1} = \cos(\theta/2) - \sin(\theta/2) \ ( i b_0 + j b_1 + k b_2 )

Y aplicamos el producto tipo sandwitch:

v' = Q \ v \ Q^{-1}

El vector v' es la rotación del vector v un angulo \theta alrededor de un eje b.

El producto estilo sandwitch

Algo sorprendente de los quaternions es que se aplican en pares haciendo un sandwitch.

v' = Q \ v \ Q^{-1}

La primera mitad del sandwitch se forma definiendo el quaternion Q como de costumbre, pero utilizando la mitad del ángulo: En lugar de \cos \theta usamos \cos(\theta/2) y en lugar de \sin \theta usamos \sin(\theta/2).

La segunda mitad del sandwitch se forma definiendo el quaternion Q^{-1} que es el inverso de Q. El quaternion inverso está definido como:

Q^{-1} = \frac{ 1 }{ \|Q\|^2 } \ Q^{*}

donde:

Q^{*} = q_0 - i q_1 - j q_2 - k q_3

\|Q\|^2 = Q \ Q^{*} = q_0^2 + q_1^2 + q_2^2 + q_3^2

El factor \|Q\|^2 es para normalizar el quaternion. Si Q tiene norma = 1, este factor no es necesario, por lo que:

Q^{-1} = Q^{*}, \ \ \ \ \|Q\| = 1

En realidad esa es la forma estándar de aplicar rotaciones en 3D, 4D y en altas dimensiones. El hecho de que el producto sandwitch no se use en dos dimensiones es porque las dos dimensiones son un caso especial que admite tanto el producto sandwitch como el de “un solo lado” y ya se hizo costumbre usar el mas corto.

¿Como funciona?

v' = Q \ v \ Q^{-1}

v' =  Q \ v \ Q^{-1} = (\cos \frac{\theta}{2} + \sin \frac{\theta}{2} \ b) \ v \ (\cos \frac{\theta}{2} - \sin \frac{\theta}{2} \ b)

v' = (Q \ v) \ Q^{-1} = (\cos \frac{\theta}{2} \ v + \sin \frac{\theta}{2} \ b \ v) \ (\cos \frac{\theta}{2} - \sin \frac{\theta}{2} \ b)

v' = \cos^2 \frac{\theta}{2} \ v - (\cos \frac{\theta}{2} \ \sin \frac{\theta}{2}) \ v \ b + (\sin \frac{\theta}{2} \ \cos \frac{\theta}{2}) \ b \ v - \sin^2 \frac{\theta}{2} \ b \ v \ b

El producto v \ b que vemos en las ecuaciones es el producto de dos quaternions “puros” (sólo tienen la parte vectorial) y esta definido de la siguiente forma:

v \ b = (i v_0 + j v_1 + k v_2) \ (i b_0 + j b_1 + k b_2) = -v \cdot b + v \times b,

donde v \cdot b es el producto interior y v \times b es el producto cruz.

Notemos que la suma algebraica -v \ b + b \ v no es igual a cero, debido a que el producto cruz no es conmutativo. Esto tiene como consecuencia que -v \ b + b \ v = (v \cdot b - v \times b) + (-b \cdot v + b \times v) = 0 + 2 \ b \times v = 0 - 2 \ v \times b.

Con esto podemos reducir esa suma algebraica y obtener la ecuación:

v' = \cos^2 \frac{\theta}{2} \ v + 2 \ (\cos \frac{\theta}{2} \ \sin \frac{\theta}{2}) \ b \times  v - \sin^2 \frac{\theta}{2} \ b \ v \ b

Un dato curioso es que la expresión b \ v \ b, representa la reflexión del vector v en el plano cuya normal es b. Donde b y v son quaternion puros (sólo tienen parte vectorial, no parte escalar). Podríamos expresar ésta reflexión de una forma clásica como sigue:

b \ v \ b = 2 \ (-v \cdot b) \ b + v

donde el vector v es trasladado en dirección negativa de la normal b el doble de la distancia de v proyectada en b. Finalmente la ecuación queda así:

v' = \cos^2 \frac{\theta}{2} \ v + 2 \ (\cos \frac{\theta}{2} \ \sin \frac{\theta}{2}) \ b \times v - \sin^2 \frac{\theta}{2} \ (2 \ (-v \cdot b) \ b + v)

v' = \cos^2 \frac{\theta}{2} \ v - \sin^2 \frac{\theta}{2} \ v  + 2 \ (\cos \frac{\theta}{2} \ \sin \frac{\theta}{2}) \ b \times v + 2 \ \sin^2 \frac{\theta}{2} \ (v \cdot b) \ b

Aplicando las siguientes identidades trigonométricas:

\cos \theta = \cos^2 \frac{\theta}{2} - \sin^2 \frac{\theta}{2}

\sin \theta = 2 \cos \frac{\theta}{2} \ \sin \frac{\theta}{2}

1 - \cos \theta = 2 \ \sin^2 \frac{\theta}{2}

llegamos a la siguiente ecuación:

v' = \cos \theta \ v + \sin \theta \ b \times v + (1 - \cos \theta) \ ( v \cdot b ) \ b

La cual es conocida como la Formula de Rodrigues la cual fué descubierta por Olinde Rodriguez independientemente de Hamilton tres años antes. Formula de Rodrigues es un truco interesante para rotar vectores pero no olvidemos que la creación de Hamilton fué toda una álgebra completa, que va más allá de las rotaciones.

El álgebra de quaternions NO es CONMUTATIVA

Algunas cosas en 3 dimensiones no son como las esperábamos. Al contrario del álgebra compleja, el álgebra de quaternions es NO CONMUTATIVA. Esto quiere decir que, la multiplicación de quaternions:

Q_0 \ Q_1 \not= Q_1 \ Q_0

Esto es similar a lo que ocurre con el álgebra de matrices en donde dadas dos matrices A y B tenemos que el producto A \ B \not= B \ A en general.

En el caso de los quaternions esta no conmutatividad se debe a que el producto cruz es no conmutativo. Veamos cómo está definido el producto de quaternions de forma vectorial:

P \ Q = (p_0, \vec p) \ (q_0, \vec q) = (p_0 \ q_0 - \vec p \cdot \vec q, \ p_0 \ \vec q + q_0 \ \vec p + \vec p \times \vec q)

Vemos tanto el producto escalar, como el producto interior son conmutativos, pero el producto cruz \vec p \times \vec q no es conmutativo y por lo tanto el orden de los factores altera el resultado.

Combinación de rotaciones

Supongamos que queremos aplicar primero la rotación dada por el quaternion Q_1 y luego la rotación dada por el quaternion Q_2. La composición de transformaciones:

Q_1 \circ Q_2 = Q_1( Q_2 ( v ) )

puede expresarse como un sólo quaternion combinado

Q_{12} = Q_1 \ Q_2

obtenido por medio del producto de quaternions.

Q_1 \circ Q_2 = Q_1( Q_2 ( v ) ) = Q_{12}( v )

Esto es análogo a como se concatenan las matrices de rotación.

SLERP

La interpolación de rotaciones es una de las mayores aplicaciones de los quaternions en computación gráfica. Se utiliza para animación por key-frames, skinning, splines circulares, hodographs y deformaciones entre otros.

Supongamos que tenemos dos quaternions P = (p_0, \vec p) y Q = (q_0, \vec q) que representan rotaciones y queremos encontrar las rotaciónes que se encuentran entre P y Q, es decir, queremos hallar el quaternion R(\lambda) que representa esa rotacion intermedia dado el parámetro escalar \lambda que esta entre cero y uno.

Sea \theta el ángulo de entre los vectores \vec p y \vec q, la interpolacion lineal se logra mediante la concatenación de un quaternion R con el quaternion inicial P dando lugar al quaternion final Q. Esto se puede expresar de la siguiente forma:

Q = R \ P

Q = (\cos(\theta/2) + \sin(\theta/2) \ \vec p \times \vec q) \ P

Q = \cos(\theta/2) \ P + \sin(\theta/2) \ (\vec p \times \vec q) \ P

\frac{Q  - \cos(\theta/2) \ P}{\sin(\theta/2)} = (\vec p \times \vec q) \ P

Teniendo en cuenta este resultado intermedio, podemos plantear la siguiente parametrización del quaternion R con el parámetro \lambda:

R(\lambda) = (\cos(\lambda \theta/2) + \sin(\lambda \theta/2) \ \vec p \times \vec q) \ P

R(\lambda) = \cos(\lambda \theta/2) \ P + \sin(\lambda \theta/2) \ (\vec p \times \vec q) \ P

Reemplazando (\vec p \times \vec q) \ P con lo obtenido en el paso anterior:

R(\lambda) = \cos(\lambda \theta/2) \ P + \sin(\lambda \theta/2) \ [ \frac{Q  - \cos(\theta/2) \ P}{\sin(\theta/2)} ]

R(\lambda) = \cos(\lambda \theta/2) \ P + \frac{\sin(\lambda \theta/2) \ Q  - \sin(\lambda \theta/2) \ \cos(\theta/2) \ P}{\sin(\theta/2)}

Arreglando los terminos:

R(\lambda) = \frac{P \ \sin(\theta/2) \cos(\lambda \theta/2) - P \ \cos(\theta/2) \sin(\lambda \theta/2) + Q \ \sin(\lambda \theta/2)}{\sin(\theta/2)}

R(\lambda) = \frac{P \ (\sin(\theta/2) \cos(\lambda \theta/2) - \cos(\theta/2) \sin(\lambda \theta/2))}{\sin(\theta/2)} + \frac{Q \ \sin(\lambda \theta/2)}{\sin(\theta/2)}

Aplicando identidades trigonométricas elementales tenemos:

R(\lambda) = \frac{\sin((1 - \lambda) \theta/2)}{\sin(\theta/2)} \ P + \frac{\sin(\lambda \theta/2)}{\sin(\theta/2)} \ Q

Esta interpolación lineal es la que se utiliza de forma estándar en computación gráfica.

Quaternion en forma matricial

Finalmente, en algunas ocasiones es útil pasar de quaternions a matrices y viceversa (por ejemplo si queremos pasar las matrices a OpenGL).

El método es como sigue:

Dado el quaternion Q, y dados tres vectores ortonormales e_1 = (1, 0, 0), e_2 = (0, 1, 0) y e_3 = (0, 0, 1), transformamos los vectores usando Q y obtenemos:

e_1' = Q \ e_1 \ Q^{-1}

e_2' = Q \ e_2 \ Q^{-1}

e_3' = Q \ e_3 \ Q^{-1}

Note que los vectores rotados resultantes siguen siendo ortonormales y definen un nuevo frame. Entonces definimos la matriz M utilizando esos tres vectores como filas:

M = [ e_1', e_2', e_3' ]

La matriz M también se puede definir analíticamente término por término usando el mismo método pero utilizando el álgebra en lugar de los valores actuales. EL resultado sería la matriz que se muestra en esta página.

Resumen final

Hemos visto que los quaternions generalizan a los números complejos y son de la siguiente forma:

Q = q_0 + i q_1 + j q_2 + k q_3

donde los números i, j y k son números imaginarios. El cuadrado de los números imaginarios define el producto interior:

i^2 = j^2 = k^2 = i j k = -1

La multiplicación de los números imaginarios define el producto cruz:

i j = k, \ \ \ \ j i = -k
j k = i, \ \ \ \ k j = -i
k i = j, \ \ \ \ i k = -j

Para rotar un vector v un angulo \theta al rededor de un vector b = (b_0, b_1, b_2) es necesario definir dos quaternions unitarios:

Q = \cos(\theta/2) + \sin(\theta/2) \ ( i \ b_0 + j \ b_1 + k \ b_2 )
Q^{-1} = \cos(\theta/2) - \sin(\theta/2) \ ( i \ b_0 + j \ b_1 + k \ b_2 )

y aplicar el producto sandwitch:

v' = Q \ v \ Q^{-1}

En general el quaternion inverso Q^{-1} puede ser construido a partir de Q de la siguiente forma:

Q^{-1} = \frac{ 1 }{ \|Q\|^2 } \ Q^{*}

donde Q^{*} es el quaternion conjugado:

Q^{*} = q_0 - i q_1 - j q_2 - k q_3

y \|Q\|^2 es el cuadrado de la norma:

\|Q\|^2 = Q \ Q^{*} = q_0^2 + q_1^2 + q_2^2 + q_3^2

Debido a que el factor \|Q\|^2 esta presente en la inversa por motivos de normalización, los quaternions unitarios (de norma = 1) no lo necesitan y la inversa es igual al conjugado:

Q^{-1} = Q^{*}, \ \ \ \ \|Q\| = 1

El producto de dos quaternions puros v y b (solo tienen la parte de vector y no parte de escalar) se puede expresar en términos del producto interior y del producto cruz de la siguiente forma:

v \ b = (i v_0 + j v_1 + k v_2) \ (i b_0 + j b_1 + k b_2) = -v \cdot b + v \times b,

EL producto de dos quaternions generales también se puede expresar en términos del producto interior y del producto cruz de la siguiente forma:

P \ Q = (p_0, \vec p) \ (q_0, \vec q) = (p_0 \ q_0 - \vec p \cdot \vec q, \ p_0 \ \vec q + q_0 \ \vec p + \vec p \times \vec q)

Un dato curioso es que la expresión b \ v \ b, representa la reflexión del vector v en el plano cuya normal es b. Donde b y v son quaternion puros (sólo tienen parte vectorial, no parte escalar). Podríamos expresar ésta reflexión de una forma clásica como sigue:

b \ v \ b = 2 \ (-v \cdot b) \ b + v

donde el vector v es trasladado en dirección negativa de la normal b el doble de la distancia de v proyectada en b.

El producto sandwitch v' = Q \ v \ Q^{-1} para la rotación de un vector es equivalente a la Formula de Rodrigues:

v' = \cos \theta \ v + \sin \theta \ b \times v + (1 - \cos \theta) \ ( v \cdot b ) \ b

Las rotaciones se pueden concatenar fácilmente de la siguiente forma:

Q_{12} = Q_1 \ Q_2

Supongamos que tenemos dos quaternions P = (p_0, \vec p) y Q = (q_0, \vec q) que representan rotaciones y queremos encontrar las rotaciónes que se encuentran entre P y Q, es decir, queremos hallar el quaternion R(\lambda) que representa esa rotacion intermedia dado el parametro escalar \lambda.

Sea \theta el ángulo que existe entre los vectores \vec p y \vec q, la interpolacion lineal se logra mediante una rotación con una fracción del ángulo \lambda \ \theta que vaya desde P hasta Q:

R(\lambda) = \frac{\sin((1 - \lambda) \theta/2)}{\sin(\theta/2)} \ P + \frac{\sin(\lambda \theta/2)}{\sin(\theta/2)} \ Q

Advertisements

3 thoughts on “Otro tutorial de Quaternions

  1. Argel Michinel says:

    Hola Mauricio. Le escribo para agradecerle por este artículo.
    Tenía días buscando información referente al manejo de las rotaciones mediante cuaterniones ya queme encuentro trabajando en un tema de tesis donde debo emplearlo y la mayoría de la información que había encontrado me costaba mucho asimilarla debido a que o estaba redactada de forma muy teórica o no ofrecían ningún ejemplo para demostrar la aplixcación de los temas que estaban explicando.
    En contraste su artículo explica el concepto de una manera más digerible y ofrece ejemplos de como es empleada esta herramienta matemática.

    Lo felicito.

    Saludos,

    Argel Michinel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s