【问题标题】:Process Serial data at a constant frequency以恒定频率处理串行数据
【发布时间】:2012-11-24 17:34:01
【问题描述】:

大家好,我正在用 C# 编写一个应用程序,它从加速度计(在 wiimote 内部)获取数据,然后对其进行处理和绘制图形。 我正在使用Brian Peek's Wiimote library,所以我使用事件处理程序来获取数据:

void wiimote_WiimoteChanged(object sender, WiimoteChangedEventArgs e)
{
    X = e.WiimoteState.AccelState.Values.X;
    Y = e.WiimoteState.AccelState.Values.Y;
    Z = e.WiimoteState.AccelState.Values.Z;          

}

我想以 100Hz 的速率绘制/保存/处理数据,所以我创建了一个计时器对象 (WiiTimer),将其“Tick Interval”设置为 10ms,然后在每个滴答声中,存储数据的计时器/已处理:

private void WiiTimer_Tick(object sender, EventArgs e)
{

   //Signal Processing (Some median and low pass filtering etc.) Ive removed the code for that since it is irrelevant here

    sw.WriteLine((X * 9.8).ToString() + ", " + (Y * 9.8).ToString() + ", " + (Z*9.8).ToString() );
  //Write Processed Data to file            

}

现在的问题是,数据实际上并没有以 100Hz 的频率保存,因为信号处理需要一些时间,因此计时器无法每 10 毫秒调用一次 OnTick 事件处理程序。因此数据存储速率取决于您在机器上运行代码的速度、您运行的其他程序数量等。

我该如何解决这个问题?我想过在单独的线程上运行计时器,但这会导致其他问题,正如有人在此处的另一个问题上指出的那样: “您的计时器事件可能想要从其他线程访问对象。但这样做会导致竞争条件。”

有什么想法吗?

【问题讨论】:

  • 知道信号处理需要多长时间吗?信号处理本身会花费 >= 10 毫秒吗?
  • @Aleksey 我怀疑这正是问题所在,Timer OnTick 事件处理程序处理数据,将其显示在 Windows 窗体中的图表上,然后将其保存到文件中,所以我很确定超过 10 毫秒
  • 我只对处理感兴趣,而不是保存到文件。与保存到文件一样,您可以将多个结果分组并每 N 次刷新到 IO。
  • 编辑:我的想法是,我能做的只是使用 WiiTimer 来保存数据,然后使用另一个计时器(可能具有更大的滴答时间)进行信号处理/写入归档大块数据,这是正确的方法吗?
  • 您可以尝试将来自 WiiMote 的传入数据存储在队列中,然后使用您的计时器来处理队列吗?

标签: c# signal-processing wiimote


【解决方案1】:

计时器并不是特别精确,因此如果您实际上想要每秒 100 个样本,最好的方法是这样的:

private class Result
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}

private Result lastResult;

void wiimote_WiimoteChanged(object sender, WiimoteChangedEventArgs e)
{
    Result newResult = new Result {
        X = e.WiimoteState.AccelState.Values.X,
        Y = e.WiimoteState.AccelState.Values.Y,
        Z = e.WiimoteState.AccelState.Values.Z,
    } 

    lastResult = newResult;
}

void MainLoop()
{
    DateTime nextResultTime = DateTime.Now.AddMilliseconds(10);

    while(true)
    {
        if (DateTime.Now >= nextResultTime)
        {
            AddResult(lastResult);
            nextResultTime = nextResultTime.AddMilliseconds(10);
        }
        else
        {
            Thread.Sleep(1);
        }
    }
}

只需在后台线程上运行 MainLoop(如果 wii 事件在后台线程上触发,则可能不需要这样做)。

这将使您每秒准确地获得 100 个样本,除非机器无法处理这么快的 AddResult。在这种情况下,我认为您必须减轻 AddResult 内部的负载并在捕获数据后进行一些后处理 - 机器足够快以实时执行此操作,或者它不是。

【讨论】:

  • 抱歉,修复了计时码 - 您应该将 10 毫秒添加到最后一个结果时间,而不是循环内的当前时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-18
  • 1970-01-01
相关资源
最近更新 更多