Math 两个三维矢量之间的欧拉角

Math 两个三维矢量之间的欧拉角,math,vector,3d,geometry,euler-angles,Math,Vector,3d,Geometry,Euler Angles,如何找到2个三维向量之间的3个euler角? 当我有一个向量并且我想得到它的旋转时,通常可以使用此链接: 但是,当我根据彼此计算它们时,我该怎么做呢?正如其他人已经指出的那样,你的问题应该修改。让我们调用向量a和b。我假设length(a)==length(b)>0否则我无法回答这个问题 计算向量的大小v=a x bv给出旋转的轴。通过计算,你可以得到角度的余弦,你应该用cos(angle)=dot(a,b)/(length(a)length(b))旋转,用acos你可以唯一地确定角度(@A

如何找到2个三维向量之间的3个euler角? 当我有一个向量并且我想得到它的旋转时,通常可以使用此链接:


但是,当我根据彼此计算它们时,我该怎么做呢?

正如其他人已经指出的那样,你的问题应该修改。让我们调用向量
a
b
。我假设
length(a)==length(b)>0
否则我无法回答这个问题


计算向量的大小
v=a x b
v
给出旋转的轴。通过计算,你可以得到角度的余弦,你应该用
cos(angle)=dot(a,b)/(length(a)length(b))
旋转,用
acos
你可以唯一地确定角度(@Archie感谢你指出我之前的错误)。此时,您拥有旋转的轴角度表示法

剩下的工作是将此表示转换为您要查找的表示:Euler角度。这是一种方法,正如你所发现的。当
v=[0,0,0]
时,即当角度为0或180度时,必须处理退化情况


我个人不喜欢欧拉角,它们破坏了应用程序的稳定性,不适合插值,另请参见


首先,必须从向量2中减去向量1,才能得到相对于向量1的向量2。使用这些值可以计算Euler角度

为了直观地理解从矢量到欧拉的计算,让我们想象一个半径为1且原点位于其中心的球体。矢量在三维坐标中表示其曲面上的点。该点也可以由球面二维坐标定义:纬度和经度、俯仰和偏航


为了“滚动,在MATLAB中获取旋转矩阵非常容易 e、 g


我花了很多时间才找到这个答案,所以现在我想和大家分享一下

首先,需要找到旋转矩阵,然后使用
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 Euler旋转角度可能会给出不止一个答案

假设你正在旋转的是某种形状的
look\u向量
,你希望这个形状不倒置,仍然可以看到
target\u look\u向量

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}")
现在,如果您想对您的形状进行特定的角色轮换,我的代码也支持这一点! 您只需将
目标向量
作为一个参数。 只要确保它与你给出的目标向量垂直

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}")

交叉生成是不够的-它不能区分0度和180度角。您应该同时计算:交叉得到正弦,标量得到余弦,然后使用它们来计算角度(例如,通过C数学库中的atan2()函数)。你对这个方法有什么看法:@Archie是的,正确,固定。实际上点积和
acos
就足够了。@tomyake是的,这似乎是一个更简单的方法。你的问题不正确。如果你想对齐两个参考帧,你需要三个角度。如果你只需要对齐两个向量,那么只需要两个角度。你怎么做计算两个角度?为什么BVH文件格式()有3个角度来移动骨骼?
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)
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}")
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}")