没有参考平面的两个向量之间的有符号角度

15

(在三维空间中)我想找到一种方法来计算两个向量之间的带符号角度,而不需要任何除了这些向量之外的信息。如此问题中所回答的那样,可以通过给定向量垂直的平面的法向量来简单地计算带符号角度。但是,我无法在没有该值的情况下进行计算。显然,两个向量的叉积会产生这样的一个法向量,但是我遇到了上述答案中的矛盾:

signed_angle(x_dir, y_dir) == 90
signed_angle(y_dir, x_dir) == 90

我本来期望第二个结果是负数。这是因为在给定以下归一化输入的伪代码中,叉积cross(x_dir, y_dir)cross(y_dir, x_dir)方向相反。

signed_angle(Va, Vb)
    magnitude = acos(dot(Va, Vb))
    axis = cross(Va, Vb)
    dir = dot(Vb, cross(axis, Va))
    if dir < 0 then
        magnitude = -magnitude
    endif
    return magnitude

我不相信dir会变成负数。

我已经看到了使用建议的atan2方法解决同样问题的情况。

我正在寻找一种方法来使:

signed_angle(a, b) == -signed_angle(b, a)

以上评论中的链接无法使用。 - ephere
4个回答

20

相关的数学公式:

  dot_product(a,b) == length(a) * length(b) * cos(angle)
  length(cross_product(a,b)) == length(a) * length(b) * sin(angle)

为了得到三维向量之间的稳健角度计算,您应该实际计算:

  s = length(cross_product(a,b))
  c = dot_product(a,b)
  angle = atan2(s, c)

如果你仅使用acos(c),当角度很小时,就会出现严重的精度问题。计算s并使用atan2()可以得到所有可能情况下的强健结果。

由于s始终为非负值,因此得出的角度将在0到pi之间。总会有一个等效的负角度(angle - 2*pi),但没有几何上的理由去优先选择它。


谢谢,我会记住acos的用法。当你将函数可视化时,它似乎很明显。 - metatheorem
3
警告:此函数是可交换的,但它不应该是可交换的:从+x方向(1 0 0)到+y方向(0 1 0)的角度应为+90°。反之亦然,从+y到+x,应为-90°。但使用此函数, f(x, y) == f(y, x)。它无法区分,因为s是非负数,而叉积是唯一可以告诉您两者之间方向的东西。 - Ahmed Fasih
1
如果它是在二维空间中,那么它将是反对易的。然而,在三维空间中,它是(并且应该是)对易的——因为在三维空间中,你需要第三个向量来确定手性。如果没有第三个向量来区分前两个向量的正负角度,你就没有几何上的理由来偏好一个方向而不是另一个方向。 - comingstorm
感谢您的快速回复。我认为我有理由更喜欢从一个向量到另一个向量的方向:在双稳态雷达成像中,特别是计算双稳态角时,发射机或接收机是否比另一个提前或落后15度很重要,因为材料的响应不同。此外,原则上可以将两个三维向量重写为包含它们的平面中的两个二维向量,然后应用带符号的二维角度,对吧? - Ahmed Fasih
问题的标题是“...没有参考平面”。参考平面就像一个额外的向量一样好,因为它可以为您提供几何基础,以便优先选择一个方向而不是另一个方向。 - comingstorm
至少,这是假设您的平面独立于两个向量签名的情况下成立的,因此您可以使用平面法线来区分方向。这两个向量确实确定了通过原点的平面 - 但它不是一个参考平面:交换向量会颠倒该平面的方向;它仍然不能为您提供几何基础,以优先选择一个方向而不是另一个方向。 - comingstorm

3
两个向量之间的有符号角度,没有参考平面
angle = acos(dotproduct(normalized(a), normalized(b)));

signed_angle(a, b) == -signed_angle(b, a)

我认为这是不可能的,除非有某种参考向量。


我再想了一下,这是正确的。角度的符号取决于您用作参考的旋转轴。当然,有两个不同的轴,一个沿着叉积方向,另一个沿着相反的方向。 - Jeff
@JeffE:是的,虽然你可以通过使用叉积找到“旋转”轴,但你无法确定它最初是朝向哪个方向的 - 在a cross b还是在b cross a中 - 交换向量,你会得到面向相反方向的轴。因此,无法确定旋转角度是否在0..pi范围内或者是否在pi..pi*2范围内。 - SigTerm

2
感谢大家。在审阅了这里的评论并回顾我尝试做的事情后,我意识到我可以通过给定的标准公式来实现我需要做的事情,这个公式是有符号角度的。我只是在我的有符号角度函数的单元测试中卡住了。
供参考,我将结果角度馈送回旋转函数中。我没有考虑到这将自然使用与有符号角度相同的轴(输入向量的叉积),正确的旋转方向将根据该轴面对的任何方向而确定。
更简单地说,这两个应该只是“做正确的事情”并以不同的方向旋转:
rotate(cross(Va, Vb), signed_angle(Va, Vb), point)
rotate(cross(Vb, Va), signed_angle(Vb, Va), point)

第一个参数是旋转轴,第二个参数是旋转的量。


2
你能分享一下你的 signed_angle 实现吗? - Aaron Franke

-2

如果你只想要一个一致的结果,那么在选择 a × bb × a 作为你的法线时,任何任意的方式都可以。也许选择字典顺序较小的那个?

(但是你可能需要解释一下你实际试图解决什么问题:也许有一种不涉及计算任意三维向量之间的一致有符号角度的解决方案。)


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