欧拉角到四元数然后四元数到欧拉角

Posted

技术标签:

【中文标题】欧拉角到四元数然后四元数到欧拉角【英文标题】:Euler angle to Quaternion then Quaternion to euler angle 【发布时间】:2012-06-21 15:39:51 【问题描述】:

我正在使用 lib glm (http://glm.g-truc.net/) 测试四元数,但我遇到了问题;当我将欧拉角转换为四元数然后立即将四元数转换为欧拉角时,我的结果与我最初的欧拉角完全不同。这是正常的吗?会不会是轮换不是交际的?

代码测试:

#include <glm\quaternion.hpp>
#include <math.h>

#define PI M_PI
#define RADTODEG(x) ( (x) * 180.0 / PI )
#define DEGTORAD(x) ( (x) * PI / 180.0 )

int         main( void )

    float RotX = 90.f;
    float RotY = 180.f;
    float RotZ = -270.f;

    if ( RotX || RotY || RotZ )
    
        std::cout << "Init: x= " << RotX << ", y= " << RotY << ", z= " << RotZ << "\n";
        glm::quat key_quat(glm::detail::tvec3<float>(DEGTORAD( RotX ),
                                                     DEGTORAD( RotY ),
                                                     DEGTORAD( RotZ )));
        glm::detail::tvec3<float> v = glm::eulerAngles(key_quat);

        /*  // the result is even worse with this code here
        RotX = RADTODEG(v.x);
        RotY = RADTODEG(v.y);
        RotZ = RADTODEG(v.z);
        */

        RotX = v.x;
        RotY = v.y;
        RotZ = v.z;

        std::cout << "Final: x= " << RotX << ", y= " << RotY << ", z= " << RotZ << "\n";
    
    return (0);

结果:

Init: x= 90, y= 180, z= -270
Final: x= -90, y= -3.41509e-006, z= -90

提前谢谢你o/

【问题讨论】:

【参考方案1】:

是的,这很正常。 There are 2 ways 用欧拉角表示相同的旋转。

我个人不喜欢欧拉角,they mess up the stability of your app. 我会避开它们。另外,他们也是not very handy。

【讨论】:

感谢这个有用的答案。实际上,在我的引擎中,我使用四元数来旋转我的对象。所以,用户可以使用函数; SetRotation 和 GetRotation(带有 3 个欧拉角)。在这个函数中,我对对象四元数进行操作,并为用户保存欧拉角。如果我说用欧拉角指定它的旋转比用四元数指定它的旋转要简单得多,你可能会同意我的看法......(我将阅读/观看你链接的所有内容。我会回来) 是的,我同意,欧拉角在与用户交流时很有用。 好的。我读了你告诉我的那本书,现在我什么都明白了。非常感谢。 什么?他们不是邪恶的……完全是上下文相关的。 两种方式?有 24 种不同的野兽符合“欧拉角”的条件,如果您允许使用左手坐标系,则更多。欧拉角并不邪恶。这是轻描淡写的事情。他们是邪恶的化身。但尽管它们很邪恶,但它们在与用户交流时会很有用。【参考方案2】:

如果您最终需要四元数到欧拉角,但您需要任意旋转顺序,我遇到了一个带有转换代码的网站。有时诀窍就是找到正确的轮换顺序。 (顺便说一句,有两个相同字母的订单,如 XYX,是适当的欧拉角,但像 XYZ 是 Tait-Bryan 角)。

这是链接:http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

这是代码:

///////////////////////////////
// Quaternion to Euler
///////////////////////////////
enum RotSeqzyx, 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;
   

【讨论】:

如何读取轮换序列?例如zyx,这是否意味着先应用z然后应用y然后x(元素矩阵乘法顺序为Rx*Ry*Rz)还是zyx意味着它是Rz*Ry*Rx 不确定实际的矩阵相乘,但从直观的角度来看,您按顺序旋转,就像 zyx 一样,您将围绕对象的局部 z 轴旋转,然后是 y,然后是 x .如果您在可视化它时遇到问题,下载像 Blender 这样的 3D 建模程序会很有用,这样您就可以看到旋转。几乎所有的 3d 程序都有一个万向节旋转模式,它可以让你围绕一个 tait-bryan 轴旋转一个对象。【参考方案3】:

看看这个页面。它拥有处理 3D 转换所需的一切(甚至是一些代码示例!)。

Quaternion to Euler Angles

Euler Angles to Quaternion

All rotation conversions

【讨论】:

【参考方案4】:

欧拉 -> 四元数

从 Three.js 中提取。

这是一段对我有用的代码:

function eulerToQuaternion(eulerXYZ) 
  var c1 = Math.cos(eulerXYZ[0] / 2),
    c2 = Math.cos(eulerXYZ[1] / 2),
    c3 = Math.cos(eulerXYZ[2] / 2),
    s1 = Math.sin(eulerXYZ[0] / 2),
    s2 = Math.sin(eulerXYZ[1] / 2),
    s3 = Math.sin(eulerXYZ[2] / 2),
    x = s1 * c2 * c3 + c1 * s2 * s3,
    y = c1 * s2 * c3 - s1 * c2 * s3,
    z = c1 * c2 * s3 + s1 * s2 * c3,
    w = c1 * c2 * c3 - s1 * s2 * s3;

  return [x, y, z, w];
;

function calculate() 
  var quat = eulerToQuaternion([document.querySelector('#x').value, document.querySelector('#y').value, document.querySelector('#z').value]);

  document.querySelector('#result').innerhtml = quat.join(' &nbsp; ');
<h3>Euler radians in XYZ order:</h3>
<fieldset>
  <label>X:
    <input id="x" value="1.5" />
  </label>
  <label>Y:
    <input id="y" value="1" />
  </label>
  <label>Z:
    <input id="z" value="0" />
  </label>
  <button onClick="calculate()">To Quaternion</button>
</fieldset>
<h3>X Y Z W result:</h3>
<div id="result"></div>

【讨论】:

以上是关于欧拉角到四元数然后四元数到欧拉角的主要内容,如果未能解决你的问题,请参考以下文章

如何将欧拉角转换为四元数并从四元数中得到相同的欧拉角?

python 欧拉角,旋转矩阵,四元数之间转换

欧拉角与四元数转换——编程

怎么把向量转化为四元数或欧拉角

UnityUnity 欧拉角四元数万向节死锁四元数转轴角

基于四元数的 3D 相机应该累积四元数还是欧拉角?