4

我正在将角度轴表示转换为欧拉角。我决定检查并确保我从转换中得到的欧拉角会回到原来的轴角。我打印出值,但它们不匹配!我已阅读http://forum.onlineconversion.com/showthread.php?t=5408http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles以及本网站上的类似转换问题。

在下面的代码中,我从角度“角度”和轴 (rx,ry,rz) 开始,然后将其转换为四元数 (q0,q1,q2,q3)。我将四元数转换为欧拉角(滚动、俯仰、偏航)。然后为了检查它,我将 (roll,pitch,yaw) 转换回轴角为 cAngle 和 (cRx,cRy,cRz)。然后我对(滚动、俯仰、偏航)进行一些边界检查,以将数字保持在 -pi 和 pi 之间,然后将它们打印出来。应该是cAngle=angle和(cRx,cRy,cRz)=(rx,ry,rz),但是这两个都是错的。

我相信,旋转的顺序是 Z*Y*X,这很常见。我的数学有问题吗?我计划最终在http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/中为音高为 0 或 PI 添加特殊情况,但现在我认为问题是单独的。

        //input is angle 'angle' and axis '(rx,ry,rz)'

        //convert rx,ry,rz, angle, into roll, pitch, yaw
        double q0 = Math.Cos(angle / 2);
        double q1 = Math.Sin(angle / 2) *Math.Cos(rx);
        double q2 = Math.Sin(angle / 2) * Math.Cos(ry);
        double q3 = Math.Sin(angle / 2) * Math.Cos(rz);
        double roll = Math.Atan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1 * q1 + q2 * q2));
        double pitch = Math.Asin(2 * (q0 * q2 - q3 * q1));
        double yaw = Math.Atan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2 * q2 + q3 * q3));

        //convert back to angle axis
        double cAngle = 2 * Math.Cos(Math.Cos(roll / 2) * Math.Cos(pitch / 2) * Math.Cos(yaw / 2) + Math.Sin(roll / 2) * Math.Sin(pitch / 2) * Math.Sin(yaw / 2));
        double cRx = Math.Acos((Math.Sin(roll / 2) * Math.Cos(pitch / 2) * Math.Cos(yaw / 2) - Math.Cos(roll / 2) * Math.Sin(pitch / 2) * Math.Sin(yaw / 2)) / Math.Sin(cAngle / 2));
        double cRy = Math.Acos((Math.Cos(roll / 2) * Math.Sin(pitch / 2) * Math.Cos(yaw / 2) + Math.Sin(roll / 2) * Math.Cos(pitch / 2) * Math.Sin(yaw / 2)) / Math.Sin(cAngle / 2));
        double cRz = Math.Acos((Math.Cos(roll / 2) * Math.Cos(pitch / 2) * Math.Sin(yaw / 2) - Math.Sin(roll / 2) * Math.Sin(pitch / 2) * Math.Cos(yaw / 2)) / Math.Sin(cAngle / 2));

        //stay within +/- PI of 0 to keep the number small
        if (roll > 3.1416) roll = -Math.PI + (roll - Math.PI);
        if (roll < -3.1416) roll = Math.PI + (roll - (-1) * Math.PI);
        if (pitch > 3.1416) pitch = -Math.PI + (pitch - Math.PI);
        if (pitch < -3.1416) pitch = Math.PI + (pitch - (-1) * 3.1416F);
        if (yaw > 3.1416) yaw = -Math.PI + (yaw - Math.PI);
        if (yaw < -3.1416) yaw = Math.PI + (yaw - (-1) * Math.PI);

        Console.WriteLine("original angle, axis " + angle + ": " + rx + ", " + ry + ", " + rz);
        Console.WriteLine("converted angle, axis " + cAngle + ": " + cRx + ", " + cRy + ", " + cRz);
        Console.WriteLine("quats " + q0 + ", " + q1 + ", " + q2 + ", " + q3);
        Console.WriteLine("roll,pitch,yaw:  " + roll + ", " + pitch + ", " + yaw);
4

2 回答 2

5

我没有(也不会)检查您的代码。即使您的代码是正确的,您的测试也会因为至少 2 个原因而失败。

也就是说,即使您的转换是正确的,您也可以获得另一种表示,而不是您开始使用的表示。不管你是从欧拉角还是四元数开始。


如果你想测试你的代码,我建议检查单位基向量的正交旋转。例如[1, 0, 0]适当旋转 90 度以获得[0, 1, 0]. 检查你是否真的得到了预期[0, 1, 0]的等等。如果你得到了所有 3 个基向量的旋转,那么你的代码很可能是正确的。

该测试具有明确的优点,如果您弄乱了某些东西(例如公式中的符号),该测试可以帮助您找到错误。


我不会使用欧拉角,因为它们会破坏您应用程序的稳定性。它们也不是很方便

于 2013-02-24T09:46:32.573 回答
0

使用四元数是不正确的。

我想补充一下这个答案。即使应用程序不正确,人们也会按照惯例使用四元数,因此这很重要。

如果您的旋转主题不能滚动,那么使用四元数来表示您的旋转是不正确的。四元数将旋转的 3 个维度编码到系统中,如果您的系统只有两个,则表示不匹配且不正确。

四元数是一种过于复杂的旋转表示,用于修复万向节锁定并仅为涉及俯仰、偏航和滚动的旋转提供更好的可组合性。

如果你只有俯仰和偏航。四元数变换可以给你一个涉及滚动的答案,这从根本上是不正确的。将滚动角归零不会阻止您的变换具有滚动值。四元数没有用旋转的概念编码,没有滚动,所以在这里使用它是不正确的。

对于只能俯仰和偏航而不能滚动的事物,请使用不涉及“滚动”概念的实体,例如 3D 笛卡尔坐标或球坐标(不是欧拉角)。这就够了,也更正确了。在这些情况下,您不会遭受万向节锁定...为此使用四元数不仅是过度杀伤而且是错误的。

于 2020-12-03T19:13:04.897 回答