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

发布于 2024-10-20 18:15:20 字数 355 浏览 1 评论 0 原文

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

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

应以这种方式测量,因此如果平面是 XY 平面,Va 代表它的 X 轴单位向量。

我想我应该通过使用 Va 作为 X 轴和 Vb 和 Vn 的叉积作为 Y 轴来执行一种坐标空间变换,然后只使用一些 2d 方法,例如 atan2() 或其他方法。有什么想法吗?公式?

What I need is a signed angle of rotation between two vectors Va and Vb lying within the same 3D plane and having the same origin knowing that:

  1. The plane contatining both vectors is an arbitrary and is not parallel to XY or any other of cardinal planes
  2. Vn - is a plane normal
  3. Both vectors along with the normal have the same origin O = { 0, 0, 0 }
  4. Va - is a reference for measuring the left handed rotation at Vn

The angle should be measured in such a way so if the plane would be XY plane the Va would stand for X axis unit vector of it.

I guess I should perform a kind of coordinate space transformation by using the Va as the X-axis and the cross product of Vb and Vn as the Y-axis and then just using some 2d method like with atan2() or something. Any ideas? Formulas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

夏了南城 2024-10-27 18:15:21

令 theta 为向量之间的角度。令 C = Va 叉积 Vb。然后

sin theta = 长度(C) / (长度(Va) *
长度(Vb))

要确定 theta 是正值还是负值,请记住 C 垂直于 Va 和 Vb,指向由 右手定则。特别是,C 与 Vn 平行。在您的情况下,如果 C 指向与 Vn 相同的方向,则 theta 为负,因为您想要左手旋转。快速检查 Vn 和 C 是否指向同一方向的最简单计算方法可能就是取它们的点积;如果为正,则它们指向同一方向。

所有这些都源自叉积的基本属性。

Let theta be the angle between the vectors. Let C = Va cross product Vb. Then

sin theta = length(C) / (length(Va) *
length(Vb))

To determine if theta is positive or negative, remember that C is perpendicular to Va and Vb pointing in the direction determined by the right-hand rule. So in particular, C is parallel to Vn. In your case, if C points in the same direction as Vn, then theta is negative, since you want left-handed rotation. Probably the easiest computational way to quickly check if Vn and C point in the same direction is to just take their dot product; if it is positive they point in the same direction.

All this follows from elementary properties of the cross product.

情深缘浅 2024-10-27 18:15:21

假设Vx是x轴,给定法线Vn,通过叉积可以得到y轴,可以将向量Vb投影到Vx和Vy(通过点积可以得到Vb投影到Vy上的长度) Vx 和 Vy),给定平面上的 (x, y) 坐标,可以使用 atan2(y, x) 获取 [-pi, +pi] 范围内的角度

Suppose Vx is the x-axis, given the normal Vn, you can get the y-axis by cross product, you can project the vector Vb to Vx and Vy (by the dot product you can get the length of the projection of Vb onto Vx and Vy), given the (x, y) coordinate on the plane, you can use atan2(y, x) to get the angle in the range [-pi, +pi]

懵少女 2024-10-27 18:15:21

这是 Matlab 代码,用于计算 2D 或 3D 中两个向量 u,v 之间的符号角。该代码是不言自明的。符号约定是在 ix 和 iy ([1,0,0],[0,1,0]) 或 iy 和 iz ([0,1,0],[0, 0,1])

function thetaDEG = angDist2Vecs(u,v)

if length(u)==3
    %3D, can use cross to resolve sign
    uMod = sqrt(sum(u.^2));
    vMod = sqrt(sum(v.^2));
    uvPr = sum(u.*v);
    costheta = min(uvPr/uMod/vMod,1);

    thetaDEG = acos(costheta)*180/pi;

    %resolve sign
    cp=(cross(u,v));
    idxM=find(abs(cp)==max(abs(cp)));
    s=sign(cp(idxM(1)));
    if s < 0
        thetaDEG = -thetaDEG;
    end
elseif length(u)==2
    %2D use atan2
    thetaDEG = (atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
else
    error('u,v must be 2D or 3D vectors');
end

This is the Matlab code to compute the signed angle between two vectors u,v either in 2D or in 3D. The code is self explanatory. The sign convention is such that a positive +90° is output between ix and iy ([1,0,0],[0,1,0]) or iy and iz ([0,1,0],[0,0,1])

function thetaDEG = angDist2Vecs(u,v)

if length(u)==3
    %3D, can use cross to resolve sign
    uMod = sqrt(sum(u.^2));
    vMod = sqrt(sum(v.^2));
    uvPr = sum(u.*v);
    costheta = min(uvPr/uMod/vMod,1);

    thetaDEG = acos(costheta)*180/pi;

    %resolve sign
    cp=(cross(u,v));
    idxM=find(abs(cp)==max(abs(cp)));
    s=sign(cp(idxM(1)));
    if s < 0
        thetaDEG = -thetaDEG;
    end
elseif length(u)==2
    %2D use atan2
    thetaDEG = (atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
else
    error('u,v must be 2D or 3D vectors');
end
溺ぐ爱和你が 2024-10-27 18:15:20

我目前使用的解决方案似乎在这里缺失。
假设平面法线已归一化 (|Vn| == 1),则带符号的角度很简单:

对于从 Va 到 Vb 的右手旋转:

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

对于从 Va 到 Vb 的左手旋转:

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

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

.x 分别是点积和叉积。

不需要显式分支,也不需要除法/向量长度计算。

解释其原理:设 alpha 为向量之间的直角(0° 到 180°),beta 为我们要查找的角度(0° 到 360°),其中 beta == alphabeta == 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)

最后

tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)

The solution I'm currently using seems to be missing here.
Assuming that the plane normal is normalized (|Vn| == 1), the signed angle is simply:

For the right-handed rotation from Va to Vb:

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

For the left-handed rotation from Va to Vb:

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

which returns an angle in the range [-PI, +PI] (or whatever the available atan2 implementation returns).

. and x are the dot and cross product respectively.

No explicit branching and no division/vector length calculation is necessary.

Explanation for why this works: let alpha be the direct angle between the vectors (0° to 180°) and beta the angle we are looking for (0° to 360°) with beta == alpha or 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)

Finally

tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)
你好,陌生人 2024-10-27 18:15:20

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

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

Use cross product of the two vectors to get the normal of the plane formed by the two vectors. Then check the dotproduct between that and the original plane normal to see if they are facing the same direction.

angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
cross = crossProduct(Va, Vb);
if (dotProduct(Vn, cross) < 0) { // Or > 0
  angle = -angle;
}
凉城已无爱 2024-10-27 18:15:20

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

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

    theta = acos(Va、Vb 的点积)。假设 Va、Vb 已归一化。这将给出两个向量之间的最小角度

  2. 确定角度的符号

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

    如果(V3、Vn 的点积)为负,则 theta 为负。否则,theta 为正。

You can do this in two steps:

  1. Determine the angle between the two vectors

    theta = acos(dot product of Va, Vb). Assuming Va, Vb are normalized. This will give the minimum angle between the two vectors

  2. Determine the sign of the angle

    Find vector V3 = cross product of Va, Vb. (the order is important)

    If (dot product of V3, Vn) is negative, theta is negative. Otherwise, theta is positive.

很糊涂小朋友 2024-10-27 18:15:20

您可以使用点积获得角度向上签名。要获取角度的符号,请取 Vn * (Va x Vb) 的符号。在 XY 平面的特殊情况下,这会简化为 Va_x*Vb_y - Va_y*Vb_x

You can get the angle up to sign using the dot product. To get the sign of the angle, take the sign of Vn * (Va x Vb). In the special case of the XY plane, this reduces to just Va_x*Vb_y - Va_y*Vb_x.

第几種人 2024-10-27 18:15:20

高级客户提供了以下解决方案(最初是对问题的编辑):

SOLUTION:

sina = |Va x Vb| / ( |Va| * |Vb| )
cosa = (Va . Vb) / ( |Va| * |Vb| )

angle = atan2( sina, cosa )

sign = Vn . ( Va x Vb )
if(sign<0)
{
    angle=-angle
}

Advanced Customer provided the following solution (originally an edit to the question):

SOLUTION:

sina = |Va x Vb| / ( |Va| * |Vb| )
cosa = (Va . Vb) / ( |Va| * |Vb| )

angle = atan2( sina, cosa )

sign = Vn . ( Va x Vb )
if(sign<0)
{
    angle=-angle
}
旧伤慢歌 2024-10-27 18:15:20

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

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

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

Cross one vector into the other and normalize to get the unit vector.

The sine of the angle between the two vectors equals the magnitude of the cross product divided by the magnitudes of the two vectors:

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

那请放手 2024-10-27 18:15:20

对于使用 Python 的用户,Adrian Leonhard 提供的解决方案现已在 scikit-spatial 库(最新版本即将推出)。查找 Vectorangle_signed_3d

下面是两个例子:

>>> import numpy as np
>>> from skspatial.objects import Vector
>>> np.degrees(Vector([1, 0, 0]).angle_signed_3d([0, -1, 0], direction_positive=[0, 0, 2]))
-90.0

>>> np.degrees(Vector([1, 0, 0]).angle_signed_3d([0, -1, 0], direction_positive=[0, 0, -5]))
90.0

For those using Python, the solution provided by Adrian Leonhard is now implemented in the scikit-spatial library (the latest version will be available soon). Look for the angle_signed_3d of the Vector class.

Here are two examples:

>>> import numpy as np
>>> from skspatial.objects import Vector
>>> np.degrees(Vector([1, 0, 0]).angle_signed_3d([0, -1, 0], direction_positive=[0, 0, 2]))
-90.0

>>> np.degrees(Vector([1, 0, 0]).angle_signed_3d([0, -1, 0], direction_positive=[0, 0, -5]))
90.0
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文