欧拉角与四元数双向转换结果不一致的问题求助
欧拉角与四元数双向转换结果不一致的问题求助
大家好,我最近在调试代码时碰到了个头疼的问题:把欧拉角转成四元数再转回欧拉角,结果和原始的欧拉角对不上,有没有大佬能帮我看看咋解决呀?
下面是我的测试代码:
import numpy as np from scipy.spatial.transform import Rotation as R # Define test rotation in degrees testrotation = np.zeros([2, 2, 3]) testrotation[0, 0] = [45, 30, 60] testrotation[0, 1] = [10, 20, 30] testrotation[1, 0] = [90, 0, 45] testrotation[1, 1] = [90, 45, 30] #################### Euler -> Quaternions using SciPy scipy_quaternions_temp = R.from_euler("ZYX", np.radians(testrotation.reshape(-1, 3))) scipy_quaternions = np.roll(scipy_quaternions_temp.as_quat(), shift=1, axis=-1).reshape(testrotation.shape[0], testrotation.shape[1], 4) print("Quaternions (wxyz format):\n", scipy_quaternions, "\n") #################### Quaternions -> Euler using SciPy scipy_euler_angles = np.rad2deg(scipy_quaternions_temp.as_euler('zyx', degrees=False)).reshape(testrotation.shape[0], testrotation.shape[1], 3) print("Euler angles after conversion and normalization:\n", scipy_euler_angles, "\n") #################### Euler -> Quaternions (back conversion) using SciPy scipy_quat_reconverted = R.from_euler('zyx', np.radians(scipy_euler_angles.reshape(-1, 3))).as_quat().reshape(testrotation.shape[0], testrotation.shape[1], 4) scipy_quat_reconverted = np.roll(scipy_quat_reconverted, shift=1, axis=-1) print("Reconverted quaternions (wxyz format):\n", scipy_quat_reconverted, "\n") sys.exit()
运行后输出的结果里,转回的欧拉角和原始值明显不一样:
Quaternions (wxyz format): [[[ 0.82236317 0.36042341 0.39190384 0.20056212] [ 0.95154852 0.23929834 0.18930786 0.03813458]] [[ 0.65328148 0.27059805 0.27059805 0.65328148] [ 0.70105738 -0.09229596 0.43045933 0.56098553]]] Euler angles after conversion and normalization: [[[ 4.42303689e+00 5.21060674e+01 4.51703838e+01] [-1.11605468e+00 2.22421809e+01 2.84517753e+01]] [[ 9.00000000e+01 4.50000000e+01 1.27222187e-14] [ 9.00000000e+01 3.00000000e+01 -4.50000000e+01]]] Reconverted quaternions (wxyz format): [[[ 0.82236317 0.36042341 0.39190384 0.20056212] [ 0.95154852 0.23929834 0.18930786 0.03813458]] [[ 0.65328148 0.27059805 0.27059805 0.65328148] [ 0.70105738 -0.09229596 0.43045933 0.56098553]]]
不过奇怪的是,把转回的欧拉角再转成四元数,结果又和第一次的四元数完全一致,这说明旋转本身是对的,但欧拉角的数值为啥不一样呢?
问题解析与解决思路
嗨,这个问题我之前也踩过坑!核心原因其实是欧拉角的表示不唯一——同一个旋转状态可以对应多组不同的欧拉角,而SciPy在把四元数转回欧拉角时,会自动将角度归一化到行业通用的标准范围内,你的原始欧拉角可能不在这个范围内,或者存在等价的角度组合。
咱们具体拆解一下:
- 欧拉角的多义性:比如绕Z轴转90°+绕Y轴转45°+绕X轴转0°,和绕Z轴转90°+Y轴转45°+X轴转360°是完全相同的旋转,但欧拉角数值不一样。另外当中间旋转轴(比如ZYX顺序里的Y轴)角度接近±90°时,还会出现万向锁,这时候另外两个轴的角度可以有无数种组合表示同一个旋转。
- SciPy的归一化规则:
as_euler方法默认会把欧拉角调整到标准区间,以ZYX顺序为例:- Z轴角度:[-180°, 180°]
- Y轴角度:[-90°, 90°]
- X轴角度:[-180°, 180°]
看你的输出,比如第三组原始欧拉角[90, 0, 45]转回后变成了[90, 45, ~0],这其实是同一个旋转的不同欧拉角表示——当Y轴角度为0时,Z轴和X轴的旋转顺序可以交换,组合出等价的旋转效果。
验证旋转一致性的方法
如果想确认旋转本身是正确的,可以对比两次旋转对同一个向量的变换结果:
# 取一个测试向量 vec = np.array([1, 0, 0]) # 原始欧拉角对应的旋转 rot_original = R.from_euler("ZYX", np.radians(testrotation[1,0])) # 转回的欧拉角对应的旋转 rot_reconverted = R.from_euler("ZYX", np.radians(scipy_euler_angles[1,0])) # 对比变换后的向量 print("Original transformed vec:", rot_original.apply(vec)) print("Reconverted transformed vec:", rot_reconverted.apply(vec))
运行后会发现两个向量几乎完全一致(误差是浮点计算导致的),这就说明转换是正确的。
如何让转回的欧拉角和原始值一致?
如果业务场景必须让转回的欧拉角和原始数值一致,你需要自定义角度的归一化逻辑:比如对SciPy返回的角度加上/减去360°的整数倍,或者针对万向锁场景做特殊处理,但这需要结合你的具体应用场景来实现。
总结一下:
- 四元数转回欧拉角出现数值差异是正常现象,根源是欧拉角的表示不唯一
- 只要旋转对向量的变换结果一致,就说明转换逻辑没有问题
- 若需要和原始欧拉角数值完全匹配,需自定义角度归一化规则
备注:内容来源于stack exchange,提问作者Kong
相关产品推荐
相关产品推荐

