11

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

我接下来要做的是将所有点转换到所述平面上并在 2D 中查看它。

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

  • 在平面上找一个中心点(平均点)
  • 从所有数据点中减去它以将它们排列在原点周围
  • 旋转法线使其变为 (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:

我希望n2y 分量为零。但是,对于向量(-0.6367、0.7697、0.0467),这已经失败了。

4

4 回答 4

12

如果你有一个平面,你就有一个法线向量和一个原点。我根本不会做任何“轮换”。你离你的答案只有几个向量操作。

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

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

技巧:

  • 注意你的向量指向的方向。如果他们指向错误的方向,则否定结果向量或更改叉积的顺序。
  • 如果您的平面法线与原始 x 轴完全相同,您将遇到麻烦。
于 2009-06-21T14:45:32.540 回答
1

怎么样:

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

这个想法是将法线向量与 Z 对齐,通过这样做,您的平面现在是 XY 平面。

于 2009-06-21T14:27:11.050 回答
1

这是用 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]))
于 2021-03-16T15:41:28.863 回答
0

虽然还有其他有趣的回应,但这是我们在等待答案时想出的解决方案:

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;

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

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

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

于 2009-06-21T15:13:04.840 回答