如何以60Hz的频率精确采样数据? [英] How to precisely sample data with frequency of 60Hz?

查看:167
本文介绍了如何以60Hz的频率精确采样数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

实际上,我使用InvokeRepeating方法每1/x秒调用另一个方法.问题在于,调用和获得的数据之间的延迟精度不佳.

Actually, I use the InvokeRepeating method to invoke another method every 1/x seconds. The problem is that the precision of the delay between the invoke and the data I got is not good.

我如何精确地以60Hz的频率采样transform.position.

How I can precisely sample transform.position with a frequency of 60Hz.

这是我的代码:

public class Recorder : MonoBehaviour {

public float samplingRate = 60f; // sample rate in Hz
public string outputFilePath;

private StreamWriter _sw;

private List<Data> dataList = new List<Data>();

public void OnEnable()
{

    InvokeRepeating("SampleNow", 0, 1 / samplingRate);
}

public void OnDisable()
{
    _sw = System.IO.File.AppendText(outputFilePath);

    for (int k=0; k< dataList.Count; k++)
    {
        _sw.WriteLine("t {0} x {1} y {2} z {3}",
           dataList[k].time, dataList[k].x, dataList[k].y, dataList[k].z);
    }

    _sw.Close();
    CancelInvoke();
}

public void SampleNow()
{
    dataList.Add(new Data(Time.time, transform.position.x, transform.position.y, transform.position.z));
}

public class Data
{
    public float time;
    public float x;
    public float y;
    public float z;

    public Data(float time, float x, float y, float z)
    {
        this.time = time;
        this.x = x;
        this.y = y;
        this.z = z;
    }
  }
}

因此,我可以得到这种结果:

So, with this, I can get this kind of result:

t 0 x 0 y 0 z 0
t 0.02 x 0.283776 y -0.76 z 0
t 0.04 x 0.599808 y -0.52 z 0
t 0.06 x 0.599808 y -0.52 z 0
t 0.08 x 0.599808 y -0.52 z 0
t 0.09999999 x 0.599808 y -0.52 z 0
t 0.12 x 0.599808 y -0.52 z 0
t 0.14 x 0.599808 y -0.52 z 0
t 0.16 x 0.599808 y -0.52 z 0
t 0.18 x 0.599808 y -0.52 z 0
t 0.2 x 0.599808 y -0.52 z 0
t 0.22 x 0.599808 y -0.52 z 0
t 0.24 x 0.599808 y -0.52 z 0
t 0.26 x 0.599808 y -0.52 z 0
t 0.28 x 0.599808 y -0.52 z 0
t 0.3 x 0.599808 y -0.52 z 0
t 0.32 x 0.599808 y -0.52 z 0
t 0.3338465 x 0.599808 y -0.52 z 0
t 0.3338465 x 0.2918357 y -0.7538424 z 0
t 0.34 x 0.2918357 y -0.7538424 z 0
t 0.3539519 x 0.2918357 y -0.7538424 z 0
t 0.3539519 x 0.6092016 y -0.5125771 z 0
t 0.3705226 x 0.6092016 y -0.5125771 z 0
t 0.3870888 x 0.8340279 y -0.3137283 z 0
t 0.4036556 x 0.9750986 y -0.114934 z 0
t 0.42 x 0.9865224 y 0.09031145 z 0
...

如您在此结果中看到的,我可以收集不同时间(t = 0.04到t = 0.32)的重复位置,最糟糕的是,我可以获得不同位置(给定无限加速度)的重复时间(t = 0.3539519)

As you can see in this result, I can collect duplicate position for different time (t=0.04 to t=0.32) and worst, I can get duplicate time (t=0.3539519) with different positions (giving an infinite acceleration).

实际上,分析的游戏对象随时间呈线性函数移动,在X-Y轴上绕20秒做一个圆.

In fact, the gameobject analysed moved with a linear fonction in time, doing a circle in the axis X-Y over 20sec.

因此,此代码无法为我进行科学分析提供很好的精度.

So, this code doesn't give me a good precision for a scientific analyze.

如何使用Unity3D获得更高的精度?

What can I do to get more precision with Unity3D?

推荐答案

这很复杂.虽然,我不会说在Unity中要做 impossbile .可以精确完成,但不能使用InvokeRepeating.

This is complicated. Although, I wouldn't say that it is impossbile to do in Unity. It can be done precisely but not with InvokeRepeating.

您还有另外两种方法可以做到这一点.

1 . Coroutine

1.Coroutine

如果InvokeRepeating太慢,可能是因为它使用反射来调用该函数,或者实现不完美.

If InvokeRepeating is too slow, maybe that's because it uses reflection to call the function or maybe the implementation is not perfect.

您可以通过使用协程进行直接函数调用并等待 WaitForSecondsRealtime 代替 WaitForSeconds . WaitForSecondsRealtime 在Unity中是新功能,不依赖于等待的帧速率. WaitForSeconds 可以.

You may be able to accomplish this by doing a direct function call with a coroutine and waiting with WaitForSecondsRealtime instead of WaitForSeconds. WaitForSecondsRealtime is new in Unity and does not depend on the frame-rate to wait. WaitForSeconds does.

public float samplingRate = 60f; // sample rate in Hz

void OnEnable()
{
    StartCoroutine(startSampling());
}

IEnumerator startSampling()
{
    WaitForSecondsRealtime waitTime = new WaitForSecondsRealtime(1f / samplingRate);
    while (true)
    {
        SampleNow();
        yield return waitTime;
    }
}

public void SampleNow()
{
    Debug.Log("Called");
    dataList.Add(new Data(Time.time, transform.position.x, transform.position.y, transform.position.z));
}

2 . Thread (推荐)

2.Thread (Recommended)

这,我建议您使用,因为您将完全避免Unity主线程降低任何帧速率.在另一个Thread中进行计时器操作.

This, I recommended you should use as you will totally avoid any framerate slow down by Unity's main Thread. Do the timer operation in another Thread.

与此有关的问题是,您无法在另一个线程中使用transform.position.xTransform类. Unity阻止您这样做.您将必须在Main Thread中执行dataList.Add(new Data(Time.time, transform.position.x....另一种选择是将transform.position存储在全局Vector3变量中,然后从另一个Thread中访问它.

The problem with this is that you won't be able to use transform.position.x or the Transform class in another Thread. Unity prevents you from doing so. You will have to execute dataList.Add(new Data(Time.time, transform.position.x... in the Main Thread. Another option is to store transform.position in a global Vector3 variable then acceess it from another Thread.

不能在另一个Thread中也使用Time.time,并且必须将其保存在主Thread中,然后将其存储在将在功能

You cannot also use Time.time in another Thread and will have to get it in the Main Thread, then store it in a local variable that will be used in a Thread function

您还必须使用lock关键字将其设置为线程安全.请注意,使用lock关键字会使您的游戏变慢一点.它会从您的游戏中至少删除2或3帧,但这是必需的,值得它提供的好处和保护.

You will also have to use the lock keyword to make it Thread-safe. Note that using the lock keyword will slow down your Game a little bit. It will remove at-least 2 or 3 frames from your game, but it is required and worth the benefit and protection it provides.

下面的代码将完成我刚刚说的所有事情.出于测试目的,采样率设置为每秒2次.当您认为它可以正常工作时,可以在samplingRate变量中将其更改为60Hz.

The code below will do everything I just said. The sampling rate is set to 2 times per-sec for testing purposes. You can change chage that to your 60Hz in the samplingRate variable, when you think it is working propery.

private List<Data> dataList = new List<Data>();

Thread samplingThread;
const float samplingRate = 2f; // sample rate in Hz

Vector3 posInThread;
float TimetimeInThread;

private System.Object threadLocker = new System.Object();


void OnEnable()
{
    startSampling();
}

void startSampling()
{
    samplingThread = new Thread(OnSamplingData);
    samplingThread.Start();
}

void Update()
{
    lock (threadLocker)
    {
        //Update this values to be used in another Thread
        posInThread = transform.position;
        TimetimeInThread = Time.time;
    }
}

void OnSamplingData()
{
    long oldTime = DateTime.Now.Ticks;
    long currentTime = oldTime;
    const float waitTime = 1f / samplingRate;
    float counter;

    while (true)
    {
        currentTime = DateTime.Now.Ticks;
        counter = (float)TimeSpan.FromTicks(currentTime - oldTime).TotalSeconds;

        if (counter >= waitTime)
        {
            //Debug.Log("counter: " + counter + "  waitTime: " + waitTime);
            oldTime = currentTime; //Store current Time
            SampleNow();
        }
        Thread.Sleep(0); //Make sure Unity does not freeze
    }
}

public void SampleNow()
{
    Debug.Log("Called");
    lock (threadLocker)
    {
        dataList.Add(new Data(TimetimeInThread, posInThread.x, posInThread.y, posInThread.z));
    }
}

void OnDisable()
{
    if (samplingThread != null && samplingThread.IsAlive)
    {
        samplingThread.Abort();
    }
    samplingThread.Join(); //Wait for Thread to finish then exit
}

这篇关于如何以60Hz的频率精确采样数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆