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

Unity+Azure Kinect开发:衣物与用户运动方向相反问题求助

问题:Azure Kinect骨骼追踪映射Unity衣物模型时方向相反

我在开发Unity项目,使用Azure Kinect SDK追踪用户肢体动作并映射到衣物模型,但衣物运动方向与用户实际方向完全相反。尝试过调整位置计算逻辑、添加旋转偏移,问题仍未解决。当前通过Spine Chest和Pelvis关节数据更新上下衣物的位置与旋转,相关代码如下:

using Microsoft.Azure.Kinect.Sensor;
using Microsoft.Azure.Kinect.BodyTracking;
using UnityEngine;
using UnityEngine.UI;

public class KinectManager2 : MonoBehaviour
{
    public GameObject topClothingObject;
    public GameObject bottomClothingObject;
    private Device kinectDevice;
    private Tracker bodyTracker;
    private Texture2D colorTexture;
    public RawImage backgroundImage;

private Vector3 initialPelvisPosition;
private Vector3 initialSpineChestPosition;

void Start()
{
    InitializeKinect();
    InitializeBodyTracker();
}

void Update()
{
    if (kinectDevice != null)
    {
        using (Capture capture = kinectDevice.GetCapture())
        {
            if (capture != null)
            {
                if (capture.Color != null)
                {
                    ProcessColorFrame(capture.Color);
                }
                if (bodyTracker != null)
                {
                    ProcessBodyFrame(capture);
                }
            }
        }
    }
}

void OnDestroy()
{
    if (bodyTracker != null)
    {
        bodyTracker.Dispose();
    }
    if (kinectDevice != null)
    {
        kinectDevice.StopCameras();
        kinectDevice.Dispose();
    }
}

private void InitializeKinect()
{
    kinectDevice = Device.Open();
    if (kinectDevice != null)
    {
        kinectDevice.StartCameras(new DeviceConfiguration
        {
            ColorFormat = ImageFormat.ColorBGRA32,
            ColorResolution = ColorResolution.R1080p,
            DepthMode = DepthMode.WFOV_Unbinned,
            CameraFPS = FPS.FPS15,
            SynchronizedImagesOnly = true
        });
        colorTexture = new Texture2D(1920, 1080, TextureFormat.BGRA32, false);
    }
    else
    {
        Debug.LogError("Failed to open Kinect device.");
    }
}

private void InitializeBodyTracker()
{
    if (kinectDevice != null)
    {
        bodyTracker = Tracker.Create(kinectDevice.GetCalibration(), new TrackerConfiguration
        {
            ProcessingMode = TrackerProcessingMode.Gpu,
            SensorOrientation = SensorOrientation.Default
        });
        if (bodyTracker == null)
        {
            Debug.LogError("Failed to create body tracker.");
        }
    }
}

private void ProcessColorFrame(Microsoft.Azure.Kinect.Sensor.Image colorImage)
{
    if (colorImage.Format == ImageFormat.ColorBGRA32)
    {
        int width = colorImage.WidthPixels;
        int height = colorImage.HeightPixels;
        byte[] colorData = new byte[width * height * 4];
        colorImage.Memory.CopyTo(colorData);

        byte[] flippedColorData = new byte[width * height * 4];
        for (int y = 0; y < height; y++)
        {
            int srcIndex = y * width * 4;
            int dstIndex = (height - 1 - y) * width * 4;
            System.Buffer.BlockCopy(colorData, srcIndex, flippedColorData, dstIndex, width * 4);
        }

        colorTexture.LoadRawTextureData(flippedColorData);
        colorTexture.Apply();
        backgroundImage.texture = colorTexture;
    }
    else
    {
        Debug.LogError("Unsupported color image format: " + colorImage.Format);
    }
}

private void ProcessBodyFrame(Capture capture)
{
    bodyTracker.EnqueueCapture(capture);

    using (Frame frame = bodyTracker.PopResult())
    {
        if (frame != null && frame.NumberOfBodies > 0)
        {
            Body body = frame.GetBody(0);
            UpdateClothing(body);

            long latency = (long)(frame.DeviceTimestamp.TotalMilliseconds - capture.Depth.DeviceTimestamp.TotalMilliseconds);
            Debug.Log($"Latency: {latency} ms");
        }
    }
}

private void UpdateClothing(Body body)
{
    if (topClothingObject != null && bottomClothingObject != null)
    {
        Vector3 pelvisPosition = GetVector3FromJoint(body.Skeleton.GetJoint(JointId.Pelvis));
        Vector3 spineChestPosition = GetVector3FromJoint(body.Skeleton.GetJoint(JointId.SpineChest));

        float distanceToCamera = pelvisPosition.z;
        float scaleFactor = Mathf.Clamp(1f / (distanceToCamera * 0.01f), 0.5f, 2f);

        Vector3 cameraPosition = new Vector3(0f, 0f, 0f); // Assuming the Kinect camera is at the origin

        Vector3 topClothingPosition = spineChestPosition - cameraPosition;
        topClothingPosition.y += 0.2f;
        topClothingObject.transform.position = topClothingPosition;

        Vector3 bottomClothingPosition = pelvisPosition - cameraPosition;
        bottomClothingPosition.y -= 0.1f;
        bottomClothingObject.transform.position = bottomClothingPosition;

        Quaternion spineChestRotation = GetQuaternionFromJoint(body.Skeleton.GetJoint(JointId.SpineChest));
        Quaternion topRotationOffset = Quaternion.Euler(90f, 90f, 0f);
        Quaternion topFinalRotation = spineChestRotation * topRotationOffset;
        topClothingObject.transform.rotation = topFinalRotation;

        Quaternion pelvisRotation = GetQuaternionFromJoint(body.Skeleton.GetJoint(JointId.Pelvis));
        Quaternion bottomRotationOffset = Quaternion.Euler(0f, 120f, 10f);
        Quaternion bottomFinalRotation = pelvisRotation * bottomRotationOffset;
        bottomClothingObject.transform.rotation = bottomFinalRotation;

        topClothingObject.transform.localScale = Vector3.one * scaleFactor;
        bottomClothingObject.transform.localScale = Vector3.one * scaleFactor;
    }
    else
    {
        Debug.LogWarning("Clothing objects are null. Please assign FBX objects to the topClothingObject and bottomClothingObject variables.");
    }
}

private Quaternion GetQuaternionFromJoint(Microsoft.Azure.Kinect.BodyTracking.Joint joint)
{
    return new Quaternion(joint.Quaternion.X, joint.Quaternion.Y, joint.Quaternion.Z, joint.Quaternion.W);
}

private Vector3 GetVector3FromJoint(Microsoft.Azure.Kinect.BodyTracking.Joint joint)
{
    float scaleFactor = 0.001f;
    Vector3 jointPosition = new Vector3(joint.Position.X * scaleFactor, joint.Position.Y * scaleFactor, joint.Position.Z * scaleFactor);
    return jointPosition;
}
}

我也曾尝试基于Kinect相机位置计算相对位置,但问题仍未解决,希望获得修复方案让衣物与用户运动方向一致。


修复方案

1. 修正坐标系转换(核心问题)

Azure Kinect与Unity的坐标系Z轴方向相反:

  • Kinect:Z轴朝向用户(向前)
  • Unity:Z轴远离相机(向后)

修改GetVector3FromJoint方法,反转Z轴:

private Vector3 GetVector3FromJoint(Microsoft.Azure.Kinect.BodyTracking.Joint joint)
{
    float scaleFactor = 0.001f;
    // 反转Z轴匹配Unity坐标系
    Vector3 jointPosition = new Vector3(joint.Position.X * scaleFactor, joint.Position.Y * scaleFactor, -joint.Position.Z * scaleFactor);
    return jointPosition;
}

2. 调整旋转顺序与偏移角度

Unity旋转是偏移旋转在前,关节旋转在后的乘法顺序,同时修正偏移方向:

private void UpdateClothing(Body body)
{
    // ... 原有位置计算代码 ...

    // 上衣旋转:先应用偏移,再叠加关节旋转
    Quaternion spineChestRotation = GetQuaternionFromJoint(body.Skeleton.GetJoint(JointId.SpineChest));
    Quaternion topRotationOffset = Quaternion.Euler(90f, -90f, 0f); // 修正Y轴偏移方向
    Quaternion topFinalRotation = topRotationOffset * spineChestRotation;
    topClothingObject.transform.rotation = topFinalRotation;

    // 下装旋转:同理调整偏移方向
    Quaternion pelvisRotation = GetQuaternionFromJoint(body.Skeleton.GetJoint(JointId.Pelvis));
    Quaternion bottomRotationOffset = Quaternion.Euler(0f, -60f, 10f); // 反转Y轴偏移角度
    Quaternion bottomFinalRotation = bottomRotationOffset * pelvisRotation;
    bottomClothingObject.transform.rotation = bottomFinalRotation;

    // ... 原有缩放代码 ...
}

3. 简化位置计算

转换坐标系后,无需再减去相机原点(Kinect已对齐Unity原点):

Vector3 topClothingPosition = spineChestPosition;
topClothingPosition.y += 0.2f;
topClothingObject.transform.position = topClothingPosition;

Vector3 bottomClothingPosition = pelvisPosition;
bottomClothingPosition.y -= 0.1f;
bottomClothingObject.transform.position = bottomClothingPosition;

4. 重置模型初始旋转

检查衣物FBX模型的Inspector面板,将Rotation重置为(0,0,0),避免模型自身初始偏移影响映射效果。


测试步骤

  1. 在Scene视图中绘制关节Gizmo,确认转换后的关节位置与用户实际位置一致;
  2. 暂时移除旋转偏移,直接使用关节原始旋转,观察方向是否正确;
  3. 逐步添加旋转偏移,微调角度至完全匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:41:00