滚动平均值,vb.net
Rolling average, vb.net
我有一个传感器(实际上是 Quartzonix 压力传感器)通过串口输出数据,大约每秒 3 次。我想设置一些代码,根据 x 样本量给我一个平均读数。
输出看起来像这样:
01+ 1.502347091823e01
01+ 1.501987234092e01
01+ 1.50234524524e01
01+ 1.502123412341e01
01+ 1.502236234523e01
01+ 1.50198345e01
01+ 1.502346234523e01
.. 并将一直持续下去,直到 com 端口关闭或传感器收到另一个命令。
这是我目前拥有的代码,该代码向我展示了传感器实际输出的内容:
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Dim a As String
a = "MC" & Chr(13)
MyComPort.WriteLine(a)
Do
Dim Incoming As String = MyComPort.ReadLine()
Dim incomingtext As String = Incoming.Remove(0, 3)
If Incoming Is Nothing Then
Exit Do
Else
txtRawData.Text = Incoming
boxPSIA.Text = Format(Val(incomingtext), "##0.000")
End If
Application.DoEvents()
Loop
End Sub
“$01MC”是传感器开始吐出数据所需的命令。当我点击开始按钮时,我发生了一些奇怪的超时事件,但那是另一个节目(可能需要调整 .readtimeout,不确定)。
我有一个文本框 txtReadingsToAvg,用于输入平均读数的数量。我只是没有思考如何真正让它计算平均值(比如,单击按钮然后随地吐痰它输出到一个消息框,甚至另一个文本框)。
不确定您的代码是如何工作的。你说你得到值 @ 大约 3 赫兹?那么直线上升 Do...Loop
就太快了。当串行端口接收到数据时会引发一个事件。好好利用它。
您可能需要稍微更改一下以满足您的需要
' WithEvents allows events to be handled with "Handles" keyword
Private WithEvents myComPort As SerialPort
Private dataQueue As Queue(Of Double)
Private numReadingsToAvg As Integer = 0
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
' make a new queue here to initialize or clear an old queue
dataQueue = New Queue(Of Double)()
' read the num avgs text box. you may want to change on the fly also
numReadingsToAvg = Integer.Parse(Me.txtReadingsToAvg.Text)
myComPort.WriteLine("MC" & Chr(13))
End Sub
Private Sub myComPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles myComPort.DataReceived
Dim incomingLine As String = myComPort.ReadLine()
' DataReceived event happens on its own thread, not the UI
' must invoke call back to UI to change properties of controls
txtRawData.Invoke(Sub() txtRawData.Text = incomingLine)
Dim incomingValue As String = incomingLine.Remove(0, 3).Trim()
If Not String.IsNullOrWhiteSpace(incomingValue) Then
Exit Sub
Else
Dim measurement As Double = Double.Parse(incomingValue)
boxPSIA.Invoke(Sub() boxPSIA.Text = Format(measurement, "##0.000"))
dataQueue.Enqueue(measurement)
' if there are too many items, remove the last one
If dataQueue.Count > numReadingsToAvg Then
dataQueue.Dequeue()
End If
Dim average As Double = dataQueue.Average()
' you need to add this textbox
anotherTextBox.Invoke(Sub() anotherTextBox.Text = Format(average, "##0.000"))
End If
End Sub
顺便说一句,Application.DoEvents() should rarely (never) be used, as there's always a better way to remedy whatever problem you are bandaging with DoEvents。您的原始示例在 UI 上因永无止境的循环 运行ning 阻塞了 UI 线程。如果你需要 运行 这样的循环,它应该几乎总是 运行 在与 UI 线程不同的线程上。在我的例子中,没有环路,时间由端口自己决定。无需在 UI 线程上发生任何此类情况。
我有一个传感器(实际上是 Quartzonix 压力传感器)通过串口输出数据,大约每秒 3 次。我想设置一些代码,根据 x 样本量给我一个平均读数。
输出看起来像这样:
01+ 1.502347091823e01
01+ 1.501987234092e01
01+ 1.50234524524e01
01+ 1.502123412341e01
01+ 1.502236234523e01
01+ 1.50198345e01
01+ 1.502346234523e01
.. 并将一直持续下去,直到 com 端口关闭或传感器收到另一个命令。
这是我目前拥有的代码,该代码向我展示了传感器实际输出的内容:
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Dim a As String
a = "MC" & Chr(13)
MyComPort.WriteLine(a)
Do
Dim Incoming As String = MyComPort.ReadLine()
Dim incomingtext As String = Incoming.Remove(0, 3)
If Incoming Is Nothing Then
Exit Do
Else
txtRawData.Text = Incoming
boxPSIA.Text = Format(Val(incomingtext), "##0.000")
End If
Application.DoEvents()
Loop
End Sub
“$01MC”是传感器开始吐出数据所需的命令。当我点击开始按钮时,我发生了一些奇怪的超时事件,但那是另一个节目(可能需要调整 .readtimeout,不确定)。
我有一个文本框 txtReadingsToAvg,用于输入平均读数的数量。我只是没有思考如何真正让它计算平均值(比如,单击按钮然后随地吐痰它输出到一个消息框,甚至另一个文本框)。
不确定您的代码是如何工作的。你说你得到值 @ 大约 3 赫兹?那么直线上升 Do...Loop
就太快了。当串行端口接收到数据时会引发一个事件。好好利用它。
您可能需要稍微更改一下以满足您的需要
' WithEvents allows events to be handled with "Handles" keyword
Private WithEvents myComPort As SerialPort
Private dataQueue As Queue(Of Double)
Private numReadingsToAvg As Integer = 0
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
' make a new queue here to initialize or clear an old queue
dataQueue = New Queue(Of Double)()
' read the num avgs text box. you may want to change on the fly also
numReadingsToAvg = Integer.Parse(Me.txtReadingsToAvg.Text)
myComPort.WriteLine("MC" & Chr(13))
End Sub
Private Sub myComPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles myComPort.DataReceived
Dim incomingLine As String = myComPort.ReadLine()
' DataReceived event happens on its own thread, not the UI
' must invoke call back to UI to change properties of controls
txtRawData.Invoke(Sub() txtRawData.Text = incomingLine)
Dim incomingValue As String = incomingLine.Remove(0, 3).Trim()
If Not String.IsNullOrWhiteSpace(incomingValue) Then
Exit Sub
Else
Dim measurement As Double = Double.Parse(incomingValue)
boxPSIA.Invoke(Sub() boxPSIA.Text = Format(measurement, "##0.000"))
dataQueue.Enqueue(measurement)
' if there are too many items, remove the last one
If dataQueue.Count > numReadingsToAvg Then
dataQueue.Dequeue()
End If
Dim average As Double = dataQueue.Average()
' you need to add this textbox
anotherTextBox.Invoke(Sub() anotherTextBox.Text = Format(average, "##0.000"))
End If
End Sub
顺便说一句,Application.DoEvents() should rarely (never) be used, as there's always a better way to remedy whatever problem you are bandaging with DoEvents。您的原始示例在 UI 上因永无止境的循环 运行ning 阻塞了 UI 线程。如果你需要 运行 这样的循环,它应该几乎总是 运行 在与 UI 线程不同的线程上。在我的例子中,没有环路,时间由端口自己决定。无需在 UI 线程上发生任何此类情况。