Вы находитесь на странице: 1из 13

Véronique Gaildrat 02/03/08

COURS : Quaternions

1 Nombres Complexes (bref rappel)

Les nombres complexes sont composés d'une partie réelle et d'une partie imaginaire :
z = a + b*i avec i*i = -1
Le conjugué de z noté z' est égal à :
z'= a – b*i
Et la norme est :
||z|| = sqrt(z * z') = sqrt(a²+b²)
En utilisant les propriétés précédentes, nous pouvons donner la multiplication de deux
complexes :
z1*z2 = (a1*a2 – b1*b2) + (a1*b2 + b1*a2)*i

2 Introduction aux quaternions

Les quaternions sont une extension de nombres complexes (proposés par Lord William
Hamilton 19° siècle) disposant de trois parties imaginaires :
q = a + b*i + c*j + d*k avec i*i = -1, j*j = -1, k*k = -1

La multiplication de deux de ces nombres se comporte comme le produit vectoriel de vecteurs


unitaires orthogonaux :
i*j = -j*i = k
j*k = -k*j = i
k*i = -i*k = j

Le conjugué et la norme d'un quaternion se calculent de façon similaire aux nombres


complexes.
Le conjugué ne note q' ou q* (nous choisirons la notation q' pour ne pas avoir d'ambiguïté
avec le symbole de multiplication) :
q = w + x*i + y*j + z*k
q' = w – x*i – y*j – z*k

La norme d'un quaternion est définie par :


||q|| = sqrt(q*q') = sqrt(w² + x² + y² + z²)
||q'|| = ||q||
||q0*q1|| = ||q0||*||q1||

La multiplication de quaternions est associative :


(q1 * q2)*q3 = q1*(q2*q3)
La multiplication de quaternions n'est pas commutative :
q1*q2 ≠ q2*q1

1
COURS : Quaternions
Véronique Gaildrat 02/03/08

3 Représentation des quaternions

Les quaternions peuvent être représentés de différentes façons :


• comme une combinaison linéaire de 1, i, j, k,
• comme un vecteur de 4 coefficients,
• comme un scalaire pour le coefficient de la partie réelle et comme un vecteur pour
les coefficients de la partie imaginaire.

Les quaternions sont donc donnés par :


qn = wn + xn*i + yn*j + zn*k avec : n ∈ IN, xn, yn, zn, wn ∈IR
ou par :
qn = [xn yn zn wn]
ou par :
qn = (sn, Vn) avec sn ∈IR et Vn = [xn yn zn]

4 Algèbre de quaternions

4.1 Addition et Soustraction


q0 + q1 = (w0 + x0*i + y0*j + z0*k) + (w1 + x1*i + y1*j + z1*k)
= (w0 + w1) + (x0 + x1)*i + (y0 + y1)*j + (z0 + z1)*k
q0 - q1 = (w0 + x0*i + y0*j + z0*k) - (w1 + x1*i + y1*j + z1*k)
= (w0 - w1) + (x0 - x1)*i + (y0 - y1)*j + (z0 - z1)*k

4.2 Multiplication
q0*q1 = (w0 + x0*i + y0*j + z0*k)* (w1 + x1*i + y1*j + z1*k)
= (w0*w1 – x0*x1 – y0*y1 – z0*z1) +
(w0*x1 + x0*w1 + y0*z1 – z0*y1)*i +
(w0*y1 – x0*z1 + y0*w1 +z0*x1)*j +
(w0*z1 + x0*y1 – y0*x1 + z0*w1)*k

Ce qui peut s'écrire :


q0*q1 = (w0 + x0*i + y0*j + z0*k)* (w1 + x1*i + y1*j + z1*k)
= w0*q1 + x0*i*q1 + y0*j*q1 + z0*k*q1
= w0*(w1 + x1*i + y1*j + z1*k) +
x0*i*(w1 + x1*i + y1*j + z1*k) +
y0*j*(w1 + x1*i + y1*j + z1*k) +
z0*k*(w1 + x1*i + y1*j + z1*k)
= w0*w1 + w0*x1*i + w0*y1*j + w0*z1*k +
x0*w1*i – x0*x1 + x0*y1*k – x0*z1*j +
y0*w1*j - y0*x1*k - y0*y1 + y0*z1*i +
z0*w1*k + z0*x1*j - z0*y1*i - 0*z1

Le produit de deux quaternions peut être écrit dans la représentation (s, V) en utilisant le
produit de vecteurs de la façon suivante :

2
COURS : Quaternions
Véronique Gaildrat 02/03/08

q0 = (s0, V0)
q1 = (s1, V1)
q0*q1 = (s0*s1 – V0.V1, s0*V1 + s1*V0 + V0xV1)

Avec cette représentation, il est clair que q0*q1 = q1*q0 si et seulement si V0xV1 = 0 (les
deux vecteurs sont parallèles).

4.3 Partie réelle d'un quaternion


Un opérateur retournant la partie réelle d'un quaternion est utilisé :
W(q) = W(w + x*i + y*j + z*k) = w

4.4 Multiplication par un scalaire


t*q = q*t = (t*w, t*x, t*y, t*z)

4.5 Produit scalaire de deux quaternions


Un quaternion pouvant être vu comme un vecteur 4D, le produit scalaire de deux quaternions
est :
q0.q1 = w0*w1 + x0*x1 + y0*y1 + z0*z1 = q1.q0
= W(q0*q1') = W(q1*q0')

4.6 Inverse d'un quaternion


L'inverse d'un quaternion q, notée q-1 a les propriétés suivantes :
q* (q) -1 = (q) -1 *q =1
(q) -1 = q' / ||q||
((q) -1 )-1 =q
(q0*q1) -1 = (q1)-1(q0)-1

4.7 Quaternion unitaire


Un quaternion de longueur 1 est appelé quaternion unitaire (unit quaternion) et il est tel que :
||qu|| = 1 => (qu) -1 = qu'
-1
||(qu) || =1
||qu'|| =1
Le produit de deux quaternions unitaires est lui aussi unitaire :
||qu0|| = 1 et ||qu1|| = 1 => ||qu0*qu1|| = 1

Un quaternion unitaire peut être représenté par :


qu = cos(θ) + V*sin(θ)
où V est un vecteur 3D de longueur 1.

Avec les nombres complexes : exp(i*θ) = cos(θ) + i*sin(θ)


De la même manière, avec les quaternions : exp(V*θ) = cos(θ) + V*sin(θ)

Ce qui permet d'introduire l'élévation à la puissance d'un quaternion unitaire :


qut = (cos(θ) + V*sin(θ))t = exp(V*t*θ) = cos(t*θ) + V*sin(t*θ)

Il est aussi possible de définir le log d'un quaternion unitaire :


log(qu) = log(cos(θ) + V*sin(θ)) = log(exp(V*θ)) = V*θ

3
COURS : Quaternions
Véronique Gaildrat 02/03/08

Attention : A cause de la non-commutativité de la multiplication de quaternions,


exp(pu)*exp(qu) et exp(pu+qu) ne sont pas nécessairement égaux. De même log(pu*qu) et
log(pu)+log(qu) ne sont pas nécessairement égaux.

5 Représentation des rotations par des quaternions

5.1 Présentation
Un quaternion q représente la rotation d'un vecteur U d'un angle 2*θ autour du vecteur
unitaire Vu.
q = cos(θ) + Vu*sin(θ) = (cos(θ),Vu[0]*sin(θ), Vu[1]*sin(θ), Vu[2]*sin(θ))

Un point p de l'espace est représenté par le quaternion p = (0, U). Le vecteur résultant,
représenté par un quaternion est :
protated = q*p*q-1

Si q est un quaternion unitaire, on peut utiliser le fait que q-1 = q'.

5.2 Concaténation de rotations


La nécessité de combiner des rotation est très fréquente (manipulation d'objets par la souris où
chaque mouvement de la souris équivaut à une rotation devant de combiner aux précédentes).
Ceci se fait très aisément avec les quaternions de façon vraiment moins coûteuse que par la
technique matricielle.
qu1 et qu2 sont deux quaternions unitaires représentant 2 rotations. Pour appliquer qu1 en
premier et qu2 ensuite, nous devons appliquer qu2 au résultat de l'application de qu1, regrouper
les résultats grâce à l'associativité de la multiplication, et trouver la rotation résultante
représentée par qu1*qu2 :
qu2*(qu1*p*qu1-1)*qu2-1 = (qu2*qu1)*p*(qu1-1*qu2-1) = (qu2*qu1)*p*(qu2*qu1) -1

Ainsi, l'enchaînement de deux rotations représentées par deux quaternions unitaires est une
rotation représentée par le quaternion équivalent à la multiplication des deux quaternions.
qu = qu2*qu1 (rotation qu1 suivie de rotation qu2).

5.3 Interpolation linéaire sphérique


L'angle θ est l'angle entre les deux vecteurs V1, V2 de q1, q2 :
θ = acos(q1.q2)
Quaternion slerp (t; q1; q2) = q1*(q1-1*q2)t
Ce qui équivaut à :
Quaternion slerp (t; q1, q2) = (q1*sin((1-t)*θ) + q2*sin(t*θ)) / sin(θ) avec 0 < t < 1

Pour deux orientations très proches l'une de l'autre, on peut se contenter d'une interpolation
linéaire simple.

6 Preuve que les quaternions permettent la mise en œuvre de rotations

4
COURS : Quaternions
Véronique Gaildrat 02/03/08

Un quaternion unitaire qu = cosθ + V*sinθ représente la rotation d'un vecteur 3D U autour


d'un axe 3D V. Le vecteur subissant la rotation, représenté comme un quaternion est donné par
rot(U) = qu*q*qu', avec q = (0, U). Pour prouver ceci il faut tout d'abord montrer que rot(U)
est une fonction possédant les propriétés suivantes :
- le résultat est un vecteur 3D,
- conservation des longueurs des vecteurs 3D,
- Rot(U) est une transformation linéaire.

6.1 Rot(U) est un vecteur 3D


W(rot(U)) = W(qu *q* qu ')
// (q + q ')/2 = W(q) = w (les facteurs de i, j et k s'annulent)
= ((qu *q* qu ') + (qu *q* qu')') / 2
// (q')' = q; (p*q)' = q'*p'
// (qu *(q* qu'))' = (q* qu')'* qu ' = qu *q'* qu '
= (qu *q* qu ' + qu *q' qu ') / 2
= qu ((q + q') / 2)* qu '
= qu *W(q)* qu '
= W(q)
= 0

6.2 Conservation des longueurs des vecteurs 3D


||rot(U)|| = || qu *q* qu '||
= || qu ||*||q||*|| qu '||
= || qu ||*||q||*|| qu || // qu étant un quaternion
unitaire
= ||q||
= ||U||

6.3 Rot(U) est une transformation linéaire


rot(a*U1+U2) = qu *(a*q1+q2)* qu '
// en développant le calcul on vérifie la distributivité de
la
// multiplication par rapport à l'addition
= (qu *a*q1* qu ') + (qu *q2* qu ')
= a*( qu *q1* qu ') + (qu *q2* qu ')
= a*rot(U1) + rot(U2)

6.4 Preuve

6.4.1 Vecteur subissant la rotation


Dans la figure suivante, le vecteur unitaire U est l'axe de rotation. Le vecteur V va subir une
rotation autour de U d'un angle θ pour obtenir le vecteur résultat : Vrotated. Le schéma suivant
présente le plan de U,V. Le vecteur UxV (noté V0) "sort" de la page.
V1 est le résultat de la projection de V sur l'axe U.
V2 est le résultat de la projection de V sur l'axe perpendiculaire à U.
V2 = V – V1

5
COURS : Quaternions
Véronique Gaildrat 02/03/08

V0 = UxV

(U.V)*U = cos(α)*||U||*||V||*U
= cos(α)*||V||*U
cos(α) = ||V1|| / ||V||
||V1|| = cos(α)*||V||
d'où (U.V)*U = ||V1||*U

Figure 1 : Positionnement de V par rapport à U unitaire - Représentation des vecteurs utilisés

L’étape suivante est de déterminer la représentation du vecteur Vrotated exprimée relativement à


U, V et θ. Ensuite, il restera à vérifier que les opérations sur les quaternions produisent le
même résultat.

Figure 2 : Représentation de Vrotated

6
COURS : Quaternions
Véronique Gaildrat 02/03/08

Figure 3 : Projection de Vrotated sur le plan V2,V0

Figure 4 : Projection de Vrotated sur le plan V1,V2

Ainsi, le vecteur Vrotated peut être exprimé relativement aux trois vecteurs utilisés :
Vrotated = V1 + cosθ*V2 + sinθ*V0

6.4.2 Vérification du résultat de la rotation effectuée grâce aux quaternions


Conformément aux principes sur les quaternions énoncés précédemment, une rotation peut
être effectuée sur un vecteur grâce au produit de quaternions :
p = (0, V)
qu = (cos(θ/2), U*sin(θ/2))
protated = qu*p*qu-1

Rappel de la formule vectorielle de multiplication de quaternions :


q1 = (s1, V1)
q2 = (s2, V2)
q1*q2 = (s1*s2 – V1.V2, s1*V2 + s2*V1 + V1xV2)

Pour vérifier la formule précédente nous substituons à qu-1 la valeur qu' étant donné que qu est
unitaire.
Il est à noter qu'on utilise le fait que :
U.V = V.U
UxV = -VxU

7
COURS : Quaternions
Véronique Gaildrat 02/03/08

(UxV).U = 0 // car UxV est perpendiculaire à U


(UxV)xU = V2 = V-(U.V)*U
sin(θ) = sin(θ/2 + θ/2) = sin(θ/2)*cos(θ/2) + sin(θ/2)*cos(θ/2) = 2*sin(θ/2)*cos(θ/2)
cos(θ) = cos(θ/2 + θ/2) = cos(θ/2)*cos(θ/2) - sin(θ/2)*sin(θ/2) = cos²(θ/2) - sin²(θ/2)
sin²(θ/2) + cos²(θ/2) = 1
2*sin²(θ/2) = sin²(θ/2) + sin²(θ/2) + cos²(θ/2) - cos²(θ/2) = 1 – (cos²(θ/2) - sin²(θ/2))
= (1 - cos(θ))

protated = qu*p*qu'
= (cos(θ/2), U*sin(θ/2)) * (0, V) * (cos(θ/2), -U*sin(θ/2))
= [(cos(θ/2), U*sin(θ/2)) * (0, V)] * (cos(θ/2), -U*sin(θ/2))
= (-sin(θ/2)*(U.V), cos(θ/2)*V + sin(θ/2)*(UxV) * (cos(θ/2), -U*sin(θ/2))
= (-sin(θ/2)*cos(θ/2)*(U.V) + sin(θ/2)*cos(θ/2)*(V.U) – sin²(θ/2)*((UxV).U),
(sin²(θ/2)*(U.V))*U + cos²(θ/2)*V + (sin(θ/2)*cos(θ/2))*(UxV)
- (sin(θ/2)*cos(θ/2))*(VxU) - sin²(θ/2)*((UxV)xU)
)
protated = (0, (sin²(θ/2)*(U.V))*U + cos²(θ/2)*V + 2*(sin(θ/2)*cos(θ/2))*(UxV)
- sin²(θ/2)*(V – (U.V)*U)
)
= (0, 2*(sin²(θ/2)*(U.V))*U + (cos²(θ/2) - sin²(θ/2))*V
+ 2*(sin(θ/2)*cos(θ/2))*(UxV)
)
= (0, ((1 – cos(θ))*(U.V))*U + cos(θ)*V + sin(θ)*(UxV))
= (0, (U.V))*U + cos(θ)*(V – (U.V)*U) + sin(θ)*(UxV))
= expression vectorielle de la rotation de vecteur
= (0, Vrotated)

7 Exercices

7.1 Animation et quaternions

7.1.1 Comment passer d'une rotation définie selon la convention OpenGL à un


quaternion et réciproquement
Avec OpenGl, une rotation est donnée par un axe autour duquel s'effectue la rotation,
et un angle.
Le quaternion équivalent q (s, V) est donné par :

axeAngle2quaternion (double angle ; Vecteur axe ; Quaternion * q)


{
normer (axe)
q.s = cos(angle/2) ;
q.V = sin(angle/2)*axe ; }

Pour obtenir la rotation exprimée sous forme OpenGL à partir d’un quaternion :

8
COURS : Quaternions
Véronique Gaildrat 02/03/08

quaternion2axeAngle (Quaternion q ; double * angle ; Vecteur * axe)


{
phi = acos(q.s) ;
Si sin(phi) ≠ 0 Alors
axe = q.V / sin(phi) ;
Sinon
axe = Z ; // n’importe quel vecteur
Fin si
normer (axe)
angle = 2 * phi ; }

7.1.2 Pourquoi n'y a-t-il aucun problème avec angle = 0, PI ou 2*PI


q.s représente cos(angle/2).
Dans quel cas sin(phi) = sin(angle/2) = 0 ?
- Quand phi = 0, angle = 0, une rotation nulle => aucun problème si angle
quelconque,
- Quand phi = pi, angle = 2*pi, donc rotation nulle.

7.1.3 Animation d’un seul segment de robot


• Donner la structure de données associée à un seul segment de bras de robot dont la
géométrie est un parallélépipède rectangle. Le type Boîte est défini ainsi que
l’opérateur de tracé d’une Boîte (traceBoite (Boite b) {//trace d’une boite}).

type Segment = { Boite b ;


Quaternion orientationInit ;
Quaternion orientationFinale ;
Vecteur position;
} // position de la boite (translation)

• Ecrire l’opérateur de tracé OpenGL d’un segment de bras de robot utilisant la


fonction slerp.

traceSegment (Segment seg; double t) // le segment est tracé en t (0<=t<=1){


Vecteur axe ;
double angle ;
Quaternion q ;
// détermination du quaternion représentant la rotation à l’instant t
q = slerp (t, seg.orientationInit, seg.orientationFinale) ;
// détermination de l’axe-angle nécessaires à OpenGL
quaternion2axeAngle (q, &angle, &axe) ;
glTranslate (seg.position[0], seg.position[1], seg.position[2]) ;
glRotate (angle, axe[0], axe[1], axe[2]) ;
traceBoite (seg.b) ;
// attention, la pile de matrice est affectée par l’opération, dans le cas
// où ceci n’est pas souhaité, la préservation de la pile reste à l’appelant
}

9
COURS : Quaternions
Véronique Gaildrat 02/03/08

7.1.4 Animation d’un bras multisegment


• Structure de données.

On peut faire un bras de robot linéaire en utilisant une liste de Segments.


On peut avoir une structure plus complexe en prenant un arbre de Segments.
Le traitement reste identique, seul le parcours de la structure change.
On considère que la position du (i)ème bras est donnée relativement à la position
du (i-1)ème. Donc gérer la pile de matrice en ce sens.

On va traiter une liste de Segments :


type ListeSegment;
type CelluleSegment = {Segment s; ListeSegment * segmentSuivant;}
type ListeSegment = access CelluleSegment;

traceListeSegment (ListeSegment ls; double t) {


Si ls /= null Alors
traceSegment (ls->segment, t);
traceListeSegment (ls->segmentSuivant, t);
Fsi
}

main ( ) {
// déclarations utiles

// tracer le bras multi-segments
glPushMatrix ();
Pour t := 0; t <= 1; t:= t + deltaT Faire
traceListeSegment (teteListe, t);
FinPour
glPopMatrix ();
}

7.2 Mouvements de caméra


On souhaite gérer grâce à des interactions souris ou clavier les déplacements d'une caméra
placée dans une scène 3D.
La position d'une caméra est celle de l'observateur correspondant.
Cet observateur se trouve sur une sphère. Le centre de la sphère est le point visé. Le centre est
exprimé dans le repère de la scène.
L'orientation d'une caméra est donnée principalement par la donnée d'un axe de rotation
(unitaire) donné dans le repère de la sphère (xs, ys, zs) + un angle de rotation autour de cet
axe.
Mais aussi par les coordonnées des trois vecteurs unitaires (xc, yc, zc) qui forment le repère
de la caméra par rapport au repère de la sphère.
Ces transformations s'appliquent à la position par défaut de la caméra pour fournir la position
courante de la caméra.

10
COURS : Quaternions
Véronique Gaildrat 02/03/08

La position de l'observateur se calcule par : observateur = centre - rayon * zc

Caméra par défaut :

On désire piloter les déplacements de la caméra de la façon suivante :


• cap (pitch) : rotation autour de yc sans changement de position,
• roulis (roll) : rotation autour de zc sans changement de position,
• tangage (yaw) : rotation autour de xc sans changement de position,
• déplacement selon un axe du repère sans changement d'orientation,
• rapprochement ou éloignement du centre de la sphère,
• changement de la position de l'observateur sur la sphère où il se trouve, avec le point
visé inchangé (déplacement exprimé en coordonnées polaires (du, dv)).

7.2.1 Passage de la représentation (axe, angle) à la représentation (xc, yc, zc)


Pour passer de la représentation (axe, angle) au repère de la caméra donné par trois vecteurs,
on peut utiliser la gestion de matrices de OpenGL.

1. Sauvegarder l'état courant de la matrice (glGetIntegerv(GL_MATRIX_MODE, &matrix)


2. Passer en mode de gestion du repère scène (glMatrixMode(GL_MODELVIEW))
3. Initialiser la pile de matrices (glPushMatrix et glLoadIdentity)
4. Effectuer une rotation de l'angle en degrés autour de l'axe (glRotatef)
5. Récupérer la matrice ainsi générée (glGetFloatv(GL_MODELVIEW_MATRIX, mat))
6. Restaurer la pile de matrices (glPopMatrix, glMatrixMode(matrix))
7. Initialiser le repère de la caméra avec les valeurs de la matrice mat

xc yc zc
mat[0] mat[4] -mat[8] // repère indirect
mat[1] mat[5] -mat[9]
mat[2] mat[6] -mat[10]
Normaliser les 3 vecteurs

7.2.2 Cap, roulis, tangage


Ces trois changements d'orientations se font de la même manière mais selon un axe donné. Ils
modifient l'axe et l'angle de visée de la caméra.

1. Transformer le couple (axe , angle) en quaternion : q1 = (cos(angle/2), sin(angle/2)*axe).

11
COURS : Quaternions
Véronique Gaildrat 02/03/08

2. Transformer le couple (axe_repère (xc ou yc ou zc), angle_repère) en quaternion : q2 =


(cos(angle_repère/2), sin(angle_repère/2)*axe_repère).
3. Multiplier q1 par q2 pour obtenir une rotation qui est la combinée des deux autres : q3.
4. Mettre à jour axe et angle à partir du quaternion résultat q3.
5. Mettre à jour le repère de la caméra (xc, yc, zc) grâce à (axe, angle).
6. Calcul du nouveau centre de la sphère : centre = position + rayon * zc.

7.2.3 Déplacement selon un axe du repère sans changement d'orientation


Seuls le centre et la position sont modifiés.
1. Position = position + déplacement * axe_repère.
2. Calcul du nouveau centre de la sphère : centre = position + rayon * zc.

7.2.4 Rapprochement ou éloignement du centre de la sphère


Le centre et l'orientation restent inchangés.
1. Modification du rayon qui reste > 0.
2. Mise à jour de la position de la caméra : position = centre – rayon * zc.

7.2.5 Changement de la position de l'observateur sur la sphère (du, dv)


La valeur du représente un angle de déplacement sur la sphère le long d'un parallèle, et dv le
long d'un méridien.

Pour prendre en compte du, on effectue une rotation autour de l'axe ys et pour dv, autour de
l'axe x's qui est à déterminer.

1. Transformer le couple (axe , angle) en quaternion : q1 = (cos(angle/2), sin(angle/2)*axe).

12
COURS : Quaternions
Véronique Gaildrat 02/03/08

2. Créer le quaternion représentant la rotation de l'angle du autour de l'axe ys : q2.


3. Créer le quaternion représentant la rotation de l'angle du autour de l'axe x's : q3.
4. Multiplier q1 par q2 pour appliquer à la caméra la rotation autour de ys : q4.
5. Multiplier q1 par q2 pour appliquer à la caméra la rotation autour de x's: q5.
6. Mettre à jour axe et angle à partir du quaternion résultat q5.
7. Mettre à jour le repère de la caméra (xc, yc, zc) grâce à (axe, angle).
8. Mettre à jour de la position de la caméra : position = centre – rayon * zc.

13
COURS : Quaternions