GLM欧拉角和四元数之间的转换不正确

4

我正在尝试将已存储为欧拉角glm::vec3的OpenVR控制器方向转换为glm::fquat,并进行反向转换,但结果差异很大,游戏中的行为是错误的(难以解释,但物体的方向在一小范围内表现正常,然后在奇怪的轴上翻转)

这是我的转换代码:

// get `orientation` from OpenVR controller sensor data

const glm::vec3 eulerAnglesInDegrees{orientation[PITCH], orientation[YAW], orientation[ROLL]};
debugPrint(eulerAnglesInDegrees);

const glm::fquat quaternion{glm::radians(eulerAnglesInDegrees)};
const glm::vec3 result{glm::degrees(glm::eulerAngles(quaternion))};
debugPrint(result);

// `result` should represent the same orientation as `eulerAnglesInDegrees`

我期望eulerAnglesInDegreesresult应该是相同的,或者是同一方向的等效表示,但事实并非如此。以下是我得到的一些示例值:

39.3851 5.17816 3.29104 
39.3851 5.17816 3.29104 

32.7636 144.849 44.3845 
-147.236 35.1512 -135.616 

39.3851 5.17816 3.29104 
39.3851 5.17816 3.29104 

32.0103 137.415 45.1592 
-147.99 42.5846 -134.841 

如上所示,对于某些方向范围,转换是正确的,但对于其他方向范围则完全不同。

我做错了什么?

我查看过现有的问题并尝试了一些方法,包括尝试这里列出的每个可能的旋转顺序共轭四元数和其他一些随机的事情,例如翻转俯仰/偏航/翻滚。但没有任何一个方法给了我预期的结果。

如何使用 glm 把欧拉角转换为四元数再转换回来,以表示原始方向?


以下是更多不一致的例子:

original:      4; 175;   26; 
computed:   -175;   4; -153; 
difference:  179; 171;  179; 

original:     -6; 173;   32; 
computed:    173;   6; -147; 
difference: -179; 167;  179; 

original:      9; 268;  -46; 
computed:   -170; -88;  133; 
difference:  179; 356; -179; 

original:    -27; -73;  266; 
computed:    -27; -73;  -93; 
difference:    0;   0;  359; 

original:    -33; 111;  205; 
computed:    146;  68;   25; 
difference: -179;  43;  180; 

我试图找到一个模式来修复最终的computed结果,但似乎没有一个易于识别的简单方法。


GIF + 视频演示:

视频摘录


我的直觉/目前的理解的可视化表示:

可视化图表

  • 上面的图片显示了一个球体,我在中心。当我将枪瞄准绿色半球时,方向是正确的。当我将枪瞄准红色半球时,方向不正确-看起来每个轴都被反转了,但我不能确定是否确实如此。

遇到了欧拉角万向锁问题。最好将相机控制作为四元数或约束参考框架角度。有些旋转具有多组欧拉角,180度旋转有无限的可能性。 - Swift - Friday Pie
3个回答

4

32.7636 144.849 44.3845 -147.236 35.1512 -135.616

这些是一样的。左边是33或右边是147。你们相互之间相差180度。现在向上看145——那是过了直线朝上方向,离地平线35度,你的背弯起来了。现在滚动身体,让背朝向天空。

如果您必须使用欧拉角,请尝试将俯仰角保持在-90至+90范围内,横滚角保持在-180至+180范围内。

if (pitch > 90) {
   pitch -= 90;
   yaw += 180;
   roll += 180;
}
if (roll > 180) {
   roll = 360 - roll;
}

或类似的东西。


俯仰角、偏航角和翻滚角不是欧拉角,而是位置角度,它们的应用取决于参考框架的类型 :P - Swift - Friday Pie
正确的,偏航/俯仰/翻滚就像物体参考框架中的YXZ欧拉角。无论顺序(和参考)如何,我的建议是相似的 - 保持角度受限。 - tony

2
任何一种用三个角度来表示旋转的定义,不仅取决于旋转的顺序(内部或外部),还取决于在将3D Rotation Group的每个元素映射到一个由三个角度组成的元组时选择了哪个角度间隔。
不幸的是,软件库通常未明确说明它们支持哪些角度子集,因此通常需要测试它们的行为或直接检查源代码。有关glm的相关问题,请参见https://github.com/g-truc/glm/issues/569,有关我工作的另一个库的相关讨论,请参见https://github.com/robotology/idyntree/pull/504
在 glm 的 master 分支中,通过快速检查代码(https://github.com/g-truc/glm/blob/6543cc9ad1476dd62fbfbe3194fcf19412f0cbc0/glm/gtc/quaternion.inl#L10),以及从 C++ 中 asin 图像大致为 (-90.0, 90.0)atan2 图像大致为 (-180.0, 180.0) 这一事实可以看出,在 glm 中假定的区间大致为 (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0),因此通过限制第二个角度(即偏航角,使用您正在使用的名称)在 (-90.0, 90.0) 范围内来将提供的角度映射到等效的 (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0) 范围内的角度。因此,在 GLM 层面上所看到的就是这种映射关系。
然而,这些角度是否等效取决于它们的使用方式,即如果您有一个库将欧拉角夹在使用范围之外,而不是将其转换为等效角度,则会得到奇怪的结果。因此,我认为了解您的问题以了解如何生成这些角度(中间角度特别是似乎是范围(-90, 270)的一部分,这是一个奇怪但有效的选择),以及如何解释它们以呈现可视化对象非常重要。一旦您理解了这一点,即使呈现函数对原始应用程序范围内的角度工作正常,您也可以编写一个函数来将“原始应用程序角度”映射到“GLM角度”及其反函数,以便用于您的原始目的。

1

大致遵循tony建议,经过一些试错和模式识别,我设法找到了一种在转换后恢复原始值的方法。

  • oxoyoz是任何转换之前以度为单位的原始俯仰偏航翻滚

  • fxfyfz是通过将“欧拉角->四元数->欧拉角”转换(通过glm::degrees(glm::eulerAngles(glm::normalize(quaternion))))获得的新的以度为单位的俯仰偏航翻滚

if (oy > 90.f)
{
    fx -= 180.f;
    fy -= 180.f;
    fy *= -1.f;
    fz += 180.f;

    if (ox > 0.f)
    {
        fx += 360.f;
    }
}

上述代码似乎使原始角度值和转换后的角度值完全匹配。虽然它回答了最初的问题,但并没有解决我的实际问题...我正在将其转换为四元数以便于平滑地插值到另一个角度。然而,似乎在转换后对四元数使用glm::mix仍会导致非常不可预测的旋转。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接