是否有将四元数旋转转换为欧拉角旋转的算法?

Wil*_*ker 27 language-agnostic math animation quaternions euler-angles

是否存在将旋转的四元数表示转换为欧拉角表示的现有算法?欧拉表示的旋转顺序是已知的,并且可以是六个排列中的任何一个(即xyz,xzy,yxz,yzx,zxy,zyx).我已经看到了固定旋转顺序的算法(通常是NASA标题,银行,滚动惯例),但不是任意旋转顺序.

此外,因为存在单个方向的多个欧拉角表示,所以该结果将是模糊的.这是可以接受的(因为方向仍然有效,它可能不是用户期望看到的方向),但是如果存在一个采用旋转限制的算法(即自由度数和考虑到每个自由度的限制,并在给定这些约束条件下产生"最明智的"欧拉表示.

我有一种感觉,这个问题(或类似的东西)可能存在于IK或刚体动力学领域.


解决:我刚刚意识到我可能不清楚我是通过遵循图形宝石的Ken Shoemake的算法来解决这个问题的.当时我确实回答了我自己的问题,但是我发现它可能并不清楚我这样做了.有关详细信息,请参阅下面的答案.


只是为了澄清 - 我知道如何从四元数转换为所谓的' Tait-Bryan '表示 - 我称之为'NASA'约定.这是zxy的旋转顺序(假设'Z'轴向上)的惯例.我需要一个适用于所有轮换订单的算法.

那么,解决方案可能是采用zxy顺序转换,并从其中获得其他五个转换以获得其他旋转顺序.我想我希望有一个更"全面"的解决方案.无论如何,我很惊讶我找不到现有的解决方案.

另外,这也许应该是一个单独的问题,任何转换(假设已知的旋转顺序,当然)将选择一个欧拉表示,但实际上有很多.例如,给定yxz的旋转顺序,两个表示(0,0,180)和(180,180,0)是等价的(并且将产生相同的四元数).有没有办法用限制自由度来约束解决方案?就像你在IK和僵硬的身体动力学中做的那样?即,在上述示例中,如果关于Z轴仅存在一个自由度,则可以忽略第二表示.


我已经找到了一篇可能是这个pdf算法的论文,但我必须承认,我发现逻辑和数学有点难以理解.当然还有其他解决方案吗?任意轮换顺序真的如此罕见吗?当然,每个允许骨架动画和四元数插值(即Maya,Max,Blender等)的主要3D包必须解决这个问题吗?

Wil*_*ker 13

这看起来像旧技术的经典案例被忽略了 - 我设法从车库中挖出了一个图形宝石IV的副本,看起来Ken Shoemake不仅有从任意旋转顺序的欧拉角转换的算法,而且还有答案我关于这个问题的大多数其他问题.万岁书籍.如果我能够对Shoemake先生的回答进行投票,并以声望点奖励他.

我想建议任何使用Euler角度的人都应该从他们的本地库中获取Graphics Gems IV的副本并阅读从第222页开始的部分.它必须是我所读过的问题的最清晰,最简洁的解释.


这是我发现的一个有用的链接 - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - 它遵循与Shoemake相同的系统; 旋转顺序的24种不同排列被编码为四个独立的参数 - 内轴,奇偶校验,重复和帧 - 然后允许您将算法从24个减少到2个.可能是一个有用的维基 - 我还没有来之前穿过它.

这里提供的旧链接似乎被打破另一个副本"从旋转矩阵计算欧拉角".


Jon*_*röm 10

在Z轴向上的右手笛卡尔坐标系中,执行以下操作:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)


fro*_*975 7

我一直在寻找类似解决方案的几天,我终于遇到了这个网站,它有一个将四元数转换为任意Euler和Tait-Bryan旋转的算法!

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

这是代码:

///////////////////////////////
// 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;
   }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这是我写的一篇关于将四元数转换为欧拉角的论文。

使用几何方法将任意旋转序列的四元数转换为欧拉角

我还在这个位置放置了许多文档,讨论四元数、欧拉角和旋转矩阵 (DCM) 的各个方面。

EuclideanSpace.com:数学 - 将四元数转换为欧拉

另请参阅我的第一个答案