【问题标题】:Rolling average, vb.net滚动平均值,vb.net
【发布时间】:2016-10-17 12:15:20
【问题描述】:

我有一个传感器(实际上是 Quartzonix 压力传感器)通过串行端口输出数据,大约每秒 3 次。我想设置一些代码,以根据样本数量为我提供平均读数。

输出看起来像这样:

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 = "$01MC" & 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 用于输入平均读数的数量。我只是不知道如何实际得到它来计算平均值(例如,单击按钮,然后将其吐出)到一个 msgbox,甚至到另一个文本框)。

【问题讨论】:

  • 对于 n 个读数,队列或移位寄存器 n 个元素长怎么样。每次获得新读数时,将其推入队列,最旧的读数会下降然后结束。然后平均队列。
  • 您想要的是程序开始后的平均值还是传感器开始记录后的平均值?
  • 还是最后n秒/m个样本的平均值?
  • @peterG 这可能行得通.. 但是如何编码呢?​​
  • 如果您将过去的值存储在列表中,那么avg = Readings.Average() 将为您提供当前集合的平均值。对于移动平均线,将结果存储在不同的列表中或使用.Take() 即时计算。很难说你在追求什么

标签: vb.net average transducer


【解决方案1】:

甚至不确定您的代码是如何工作的。你说你得到大约 3 Hz 的值?然后直接向上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("$01MC" & 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 上运行永无止境的循环而阻塞了 UI 线程。如果您需要运行这样的循环,它应该几乎总是在与 UI 线程不同的线程上运行。在我的示例中,没有循环,时间由端口本身决定。无需在 UI 线程上发生任何这些。

【讨论】:

  • 这似乎不起作用.. boxPSIA 和 anotherTextBox 没有更新。
  • 你需要调试它。在Exit Sub 上放一个断点,看看incomingLine 等于什么
  • 好吧,如果我在 if/then 语句上方添加 boxPSIA.Text = Format(Val(incomingValue), "##0.000"),boxpsia 会随着原始数据文本更新(但在正确的格式)..
  • 奇怪的是,如果我将 dataqueue.enqueue(1) 放在 form_load 事件上,事情就开始运作良好。稍微调整了一下,一切正常。感谢您的帮助!
  • 太棒了!再看一遍,您应该将Private numReadingsToAvg As Integer = 0 设置为0 以外的值。这可能是If dataQueue.Count > numReadingsToAvg Then dataQueue.Dequeue() 在队列中没有点时尝试出列的问题。您的解决方法解决了这个问题......但无论如何您都需要将numReadingsToAvg 更改为现实的东西 - 它是您原始问题中的“x-amount of samples”。
猜你喜欢
  • 1970-01-01
  • 2021-05-08
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
相关资源
最近更新 更多