如何将欧拉角转换为四元数并从四元数中得到相同的欧拉角?

6
我正在使用欧拉角按照XYZ的顺序旋转一个三维形状,这意味着对象首先沿着X轴旋转,然后是Y和Z。我想将欧拉角转换为四元数,然后使用一些[最好是]Python代码或伪代码或算法从四元数中获取相同的欧拉角。下面是一些将欧拉角转换为四元数,然后再将四元数转换为欧拉角的代码。然而,这并不能给我相同的欧拉角。
我认为问题在于我不知道如何将偏航、俯仰和滚转与X、Y和Z轴关联起来。此外,我不知道如何更改代码中的转换顺序,以正确地将欧拉角转换为四元数,然后将四元数转换为欧拉角,以便我能够获得相同的欧拉角。有人可以帮助我吗?
以下是我使用的代码:
这个函数将欧拉角转换为四元数:
def euler_to_quaternion(yaw, pitch, roll):

        qx = np.sin(roll/2) * np.cos(pitch/2) * np.cos(yaw/2) - np.cos(roll/2) * np.sin(pitch/2) * np.sin(yaw/2)
        qy = np.cos(roll/2) * np.sin(pitch/2) * np.cos(yaw/2) + np.sin(roll/2) * np.cos(pitch/2) * np.sin(yaw/2)
        qz = np.cos(roll/2) * np.cos(pitch/2) * np.sin(yaw/2) - np.sin(roll/2) * np.sin(pitch/2) * np.cos(yaw/2)
        qw = np.cos(roll/2) * np.cos(pitch/2) * np.cos(yaw/2) + np.sin(roll/2) * np.sin(pitch/2) * np.sin(yaw/2)

        return [qx, qy, qz, qw]

这将四元数转换为欧拉角:

def quaternion_to_euler(x, y, z, w):

        import math
        t0 = +2.0 * (w * x + y * z)
        t1 = +1.0 - 2.0 * (x * x + y * y)
        X = math.degrees(math.atan2(t0, t1))

        t2 = +2.0 * (w * y - z * x)
        t2 = +1.0 if t2 > +1.0 else t2
        t2 = -1.0 if t2 < -1.0 else t2
        Y = math.degrees(math.asin(t2))

        t3 = +2.0 * (w * z + x * y)
        t4 = +1.0 - 2.0 * (y * y + z * z)
        Z = math.degrees(math.atan2(t3, t4))

        return X, Y, Z

我使用它们的方式如下:

import numpy as np
euler_Original = np.random.random(3) * 360).tolist() # Generate random rotation angles for XYZ within the range [0, 360)
quat = euler_to_quaternion(euler_Original[0], euler_Original[1], euler_Original[2]) # Convert to Quaternion
newEulerRot = quaternion_to_euler(quat[0], quat[1], quat[2], quat[3]) #Convert the Quaternion to Euler angles

print (euler_Original)
print (newEulerRot)

打印语句对于“euler_original”和“newEulerRot”打印了不同的数字,这不是我想要的结果。例如,如果“euler_original”包含弧度数值为“(0.2, 1.12, 2.31)”的数字,则得到的四元数为“[0.749, 0.290, -0.449, 0.389]”,将四元数转换为欧拉角则得到“(132.35, 64.17, 11.45)”,这个结果是相当错误的。我想知道如何解决这个问题。
虽然我有兴趣通过修改代码来使上述代码工作,但我更愿意学习如何正确设置方程。这样我就会知道即使旋转顺序(XYZ -> YZX等)改变应用欧拉角时也能获取正确的四元数。

你使用的是哪种欧拉角?zyx Tait-Bryan角? - Yonatan Simson
2个回答

11
我们可以使用scipy.spatial.transform中的Rotation
from scipy.spatial.transform import Rotation

# Create a rotation object from Euler angles specifying axes of rotation
rot = Rotation.from_euler('xyz', [90, 45, 30], degrees=True)

# Convert to quaternions and print
rot_quat = rot.as_quat()
print(rot_quat)

结果将会是:

[ 0.56098553  0.43045933 -0.09229596  0.70105738]

然后,您还可以将其转换为欧拉角:

print(rot.as_euler('xyz', degrees=True))

导致的结果是:

[90. 45. 30.]

最后进行检查,从上面计算出的四元数创建旋转对象,并将其转换为欧拉角:

rot = Rotation.from_quat(rot_quat)

# Convert the rotation to Euler angles given the axes of rotation
print(rot.as_euler('xyz', degrees=True))

导致结果:

[90. 45. 30.]

5

主要问题:

euler_to_quaternion的输入顺序与quaternion_to_euler的输出顺序不同

前者采用Z, Y, X(偏航角,俯仰角,翻滚角)的角度顺序,而后者返回X, Y, Z。修复方法:

def euler_to_quaternion(roll, pitch, yaw):
# or
euler_to_quaternion(euler_Original[2], euler_Original[1], euler_Original[0])

小问题

euler_to_quaternion接受弧度值,而quaternion_to_euler返回角度值。

这并不是一个真正的问题,但最好将角度保持在弧度中,因为大多数库函数使用弧度。

X = math.atan2(t0, t1)
Y = math.asin(t2)
Z = math.atan2(t3, t4)

如果我输入[math.radians(89), math.radians(90), math.radians(89.5)]作为滚转、俯仰和偏航的值,这个会失败。quaternion_to_euler将返回奇怪的值。你能试一下吗? - Amir
是的,这是可能的。同样适用于 [math.radians(179.9), math.radians(179.5), math.radians(179.8)]。我对四元数还不是很熟悉。它们是否可以在欧拉角空间中旋转超过90度?基本上,无论我在欧拉角空间中输入多少度以上的旋转,quaternion_to_euler函数都会返回奇怪的数字。 - Amir
@Amir 不好意思,忽略我之前的评论。奇怪的值是由于欧拉角的万向节锁定问题引起的;将Y(俯仰)设置为90会有效地将X和Z轴折叠在一起,因此偏航和横滚没有唯一解 - 它们都具有相同的效果,因此失去了自由度。至于另一种情况,您超出了Y(“ [-90,90]”)的有效范围,这意味着您的相机被“倒置”。 - meowgoesthedog
在将欧拉角转换为四元数时,通常情况下是否不能有超过-90度的值?还是因为这里设置的公式方式导致的? - Amir
@Amir 这是由于角度本身的性质(或其所代表的内容)导致的。只有特定的组合才会产生万向节锁定,例如仅将横滚或偏航设置为90度。请参阅维基页面 - meowgoesthedog

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