将法线向量旋转到轴平面上

发布于 2024-07-25 13:09:57 字数 703 浏览 11 评论 0原文

我在 3D 空间中有一组数据点,它们显然都落在特定的平面上。 我使用 PCA 来计算平面参数。 PCA 的第三个分量给出了平面的法向量(最弱分量)。

接下来我想做的是将所有点变换到所述平面上并以二维方式查看它。

我的想法是执行以下操作:

  • 在平面上找到一个中心点(平均点)
  • 从所有数据点中减去它,将它们排列在原点周围
  • 旋转法线,使其变为 (0,0,-1)
  • 将此旋转应用于所有数据点都
  • 使用正交投影(基本上,跳过 z 轴)

现在我一直在寻找正确的旋转操作。 我尝试使用 acos 或 atan 并设置两个旋转矩阵。 似乎两种方法(使用 acos、使用 atan)都给了我错误的结果。 也许你可以在这里帮助我!

Matlab 代码如下:

b = atan(n(1) / n(2));
rotb = [cos(b) -sin(b) 0; sin(b) cos(b) 0; 0 0 1];
n2 = n * rotb;
a = atan(n(1) / n(3));
rota = [cos(a) 0 sin(a); 0 1 0; -sin(a) 0 cos(a)];
n3 = n2 * rotaows:

我期望 n2 的 y 分量为零。 然而,对于向量(-0.6367、0.7697、0.0467)来说,这已经失败了。

I have a set of data points in 3D space which apparently all fall onto a specific plane. I use PCA to compute the plane parameters. The 3rd component of PCA gives me the normal vector of the plane (weakest component).

What I want to do next is to transform all the points onto said plane and look at it in 2D.

My idea was to do the following:

  • Find a center point (average point) on the plane
  • Substract it from all data points to arrange them around the origin
  • Rotate the normal so that it becomes (0,0,-1)
  • Apply this rotation to all data points
  • Use orthogonal projection (basically, skip z axis)

Now I'm stuck at finding the right rotation operation. I tried working with acos or atan and setting up two rotation matrices. Seems both methods (using acos, using atan) give me the wrong result. Perhaps you can help me out here!

Matlab code follows:

b = atan(n(1) / n(2));
rotb = [cos(b) -sin(b) 0; sin(b) cos(b) 0; 0 0 1];
n2 = n * rotb;
a = atan(n(1) / n(3));
rota = [cos(a) 0 sin(a); 0 1 0; -sin(a) 0 cos(a)];
n3 = n2 * rotaows:

I expect n2 to have y component of zero. However that fails already for the vector (-0.6367, 0.7697, 0.0467).

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

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

发布评论

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

评论(4

意中人 2024-08-01 13:09:57

如果你有一个平面,你就有一个法向量和一个原点。 我根本不会做任何“轮换”。 只需进行一些向量运算即可得到答案。

  • 我们将平面的法向量称为新的 z 轴。
  • 您可以通过将旧的 x 轴与新的 z 轴(平面的法线)交叉来生成新的 y 轴。
  • 通过将新的 z 与新的 y 交叉来生成新的 x 轴。
  • 将所有新轴向量变成单位向量(长度为 1)。
  • 对于您拥有的每个点,创建一个从新原点到该点的向量(点的向量减去 -plane_origin)。 只需用新的 x 和新 y 单位向量点,你就得到一对 (x,y),你可以绘制!

如果您已经有了叉积和点积函数,那么这只是几行代码。 我知道它是有效的,因为我编写的大多数 3D 视频游戏都是这样工作的。

技巧:

  • 注意向量指向的方向。 如果它们指向错误的方向,则对所得向量取反或更改叉积的顺序。
  • 如果您的平面法线与原始 x 轴完全相同,则会遇到麻烦。

If you have a plane, you have a normal vector and an origin. I wouldn't do any "rotations" at all. You're just a few vector operations away from your answer.

  • Let's call your plane's normal vector the new z axis.
  • You can generate the new y axis by crossing the old x axis with the new z axis (your plane's normal).
  • Generate the new x axis by crossing the new z with the new y.
  • Make all your new axis vectors into unit vectors (length 1).
  • For every point you have, create a vector that's from your new origin to the point (vector subtraction of point - plane_origin). Just dot with the new x and new y unit vectors and you get a pair (x,y) you can plot!

If you have cross and dot product functions already, this is just a few lines of code. I know it works because most of the 3D videogames I wrote worked this way.

Tricks:

  • Pay attention to which directions your vectors are pointing. If they point the wrong way, negate the resultant vector or change the order of the cross product.
  • You have trouble if your plane's normal is exactly the same as your original x axis.
べ映画 2024-08-01 13:09:57

怎么样:

将法线向量分解为 XY 平面上的向量和 Z 向量。 然后绕 Z 轴旋转,使 XY 向量与其中一个轴对齐。 然后找到法线与 Z 轴的点积,并沿着您所对齐的 X、Y 中的任意一个旋转。

这个想法是将法线向量与 Z 对齐,这样你的平面现在就是 XY 平面。

How about:

Decompose the normal vector into a vector in the XY-plane and a Z vector. Then apply a rotation around the Z-axis to line up the XY vector with one of the axis. Then find the dot product of the the normal with the Z-axis, and rotate along which ever of X,Y you lined up with.

The idea is to line the normal vector up with Z, and by doing that your plane is now the XY plane.

勿挽旧人 2024-08-01 13:09:57

这是用 Python 做出的公认答案:

import numpy as np

def rotate(points, normal, around):
  # Let's rotate the points such that the normal is the new Z axis
  # Following https://stackoverflow.com/questions/1023948/rotate-normal-vector-onto-axis-plane
  old_x_axis = np.array([1, 0, 0])

  z_axis = normal
  y_axis = np.cross(old_x_axis, z_axis)
  x_axis = np.cross(z_axis, y_axis)
  
  axis = np.stack([x_axis, y_axis, z_axis])

  return np.dot(points - around, axis.T)

points = np.array([
    [0, 1, 1],
    [0, 1, 0.2],
    [1, 0, -7]
])

v1 = points[1] - points[0]
v2 = points[2] - points[0]

normal = np.cross(v1, v2)
print("rotated points", rotate(points, normal, points[0]))

Here's the accepted answer made in Python:

import numpy as np

def rotate(points, normal, around):
  # Let's rotate the points such that the normal is the new Z axis
  # Following https://stackoverflow.com/questions/1023948/rotate-normal-vector-onto-axis-plane
  old_x_axis = np.array([1, 0, 0])

  z_axis = normal
  y_axis = np.cross(old_x_axis, z_axis)
  x_axis = np.cross(z_axis, y_axis)
  
  axis = np.stack([x_axis, y_axis, z_axis])

  return np.dot(points - around, axis.T)

points = np.array([
    [0, 1, 1],
    [0, 1, 0.2],
    [1, 0, -7]
])

v1 = points[1] - points[0]
v2 = points[2] - points[0]

normal = np.cross(v1, v2)
print("rotated points", rotate(points, normal, points[0]))
浪漫之都 2024-08-01 13:09:57

尽管还有其他有趣的回应,但这是我们在等待答案时找到的解决方案:(

function roti = magic_cosini(n)
    b = acos(n(2) / sqrt(n(1)*n(1) + n(2)*n(2)));
    bwinkel = b * 360 / 2 / pi;
    if (n(1) >= 0)
        rotb = [cos(-b) -sin(-b) 0; sin(-b) cos(-b) 0; 0 0 1];
    else
        rotb = [cos(-b) sin(-b) 0; -sin(-b) cos(-b) 0; 0 0 1];
    end
    n2 = n * rotb;
    a = acos(n2(3) / sqrt(n2(2)*n2(2) + n2(3)*n2(3)));
    awinkel = a * 360 / 2 / pi;
    rota = [1 0 0; 0 cos(-a) -sin(-a); 0 sin(-a) cos(-a)];
    roti = rotb * rota;

它返回一个希望正确的双旋转矩阵)

我们之前遇到并在这里修复的缺陷是特别是。 处理 X 分量的符号,余弦计算中未涵盖该符号。 这让我们旋转了一次错误的方向(旋转180°-角度)。

我希望我也能抽出时间尝试Nosredna的解决方案! 避免使用三角学总是好的。

Although there were other interesting responses, this is the solution we figured out while waiting for answers:

function roti = magic_cosini(n)
    b = acos(n(2) / sqrt(n(1)*n(1) + n(2)*n(2)));
    bwinkel = b * 360 / 2 / pi;
    if (n(1) >= 0)
        rotb = [cos(-b) -sin(-b) 0; sin(-b) cos(-b) 0; 0 0 1];
    else
        rotb = [cos(-b) sin(-b) 0; -sin(-b) cos(-b) 0; 0 0 1];
    end
    n2 = n * rotb;
    a = acos(n2(3) / sqrt(n2(2)*n2(2) + n2(3)*n2(3)));
    awinkel = a * 360 / 2 / pi;
    rota = [1 0 0; 0 cos(-a) -sin(-a); 0 sin(-a) cos(-a)];
    roti = rotb * rota;

(It's returning a hopefully correct double rotation matrix)

The flaw we had before and fixed here was to esp. deal with the sign of the X component, which was not covered in the cosine computations. This made us rotate in the wrong direction once (rotating with 180° - angle).

I hope I will also find time to try Nosredna's solution! It's always good to avoid trigonometry.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文