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,提问作者김김태영
相关产品推荐
相关产品推荐

