Pythonを使用してArduinoで動作するセンサーからのリアルタイム情報を処理する

問題の声明



Arduinoに接続されたデジタルおよびアナログセンサーは、リアルタイム処理を必要とする大量の情報を生成します[1]。



現在、Arduinoからのデータはコマンドラインから出力されるか、GUIに遅れて表示されます。 そのため、リアルタイムのデータは保存されないため、さらに分析することはできません。



この出版物は、Arduinoで動作するセンサーからの情報とそのグラフィカルな表現をリアルタイムで保存する問題に対するソフトウェアソリューションに専念しています。 これらの例は、ポテンショメーターやPIRモーションセンサーなどのよく知られたセンサーで使用されています。



CSVファイルを使用して、Arduinoで動作するセンサーから受信したデータを保存する





例として2つのセンサーを使用してArduinoデータを保存することを検討してください-デジタル出力信号を使用したアナログ出力信号とモーション(PIR)を備えたポテンショメーター。



次の図に示すように、ポテンショメーターはアナログ出力0に接続され、PIRモーションセンサーはデジタル出力11に接続されます。









この回路を機能させるには、pyFirmataモジュールとStandardFirmataスケッチをPythonのArduinoボードにロードする必要があります。



任意のPythonファイルに、両方のセンサーからSensorDataStore.csvファイルへのデータの書き込みを開始および停止する次のコードを配置します。



リストNo.1
 #!/usr/bin/python import csv import pyfirmata from time import sleep port = 'COM3' board = pyfirmata.Arduino(port) it = pyfirmata.util.Iterator(board) it.start() pirPin = board.get_pin('d:11:i') a0 = board.get_pin('a:0:i') print(pirPin) with open('SensorDataStore.csv', 'w+') as f: w = csv.writer(f) w.writerow(["Number", "Potentiometer", "Motion sensor"]) i = 0 pirData = pirPin.read() m=25 n=1 while i < m: sleep(1) if pirData is not None: i += 1 potData = a0.read() pirData = pirPin.read() row = [i, potData, pirData] print(row) w.writerow(row) print ("Done. CSV file is ready!") board.exit()
      
      







リスト1の結果、ファイル「SensorDataStore.csv」のデータの記録を取得します。









センサーデータの保存に関連するコードを検討してください。 CSVファイルへの書き込みの最初の行は、列の内容を説明するヘッダー行です。w.writerow([“ Number”、“ Potentiometer”、“ Motion sensor”])



ポテンショメータのノブを回すか、モーションセンサーの近くに手を動かすと、与えられた回路に対して人工的に作成できるデータ変更のダイナミクスが表示される場合、データファイルのエントリ数が重要になります。 波形を復元するには、データをファイルに書き込む頻度を信号の頻度の2倍にする必要があります。 記録周波数を調整するには、ステップ-nを使用し、測定時間、サイクル数-mを調整します。 ただし、下からのnに対する制限は、センサー自体の速度によって課せられます。 次のコードは、上記のプログラムでこの機能を実行します。



 m=25 n=1 while i < m: sleep(n) if pirData is not None: i += 1 row = [i, potData, pirData] w.writerow (row)
      
      





指定されたプログラムは、次のように設計要件に従って変更できます。





CSVファイルのデータのグラフィカル分析



SensorDataStore.csvファイルを使用して、ポテンショメータとモーションセンサーからデータ配列を学習し、これらの配列にデータをプロットするためのプログラムを作成します。



リスト#2
 import sys, csv import csv from matplotlib import pyplot i = [] mValues = [] pValues = [] with open('SensorDataStore.csv', 'r') as f: reader = csv.reader(f) header = next(reader, None) for row in reader: i.append(int(row[0])) pValues.append(float(row[1])) if row[2] == 'True': mValues.append(1) else: mValues.append(0) pyplot.subplot(2, 1, 1) pyplot.plot(i, pValues, '-') pyplot.title('Line plot - ' + header[1]) pyplot.xlim([1, 25]) pyplot.xlabel('X Axis') pyplot.ylabel('Y Axis') pyplot.subplot(2, 1, 2) pyplot.bar(i, mValues) pyplot.title('Bar chart - ' + header[2]) pyplot.xlim([1, 25]) pyplot.xlabel('X Axis') pyplot.ylabel('Y Axis') pyplot.tight_layout() pyplot.show()
      
      







リスト2の結果、同じフォームに2つのグラフが表示され、ポテンショメーターとモーションセンサーの出力データがリアルタイムで表示されます。









このプログラムでは、 SensorDataStore.csvファイルを1 行ずつ読み取って、センサー値の2つの配列pValuesmValuesを作成しました。 ここで、 pValuesmValuesは、それぞれポテンショメーターとモーションセンサーのセンサーデータを表します。 matplotlibメソッドを使用して、同じ形式で2つのグラフを作成します。



Arduinoセンサーから受信したpValuesおよびmValuesデータ配列を生成する各サイクル後に生成されたコードは、インターフェースを更新し、これによりデータがリアルタイムで受信されたと結論付けることができます。



ただし、データを保存および視覚化する方法には次の機能があります。データセット全体が最初にSensorDataStore.csvファイルに書き込まれ(リスト1)、次にこのファイルから読み取られます(リスト2)。 Arduinoから新しい値が到着するたびに、グラフを再描画する必要があります。 したがって、リスト1.2のように、スケジュールの計画と更新がリアルタイムで行われ、センサー値のセット全体が構築されないプログラムを開発する必要があります。



ポテンショメータのプログラムを作成して、アクティブ抵抗を直流に変更することで出力信号のダイナミクスを作成しています。



リスト3
 import sys, csv from matplotlib import pyplot import pyfirmata from time import sleep import numpy as np # Associate port and board with pyFirmata port = '/dev/cu.usbmodemfa1321' board = pyfirmata.Arduino(port) # Using iterator thread to avoid buffer overflow it = pyfirmata.util.Iterator(board) it.start() # Assign a role and variable to analog pin 0 a0 = board.get_pin(''a:0:i'') # Initialize interactive mode pyplot.ion() pData = [0] * 25 fig = pyplot.figure() pyplot.title(''Real-time Potentiometer reading'') ax1 = pyplot.axes() l1, = pyplot.plot(pData) pyplot.ylim([0,1]) # real-time plotting loop while True: try: sleep(1) pData.append(float(a0.read())) pyplot.ylim([0, 1]) del pData[0] l1.set_xdata([i for i in xrange(25)]) l1.set_ydata(pData) # update the data pyplot.draw() # update the plot except KeyboardInterrupt: board.exit() break
      
      







リスト3の結果として、スケジュールを取得します。









この演習のリアルタイム計画は、 pyplot ion()、draw()、set_xdata()、およびset_data()関数の組み合わせを使用して達成されますion()メソッドは、 pyplotインタラクティブモードを初期化します。 インタラクティブモードは、 pyplot.ion()のグラフのx値とy値を動的に変更するのに役立ちます。



インタラクティブモードがTrueに設定されている場合、チャートはdraw()メソッドが呼び出されたときにのみ表示されます。 pyFirmataモジュールと入力ピンを使用してArduinoボードを初期化し、センサー値を取得します。



次のコード行でわかるように、Arduinoボードとpyplotインタラクティブモードを構成した後、空のデータのセット(この場合は0: pData = [0] * 25)でチャートを初期化しました。



y、 pData値のこの配列は、 whileループでセンサーから値を追加するために使用されますwhileループは、このデータ配列に新しい値を追加し続け、xおよびy値のこれらの更新された配列でグラフを再描画します。



リスト3では、配列の最初の要素を削除して配列のサイズを制限しながら、配列の最後に新しいセンサー値を追加します。



 pData.append(float(a0.read())) del pData[0]
      
      





set_xdata()およびset_ydata()メソッドは、これらの配列からxおよびy軸データを更新するために使用されます。 これらの更新された値は、 whileループの各反復でdraw()メソッドを使用して適用されます



 l1.set_xdata([i for i in xrange(25)]) l1.set_ydata(pData) # update the data pyplot.draw() # update the plot
      
      





また、 xrange()関数を使用して、 指定された長さ(この場合は25)に従って値の範囲を生成します。 コードスニペット[i for i in xrange(25)]は、0から徐々に始まり24で終わる25個の整数のリストを生成します。



Tkinterウィンドウでのチャート統合



強力なPython統合機能のおかげで、 matplotlibライブラリによって作成されたグラフィックをTkinter GUIにリンクすることは非常に便利です。 Tkintermatplotlibを接続するプログラムを作成します。



リスト4
 import sys from matplotlib import pyplot import pyfirmata from time import sleep import Tkinter def onStartButtonPress(): while True: if flag.get(): sleep(1) pData.append(float(a0.read())) pyplot.ylim([0, 1]) del pData[0] l1.set_xdata([i for i in xrange(25)]) l1.set_ydata(pData) # update the data pyplot.draw() # update the plot top.update() else: flag.set(True) break def onPauseButtonPress(): flag.set(False) def onExitButtonPress(): print "Exiting...." onPauseButtonPress() board.exit() pyplot.close(fig) top.quit() top.destroy() print "Done." sys.exit() # Associate port and board with pyFirmata port = 'COM4' board = pyfirmata.Arduino(port) # Using iterator thread to avoid buffer overflow it = pyfirmata.util.Iterator(board) it.start() # Assign a role and variable to analog pin 0 a0 = board.get_pin('a:0:i') # Tkinter canvas top = Tkinter.Tk() top.title("Tkinter + matplotlib") # Create flag to work with indefinite while loop flag = Tkinter.BooleanVar(top) flag.set(True) pyplot.ion() pData = [0.0] * 25 fig = pyplot.figure() pyplot.title('Potentiometer') ax1 = pyplot.axes() l1, = pyplot.plot(pData) pyplot.ylim([0, 1]) # Create Start button and associate with onStartButtonPress method startButton = Tkinter.Button(top, text="Start", command=onStartButtonPress) startButton.grid(column=1, row=2) # Create Stop button and associate with onStopButtonPress method pauseButton = Tkinter.Button(top, text="Pause", command=onPauseButtonPress) pauseButton.grid(column=2, row=2) # Create Exit button and destroy the window exitButton = Tkinter.Button(top, text="Exit", command=onExitButtonPress) exitButton.grid(column=3, row=2) top.mainloop()
      
      







リスト4の結果、Tkinterがウィンドウに組み込まれます。 ボタンインターフェイス要素を持つグラフ-startButton、pauseButton、exitButton。









開始ボタンと終了ボタンは、対応する関数onStartButtonPress()およびonExitButtonPress()を使用したスケジュールの更新やスケジュールのクローズなど、 matplotlib操作のブレークポイントを提供します。 onStartButtonPress()関数は、 matplotlib ライブラリpyFirmataライブラリ間の相互作用点でも構成されています 。 次のコードスニペットからわかるように、 draw()メソッドを使用してグラフの更新を開始し、 read()メソッドを使用して取得されたアナログ出力a0からの各観測に対してupdate()メソッドを使用してTkinterウィンドウを更新します。



onExitButtonPress()関数は、名前自体で説明されているように、exit関数を実装します。 Arduinoボードをシリアルポートから切断する前に、 pyplot figureとTkinterウィンドウを閉じます。



Arduinoポートパラメータに適切な変更を加えた後、No。4のリストを開始します。 前のスクリーンショットに示されているようなウィンドウが画面に表示されるはずです。 このコードでは、「開始」ボタンと「一時停止」ボタンを使用してリアルタイムのチャートを管理できるようになりました。 「スタート」ボタンを押して、ポテンショメーターのノブを回し始めます。 [一時停止]ボタンを押すと、プログラムが新しい値の作成を停止したことがわかります。 「一時停止」ボタンを押すと、ノブを回してもスケジュールは変更されません。



[スタート]ボタンをもう一度クリックすると、グラフがリアルタイムで更新され、一時停止中に生成された値が破棄されることがすぐにわかります。 [終了]ボタンをクリックして、プログラムを安全に閉じます。



この出版物の資料の準備において、Misov O. P.



結論



この出版物では、Pythonを使用したファイルの作成、読み取り、書き込み、これらのファイルへのデータの保存、センサー値のプロットとリアルタイムグラフの作成という2つの主要なPythonプログラミングパラダイムを紹介します。 また、Arduinoセンサーデータをリアルタイムで保存および表示する方法も学びました。 Arduinoプロジェクトの支援に加えて、これらのメソッドは日常のPythonプロジェクトでも使用できます。



参照資料



  1. Arduino用のPythonプログラミング。
  2. Cプログラミング言語(KerniganおよびRitchie)。



All Articles