如何从前向矢量计算俯仰和偏航值

Posted

技术标签:

【中文标题】如何从前向矢量计算俯仰和偏航值【英文标题】:How to compute pitch and yaw values from forward vector 【发布时间】:2020-02-24 08:36:43 【问题描述】:

我想根据相机在实例化时获得的位置和目标点来计算相机俯仰和偏航值。如果我将pitch初始化为0,yaw初始化为-90(如LearnOpenGL Camera Tutorial),当第一次旋转发生时,相机突然跳跃;之后旋转正常工作。因此,首先,从该教程中给出的方程式开始,我尝试从前向向量中获取俯仰和偏航值:

float pitch = glm::degrees(glm::asin(forward.y));
float yaw = glm::degreees(glm::acos((float)x/glm::cos(glm::asin(y))));

还有

float yaw = glm::degreees(glm::asin((float)z/glm::cos(glm::asin(y))));

而 cos(sin(y)) 不应为 0,因此 y 不应为 0 或 -1。 我试图实现这些,但偏航的 2 个值不一样,偏航的第一个值似乎是我正在寻找的,但俯仰不是。 之后,我尝试了一种简单的方法,知道俯仰是前向矢量和 y 轴之间的角度,而偏航是前向矢量和 x 轴之间的角度,我尝试(在纸上)计算以下内容:

const glm::vec3 yUnit(0, 1, 0);
const glm::vec3 xUnit(1, 0, 0);
float pitch = glm::degrees(glm::acos(glm::dot(forward, yUnit)));
float yaw = glm::degrees(glm::acos(glm::dot(forward, xUnit)));

有以下2个输入:position = (0, 0, 0), target = (0, 1, 2,5), forward = target - position = (0, 1, 2.5), normalized forward ~ (0 , 0.37, 0.926),结果是俯仰 ~ 68 度和偏航 = 90 度。 此外,我在应用程序中打印了俯仰和偏航值,预期值应该是俯仰 = -20 和偏航 = -90。

如果我错了,你能解释一下,为什么以及在哪里犯错?

【问题讨论】:

【参考方案1】:

根据您的预期值,您的音高似乎应该在 -90 到 90 度之间变化,因此您应该使用 asin 而不是 acos 作为音高。

在计算偏航角时,需要将前向向量投影到 xz 平面上。您可以通过将 y 分量设置为零然后对其进行归一化来做到这一点。

偏航角需要在 0 到 360 度之间变化,但 acos 只返回 0 到 180 度之间。第一个 180 度的偏航角是正确的,但是随着偏航角从 180 增加到 360,acos 将从 180 减小到零。 当前向向量和(0,0,1)的点积大于零时,偏航角会大于180度,所以需要用360减去acos值来调整。

根据您的预期值,我对正确偏航和俯仰值的猜测是

pitch = degrees(-asin(dot(forward, y_unit)))
forward.y = 0
forward = normalize(forward)
yaw = degrees(acos(dot(forward, x_unit)))
if(dot(forward, z_unit) > 0)
    yaw = 360 - yaw

【讨论】:

【参考方案2】:

无法指向任何要更改的单行,但建议:

第一次旋转时,相机突然跳动

这表明某些东西没有被初始化为您认为的样子。在这种情况下,俯仰和偏航是可以从前向向量计算的派生值,对吧?当你有派生值时,你不应该直接初始化它们,因为你有可能会弄错。如果两个应该“相同”的值不同,就会发生奇怪的事情。而是初始化前向向量并立即从中计算俯仰和偏航。

而 cos(sin(y)) 不应为 0

我们都会不时犯这个错误。但在这种情况下,我认为这并不重要,因为以后的原因。

但是,您可能想要测试当前向向量为 (0, 0, 0) 时会发生什么。在图形编程中以某种方式获得全零向量是非常常见的。

浮动偏航 = glm::degreees(glm::acos((float)x/glm::cos(glm::asin(y))));

您是否已决定使用哪种欧拉角表示法?在简单的情况下,您永远不需要使用多个单轴坐标值来计算角度,但在这里您使用的是两个。

简单的情况是偏航(航向)、俯仰和滚动是独立的角度,因此如果俯仰改变,偏航不会改变,反之亦然。您最后一个带有 xUnit 和 yUnit 向量的代码块似乎正在这样做。

但是,在飞行模拟器和航空航天计算中,偏航 - 俯仰 - 横滚有点复杂,因为它们不是独立的。偏航角可以在俯仰平面上测量,而不是绝对 XZ。航空航天偏航通常是从 Z 轴的“北”而不是 X 轴测量的。因此,您需要清楚要测量的偏航和俯仰类型,并始终保持一致。而且您需要研究任何教科书示例或代码,以了解它们如何使用俯仰 - 偏航 - 滚动以及它是否与您的一致。

我建议现在坚持简单的单坐标测量。

浮动间距 = glm::degrees(glm::acos(glm::dot(forward, yUnit)));

再一次,你确定 forward 是正常化的吗? LookAt 通常被编码为比数学库更宽容,因此很容易犯错误。

而且,您是否检查过您的数学库在 -180 到 180 度之外的值?还要担心一件事。

希望这会有所帮助。如果您发现欧拉角既烦人又烦人,那么您并不孤单!这就是为什么许多 3D 书籍和教程都推荐学习四元数的原因。

【讨论】:

以上是关于如何从前向矢量计算俯仰和偏航值的主要内容,如果未能解决你的问题,请参考以下文章

CoreMotion 不会让我滚动、俯仰和偏航

如何使用 deviceMotion 姿态值(滚动、俯仰和偏航)来检测运动

来自磁力计数据的方向

iOS CoreMotion CMAttitude 相对于北极

未校准的磁力计问题

GLSL:用旋转矢量旋转?