Kinect导致Unity3D中的帧速率限制

本文关键字:帧速率 导致 Unity3D Kinect | 更新日期: 2023-09-27 17:57:49

我目前正在开发一个Unity3D项目,该项目同时使用Oculus Rift和Kinect。然而,Kinect将帧速率限制在30帧/秒,Rift需要60帧/秒才能获得流畅的体验。我用的是官方的,这绝对不是性能问题。

我将此包装与官方Kinect SDK一起使用。

我已经把原因缩小到下面这段代码。我怀疑getSkeleton()函数会阻塞主线程,直到它从Kinect接收到数据为止。由于Kinect只能以每秒30帧的速度运行,因此应用程序的其他部分不能运行得更快。

    public bool pollSkeleton () {
    if (!updatedSkeleton)
    {
        updatedSkeleton = true; 
        if (kinect.pollSkeleton())
        {
            newSkeleton = true;
            System.Int64 cur = kinect.getSkeleton().liTimeStamp;
            System.Int64 diff = cur - ticks;
            ticks = cur;
            deltaTime = diff / (float)1000;
            processSkeleton();
        }
    }
    return newSkeleton;
}

也许我可以运行一个单独的线程,但由于我没有多线程编程的经验,我想知道是否有人有更简单的解决方案?

我的猜测是,很多Oculus Rift开发人员将使用Kinect+Unity3D组合,因此会遇到和我一样的限制。任何帮助都将不胜感激!

编辑:我创建了一个单独的线程来从kinect中轮询骨架信息:这是修改后的SkeletonWrapper.cs的完整代码。希望它能帮助你们中的一些人解决同样的问题。

using UnityEngine;
using System.Collections;
using Kinect;
using System.Threading;
public class SkeletonWrapper : MonoBehaviour {
public DeviceOrEmulator devOrEmu;
private Kinect.KinectInterface kinect;
private bool updatedSkeleton = false;
private bool newSkeleton = false;
[HideInInspector]
public Kinect.NuiSkeletonTrackingState[] players;
[HideInInspector]
public int[] trackedPlayers;
[HideInInspector]
public Vector3[,] bonePos;
[HideInInspector]
public Vector3[,] rawBonePos;
[HideInInspector]
public Vector3[,] boneVel;
[HideInInspector]
public Quaternion[,] boneLocalOrientation;
[HideInInspector]
public Quaternion[,] boneAbsoluteOrientation;
public Kinect.NuiSkeletonPositionTrackingState[,] boneState;    
private System.Int64 ticks;
private float deltaTime;
private Matrix4x4 kinectToWorld;
public Matrix4x4 flipMatrix;
private Thread thread = null;
private bool isThreadRunning = false;
// Use this for initialization
void Start () {
    kinect = devOrEmu.getKinect();
    players = new Kinect.NuiSkeletonTrackingState[Kinect.Constants.NuiSkeletonCount];
    trackedPlayers = new int[Kinect.Constants.NuiSkeletonMaxTracked];
    trackedPlayers[0] = -1;
    trackedPlayers[1] = -1;
    bonePos = new Vector3[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
    rawBonePos = new Vector3[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
    boneVel = new Vector3[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
    boneState = new Kinect.NuiSkeletonPositionTrackingState[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
    boneLocalOrientation = new Quaternion[2, (int)Kinect.NuiSkeletonPositionIndex.Count];
    boneAbsoluteOrientation = new Quaternion[2, (int)Kinect.NuiSkeletonPositionIndex.Count];
    //create the transform matrix that converts from kinect-space to world-space
    Matrix4x4 trans = new Matrix4x4();
    trans.SetTRS( new Vector3(-kinect.getKinectCenter().x,
                              kinect.getSensorHeight()-kinect.getKinectCenter().y,
                              -kinect.getKinectCenter().z),
                 Quaternion.identity, Vector3.one );
    Matrix4x4 rot = new Matrix4x4();
    Quaternion quat = new Quaternion();
    double theta = Mathf.Atan((kinect.getLookAt().y+kinect.getKinectCenter().y-kinect.getSensorHeight()) / (kinect.getLookAt().z + kinect.getKinectCenter().z));
    float kinectAngle = (float)(theta * (180 / Mathf.PI));
    quat.eulerAngles = new Vector3(-kinectAngle, 0, 0);
    rot.SetTRS( Vector3.zero, quat, Vector3.one);
    //final transform matrix offsets the rotation of the kinect, then translates to a new center
    kinectToWorld = flipMatrix*trans*rot;
    thread = new Thread(ThreadUpdate);
    thread.Start(); 
}
void OnDestroy()
{
    if (isThreadRunning)
    {
        isThreadRunning = false;
        thread.Abort();
        thread = null;
    }
}
// Update is called once per frame
void Update () {
}
void LateUpdate () {
    updatedSkeleton = false;
    newSkeleton = false;
}
private void ThreadUpdate()
{
    isThreadRunning = true;
    while (isThreadRunning)
    {
        // This function is capping the FPS to 30. 
        if (kinect.pollSkeleton())
        {
            System.Int64 cur = kinect.getSkeleton().liTimeStamp;
            System.Int64 diff = cur - ticks;
            ticks = cur;
            deltaTime = diff / (float)1000;
            processSkeleton();
            newSkeleton = true;
        }
    }
}
/// <summary>
/// First call per frame checks if there is a new skeleton frame and updates,
/// returns true if there is new data
/// Subsequent calls do nothing have the same return as the first call.
/// </summary>
/// <returns>
/// A <see cref="System.Boolean"/>
/// </returns>
public bool pollSkeleton () {
    //if (!updatedSkeleton)
    //{
    //    updatedSkeleton = true;
    //    //this function is capping the FPS to 30. 
    //    //It might be blocking the main thread because it waits for the kinects skeleton input which only runs 30 fps
    //    //possible solution: run function in seperate thread
    //    if (kinect.pollSkeleton())
    //    {
    //        newSkeleton = true;
    //        System.Int64 cur = kinect.getSkeleton().liTimeStamp;
    //        System.Int64 diff = cur - ticks;
    //        ticks = cur;
    //        deltaTime = diff / (float)1000;
    //        processSkeleton();
    //    }
    //}
    return newSkeleton;
}
private void processSkeleton () {
    int[] tracked = new int[Kinect.Constants.NuiSkeletonMaxTracked];
    tracked[0] = -1;
    tracked[1] = -1;
    int trackedCount = 0;
    //update players
    for (int ii = 0; ii < Kinect.Constants.NuiSkeletonCount; ii++)
    {
        players[ii] = kinect.getSkeleton().SkeletonData[ii].eTrackingState;
        if (players[ii] == Kinect.NuiSkeletonTrackingState.SkeletonTracked)
        {
            tracked[trackedCount] = ii;
            trackedCount++;
        }
    }
    //this should really use trackingID instead of index, but for now this is fine
    switch (trackedCount)
    {
    case 0:
        trackedPlayers[0] = -1;
        trackedPlayers[1] = -1;
        break;
    case 1:
        //last frame there were no players: assign new player to p1
        if (trackedPlayers[0] < 0 && trackedPlayers[1] < 0)
            trackedPlayers[0] = tracked[0];
        //last frame there was one player, keep that player in the same spot
        else if (trackedPlayers[0] < 0) 
            trackedPlayers[1] = tracked[0];
        else if (trackedPlayers[1] < 0)
            trackedPlayers[0] = tracked[0];
        //there were two players, keep the one with the same index (if possible)
        else
        {
            if (tracked[0] == trackedPlayers[0])
                trackedPlayers[1] = -1;
            else if (tracked[0] == trackedPlayers[1])
                trackedPlayers[0] = -1;
            else
            {
                trackedPlayers[0] = tracked[0];
                trackedPlayers[1] = -1;
            }
        }
        break;
    case 2:
        //last frame there were no players: assign new players to p1 and p2
        if (trackedPlayers[0] < 0 && trackedPlayers[1] < 0)
        {
            trackedPlayers[0] = tracked[0];
            trackedPlayers[1] = tracked[1];
        }
        //last frame there was one player, keep that player in the same spot
        else if (trackedPlayers[0] < 0)
        {
            if (trackedPlayers[1] == tracked[0])
                trackedPlayers[0] = tracked[1];
            else{
                trackedPlayers[0] = tracked[0];
                trackedPlayers[1] = tracked[1];
            }
        }
        else if (trackedPlayers[1] < 0)
        {
            if (trackedPlayers[0] == tracked[1])
                trackedPlayers[1] = tracked[0];
            else{
                trackedPlayers[0] = tracked[0];
                trackedPlayers[1] = tracked[1];
            }
        }
        //there were two players, keep the one with the same index (if possible)
        else
        {
            if (trackedPlayers[0] == tracked[1] || trackedPlayers[1] == tracked[0])
            {
                trackedPlayers[0] = tracked[1];
                trackedPlayers[1] = tracked[0];
            }
            else
            {
                trackedPlayers[0] = tracked[0];
                trackedPlayers[1] = tracked[1];
            }
        }
        break;
    }
    //update the bone positions, velocities, and tracking states)
    for (int player = 0; player < 2; player++)
    {
        //print(player + ", " +trackedPlayers[player]);
        if (trackedPlayers[player] >= 0)
        {
            for (int bone = 0; bone < (int)Kinect.NuiSkeletonPositionIndex.Count; bone++)
            {
                Vector3 oldpos = bonePos[player,bone];
                bonePos[player,bone] = kinectToWorld.MultiplyPoint3x4(kinect.getSkeleton().SkeletonData[trackedPlayers[player]].SkeletonPositions[bone]);
                //bonePos[player,bone] = kinectToWorld.MultiplyPoint3x4(bonePos[player, bone]);
                rawBonePos[player, bone] = kinect.getSkeleton().SkeletonData[trackedPlayers[player]].SkeletonPositions[bone];

                Kinect.NuiSkeletonBoneOrientation[] or = kinect.getBoneOrientations(kinect.getSkeleton().SkeletonData[trackedPlayers[player]]);
                boneLocalOrientation[player,bone] = or[bone].hierarchicalRotation.rotationQuaternion.GetQuaternion();
                boneAbsoluteOrientation[player,bone] = or[bone].absoluteRotation.rotationQuaternion.GetQuaternion();
                //print("index " + bone + ", start" + (int)or[bone].startJoint + ", end" + (int)or[bone].endJoint);
                boneVel[player,bone] = (bonePos[player,bone] - oldpos) / deltaTime;
                boneState[player,bone] = kinect.getSkeleton().SkeletonData[trackedPlayers[player]].eSkeletonPositionTrackingState[bone];
                //print(kinect.getSkeleton().SkeletonData[player].Position.z);
            }
        }
    }
}

}

Kinect导致Unity3D中的帧速率限制

我对Kinect SDK的工作原理不太了解。它可能已经在管理一个正在进行骨架跟踪的后台线程,而pollSkeleton方法只是简单地阻止,直到下一帧可用。

这似乎是一个合理的假设,因为SDK同时支持轮询和基于事件的通知,这意味着如果你不轮询,其他东西将触发下一帧的获取并向你发送事件。

如果是这种情况,那么您可以通过简单地扫描线程中可用的骨架数据上的时间戳来解决问题。。。

System.Int64 lastSkeletonTime = 0;
public bool pollSkeleton () 
{
  if (kinect.getSkeleton().liTimeStamp > lastSkeletonTime) {
    updatedSkeleton = true; 
    newSkeleton = true;
    System.Int64 cur = kinect.getSkeleton().liTimeStamp;
    System.Int64 diff = cur - lastSkeletonTime;
    deltaTime = diff / (float)1000;
    lastSkeletonTime = cur;
    processSkeleton();
  }
  return newSkeleton;
}

如果这不起作用,那么很可能您需要启动一个后台线程,或者切换到处理事件。