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

HoloLens 2非初始空间环境下摄像头到Unity空间转换失败

HoloLens 2跨空间移动后摄像头-Unity空间变换矩阵获取失败解决方案

问题背景

在Unity+MRTK+OpenXR的HoloLens 2项目中,需通过Windows.Media.Capture获取摄像头帧对应的Unity空间位姿(摄像头空间到Unity场景空间的变换矩阵),实现逻辑如下:

  • 通过MR OpenXR插件获取代表Unity空间的Windows.Perception.Spatial.SpatialCoordinateSystem实例
  • 从MediaFrameReader的MediaFrameReference中提取摄像头空间的SpatialCoordinateSystem
  • 调用SpatialCoordinateSystem.TryGetTransformTo()获取变换矩阵

但出现以下异常场景:

  • 初始空间启动应用,矩阵获取正常
  • 设备待机后移动到无空间关联的新房间,唤醒后camToUnitySysMatrix.HasValue返回false(两个坐标系实例均不为空)
  • 返回初始空间后,矩阵获取恢复正常

已尝试措施:切换环境后重新获取unityReferenceCoordinateSystem、重新实例化MediaCapture和MediaFrameReader,问题依旧

问题根源

HoloLens 2的空间坐标系依赖设备构建的空间锚点地图,当移动到完全无关联的新空间时,设备会构建新的独立空间地图。此时初始的Unity参考坐标系绑定的是旧空间的锚点,和新空间中摄像头的坐标系属于两个完全独立的空间体系,没有共同的空间锚点关联,因此TryGetTransformTo()无法计算出有效变换矩阵。

解决方案

1. 监听空间状态变化,动态更新Unity参考坐标系

利用OpenXR的空间事件监听空间地图的重置/重建状态,在检测到进入新空间时,重新获取绑定当前活跃空间的Unity参考坐标系:

private void Start()
{
    // 注册空间重置事件
    UnityEngine.XR.OpenXR.WindowsMR.WindowsMRSpatialCoordinateSystemManager.Instance.OnCoordinateSystemChanged += OnCoordinateSystemChanged;
}

private void OnCoordinateSystemChanged(Windows.Perception.Spatial.SpatialCoordinateSystem newCoordSys)
{
    // 更新Unity参考坐标系
    unityReferenceCoordinateSystem = newCoordSys;
    // 同步重新初始化MediaCapture与MediaFrameReader
    ReinitializeMediaCapture();
}

2. 等待空间感知初始化完成后再执行绑定

设备唤醒或空间切换后,不要立即获取坐标系,需等待设备完成当前空间的感知初始化:

private async void ReinitializeMediaCapture()
{
    // 等待空间感知模块初始化完成
    await UnityEngine.XR.OpenXR.Features.MixedReality.SpatialAwarenessFeature.WaitForInitializationAsync();
    
    // 重新初始化MediaCapture
    var initSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings
    {
        StreamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.Video,
        PhotoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.VideoPreview
    };
    var mediaCapture = new Windows.Media.Capture.MediaCapture();
    await mediaCapture.InitializeAsync(initSettings);
    
    // 重新创建MediaFrameReader
    var frameSourceGroup = await Windows.Media.Capture.Frames.MediaFrameSourceGroup.FromIdAsync(selectedGroup.Id);
    var frameReader = await mediaCapture.CreateFrameReaderAsync(frameSourceGroup, Windows.Media.MediaProperties.MediaEncodingSubtypes.Bgra8);
    frameReader.FrameArrived += FrameReader_FrameArrived;
    await frameReader.StartAsync();
}

3. 每次计算前获取最新坐标系

避免缓存SpatialCoordinateSystem实例,每次需要计算变换矩阵时,都重新获取最新的Unity参考坐标系:

private void FrameReader_FrameArrived(Windows.Media.Capture.Frames.MediaFrameReader sender, Windows.Media.Capture.Frames.MediaFrameArrivedEventArgs args)
{
    using (var frameReference = sender.TryAcquireLatestFrame())
    {
        if (frameReference == null) return;
        
        var camCoordSys = frameReference.CoordinateSystem;
        if (camCoordSys == null) return;
        
        // 获取当前活跃的Unity参考坐标系
        var currentUnityCoordSys = UnityEngine.XR.OpenXR.WindowsMR.WindowsMRSpatialCoordinateSystemManager.Instance.CurrentCoordinateSystem;
        if (currentUnityCoordSys == null) return;
        
        var transform = camCoordSys.TryGetTransformTo(currentUnityCoordSys);
        if (transform.HasValue)
        {
            // 转换为Unity兼容的矩阵
            var unityMatrix = new UnityEngine.Matrix4x4(
                (float)transform.Value.M11, (float)transform.Value.M12, (float)transform.Value.M13, (float)transform.Value.M14,
                (float)transform.Value.M21, (float)transform.Value.M22, (float)transform.Value.M23, (float)transform.Value.M24,
                (float)transform.Value.M31, (float)transform.Value.M32, (float)transform.Value.M33, (float)transform.Value.M34,
                (float)transform.Value.M41, (float)transform.Value.M42, (float)transform.Value.M43, (float)transform.Value.M44
            );
            // 执行后续位姿逻辑
        }
        else
        {
            // 触发坐标系重新初始化
            Debug.LogWarning("变换矩阵获取失败,尝试重新绑定空间坐标系");
            OnCoordinateSystemChanged(currentUnityCoordSys);
        }
    }
}

关键注意点

  • 不要长期缓存SpatialCoordinateSystem实例,HoloLens空间切换后会更新活跃坐标系,旧实例会失效
  • 空间感知初始化需要时间,必须异步等待就绪后再执行绑定操作
  • 当TryGetTransformTo()失败时,主动触发坐标系更新与MediaCapture重建,避免持续失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 13:50:17