Аналитическое определение угла поворота с учетом ограничения по высоте

У меня есть две 3D-точки, которые я хочу повернуть вокруг оси Y:

  • А=(х1, у1, z1)
  • В=(х2, у2, z2)

И у меня есть еще две трехмерные точки, которые будут использоваться в качестве ссылки для определения ограничения для моего угла поворота.

  • C=(x'1, y'1, z'1)
  • D=(x'2, y'2, z'2)

Я хочу повернуть точки A и B вокруг оси Y так, чтобы высота отрезка AB (z1-z2) была такой же, как высота отрезка CD (z'1-z'2).

(Редактировать: судя по комментариям, рисунок ниже не совсем понятен. Рисунок ниже представляет собой проекцию 3D-точек на плоскость XZ. Он предназначен для отображения проекции точек A и B, которые будут повернуты вокруг ось Y так, чтобы высота (h= z1-z2) отрезка AB была равна высоте отрезка CD (h'=z'1-z'2))

И отрезок AB всегда будет достаточно длинным, чтобы решение всегда существовало.

Для этого я сначала записал уравнение, которое вращает точки A и B вдоль оси Y, и приравнял их к точкам C и D. (Меня будет интересовать только последняя строка уравнения для сохранения высоты ограничение):

Используя последнюю строку, я могу извлечь следующую систему уравнений:

Затем, чтобы решить эту систему уравнений и найти угол тета, я вычел второе уравнение из первого уравнения и получил следующее:

В случае, когда (z'1-z'2)=0, уравнение можно легко решить и вычислить угол следующим образом:

Однако я сталкиваюсь с трудностями в случае, когда (z'1-z'2) не равно нулю.

Я попытался решить случай, когда (z'1-z'2) не равно нулю, разделив первое уравнение на второе уравнение:

Затем, умножив обе части уравнения на знаменатель, я смог выделить угол тета с одной стороны и вычислить арктанс2, чтобы найти тета следующим образом:

Я понимаю, что, используя этот метод, я предполагаю, что z'2 не равно нулю, чтобы иметь решение. И я также понимаю, что я мог бы разделить уравнение 2 на уравнение 1 в системе уравнений и получить z'1 в знаменателе, который, как я предполагал, также был бы ненулевым.

Итак, я разработал функцию Python, которая вычисляет тету, используя приведенные выше уравнения, и возвращает матрицу вращения, которая затем будет использоваться для поворота A и B вдоль оси Y:

def get_rot_y(current_points, reference_points):
    (x1, y1, z1) = current_points[:, 0]
    (x2, y2, z2) = current_points[:, 1]
    (xp1, yp1, zp1) = reference_points[:, 0]
    (xp2, yp2, zp2) = reference_points[:, 1]

    if ((zp1-zp2) == 0):
        th = np.arctan2((z2-z1), (x2-x1))
    else:
        th = np.arctan2((-z1 + (zp1*z2 / zp2)), (-x1 + (zp1*x2 / zp2)))
    rot_y = np.array([
        [np.cos(th), 0, np.sin(th)],
        [0, 1, 0],
        [-np.sin(th), 0, np.cos(th)],
    ])
    return rot_y

Затем я протестировал функцию для случая, когда высота (z'1-z'2) не равна нулю:

A = np.array([1450, 0.5, -1545])
B = np.array([6000, 0.7, -1650])

C = np.array([1500, 0, -1500])
D = np.array([5600, 0, -1600])

current_height = A[2] - B[2]
desired_height = C[2] - D[2]

current_points = np.array([A, B]).T
reference_points = np.array([C, D]).T

rot_y = get_rot_y(current_points=current_points, reference_points=reference_points)
transfomed_points = rot_y @ current_points

transformed_height = transfomed_points[2,0] - transfomed_points[2,1]
print(f"current_height= {current_height}")
print(f"desired_height= {desired_height}")
print(f"transformed_height= {transformed_height}")

Однако при выполнении кода я получаю следующий вывод:

current_height= 105.0
desired_height= 100
transformed_height= 102.95657644356697

Как видно выше, высота преобразованных точек не равна желаемой высоте.

Что я делаю не так? Есть ли аналитическое решение моей проблемы, которое можно применить в случае, когда (z'1-z'2) не равно нулю?

«Я хочу повернуть точки A и B вдоль оси y так, чтобы высота отрезка AB (z1-z2) была такой же, как высота отрезка CD (z'1-z'2). " "Вращение вдоль оси" не имеет смысла. 2D-вращение должно быть определено вокруг точки. Сегмент не имеет канонической высоты, и вам необходимо тщательно определить, что это означает.

possum 05.04.2024 12:25

@emptyjar не относится ли ограничение высоты к разнице в координатах z конечных точек отрезков линии? возможно, лучше сформулировать так: я хочу повернуть точки A и B вокруг оси y, и разница в координатах z точек A и B (т. е. z1-z2) будет такой же, как разница в координатах z. координаты точек C и D (т.е. z'1-z'2).

rohan 05.04.2024 12:27

попробуйте th = np.arctan2(((zp1-zp2)*(x2-x1)), ((zp1-zp2)*(z1-z2))) вместо th = np.arctan2((-z1 + (zp1*) z2 / zp2)), (-x1 + (zp1*x2 / zp2))) я просто использовал atan2 напрямую вместо вашего вывода. (z'1-z'2) представляет собой нужную разницу высот (x2-x1) представляет собой часть x вектора AB. (z1-z2) представляет текущую разницу высот.

rohan 05.04.2024 12:30

@Эли Хатем, как отметил пустая банка, ваша постановка проблемы — чепуха. Вы вращаетесь ВОКРУГ оси, а не ВДОЛЬ. Ваша диаграмма тоже нечитабельна. Нам нужно угадать (из вашей матрицы), что вы хотите вращаться вокруг оси Y. ПРЕДПОЛАГАЕМЫЙ угол равен arcsin(h/L), где h — предполагаемая высота (разница в z), а L — длина сегмента, проецируемого на плоскость xz. ТЕКУЩИЙ угол в этой плоскости равен atan2( z2-z1,x2-x1). Так что просто поверните (вокруг оси Y) на ПРЕДПОЛАГАЕМЫЙ - ТЕКУЩИЙ угол.

lastchance 05.04.2024 13:01
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ПРЕДПОЛАГАЕМЫЙ угол равен arcsin(h/L), где h — предполагаемая высота (разница в z), а L — длина сегмента, проецируемого на плоскость xz. ТЕКУЩИЙ угол в этой плоскости равен atan2( z2-z1,x2-x1). Так что просто поверните (вокруг оси Y) на ПРЕДПОЛАГАЕМЫЙ - ТЕКУЩИЙ угол.

Но будьте осторожны с двумя вещами.

Во-первых, arcsin может дать неверный квадрант, если вы не будете осторожны.

Во-вторых, формулы, которые вы (и я) используете, определяют углы против часовой стрелки от x к z, как показано на вашей диаграмме (как это обычно делается для графика xy, поскольку положительная ось z будет ВНЕ вашего рисунка). Однако положительное вращение вокруг оси Y будет ПО ЧАСОВОЙ СТРЕЛКЕ (поскольку положительная ось Y находится в вашем чертеже для правосторонней системы координат). Это учтено в приведенном ниже коде путем простого изменения углового изменения.

import numpy as np

def rot_y( theta ):
    '''Right-handed rotation about the y axis'''
    return np.array( [ [  np.cos( theta ), 0, np.sin( theta ) ],
                       [      0          , 1,     0           ],
                       [ -np.sin( theta ), 0, np.cos( theta ) ] ] )


A = np.array( [ 1450, 0.5, -1545 ] )
B = np.array( [ 6000, 0.7, -1650 ] )

C = np.array( [ 1500,   0, -1500 ] )
D = np.array( [ 5600,   0, -1600 ] )

# Angle COUNTER-CLOCKWISE from X-AXIS (from x toward z)
current_angle = np.arctan2( B[2]-A[2], B[0]-A[0] )
desired_angle = np.arcsin ( ( D[2]-C[2] ) / np.sqrt( (B[0]-A[0])**2 + (B[2]-A[2])**2 ) )
if D[0]-C[0] < 0: desired_angle = np.pi - desired_angle       # Get correct quadrant for arcsin
theta = desired_angle - current_angle

# Note that a positive rotation about the y axis will go CLOCKWISE (i.e. from z toward x) ...
# ... so we need to reverse it
theta = -theta
R = rot_y( theta )

# Rotate about its centre
centre = ( A + B ) / 2
AP = centre + R @ ( A - centre ).T
BP = centre + R @ ( B - centre ).T

print( f"current_height = {(B[2]-A[2])}")
print( f"desired_height = {(D[2]-C[2])}")
print( f"transformed_height = {(BP[2]-AP[2])}" )

Выход:

current_height = -105.0
desired_height = -100
transformed_height = -100.0

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