13

我正在使用 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/

4

4 回答 4

16

是的,这很正常。有两种方法可以用欧拉角表示相同的旋转。

我个人不喜欢欧拉角,它们会破坏应用程序的稳定性。我会避开他们。另外,它们也不是很方便

于 2012-06-19T15:28:56.100 回答
12

If you end up needing quaternion's to Euler angles, but you need an arbitrary rotation order, I came across a site with conversion code. Sometimes the trick is just finding the right rotation order. (Btw, the orders that have the same letter twice, like XYX, are proper Euler angles, but the ones like XYZ are Tait-Bryan angles).

Here's the link: http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

And here's the 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;
   }
}
于 2014-12-16T03:24:09.757 回答
11

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

四元数到欧拉角

欧拉角到四元数

所有旋转转换

于 2012-07-03T13:14:26.557 回答
-2

欧拉 -> 四元数

从 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>

于 2016-05-14T20:50:03.257 回答