是否存在将旋转的四元数表示转换为欧拉角表示的现有算法?欧拉表示的旋转顺序是已知的,并且可以是六个排列中的任何一个(即xyz,xzy,yxz,yzx,zxy,zyx)。我已经看到了固定旋转顺序的算法(通常是NASA标题,库,滚动约定),但不是任意旋转顺序。
此外,由于单个方向存在多个欧拉角表示,因此该结果将是模糊的。这是可以接受的(因为方向仍然有效,它可能不是用户期望看到的那个),但是如果有一个采用旋转限制的算法会更好(即自由度的数量和每个自由度的限制)考虑到这些约束,产生了“最明智的”欧拉表示。
我感觉这个问题(或类似的东西)可能存在于IK或刚体动力学领域。
解决了:我刚才意识到我可能不清楚我是通过遵循Ken Geoe的Ken Shoemake算法来解决这个问题的。当时我确实回答了我自己的问题,但是我发现它可能并不清楚我这样做了。有关更多详细信息,请参阅下面的答案。
只是为了澄清 - 我知道如何从四元数转换为所谓的'Tait-Bryan'表示 - 我称之为'NASA'约定。这是zxy的旋转顺序(假设'Z'轴向上)的惯例。我需要一个所有轮换订单的算法。
然后,解决方案可能是采用zxy顺序转换,并从其中获得其他旋转顺序的其他五个转换。我想我希望有一个更“全面”的解决方案。无论如何,我很惊讶我找不到现有的解决方案。
此外,这或许应该是一个单独的问题,任何转换(当然,假设已知的旋转顺序)将选择一个 Euler表示,但实际上有很多。例如,给定yxz的旋转顺序,两个表示(0,0,180)和(180,180,0)是等价的(并且将产生相同的四元数)。有没有办法用限制自由度来约束解决方案?就像你在IK和僵硬的身体动力学中做的那样?即在上面的例子中,如果绕Z轴只有一个自由度,则可以忽略第二个表示。
我已经找到了一篇可能是this pdf算法的论文,但我必须承认,我发现逻辑和数学有点难以理解。当然还有其他解决方案吗?任意轮换顺序真的如此罕见吗?当然,每个主要的3D软件包都允许骨架动画和四元数插值(即Maya,Max,Blender等)必须解决这个问题吗?
答案 0 :(得分:13)
这看起来像旧技术的经典案例被忽略了 - 我设法从车库中挖出了一个Graphics Gems IV的副本,看起来Ken Shoemake不仅有一个算法从任意角度转换轮换顺序,但也回答了我关于这个主题的大部分其他问题。万岁书籍。如果我能够对Shoemake先生的回答进行投票,并以声望点奖励他。
我想建议任何使用Euler角度的人都应该从他们的本地库中获取Graphics Gems IV的副本并阅读从第222页开始的部分。它必须是我所读过的问题的最清晰,最简洁的解释。
这是我发现的一个有用的链接 - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - 这与Shoemake的系统相同;旋转顺序的24种不同排列被编码为四个独立的参数 - 内轴,奇偶校验,重复和帧 - 然后允许您将算法从24个减少到2个。可能是一个有用的维基 - 我还没有来之前穿过它。
提供的旧链接似乎被破坏here是“从旋转矩阵计算欧拉角度的另一个副本 ”
答案 1 :(得分: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));
}
}
答案 2 :(得分: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;
}
}
答案 3 :(得分:3)
我在noelhughes.net网站上发布了题为“使用几何方法对任意旋转序列进行欧拉角转换的四元数”的论文。我也有算法将任何一组欧拉角转换为四元数和四元数到/从方向余弦矩阵转换,我将在本周末发布。这些也在Martin Bakers网站上,虽然有点难以找到。 Google我的名字,Noel Hughes和四元数,你应该找到它。
答案 4 :(得分:3)
我这样解决:
第1步:确定您想要的Euler旋转的约定,例如 zyx 。
第2步:计算旋转的分析旋转矩阵。 例如,如果您想要R( zyx ),
** R *** zyx * = ** R *** x *( phi )* ** R *** y *( theta )* ** R *** z *( psi ),元素变为
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)
第3步:通过检查,您可以使用上述元素找到三个角度的sin或tan。在这个例子中,
tan(phi) = -R23/R33
sin(theta) = -R13
tan(psi) = -R12/R11
第4步:计算四元数中的旋转矩阵(请参阅wikipedia),了解上面3)中计算角度所需的元素。
可以使用相同的程序计算其他约定。
答案 5 :(得分:3)
答案 6 :(得分:3)
对于那些在谷歌搜索时偶然发现这个页面的人,我最近发现所有12个内在的Tait-Bryan(1-2-3,3-2-1等)和Proper Euler(1-2)的这些转换的推导-1,3-1-3等)旋转序列在以下两个参考文献中:
感谢frodo2975第二个链接。
答案 7 :(得分:2)
Wikipedia显示如何使用四元数的各个部分并计算欧拉角。