Question

Existe-t-il un algorithme permettant de convertir une représentation en quaternion d’une rotation en une représentation en angle de Euler? L'ordre de rotation pour la représentation d'Euler est connu et peut être n'importe laquelle des six permutations (c'est-à-dire xyz, xzy, yxz, yzx, zxy, zyx). J'ai vu des algorithmes pour un ordre de rotation fixe (généralement l'en-tête, la banque, la convention de roulement de la NASA), mais pas pour un ordre de rotation arbitraire.

De plus, comme il existe plusieurs représentations sous un même angle d’Euler pour une même orientation, ce résultat sera ambigu. C’est acceptable (car l’orientation est toujours valide , ce n’est peut-être pas celui que l’utilisateur s’attend à voir), mais il serait même préférable qu’un algorithme prenne des limites de rotation (c.-à-d. le nombre de degrés de liberté et les limites de chaque degré de liberté) ont été pris en compte et ont donné la représentation d'Euler «la plus raisonnable» compte tenu de ces contraintes.

J'ai le sentiment que ce problème (ou quelque chose de similaire) peut exister dans les domaines des connaissances indigènes ou de la dynamique des corps rigides.

Résolu: je viens de réaliser qu'il n'était peut-être pas évident d'avoir résolu ce problème en suivant les algorithmes de Ken Shoemake de Graphics Gems. J'ai répondu à ma propre question à l'époque, mais il me semble que ce n'est peut-être pas évident. Voir la réponse ci-dessous pour plus de détails.

Juste pour préciser, je sais comment passer d'un quaternion à la soi-disant Tait- La représentation de Bryan - ce que j'appelais la convention «NASA». Ceci est un ordre de rotation (en supposant que l’axe 'Z' est en haut) de zxy. J'ai besoin d'un algorithme pour tous les ordres de rotation.

La solution consiste peut-être à prendre la conversion d’ordre zxy et à en tirer cinq autres conversions pour les autres ordres de rotation. J'imaginais que j'espérais qu'il y avait une solution plus globale. En tout cas, je suis surpris de ne pas avoir trouvé les solutions existantes.

De plus, et cela devrait peut-être être une question distincte, toute conversion (en supposant un ordre de rotation connu) va sélectionner une représentation d'Euler, mais il en existe plusieurs. Par exemple, pour un ordre de rotation de yxz, les deux représentations (0,0180) et (180,180,0) sont équivalentes (et donneraient le même quaternion). Existe-t-il un moyen de contraindre la solution en limitant les degrés de liberté? Comme tu le fais avec les IK et la dynamique des corps rigides? C'est-à-dire que dans l'exemple ci-dessus, s'il n'y avait qu'un degré de liberté autour de l'axe Z, la deuxième représentation peut être ignorée.

J'ai repéré un document qui pourrait être un algorithme dans ce pdf mais je dois avouer que la logique et les mathématiques me semblent difficiles à suivre. Il y a sûrement d'autres solutions là-bas? L'ordre de rotation arbitraire est-il vraiment si rare? Tous les logiciels 3D majeurs permettant l’animation squelettique ainsi que l’interpolation de quaternions (Maya, Max, Blender, etc.) doivent avoir résolu exactement ce problème?

Était-ce utile?

La solution

Cela ressemble à un cas classique de vieille technologie négligée - j'ai réussi à extraire une copie de Graphics Gems IV du garage et il semble que Ken Shoemake ne possède pas seulement un algorithme pour convertir à partir d'angles d'Euler arbitraires ordre de rotation, mais répond également à la plupart de mes autres questions sur le sujet. Hourra pour les livres. Si seulement je pouvais voter la réponse de M. Shoemake et le récompenser par des points de réputation.

Je suppose qu'il est recommandé à toute personne travaillant avec des angles d'Euler d'obtenir une copie de Graphics Gems IV de sa bibliothèque locale et de lire la section à partir de la page 222. Ce doit être l’explication la plus claire et la plus concise du problème que j’ai lu à ce jour.

Voici un lien utile que j'ai trouvé depuis - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - Cela suit le même système que Shoemake; les 24 différentes permutations d'ordre de rotation sont codées en quatre paramètres distincts - axe interne, parité, répétition et cadre - ce qui vous permet ensuite de réduire l'algorithme de 24 à 2 cas. Pourrait être un wiki utile en général - je n'étais pas venu à travers elle avant.

L'ancien lien fourni semble être rompu ici . est une autre copie des "angles de calcul d'Euler calculés à partir d'une matrice de rotation ".

Autres conseils

Dans un système de coordonnées cartésien à droite avec l'axe Z dirigé vers le haut, procédez comme suit:

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

Je recherche une solution similaire depuis plusieurs jours et j'ai enfin découvert ce site Web qui dispose d'un algorithme de conversion des quaternions en rotations Euler et Tait-Bryan arbitraires!

Voici le lien: http://bediyap.com/programming/convert -quaternion-to-euler-rotations /

Et voici le code:

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

J'ai posté mon article intitulé "Conversion d'angle de Quaternion à Euler pour une séquence de rotation arbitraire à l'aide de méthodes géométriques". sur mon site Web à noelhughes.net. J'ai aussi des algorithmes pour convertir n'importe quel ensemble d'angles d'Euler en quaternion et quaternion vers / depuis la matrice de cosinus de direction que je publierai ce week-end. Celles-ci se trouvent également sur le site Web de Martin Bakers, bien qu’un peu difficile à trouver. Google mon nom, Noel Hughes, et quaternions et vous devriez le trouver.

Je le résous de cette façon:

étape 1 : assurez-vous de la convention de rotation d'Euler souhaitée, par exemple, zyx .

étape 2 : calculez la matrice de rotation analytique pour la rotation. Par exemple, si vous voulez R ( zyx ),

** R *** zyx * = ** R *** x * ( phi ) * ** R *** y * ( thêta ) * ** R *** z * ( psi ), où les éléments deviennent

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) 

étape 3 : lors de l'inspection, vous pouvez déterminer le péché ou le bronzage des trois angles à l'aide des éléments ci-dessus. Dans cet exemple,

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

étape 4 : calculez la matrice de rotation de votre quaternion (voir wikipedia ), pour les éléments dont vous avez besoin pour calculer les angles, comme indiqué en 3) ci-dessus.

D'autres conventions peuvent être calculées à l'aide de la même procédure.

Voici un article que j'ai écrit sur la conversion d'un quaternion en angles d'Euler.

Lien 1

J'ai également placé à cet endroit un certain nombre de documents traitant de divers aspects des quaternions, des angles d'Euler et des matrices de rotation (DCM).

Lien 2

Pour ceux qui tombent sur cette page pendant la recherche sur Google, j’ai récemment trouvé des dérivations de ces conversions pour les 12 Tait-Bryan intrinsèques (1-2-3, 3-2-1, etc.) et Proper Euler (1-2). -1, 3-1-3, etc.) dans les deux références suivantes:

Merci à frodo2975 pour le deuxième lien.

Wikipedia montre comment vous pouvez utiliser les parties du quaternion et calculer les angles d'Eutler.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top