Повернуть вектор нормали на плоскость оси

У меня есть набор точек данных в трехмерном пространстве, которые, очевидно, все попадают на конкретную плоскость. Я использую PCA для вычисления параметров самолета. 3-й компонент 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:

Я жду n2 иметь у компонент ноль. Однако это уже не работает для вектора (-0,6367, 0,7697, 0,0467).

4 ответа

Решение

Если у вас есть плоскость, у вас есть нормальный вектор и начало координат. Я бы вообще не делал никаких "вращений". Вы только несколько векторных операций от вашего ответа.

  • Давайте назовем нормальный вектор вашей плоскости новой осью z.
  • Вы можете создать новую ось Y, пересекая старую ось X с новой осью Z (ваша плоскость нормальная).
  • Создайте новую ось x, пересекая новую z с новой y.
  • Сделайте все ваши новые векторы оси единичными векторами (длина 1).
  • Для каждой имеющейся точки создайте вектор от вашего нового начала до точки (векторное вычитание точки - plane_origin). Просто поставьте точки с новыми векторами единиц x и y, и вы получите пару (x, y), которую вы можете построить!

Если у вас уже есть перекрестные и точечные функции продукта, это всего лишь несколько строк кода. Я знаю, что это работает, потому что большинство 3D-видеоигр, которые я написал, работали именно так.

Трюки:

  • Обратите внимание, на какие направления указывают ваши векторы. Если они указывают неверный путь, отмените результирующий вектор или измените порядок перекрестного произведения.
  • У вас возникнут проблемы, если нормали вашей плоскости точно такие же, как ваша первоначальная ось X.

Как насчет:

Разложить вектор нормали на вектор в плоскости XY и вектор Z. Затем примените вращение вокруг оси Z, чтобы выровнять вектор XY с одной из осей. Затем найдите скалярное произведение нормали с осью Z и поверните, вдоль которой вы бы выстроились по оси X,Y.

Идея состоит в том, чтобы выровнять вектор нормали по Z, и теперь ваша плоскость становится плоскостью XY.

Вот принятый ответ на 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]))

Хотя были и другие интересные ответы, это решение мы нашли в ожидании ответов:

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°).

Надеюсь, я найду время попробовать решение Носредны! Всегда хорошо избегать тригонометрии.

Другие вопросы по тегам