6作目で実用的な細折れ線のリアルタイムグラフですが、5作目は点プロットだったのですが
6作目はLINEプロットなので、少し面倒な扱いになってますがデバッグ終わったらアップします。
VB.NETのプログラミング練習も5作目となって、受信データをリアルタイム
グラフ表示するプログラムに取り組みました。結構苦戦して、3日x6時間=18時間まるまるかかりました。リアルタイム速度限界を測定した動画をアップしてあります。(Lenovo ThinkPadX230 Core i5 3320M RAM6GB)
グラフィック画面サイズ:600×240です。面積に反比例して遅くなります。
PCのタスク状態で10%近く速度ばらつきが発生します。
※未だ、LatencyTimerを短くしてないときの実験値ですので後日
グラフ仕様が完成したところでTOTALスループット実験します。
■マイコンからのシリアルボーレート毎のリアルタイム速度
11520bpsの場合:6CH7msec周期までリアルタイム対応できます。
460800bpsの場合:6CH5msec周期までリアルタイム対応できます。
921600bpsの場合:6CH5msec周期なのでプログラムの限界となってます。
■必須項目:Win10の設定
USBシリアルFTDIドライバのLatencyTimer機能が働いていて、64byte以下の少量データの連続転送時に遅延が発生するようになってるそうです。
詳細はこちらのページです。https://www.hdl.co.jp/USB/FTDI/lt/
この設定をBM設定といいます。これが、デフォルトだと16msecとなってるとバッファ遅延でリアルタイムになりませんので、BM=1msecに設定しなおせば、リアルタイムになります。
●変更方法:
デバイスマネージャ>ポート(COMxx)>Property>ポートの設定タブ
>詳細設定>BMオプション待ち時間を1msecにします。
QIITAのこちらの記事に感謝です。
●当初はCHARTオブジェクトでグラフを作っていた
2日がかりでCHARTを使って、作ってみたのですが、
FASTLINEを使っても、6CHだとスクロール速度が遅くて
とてもリアルタイム表示ができませんでした。
●QIITAでプロの方がグラフの速度について投稿がありました。
https://qiita.com/twentyfourhours/items/c31922ffe94649cd3a00
CHARTオブジェクトだとFASTLINEで1周期30msecだそうで、とても、マイコンからの数msec周期のデータをリアルタイムに表示できませんので、グラフィックとして、データをプロットする方式でないとリアルタイム速度がでないと説明されています。この記事に感謝いたします。
■VB.NETでの高速描画の解説記事で新しいものが検索ではない=>たった一つDOBON.NET様にあった
VB.NETに限らず、グラフ描画の解説記事は、非常に少ないし
2012年から2015年くらいのものが多く、当時のバージョンのプログラムが現在のVS2019では動作しないので、初心者としては困ります。
その中で、何とか使える過去記事を見つけました。
有名なDOBON.NET様の記事にありました。感謝です。
https://dobon.net/vb/dotnet/graphics/createimage.html
描画順方式:PictureBOXのimageとしてBITMAPオブジェクトを用意してBITMAPに描画して、imageにBITMAPを書き込むことで、毎回更新していく描画方式です。
この記事の一番下のサンプルプログラムを参考にさせていただきました。
「PictureBox.Imageプロパティに設定している画像を描き変えた時、すぐに表示に反映する」
TIPS:
初回だけPictureBoxとBITMAPの定義を通して、次回からはパスさせることが重要です。これをしないと過去プロットが全部消えて今のプロットしかでてきません。PictureBox1.imageという名称で扱うことがポイントです。
別の名称にすると名前定義がされてないとエラーになってしまいました。
'Imports System.Drawing 'PictureBox.ImageプログラミングにImageオブジェクトを設定する If PictureBox1.Image Is Nothing Then PictureBox1.Image = New Bitmap(100, 50) End If 'ImageオブジェクトのGraphicsオブジェクトを作成する Dim g As Graphics = Graphics.FromImage(PictureBox1.Image) '全体を白で塗りつぶす g.FillRectangle(Brushes.White, g.VisibleClipBounds) '現在の時刻を描画する g.DrawString(DateTime.Now.ToLongTimeString(), _ SystemFonts.DefaultFont, Brushes.Black, 10, 10) 'Graphicsオブジェクトのリソースを解放する g.Dispose() 'Imageプロパティの変更を反映させるために、PictureBox1を再描画する PictureBox1.Invalidate() |
■MSDocsの解説記事リンク
imports System.Drawing を使って描画します。
Graphics クラス
ここで、私がプロットに使っているのは
Graphics. Fill Ellipse メソッド
6本分の色をbrush型変数=Brushes.色名で変更してます。
Brushes クラス
■Plotのメソッド関数plotC()のソースは下記です。
データ表示のメソッドPrintdata()メソッドから毎回呼び出されて動作します。
Sub plotC(ByVal dataNo As Integer, ByVal value As Integer, ByVal colorN As Integer) If PictureBox1.Image Is Nothing Then ‘初回だけBITMAPを定義する Picture1.imageという名称をつかうこと PictureBox1.Image = New Bitmap(600, 240) End If Dim g As Graphics = Graphics.FromImage(PictureBox1.Image) Dim px As Integer Dim py As Integer’======================COLORS========================================= Dim b As BrushSelect Case colorN Case 1 b = Brushes.Red Case 2 b = Brushes.Blue Case 3 b = Brushes.Green Case 4 b = Brushes.Magenta Case 5 b = Brushes.Orange Case 6 b = Brushes.BlackEnd Select’===========-PLOT パラメータの定義 手計算で処理======================== ‘Dim yh As Integer = 240 ‘Dim xw As Integer = 600 ‘Dim Ymax As Integer = 7000 ‘Dim Ymin As Integer = 0 ‘Dim Xmax As Integer = 600 ‘Dim Xmin As Integer = 0 ‘Dim dstep As Double ‘Dim dvalue As Double ‘————-ドット変換値は手計算する——- ‘dstep = xw / (Xmax – Xmin) ‘=600/(600-0)=1 ‘dvalue = yh / (Ymax – Ymin) ‘=240/(7000)=0.0342857’=================================================================== If dataNo Mod 600 = 0 Then ‘1画面終了したらimageクリアtp = sw.Elapsed totalsec = tp.TotalSeconds ‘Debug.Print(“tp.sec=” & totalsec) sw.Stop() If totalsec > 0.6 Then TextBox11.Text = CStr(totalsec) End Ifsw.Reset() sw.Start()PictureBox1.Image = Nothing Elsepx = Int(1 * dataNo Mod 600) py = Int(0.0342857 * value) ‘Debug.Print(“px=” & CStr(px) & “py=” & CStr(py)) g.FillEllipse(b, px, py, 3, 3) g.Dispose() PictureBox1.Invalidate()End IfEnd Sub |
マイコン側のテスト用プログラムは6個のデータを値の大小を6水準ずらしてSINカーブで波形データを作って送信してます。mbed NucleoF446ZEを使って、高速なデータ通信まで対応してます。最後にタイムスタンプをつけて7個のデータを送信してます。
//mbed プログラム
#include “mbed.h” } |
■6CHリアルタイムプロット サンプルププログラム
①Form1は、ご自分で作成してください。
ボタン3個とTEXTBOX12個とPictureBox1を1個つくるだけです。
SerialPortは、マイコンによりますが、COM10 115200です。
PictureBox1のPropertyでsizeは、600×240に設定してください。
②GISTにコードアップしてあります。
https://gist.github.com/dj1711572002/f196dce54c1d80cc931dbd7b351c9a43
‘Seril Port Recieve Sample Program ‘Form1 Object must create ,button1,2 & textbox 1,2Imports System.Drawing Imports System.IO.PortsPublic Class Form1 Dim onetime As Integer = 1 Delegate Sub DataDelegate(ByVal sdata As String) ‘=========Available Parameters in thie Calss================= Dim dataAry(600000, 7) As Long Dim dataNo As Long Dim Mave() As Double Dim sw As New System.Diagnostics.Stopwatch() Dim tp As TimeSpan Dim timestamp As Integer Dim stime As Integer ‘plot start sampling time Dim etime As Integer ‘plot end sampling time Dim totalsec As Double ‘============================================================= Private Sub PrintData(ByVal sdata As String)Dim delimiter As String = “,” Dim i As Integer Dim dcount As String Timer1.Interval = 1 ‘タイマー開始 Timer1.Enabled = True ‘TextBox1.text=sdata dcount = sdata & “:” & TextBox2.Text ‘ListBox1.Items.Add(dcount) ‘Debug.Print(“sdata=” & sdata) ‘Debug.Print(“timestamp=” & CStr(timestamp)) Dim dStr() As String = Split(sdata, delimiter, -1, CompareMethod.Text) Dim colN As Integer colN = 7 ReDim Mave(colN) If (dStr.Length = 7) Then dataNo += 1 For i = 0 To dStr.Length – 1 dataAry(dataNo, i) = CInt(dStr(i)) timestamp = dataAry(dataNo, 6) ‘=================PLOT SUBへ================= If i < 6 Then plotC(dataNo, dataAry(dataNo, i), i + 1) ‘plotC(x,y,colorN) End If ‘=========================================== ” TextBox2.Text = dataNo TextBox3.Text = dStr.Length TextBox4.Text = dStr(0) TextBox5.Text = dStr(1) TextBox6.Text = dStr(2) TextBox7.Text = dStr(3) TextBox8.Text = dStr(4) TextBox9.Text = dStr(5) TextBox10.Text = dStr(6) ‘Debug.Print(“dataAry(” & CStr(dataNo) & “,” & CStr(i) & “)=” & CStr(dataAry(dataNo, i))) ‘If dataNo > 100 Then ‘ ‘Fuction MoveAve(dataArray(,) as long ,ColumnNo,MA,rowNo) ‘ Mave = MovAve(dataAry, 7, 10, dataNo) ‘End If ‘For i = 0 To colN – 1 ‘ Debug.Print(“Mave(” & CStr(i) & “)=” & CStr(Mave(i))) ‘Next Next End IfEnd Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click SerialPort1.PortName = TextBox1.Text ‘オープンするポート名を格納 SerialPort1.Open() ‘ポートオープンEnd SubPrivate Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click If SerialPort1.IsOpen = True Then ‘ポートオープン済み SerialPort1.Close() ‘ポートクローズ End If End SubPrivate Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived Dim ReceivedData As String = ” ” ‘受信データ用変数を宣言しますTryReceivedData = SerialPort1.ReadLine ‘データを受信しますCatch ex As Exception ReceivedData = ex.Message ‘例外処理を行いますEnd Try ‘Invokeメソッドにより実行されるメソッドへのデリゲートの宣言を行い、受信データを表示します Dim adre As New DataDelegate(AddressOf PrintData) Me.Invoke(adre, ReceivedData)End SubFunction MovAve(ByRef dA(,) As Long, ByVal colN As Integer, ByVal MA As Integer, ByVal rowN As Integer) As Double() Dim i, j As Integer Dim dataSum() As Long Dim dSum As Long Dim dataA() As Double ‘= {0, 0, 0, 0, 0, 0, 0} ‘Debug.Print(“====colN=” & CStr(colN) & “MA=” & CStr(MA) & “rowN=” & CStr(rowN)) ReDim dataSum(colN + 1) ReDim dataA(colN + 1)For j = 0 To colN – 1 dSum = 0 For i = rowN – MA To rowN dSum = dSum + dA(i, j) ‘Debug.Print(“dA(” & CStr(i) & “,” & CStr(j) & “)=” & CStr(dA(i, j)) & CStr(dataSum(j))) Next dataA(j) = CDbl(dSum / MA) Next MovAve = dataA End FunctionPrivate Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height) ‘ImageオブジェクトのGraphicsオブジェクトを作成する Dim g As Graphics = Graphics.FromImage(canvas)’位置(10, 20)に100×80の四角を赤色で描く g.DrawRectangle(Pens.Red, 10, 20, 100, 80) ‘先に描いた四角に内接する楕円を黒で描く g.FillEllipse(Brushes.Black, 100, 100, 5, 5)’リソースを解放する g.Dispose()’PictureBox1に表示する PictureBox1.Image = canvas End SubSub plotC(ByVal dataNo As Integer, ByVal value As Integer, ByVal colorN As Integer) If PictureBox1.Image Is Nothing Then ‘初回だけBITMAPを定義する Picture1.imageという名称をつかうこと PictureBox1.Image = New Bitmap(600, 240) End If Dim g As Graphics = Graphics.FromImage(PictureBox1.Image) Dim px As Integer Dim py As Integer’======================COLORS========================================= Dim b As BrushSelect Case colorN Case 1 b = Brushes.Red Case 2 b = Brushes.Blue Case 3 b = Brushes.Green Case 4 b = Brushes.Magenta Case 5 b = Brushes.Orange Case 6 b = Brushes.BlackEnd Select’===========-PLOT パラメータの定義 手計算で処理======================== ‘Dim yh As Integer = 240 ‘Dim xw As Integer = 600 ‘Dim Ymax As Integer = 7000 ‘Dim Ymin As Integer = 0 ‘Dim Xmax As Integer = 600 ‘Dim Xmin As Integer = 0 ‘Dim dstep As Double ‘Dim dvalue As Double ‘————-ドット変換値は手計算する——- ‘dstep = xw / (Xmax – Xmin) ‘=600/(600-0)=1 ‘dvalue = yh / (Ymax – Ymin) ‘=240/(7000)=0.0342857’=================================================================== If dataNo Mod 600 = 0 Then ‘1画面終了したらimageクリアtp = sw.Elapsed totalsec = tp.TotalSeconds ‘Debug.Print(“tp.sec=” & totalsec) sw.Stop() If totalsec > 0.6 Then TextBox11.Text = CStr(totalsec) End Ifsw.Reset() sw.Start() PictureBox1.Image = Nothing px = Int(1 * dataNo Mod 600) End If End Sub End Class |