Unity罗盘未与陀螺仪校准,无法获取稳定真航向求助
Unity 真航向获取问题修正方案
问题核心
你的代码中直接用compassHeading - pitch校正航向的逻辑完全错误,这是导致设备平放/直立时航向值差异巨大的根本原因。真航向是基于水平平面的绝对方向,当设备倾斜时,需要通过空间旋转矩阵将罗盘的水平航向转换为设备当前朝向对应的航向,而非简单的角度加减。
修正思路
- 统一坐标系:Unity陀螺仪姿态(
Input.gyro.attitude)采用右手坐标系,需转换为Unity世界空间的左手坐标系。 - 姿态融合:结合罗盘的低频率无漂移真航向,与陀螺仪的高频率姿态数据,计算设备当前朝向对应的真航向。
- 倾斜补偿:通过设备俯仰角(pitch)和滚转角(roll),将水平面上的真航向投影到设备当前的朝向平面。
修正后的完整代码
using UnityEngine; using TMPro; using System.Collections; public class CompassController : MonoBehaviour { private float trueBearing = 0f; public TMP_Text orientationText; // 平滑融合陀螺仪与罗盘数据的系数 private float smoothFactor = 0.1f; private Quaternion currentRotation; void Start() { StartCoroutine(InitializeSensors()); } IEnumerator InitializeSensors() { // 检查用户是否启用位置服务(真航向依赖位置辅助) if (!Input.location.isEnabledByUser) { Debug.Log("用户未启用位置服务"); yield break; } // 启动位置服务 Input.location.Start(10f, 0.1f); // 等待位置服务初始化 int waitTime = 20; while (Input.location.status == LocationServiceStatus.Initializing && waitTime > 0) { yield return new WaitForSeconds(1); waitTime--; } if (waitTime <= 0 || Input.location.status == LocationServiceStatus.Failed) { Debug.Log("位置服务初始化失败"); yield break; } // 启用罗盘和陀螺仪 Input.compass.enabled = true; if (SystemInfo.supportsGyroscope) { Input.gyro.enabled = true; // 初始化当前旋转为转换后的陀螺仪姿态 currentRotation = ConvertGyroToUnity(Input.gyro.attitude); } else { Debug.Log("设备不支持陀螺仪"); } Debug.Log("传感器初始化完成"); } void Update() { if (!Input.compass.enabled) return; // 获取罗盘提供的水平真航向 float compassTrueHeading = Input.compass.trueHeading; if (Input.gyro.enabled) { // 将陀螺仪姿态转换为Unity坐标系 Quaternion gyroRotation = ConvertGyroToUnity(Input.gyro.attitude); // 平滑更新旋转数据,避免抖动 currentRotation = Quaternion.Lerp(currentRotation, gyroRotation, smoothFactor); // 计算设备倾斜后的真航向 // 1. 创建水平面上的航向旋转(绕Y轴) Quaternion headingRotation = Quaternion.Euler(0f, compassTrueHeading, 0f); // 2. 抵消设备俯仰/滚转对航向的影响 Quaternion finalRotation = headingRotation * Quaternion.Inverse(Quaternion.Euler(currentRotation.eulerAngles.x, 0f, currentRotation.eulerAngles.z)); // 3. 提取最终航向角 trueBearing = finalRotation.eulerAngles.y; } else { // 无陀螺仪时直接使用罗盘真航向(仅设备平放时准确) trueBearing = compassTrueHeading; } // 归一化航向值到0-360度范围 trueBearing = (trueBearing + 360) % 360; orientationText.text = $"当前真航向: {Mathf.Round(trueBearing)}°"; } // 将陀螺仪右手坐标系转换为Unity左手坐标系 private Quaternion ConvertGyroToUnity(Quaternion gyroAttitude) { return new Quaternion(gyroAttitude.x, gyroAttitude.y, -gyroAttitude.z, -gyroAttitude.w); } void OnDestroy() { Input.compass.enabled = false; if (SystemInfo.supportsGyroscope) { Input.gyro.enabled = false; } Input.location.Stop(); } public float GetTrueBearing() { return trueBearing; } }
关键修正点说明
- 坐标系转换:
ConvertGyroToUnity方法修正了陀螺仪与Unity坐标系的差异,确保姿态数据匹配。 - 平滑融合:通过
Quaternion.Lerp处理陀螺仪数据,避免姿态突变导致的航向抖动。 - 正确姿态融合逻辑:先基于真航向创建水平旋转,再抵消设备俯仰和滚转的影响,得到设备当前朝向对应的真航向,彻底解决倾斜状态下的航向偏差问题。
内容的提问来源于stack exchange,提问作者André Clérigo
相关产品推荐
相关产品推荐

