2013-01-13 23 views
0

我正在从一个连接到编码器的串行端口(美国数字S5光学轴编码器和QSB)读取每个数据条目时需要精确时间(ms)的项目。串行端口数据精确时间戳

我把编码器安装在一个小推车上,我用它来计算购物车的速度。

这里是我做过什么至今:

  1. 命令连接到串行端口,并写入QSB告诉编码器流数据。这里可用的命令:

    www.usdigital.com/assets/general/QSB%20Commands%20List_1.pdf www.usdigital.com/assets/general/QSB%20Applications%20Examples.pdf

  2. 使用输入行()来读取收到的数据。

  3. 将所有数据行放入一个StringBuilder并输出到一个文件。

当我将输出值阈值和间隔速率设置为尽可能快时,我能够在1ms之间获得数据条目。 这里是我的了:

----time stamp(h/m/s/ms)-------value 

与正确的时间标记数据:https://www.dropbox.com/s/pvo1dz56my4o99y/Capture1.JPG

不过,也有突然的“跳跃”,大约200毫秒时的数据是连续的(我滚在恒定速度车)

数据不正确的时间戳:https://www.dropbox.com/s/sz3sxwv4qwsb2cn/Capture2.JPG

这里是我的代码:

private void buttonOpenEncoderPort_Click(object sender, EventArgs e) 
    { 
     serialPortEncoder.Write("S0E\r\n");//start streaming data 
     System.Threading.Thread.Sleep(500); 
     serialPortEncoder.Write("W0B0\r\n");//set threshold to 0 so the encoder will stream data a the interval I set. 
     System.Threading.Thread.Sleep(500); 
     serialPortEncoder.Write("W0C0000\r\n");//set output interval to 0 so it will stream as fast as possible 
     System.Threading.Thread.Sleep(1500); 
     backgroundWorkerEncoder.RunWorkerAsync();} 
     //I am using a background worker to pull data out. 


private void backgroundWorkerEncoder_DoWork(object sender, DoWorkEventArgs e) 
    { 
     while (serialPortEncoder.IsOpen) 
     { 
      if (serialPortEncoder.BytesToRead != 0) 
      { 
       try 
       { 
        String s = serialPortEncoder.ReadLine();//read from encoder 
        LazerBucket.Add(getCurrentTimeWithMS(timeEncoder) + "-----" + s + "\r\n");//put one line of data with time stamp in a List<String> 
        richTextBoxEncoderData.BeginInvoke(new MethodInvoker(delegate() 
        { 
         richTextBoxEncoderData.Text = s; })); //update UI 

       } 
       catch (Exception ex) { MessageBox.Show(ex.ToString()); }     
      } 

     } 
    } 

private String getCurrentTimeWithMS(DateTime d)//to get time 
    { 
     StringBuilder s = new StringBuilder(); 
     d = DateTime.Now; 
     int hour = d.Hour; 
     int minute = d.Minute; 
     int second = d.Second; 
     int ms = d.Millisecond; 
     s.Append(" ----" + hour.ToString() + ":" + minute.ToString() + ":" + second.ToString() + ":" + ms.ToString()); 
     return s.ToString(); 
    } 

如果有人能找到时间跳跃的原因,我会认真对待它。 200ms是太多不容忽视的。

EDIT: 

至于建议,我试图Stopwatch但还是有200ms的延迟。但是,当我将时间戳和BytesToRead打印在一起时,我发现缓冲区中的数据随着readLine()的执行而减少。最终BytesToRead将下降到单个数字,这就是延迟发生的地方。我正在寻找更好的解决方案来实现线程。还有延迟的解释。也许我读得很快,所以缓冲区跟不上我?

EDIT: 

问题解决了。请参阅下面的答案。感谢您的回复。秒表确实有帮助。现在我正在试图确定事件驱动还是轮询更好。

+1

当您不将它作为后台任务运行时会发生什么?这可以排除主板引起的混乱现象 – lboshuizen

+0

通过串口使用正交解码器是非常非常可疑的。当你在垃圾回收器顶部的线程池调度算法之上包含Windows线程调度延迟时,它肯定不会变好。你的系统设计简直不合适。 –

+0

@lboshuizen感谢您的回复。如果我使用数据接收事件来收集数据,那么我在一秒钟内获得的数据就显着不足。 – Timtianyang

回答

1

您正在使用C#4.5?如果是这样,我强烈推荐使用async/await而不是BackgroundWorker

另外,DateTime对于实时应用程序不是很准确。我将严格推荐DateTimeSystem.Diagnostics使用Stopwatch得到经过时间开始时间一个开始时间然后。

private void backgroundWorkerEncoder_DoWork(object sender, DoWorkEventArgs e) 
{ 
    var startTime = DateTime.Now; 
    var stopwatch = Stopwatch.StartNew(); 

    while (serialPort.IsOpen && !backgroundWorker.CancellationPending) 
    { 
    if (serialPort.BytesToRead > 0) 
    { 
     try 
     { 
     var line = serialPort.ReadLine(); 
     var timestamp = (startTime + stopwatch.Elapsed); 

     var lineString = string.Format("{0} ----{1}", 
             line, 
             timestamp.ToString("HH:mm:ss:fff")); 

     // Handle formatted line string here. 
     } 
     catch (Exception ex) 
     { 
     // Handle exception here. 
     } 
    } 
    } 

对于200毫秒差异,这可能是很多方面的事情。也许BackgroundWorker处于较低优先级,并且不会达到您希望的那么多的CPU时间。也可能是SerialPort或实际串行设备本身的I/O一侧。

+0

谢谢。我尝试过秒表,但它仍然存在差异。我会给异步/等待一试。 – Timtianyang

+0

我会愿意打赌,'BackgroundWorker'的某些内容并没有完全用于实时处理。我将使用'async' /'await'发布第二个答案。你在使用VS 2012吗? – Erik

+0

我明白这一点。我正在使用VS 2010. – Timtianyang

0

当你想要精确的测量时,你不应该使用DateTime.Now,而是尝试秒表。 详细的herehere,日期时间是准确的,但不精确到毫秒。如果您需要精确度准确度,请在开始测量并获取秒表的偏移量时保存DateTime.Now。

虽然200毫秒似乎是一个很长的延迟 - 即使是对于日期时间 - 秒表可能确实可以解决您的问题。

+0

谢谢。在我的情况下,秒表确实比DateTime更好。但即使使用秒表,仍有一些延迟,从100ms到200ms不等。 – Timtianyang

0

对我来说,似乎操作系统是[你的方式。

我建议如下。

  1. 阅读来自该端口的数据或者在一个单独的PROCES(或服务)或优先级的单独的线程高于正常

  2. 存储与精确时间戳原始(!)中的数据队列稍后处理。这个“任务”应尽可能轻以避免GC或调度程序在最短的时间内进入并停止。例如,没有字符串连接或格式。这些操作会花费时间并且会给记忆带来压力。

  3. 过程,在一个单独的线程或进程的数据。如果那一个得到了一段时间,那么就不会有真正的伤害,因为时间戳是准确的。

总之;将读数从处理中分离出来。的Windows

伊莫股票的版本太多IO绑定(它爱和拥抱交换的概念)成为RT进程。使用DIFF OS,在一个diff盒,用于读取并将其发送到永保进行进一步的处理或许是考虑过一个选项(最后一招?)

+0

听起来不错。在另一个线程中处理数据是一个好的通话,但由于我是新来的线程,我会在后面碰到那个危险而又美丽的东西 – Timtianyang

+0

@Timtianyang危险?您已经在使用更高级别的API。关键的事情总是带来挑战,这是学习“游泳”的绝佳机会:-) – lboshuizen

3

后在网络上的一些无尽的研究,我发现原因的延迟。 设备管理器---> Port ----> advance ---->将延迟改为1ms将解决问题。 我现在使用一个单独的线程轮询数据。它工作得很好。