在同一平面内具有相同原点的两个3D矢量之间的有符号角度

Adv*_*mer 50 math 3d geometry vector

我需要的是位于同一3D平面内且具有相同原点的两个矢量Va和Vb之间的有符号旋转角度,知道:

  1. 包含两个矢量的平面是任意的,并且不与XY或任何其他基本平面平行
  2. Vn - 平面法线
  3. 两个向量与法线具有相同的原点O = {0,0,0}
  4. Va - 是测量Vn左手旋转的参考

应该以这样的方式测量角度,因此如果平面是XY平面,则Va将代表它的X轴单位矢量.

我想我应该通过使用Va作为X轴并将Vb和Vn的叉积作为Y轴来执行一种坐标空间变换,然后使用像atan2()或其他东西一样的2d方法.有任何想法吗?公式?

mse*_*ell 61

使用两个向量的叉积来得到由两个向量形成的平面的法线.然后检查它与原始平面法线之间的点积,看它们是否面向同一方向.

angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
cross = crossProduct(Va, Vb);
if (dotProduct(Vn, cross) < 0) { // Or > 0
  angle = -angle;
}
Run Code Online (Sandbox Code Playgroud)

  • @Advanced客户如果这个答案是正确的,请勾选吗?否则你改变了什么? (6认同)
  • 可能的改进:使用`angle = angle*sgn(dotProduct(Vn,cross))`而不是`if`语句.不确定它是否会更低/更有效但它看起来更好一点. (2认同)
  • 在我的例子中,这个解决方案的不精确度达到了 30 度。 (2认同)
  • 当向量几乎平行或几乎相反时,该解决方案非常不精确。这是因为余弦函数在这些点上几乎是平坦的,因此 acos 函数的精度非常低。请改用下面答案中的解决方案。 (2认同)

Adr*_*ard 35

我目前使用的解决方案似乎在这里缺失.假设平面法线是标准化的(|Vn| == 1),则有符号角度就是:

atan2((Vb x Va) . Vn, Va . Vb)

返回[-PI,+ PI]范围内的角度(或任何可用的atan2实现返回的角度).

.并且x分别是点和交叉产品.

没有明确的分支,也不需要除法/向量长度计算.使用Va x Vb了右手旋转代替左手一个

解释为什么这样做:让alpha为矢量之间的直接角度(0°到180°)和beta我们正在寻找的角度(0°到360°)beta == alpha或者beta == 360° - alpha

Va . Vb == |Va| * |Vb| * cos(alpha)    (by definition) 
        == |Va| * |Vb| * cos(beta)     (cos(alpha) == cos(-alpha) == cos(360° - alpha)


Va x Vb == |Va| * |Vb| * sin(alpha) * n1  
    (by definition; n1 is a unit vector perpendicular to Va and Vb with 
     orientation matching the right-hand rule)

Therefore (again assuming Vn is normalized):
   n1 . Vn == 1 when beta < 180
   n1 . Vn == -1 when beta > 180

==>  (Va x Vb) . Vn == |Va| * |Vb| * sin(beta)
Run Code Online (Sandbox Code Playgroud)

最后

tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)
Run Code Online (Sandbox Code Playgroud)

  • 我错了吗 atan2((Vb x Va) . Vn, Va . Vb) 包含一个错字?它应该是 atan2((Va x Vb) . Vn, Va . Vb) 恕我直言 (3认同)
  • @MarcoM 原始问题要求从 Va 到 Vb 的左手旋转。对于右手旋转 Va x Vb 确实是正确的。https://en.wikipedia.org/wiki/Right-hand_rule#Rotation (3认同)
  • 完美运行!迄今为止最优雅的解决方案。谢谢阿德里安。 (2认同)
  • @Alexko `sin(alpha) * n1 。当 `beta &lt; 180` 且 `sin(alpha) * n1 时,Vn == sin(alpha) == sin(beta)`。Vn == -sin(alpha) == sin(beta)` 否则。 (2认同)

小智 13

您可以分两步完成此操作:

  1. 确定两个向量之间的角度

    theta = acos(Va,Vb的点积).假设Va,Vb被归一化.这将给出两个矢量之间的最小角度

  2. 确定角度的符号

    求向量V3 = Va,Vb的叉积.(顺序很重要)

    如果(V3,Vn的点积)为负,则θ为负.否则,theta是积极的.

  • 对于标志,它不应该是V3.Vb - 产生不稳定的结果.在第2步中它应该是:Vn.(Va x Vb) - 检查原始法线(Vn)是否面向与Va和Vb交叉相同的方向. (3认同)

Ste*_*non 7

您可以使用点积来获得角度.要获得角度的符号,请使用符号Vn * (Va x Vb).在XY平面的特殊情况下,这会减少到刚好Va_x*Vb_y - Va_y*Vb_x.

  • @StephenCanon:我想我误解了你的意思(`up to sign`)。我的意思是角度的符号取决于“叉积”。 (2认同)

duf*_*ymo 2

将一个向量与另一个向量交叉并标准化以获得单位向量。

两个向量之间角度的正弦等于叉积的大小除以两个向量的大小:

http://mathworld.wolfram.com/CrossProduct.html

  • 符号可能不同,因为每个 2D 表面都有两个法向量,具体取决于您感兴趣的一侧。任一向量都同样有效。由您决定哪一个适合您的问题。如果我有一个由房间中的墙壁的两个向量定义的平面,我可以使用叉积来获取面向房间或面向房间的法线向量,具体取决于我的要求以及表达式中哪个向量排在第一位。哪一个是正确的?两者 - 这取决于上下文。 (2认同)