从欧拉角到四元数再返回的 GLM 转换不成立
GLM conversion from euler angles to quaternion and back does not hold
我正在尝试将存储为 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`
我希望 eulerAnglesInDegrees
和 result
是同一方向的相同或等效表示,但显然不是这样。这些是我打印出来的一些示例值:
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
正如您在上面看到的,对于某些方向范围,转换是正确的,但对于其他方向范围,则完全不同。
我做错了什么?
我查看了现有问题并尝试了一些事情,包括尝试 every possible rotation order listed here, conjugating the quaternion 和其他随机事情,例如翻转 pitch/yaw/roll。没有给我预期的结果。
如何使用 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 + 行为视频:
我的 intuition/current 理解的视觉表示:
- 上图是一个球体,我在中心。当我将枪对准球体的绿色一半时,方向是正确的。当我将枪对准球体的红色一半时,它是不正确的 - 似乎每个轴都倒转了,但我不是 100% 确定是这样。
32.7636 144.849 44.3845
-147.236 35.1512 -135.616
那些是一样的。左 33 或右 147。你们相距 180。现在向上看 145 - 从 horizon 过去是 35,你的背部呈弓形。
现在翻滚让你回到天空。
如果您需要使用欧拉,请尽量将俯仰保持在-90 到+90 之间,并在-180 到+180 之间滚动;
if (pitch > 90) {
pitch -= 90;
yaw += 180;
roll += 180;
}
if (roll > 180) {
roll = 360 - roll;
}
或类似的东西。
表示旋转的任何一种3个角度的定义不仅由旋转的顺序给出,如果它们是外在的或外在的,而且在定义映射时选择哪个角度区间3D Rotation Group 的每个元素到 3 个角的元组。
不幸的是,软件库通常无法明确提及它们支持哪个角度子集,因此通常有必要测试它们的行为或直接检查源代码。有关 glm 的相关问题,请参阅 https://github.com/g-truc/glm/issues/569, and see https://github.com/robotology/idyntree/pull/504 以获取有关我工作的另一个库的相关讨论。
在 glm master
中,通过快速检查代码 (https://github.com/g-truc/glm/blob/6543cc9ad1476dd62fbfbe3194fcf19412f0cbc0/glm/gtc/quaternion.inl#L10) and from the fact that in C++ asin
image is roughly (-90.0, 90.0) and atan2
image is roughly (-180.0, 180.0),glm 中的假设区间似乎大致为 (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0),因此通过将 第二角度 (偏航,使用您正在使用的名称)限制为 (-90.0, 90.0)。所以,您在GLM 级别基本上是从提供的角度到 (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0) 范围内的等效角度的映射。
然而,这个角度相等的事实取决于它们的使用方式,即,如果你有一个库 clamps超出使用范围的欧拉角,而不是将其转换为等效角,那么您将获得奇怪的结果。出于这个原因,我认为了解您的问题以了解这个角度是如何生成的会很有趣(特别是中间角度似乎是范围 (-90, 270) 的一部分,这是一个奇怪的,即使是有效的选择)以及如何解释它们以在可视化中呈现对象。一旦你明白了这一点,即使渲染函数在原始应用程序范围内的角度工作正常,你也可以编写一个函数将 "original application angles" 映射到 "GLM angles" 及其逆函数,你可以将其用于你的原始目的.
大致遵循 tony's 并经过反复试验和模式识别,我设法找到了一种在转换后恢复原始值的方法。
ox
、oy
、oz
原来是pitch、yaw,并且 roll 以度为单位,在任何转换之前;
fx
、fy
和 fz
是新的 pitch、yaw,和 roll 度数,转换 "Euler -> quaternion -> Euler"(通过 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
- 再次 - 非常不可预测的旋转。
我正在尝试将存储为 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`
我希望 eulerAnglesInDegrees
和 result
是同一方向的相同或等效表示,但显然不是这样。这些是我打印出来的一些示例值:
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
正如您在上面看到的,对于某些方向范围,转换是正确的,但对于其他方向范围,则完全不同。
我做错了什么?
我查看了现有问题并尝试了一些事情,包括尝试 every possible rotation order listed here, conjugating the quaternion 和其他随机事情,例如翻转 pitch/yaw/roll。没有给我预期的结果。
如何使用 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 + 行为视频:
我的 intuition/current 理解的视觉表示:
- 上图是一个球体,我在中心。当我将枪对准球体的绿色一半时,方向是正确的。当我将枪对准球体的红色一半时,它是不正确的 - 似乎每个轴都倒转了,但我不是 100% 确定是这样。
32.7636 144.849 44.3845 -147.236 35.1512 -135.616
那些是一样的。左 33 或右 147。你们相距 180。现在向上看 145 - 从 horizon 过去是 35,你的背部呈弓形。 现在翻滚让你回到天空。
如果您需要使用欧拉,请尽量将俯仰保持在-90 到+90 之间,并在-180 到+180 之间滚动;
if (pitch > 90) {
pitch -= 90;
yaw += 180;
roll += 180;
}
if (roll > 180) {
roll = 360 - roll;
}
或类似的东西。
表示旋转的任何一种3个角度的定义不仅由旋转的顺序给出,如果它们是外在的或外在的,而且在定义映射时选择哪个角度区间3D Rotation Group 的每个元素到 3 个角的元组。
不幸的是,软件库通常无法明确提及它们支持哪个角度子集,因此通常有必要测试它们的行为或直接检查源代码。有关 glm 的相关问题,请参阅 https://github.com/g-truc/glm/issues/569, and see https://github.com/robotology/idyntree/pull/504 以获取有关我工作的另一个库的相关讨论。
在 glm master
中,通过快速检查代码 (https://github.com/g-truc/glm/blob/6543cc9ad1476dd62fbfbe3194fcf19412f0cbc0/glm/gtc/quaternion.inl#L10) and from the fact that in C++ asin
image is roughly (-90.0, 90.0) and atan2
image is roughly (-180.0, 180.0),glm 中的假设区间似乎大致为 (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0),因此通过将 第二角度 (偏航,使用您正在使用的名称)限制为 (-90.0, 90.0)。所以,您在GLM 级别基本上是从提供的角度到 (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0) 范围内的等效角度的映射。
然而,这个角度相等的事实取决于它们的使用方式,即,如果你有一个库 clamps超出使用范围的欧拉角,而不是将其转换为等效角,那么您将获得奇怪的结果。出于这个原因,我认为了解您的问题以了解这个角度是如何生成的会很有趣(特别是中间角度似乎是范围 (-90, 270) 的一部分,这是一个奇怪的,即使是有效的选择)以及如何解释它们以在可视化中呈现对象。一旦你明白了这一点,即使渲染函数在原始应用程序范围内的角度工作正常,你也可以编写一个函数将 "original application angles" 映射到 "GLM angles" 及其逆函数,你可以将其用于你的原始目的.
大致遵循 tony's
ox
、oy
、oz
原来是pitch、yaw,并且 roll 以度为单位,在任何转换之前;fx
、fy
和fz
是新的 pitch、yaw,和 roll 度数,转换 "Euler -> quaternion -> Euler"(通过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
- 再次 - 非常不可预测的旋转。