两个三维向量之间的欧拉角

10
当我有一个向量并想要获得它的旋转时,通常可以使用此链接:计算旋转以查看3D点?。但是,当根据彼此计算它们时,如何找到两个3D向量之间的3个欧拉角呢?

3
你的问题不正确。如果你想要对齐两个"参考系",你需要三个角度。如果你只需要对齐两个向量,则只需要两个角度。 - 6502
你如何计算这两个角度?BVH文件格式(http://davedub.co.uk/bvhacker/)为什么有3个角度来移动骨骼? - tomyake
4个回答

16

正如其他人已经指出的那样,您的问题需要修改。我们将称您的向量为ab。我假设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度的情况。


我个人不喜欢欧拉角,它们会破坏应用程序的稳定性并且不适合插值,详情请参见:

叉积不足以区分0度和180度的角度。您应该计算两者:通过叉积得到正弦值,通过点积得到余弦值,然后使用它们来计算角度(例如通过C数学库中的atan2()函数)。 - Archie
你认为这个方法怎么样:http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToEuler/index.htm - tomyake
@Archie 是的,正确,已修复。实际上,点积和acos就足够了。 - Ali
@tomyake 是的,这似乎是一种更简单的方法。 - Ali

4

首先,您需要减去向量二中的向量一,以便获得相对于向量一的向量二。使用这些值,您可以计算欧拉角。

为了直观地理解从向量到欧拉角的计算,让我们想象一个半径为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。
  • 您的向量长度丢失,无法转换回来。
  • 在欧拉角中,轴的顺序很重要,如果混淆轴的顺序,则会获得不同的结果。

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

-3
在MATLAB中获取旋转矩阵非常容易,例如:
A = [1.353553385,  0.200000003,  0.35]
B = [1 2 3]

[q] = vrrotvec(A,B)
Rot_mat = vrrotvec2mat(q)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接