Math 两个三维矢量之间的欧拉角
如何找到2个三维向量之间的3个euler角? 当我有一个向量并且我想得到它的旋转时,通常可以使用此链接: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
但是,当我根据彼此计算它们时,我该怎么做呢?正如其他人已经指出的那样,你的问题应该修改。让我们调用向量
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度时,必须处理退化情况
我个人不喜欢欧拉角,它们破坏了应用程序的稳定性,不适合插值,另请参见
为了“滚动,在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}")