你如何找到 2 个 3D 向量之间的 3 个欧拉角?当我有一个向量并且我想获得它的旋转时,通常可以使用此链接:计算旋转以查看 3D 点?
但是,当它们相互计算时,我该怎么做呢?
4 回答
正如其他人已经指出的那样,应该修改您的问题。让我们称你的向量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 且原点位于其中心的球体。向量表示其表面上 3D 坐标中的一个点。该点也可以通过球面 2D 坐标定义:分别为纬度和经度、俯仰和偏航。
为了“滚动<-俯仰<-偏航”计算可以如下完成:
要计算偏航,请考虑象限,计算两个平面轴(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。
- 矢量的长度丢失,无法转换回来。
- 在 Euler 中,轴的顺序很重要,将它们混合起来,你会得到不同的结果。
我花了很多时间才找到这个答案,所以我现在想与您分享。
首先,您需要找到旋转矩阵,然后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)
找到从 1 个向量到另一个向量的 XYZ Euler 旋转角度可能会给您多个答案。
假设您正在旋转的是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}")
在 MATLAB 中获取旋转矩阵非常简单,例如
A = [1.353553385, 0.200000003, 0.35]
B = [1 2 3]
[q] = vrrotvec(A,B)
Rot_mat = vrrotvec2mat(q)