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

Unity中获取智能手机Gyro 3轴角度gimbal lock问题解决方案问询

解决方案

核心原因

Unity默认四元数转欧拉角采用ZXY旋转顺序,当X轴角度接近±90°时会触发欧拉角固有缺陷万向节死锁,直接调用eulerAngles属性得到的数值会出现跳变、错乱。

修复步骤

  • 第一步:先完成陀螺仪坐标系转换
    智能手机陀螺仪输出的四元数为右手坐标系,Unity使用左手坐标系,直接赋值会导致旋转方向错位,需要先对四元数做轴符号翻转:
// 陀螺仪四元数转Unity可用旋转
Quaternion convertedRot = new Quaternion(
    Input.gyro.attitude.x, 
    -Input.gyro.attitude.y, 
    -Input.gyro.attitude.z, 
    Input.gyro.attitude.w
);
phone.transform.rotation = convertedRot;
  • 第二步:用增量累计法获取无死锁欧拉角
    不直接转换当前姿态的四元数,改为每帧计算旋转增量,再累计得到连续三轴角度。每帧旋转增量极小,转换为欧拉角时不会触发死锁:
using UnityEngine;

public class Device_Data_Load : MonoBehaviour
{
    public GameObject phone;
    public Text text;
    public float time;
    // 无死锁欧拉角存储变量
    public Vector3 totalEulerAngles;
    private Quaternion lastGyroAtt;

    void Start()
    {
        Input.gyro.enabled = true;
        lastGyroAtt = Input.gyro.attitude;
        totalEulerAngles = Vector3.zero;
    }

    void Update()
    {
        time += Time.deltaTime;
        Quaternion currentGyroAtt = Input.gyro.attitude;
        // 计算当前帧与上一帧的旋转差
        Quaternion deltaRot = Quaternion.Inverse(lastGyroAtt) * currentGyroAtt;
        lastGyroAtt = currentGyroAtt;

        // 增量转欧拉角,处理数值跳变
        Vector3 deltaEuler = deltaRot.eulerAngles;
        deltaEuler.x = deltaEuler.x > 180 ? deltaEuler.x - 360 : deltaEuler.x;
        deltaEuler.y = deltaEuler.y > 180 ? deltaEuler.y - 360 : deltaEuler.y;
        deltaEuler.z = deltaEuler.z > 180 ? deltaEuler.z - 360 : deltaEuler.z;

        // 累计角度,适配坐标系符号
        totalEulerAngles.x += deltaEuler.x;
        totalEulerAngles.y -= deltaEuler.y;
        totalEulerAngles.z -= deltaEuler.z;

        // 可选:角度超过360度时做取模处理,避免数值过大
        totalEulerAngles.x = Mathf.Repeat(totalEulerAngles.x, 360f);
        totalEulerAngles.y = Mathf.Repeat(totalEulerAngles.y, 360f);
        totalEulerAngles.z = Mathf.Repeat(totalEulerAngles.z, 360f);

        // 赋值对象旋转
        phone.transform.rotation = new Quaternion(currentGyroAtt.x, -currentGyroAtt.y, -currentGyroAtt.z, currentGyroAtt.w);

        // 输出时使用totalEulerAngles即可得到无异常的三轴角度
        text.text = "无死锁三轴角度:" + totalEulerAngles.ToString("F2")
                  + "\r\n\n1. playTime : " + time.ToString("N2")
                  + "\r\n\n2. Gyroattitude : " + Input.gyro.attitude
                  + "\r\n\n3. phone.transform.rotation : " + phone.transform.rotation;
    }
}

注意事项

如果不需要实时输出可读的三轴角度,仅控制对象旋转,直接使用转换后的四元数赋值即可,完全不会触发万向节死锁,不需要做额外处理。

内容的提问来源于stack exchange,提问作者김김태영

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:36:03