我正在研究一个项目,该项目需要从连接到编码器的串行端口(带有QSB的美国数字S5光轴编码器)读取的每个数据条目的精确时间(ms)。
我将编码器安装在一个小推车上,我用它来计算推车的速度。
这是我到目前为止所做的:
连接到串口并向QSB写入命令以告知编码器流数据。这里有命令:
www.usdigital.com/assets/general/QSB%20Commands%20List_1.pdf www.usdigital.com/assets/general/QSB%20Applications%20Examples.pdf
使用readline()读取收到的数据。
当我将输出值阈值和间隔速率设置为尽可能快时,我能够在1ms之间获得数据条目。 这是我得到的:
----time stamp(h/m/s/ms)-------value
具有正确时间戳的数据:https://www.dropbox.com/s/pvo1dz56my4o99y/Capture1.JPG
但是,当数据连续时(我正在以恒定的速度滚动推车),会出现突然的“跳跃”,大约200ms
时间戳错误的数据: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:
问题解决了。请参阅下面的答案。谢谢你的回复。秒表确实有帮助。现在我想弄清楚事件驱动或轮询是否更好。
答案 0 :(得分:3)
在对网络进行了一些无休止的研究之后,我找到了延迟的原因。 设备管理器---&gt;端口----&gt;提前----&gt;将延迟改为1ms将解决问题。 我现在使用单独的线程轮询数据。它工作得很好。
答案 1 :(得分:1)
您使用的是C#4.5吗?如果是这样,我强烈建议您使用async
/ await
而不是BackgroundWorker
。
此外,DateTime
对于实时应用程序并不准确。我建议DateTime
严格地作为开始时间然后使用Stopwatch
中的System.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 ms 的差异,可能是各种各样的事情。也许BackgroundWorker
的优先级较低,并且没有达到您希望的CPU时间。也可能是SerialPort
的 I / O 侧或实际的串行设备本身。
答案 2 :(得分:0)
当您需要精确测量时,不应使用DateTime.Now,请尝试使用秒表。 如详细here和here,DateTime准确但不精确到毫秒。如果您需要精确的和精度,请在开始测量时保存DateTime.Now并从秒表获取偏移量。
虽然200ms似乎是一个很长的延迟 - 即使对于DateTime--秒表可能确实解决了你的问题。
答案 3 :(得分:0)
对我而言,操作系统似乎是[以你的方式]。
我建议如下。
在单独的进程(或服务)或优先级高于正常的单独线程中从端口读取数据
将原始(!)数据存储在具有准确时间戳的队列中,以便以后处理。这个“任务”应该尽可能轻,以避免GC或调度程序启动并在最短的时间内停止它。例如,没有字符串concats或格式。那些操作花费时间并且会对记忆施加压力。
在单独的线程或进程中处理该数据。如果时间戳保持一段时间没有真正受到伤害,因为时间戳是准确的。
总之;将阅读与处理分离。
Windows的Imo库存版本过多地受IO限制(它喜欢并拥抱交换的概念)适用于RT流程。在差异框上使用差异操作系统,读取并将其发送到Winbox进行进一步处理也许也是一个考虑的选择(最后的手段?)