0

我正在做一个项目,该项目需要我从连接到编码器(带有 QSB 的美国数字 S5 光轴编码器)的串行端口读取的每个数据条目的精确时间(毫秒)。

我将编码器安装在一辆小推车上,用它来计算推车的速度。

这是我到目前为止所做的:

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

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

  2. 使用 readline() 读取接收到的数据。

  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但仍然有 200 毫秒的延迟。但是当我一起打印时间戳和 BytesToRead 时,我发现缓冲区中的数据随着 readLine() 的执行而减少。最终 BytesToRead 将下降到个位数,这就是延迟发生的地方。我正在寻找有关如何实现线程的更好解决方案。以及延迟的解释。也许我读得很快,所以缓冲区跟不上我?

EDIT:

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

4

4 回答 4

3

在网上进行了一番无休止的研究后,我找到了延迟的原因。设备管理器--->端口---->高级---->将延迟更改为1毫秒即可解决问题。我现在正在使用单独的线程轮询数据。它工作得很好。

于 2013-01-15T01:54:03.120 回答
1

您使用的是 C# 4.5 吗?如果是这样,我强烈建议使用async/ awaitover BackgroundWorker

此外,DateTime对于实时应用程序来说也不是很准确。我建议DateTime严格作为开始时间,然后使用StopwatchinSystem.Diagnostics来获取自开始时间以来经过的时间。

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 时间。也可能是任何一个或实际串行设备本身的I/O端的东西。SerialPort

于 2013-01-13T23:49:29.880 回答
0

当您想要精确测量时,您不应该使用 DateTime。现在,请尝试使用秒表。如此处此处详述,DateTime 是准确的,但不精确到毫秒。如果您需要精度准确度,请在开始测量并从秒表获取偏移量时保存 DateTime.Now。

虽然 200 毫秒似乎是一个很长的延迟 - 即使对于 DateTime 也是如此 - 秒表确实可以解决您的问题。

于 2013-01-13T23:37:26.910 回答
0

对我来说,操作系统似乎 [in your way]。

我建议以下。

  1. 在单独的进程(或服务)或优先级高于正常的单独线程中从端口读取数据

  2. 将原始(!)数据存储在具有准确时间戳的队列中,以供以后处理。这个“任务”应该尽可能轻,以避免 GC 或调度程序启动并停止它,即使是最少量的时间。例如,没有字符串连接或格式。这些操作会耗费时间并给内存带来压力。

  3. 在单独的线程或进程中处理该数据。如果该时间保持一段时间,则不会造成真正的伤害,因为时间戳是准确的。

简而言之; 将读取与处理分离。

Imo 库存版本的 Windows 太多 IO 绑定(它喜欢并拥抱交换的概念),不适合 RT 进程。在 diff 框上使用 diff OS 来读取并将其发送到 Winbox 进行进一步处理可能也是一个考虑的选项(最后的手段?)

于 2013-01-14T02:01:22.823 回答