Углы Эйлера между двумя 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)
Другие вопросы по тегам