You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于特定旋转序列的两个四元数旋转差值计算方法咨询

解决方法

首先明确:四元数本身是姿态的无顺序表示,你用Quaternion difference = Quaternion.Inverse(rotation2) * rotation1计算出的差值四元数,其物理意义是固定的——它代表了从rotation2姿态变换到rotation1姿态所需的旋转,和任何欧拉角旋转序列无关。

你要的“对应六种旋转序列的差值”,本质是把这个统一的差值四元数,分解为指定旋转顺序的欧拉角分量(即把四元数拆成按某一顺序依次绕X/Y/Z轴旋转的角度)。

下面针对六种旋转序列,给出将差值四元数转换为对应欧拉角的实现(以Unity环境为例,角度单位为度):


1. XYZ顺序(先绕X轴,再绕Y轴,最后绕Z轴)

Vector3 GetEulerXYZ(Quaternion q)
{
    float x = Mathf.Atan2(2 * (q.w * q.x + q.y * q.z), 1 - 2 * (q.x * q.x + q.y * q.y));
    float y = Mathf.Asin(Mathf.Clamp(2 * (q.w * q.y - q.z * q.x), -1f, 1f));
    float z = Mathf.Atan2(2 * (q.w * q.z + q.x * q.y), 1 - 2 * (q.y * q.y + q.z * q.z));
    return new Vector3(Mathf.Rad2Deg * x, Mathf.Rad2Deg * y, Mathf.Rad2Deg * z);
}

使用方式:Vector3 diffXYZ = GetEulerXYZ(difference);

2. XZY顺序(先绕X轴,再绕Z轴,最后绕Y轴)

Vector3 GetEulerXZY(Quaternion q)
{
    float x = Mathf.Atan2(2 * (q.w * q.x + q.z * q.y), 1 - 2 * (q.x * q.x + q.z * q.z));
    float z = Mathf.Asin(Mathf.Clamp(2 * (q.w * q.z - q.y * q.x), -1f, 1f));
    float y = Mathf.Atan2(2 * (q.w * q.y + q.x * q.z), 1 - 2 * (q.y * q.y + q.z * q.z));
    return new Vector3(Mathf.Rad2Deg * x, Mathf.Rad2Deg * y, Mathf.Rad2Deg * z);
}

使用方式:Vector3 diffXZY = GetEulerXZY(difference);

3. YXZ顺序(先绕Y轴,再绕X轴,最后绕Z轴)

这是Unity内置Quaternion.eulerAngles采用的旋转顺序,直接用即可:

Vector3 diffYXZ = difference.eulerAngles;

手动实现版本:

Vector3 GetEulerYXZ(Quaternion q)
{
    float y = Mathf.Atan2(2 * (q.w * q.y + q.x * q.z), 1 - 2 * (q.y * q.y + q.x * q.x));
    float x = Mathf.Asin(Mathf.Clamp(2 * (q.w * q.x - q.z * q.y), -1f, 1f));
    float z = Mathf.Atan2(2 * (q.w * q.z + q.y * q.x), 1 - 2 * (q.x * q.x + q.z * q.z));
    return new Vector3(Mathf.Rad2Deg * x, Mathf.Rad2Deg * y, Mathf.Rad2Deg * z);
}

4. YZX顺序(先绕Y轴,再绕Z轴,最后绕X轴)

Vector3 GetEulerYZX(Quaternion q)
{
    float y = Mathf.Atan2(2 * (q.w * q.y + q.z * q.x), 1 - 2 * (q.y * q.y + q.z * q.z));
    float z = Mathf.Asin(Mathf.Clamp(2 * (q.w * q.z - q.x * q.y), -1f, 1f));
    float x = Mathf.Atan2(2 * (q.w * q.x + q.y * q.z), 1 - 2 * (q.x * q.x + q.z * q.z));
    return new Vector3(Mathf.Rad2Deg * x, Mathf.Rad2Deg * y, Mathf.Rad2Deg * z);
}

使用方式:Vector3 diffYZX = GetEulerYZX(difference);

5. ZXY顺序(先绕Z轴,再绕X轴,最后绕Y轴)

Vector3 GetEulerZXY(Quaternion q)
{
    float z = Mathf.Atan2(2 * (q.w * q.z + q.x * q.y), 1 - 2 * (q.z * q.z + q.x * q.x));
    float x = Mathf.Asin(Mathf.Clamp(2 * (q.w * q.x - q.y * q.z), -1f, 1f));
    float y = Mathf.Atan2(2 * (q.w * q.y + q.z * q.x), 1 - 2 * (q.x * q.x + q.y * q.y));
    return new Vector3(Mathf.Rad2Deg * x, Mathf.Rad2Deg * y, Mathf.Rad2Deg * z);
}

使用方式:Vector3 diffZXY = GetEulerZXY(difference);

6. ZYX顺序(先绕Z轴,再绕Y轴,最后绕X轴)

Vector3 GetEulerZYX(Quaternion q)
{
    float z = Mathf.Atan2(2 * (q.w * q.z + q.y * q.x), 1 - 2 * (q.z * q.z + q.y * q.y));
    float y = Mathf.Asin(Mathf.Clamp(2 * (q.w * q.y - q.x * q.z), -1f, 1f));
    float x = Mathf.Atan2(2 * (q.w * q.x + q.z * q.y), 1 - 2 * (q.x * q.x + q.y * q.y));
    return new Vector3(Mathf.Rad2Deg * x, Mathf.Rad2Deg * y, Mathf.Rad2Deg * z);
}

使用方式:Vector3 diffZYX = GetEulerZYX(difference);


注意事项

  • 所有转换函数中的Mathf.Clamp是为了避免浮点精度问题导致Asin输入超出[-1,1]范围,引发NaN错误。
  • 欧拉角存在万向锁问题,若差值旋转接近万向锁姿态(比如绕Y轴旋转±90度时),对应的欧拉角分量会出现不确定性,这是欧拉角的固有缺陷,无法避免。
  • 若需要将差值拆成对应旋转序列的四元数组合,只需将每个欧拉角分量转换成四元数后按顺序相乘即可。比如XYZ顺序:Quaternion diffQ = Quaternion.Euler(x,0,0) * Quaternion.Euler(0,y,0) * Quaternion.Euler(0,0,z);

内容的提问来源于stack exchange,提问作者yogesh sriraman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:40:53