Углы Эйлера между двумя 3d-векторами
Как вы находите 3 угла Эйлера между двумя трехмерными векторами? Когда у меня есть один Вектор и я хочу получить его вращение, обычно можно использовать эту ссылку: Рассчитать повороты, чтобы посмотреть на трехмерную точку?
Но как мне это сделать при расчете их в соответствии друг с другом?
3 ответа
Как уже отмечали другие, ваш вопрос должен быть пересмотрен. Давайте назовем ваши векторы a
а также b
, Я предполагаю что length(a)==length(b) > 0
в противном случае я не могу ответить на вопрос.
Рассчитайте перекрестное произведение ваших векторов v = a x b
; v
дает ось вращения. Вычисляя скалярное произведение, вы можете получить косинус угла, с которым вы должны вращаться cos(angle)=dot(a,b)/(length(a)length(b))
, и с acos
Вы можете однозначно определить угол (@Archie спасибо за указание на мою предыдущую ошибку). На данный момент у вас есть представление угла поворота вашего вращения.
Оставшаяся работа заключается в преобразовании этого представления в представление, которое вы ищете: углы Эйлера. Конверсия Оси-Угол в Эйлера - это способ сделать так, как вы его нашли. Вы должны справиться с вырожденным случаем, когда v = [ 0, 0, 0]
когда угол равен 0 или 180 градусам.
Мне лично не нравятся углы Эйлера, они портят стабильность вашего приложения и не подходят для интерполяции, см. Также
Сначала вам нужно будет вычесть вектор один из вектора два, чтобы получить вектор два относительно вектора один. С этими значениями вы можете рассчитать углы Эйлера.
Чтобы интуитивно понять расчет от вектора к Эйлеру, представим сферу с радиусом 1 и началом координат в ее центре. Вектор представляет точку на своей поверхности в трехмерных координатах. Эта точка также может быть определена сферическими 2D координатами: широтой и долготой, тангажом и рысканием соответственно.
Для расчета "roll <- pitch <- yaw" можно сделать следующее:
Для вычисления рыскания вы вычисляете тангенс двух плоских осей (x и z) с учетом квадранта.
yaw = atan2(x, z) *180.0/PI;
Шаг абсолютно одинаков, но, поскольку его плоскость вращается вместе с рысканием, "смежный" находится на двух осях. Чтобы найти его длину, нам понадобится теорема Пифагора.
float padj = sqrt(pow(x, 2) + pow(z, 2));
pitch = atan2(padj, y) *180.0/PI;
Заметки:
- Рулон не может быть рассчитан, так как вектор не имеет вращения вокруг своей оси. Я обычно устанавливаю это в 0.
- Длина вашего вектора потеряна и не может быть преобразована обратно.
- В Эйлере порядок ваших осей имеет значение, смешайте их, и вы получите разные результаты.
Мне потребовалось много времени, чтобы найти этот ответ, поэтому я хотел бы поделиться им с вами сейчас.
сначала нужно найти матрицу вращения, а затем с помощью
scipy
вы можете легко найти нужный ракурс.
Короткого пути для этого нет. так что давайте сначала объявим некоторые функции...
import numpy as np
from scipy.spatial.transform import Rotation
def normalize(v):
return v / np.linalg.norm(v)
def find_additional_vertical_vector(vector):
ez = np.array([0, 0, 1])
look_at_vector = normalize(vector)
up_vector = normalize(ez - np.dot(look_at_vector, ez) * look_at_vector)
return up_vector
def calc_rotation_matrix(v1_start, v2_start, v1_target, v2_target):
"""
calculating M the rotation matrix from base U to base V
M @ U = V
M = V @ U^-1
"""
def get_base_matrices():
u1_start = normalize(v1_start)
u2_start = normalize(v2_start)
u3_start = normalize(np.cross(u1_start, u2_start))
u1_target = normalize(v1_target)
u2_target = normalize(v2_target)
u3_target = normalize(np.cross(u1_target, u2_target))
U = np.hstack([u1_start.reshape(3, 1), u2_start.reshape(3, 1), u3_start.reshape(3, 1)])
V = np.hstack([u1_target.reshape(3, 1), u2_target.reshape(3, 1), u3_target.reshape(3, 1)])
return U, V
def calc_base_transition_matrix():
return np.dot(V, np.linalg.inv(U))
if not np.isclose(np.dot(v1_target, v2_target), 0, atol=1e-03):
raise ValueError("v1_target and v2_target must be vertical")
U, V = get_base_matrices()
return calc_base_transition_matrix()
def get_euler_rotation_angles(start_look_at_vector, target_look_at_vector, start_up_vector=None, target_up_vector=None):
if start_up_vector is None:
start_up_vector = find_additional_vertical_vector(start_look_at_vector)
if target_up_vector is None:
target_up_vector = find_additional_vertical_vector(target_look_at_vector)
rot_mat = calc_rotation_matrix(start_look_at_vector, start_up_vector, target_look_at_vector, target_up_vector)
is_equal = np.allclose(rot_mat @ start_look_at_vector, target_look_at_vector, atol=1e-03)
print(f"rot_mat @ start_look_at_vector1 == target_look_at_vector1 is {is_equal}")
rotation = Rotation.from_matrix(rot_mat)
return rotation.as_euler(seq="xyz", degrees=True)
Нахождение углов поворота Эйлера XYZ от одного вектора к другому может дать вам несколько ответов.
Предполагая, что вы вращаете,
look_at_vector
какой-то формы, и вы хотите, чтобы она оставалась не перевернутой и по-прежнему смотрела на
target_look_at_vector
if __name__ == "__main__":
# Example 1
start_look_at_vector = normalize(np.random.random(3))
target_look_at_vector = normalize(np.array([-0.70710688829422, 0.4156269133090973, -0.5720613598823547]))
phi, theta, psi = get_euler_rotation_angles(start_look_at_vector, target_look_at_vector)
print(f"phi_x_rotation={phi}, theta_y_rotation={theta}, psi_z_rotation={psi}")
Теперь, если вы хотите, чтобы в вашей фигуре была ротация определенной роли, мой код также поддерживает это! вам просто нужно дать
target_up_vector
как параметр. просто убедитесь, что он расположен вертикально по отношению к target_look_at_vector, который вы даете.
if __name__ == "__main__":
# Example 2
# look and up must be vertical
start_look_at_vector = normalize(np.array([1, 2, 3]))
start_up_vector = normalize(np.array([1, -3, 2]))
target_look_at_vector = np.array([0.19283590755300162, 0.6597510192626469, -0.7263217228739983])
target_up_vector = np.array([-0.13225754322703182, 0.7509361508721898, 0.6469955018014842])
phi, theta, psi = get_euler_rotation_angles(
start_look_at_vector, target_look_at_vector, start_up_vector, target_up_vector
)
print(f"phi_x_rotation={phi}, theta_y_rotation={theta}, psi_z_rotation={psi}")
Получить Rotation Matrix в MATLAB очень просто, например
A = [1.353553385, 0.200000003, 0.35]
B = [1 2 3]
[q] = vrrotvec(A,B)
Rot_mat = vrrotvec2mat(q)