mbed で測定したデータを PyQt + Matplotlib でリアルタイムプロット

mbed で測定した電圧値の時系列データを表示し、CSV ファイルに保存する GUI プログラムを PyQt で作成しました(以前作った物を改良)。

mbed には以下のプログラムを書き込んでいます。
Ticker で 0.1 秒間隔で割り込みを発生させ、PC から送信されてきた文字によって A0 ピンの電圧測定値の送信やタイマーのリセット等を行います。

#include "mbed.h"
Ticker flipper;
Timer t;
Serial pc(USBTX, USBRX);
AnalogIn input(A0);
char temp;

void flip() {
  if(pc.readable()){
    temp = pc.getc();

    if(temp == '*'){
      printf("%f %f\n", t.read(), input.read());
    }

    if(temp == '1'){
      t.start();
    }

    if(temp == '0'){
      t.stop();
      t.reset();
    }
  }
}

int main() {
  flipper.attach(&flip, 0.1);
}

PC 側の GUI プログラムは以下のとおりです。スタートボタンを押すと測定を開始し、ストップボタンを押すと計測終了と同時に ~/example.csv にデータを書き出します。

測定時間の誤差ですが、測定開始から約 1 分で積み上げ 2 μ s の誤差が発生していました(1 日測定したとして約 3 ms の誤差)。
Ticker がどれくらい正確か分からないですが、mbed を使った方が Arduino Uno よりも時間測定の精度が高そうです。

# coding: utf-8
import sys
import signal
import serial
import numpy as np
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QGridLayout,
			     QHBoxLayout, QVBoxLayout, QLCDNumber, QLabel)
import matplotlib as mpl
mpl.use("Qt5Agg")
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import csv

class ApplicationWindow(QWidget):
    def __init__(self):
	super().__init__()

	self.initUI()

    def initUI(self):
	self.timer = QTimer(self)
	self.timer.timeout.connect(self.update)

	self.initCanvas()

	self.lcd1 = QLCDNumber(self)
	self.lcd1.setStyleSheet("QWidget { background-color: rgb(100, 100, 255) }")
	self.lcd2 = QLCDNumber(self)
	self.lcd2.setStyleSheet("QWidget { background-color: rgb(100, 100, 255) }")

	startButton = QPushButton("Start")
	startButton.clicked.connect(self.onStartButton)
	stopButton = QPushButton("Stop")
	stopButton.clicked.connect(self.onStopButton)

	l1 = QHBoxLayout()
	l1.addWidget(self.canvas)

	l2 = QVBoxLayout()
	l2.addWidget(startButton)
	l2.addWidget(stopButton)

	l3 = QGridLayout()
	l3.addWidget(QLabel("Time:"), 0, 0)
	l3.addWidget(self.lcd1, 0, 1)
	l3.addWidget(QLabel("s"), 0, 2)
	l3.addWidget(QLabel("Voltage:"), 1, 0)
	l3.addWidget(self.lcd2, 1, 1)
	l3.addWidget(QLabel("V"), 1, 2)

	l23 = QVBoxLayout()
	l23.addLayout(l2)
	l23.addLayout(l3)
	l23.addStretch(1)

	lMain = QHBoxLayout()
	lMain.addLayout(l1)
	lMain.addLayout(l23)

	self.setLayout(lMain)

	self.setWindowTitle("Realtime Monitor")

    def initCanvas(self):
	self.fig = mpl.figure.Figure(figsize=(6, 5), dpi=100)
	self.axes = self.fig.add_subplot(111)
	self.axes.hold(False)
	self.canvas = FigureCanvas(self.fig)
	self.initFigure()

    def initTime(self):
	ser.write("*".encode())
	self.serData = ser.readline().strip().rsplit()
	self.t0 = float(self.serData[0])

    def initFigure(self):
	self.t = np.zeros(100)
	self.y = np.zeros(100)
	self.li, = self.axes.plot(self.t, self.y)
	self.axes.set_xlabel("Time[s]")
	self.axes.set_ylabel("Voltage[V]")
	self.axes.set_ylim(0, 3.3)

    def update(self):
	ser.write("*".encode())
	self.serData = ser.readline().strip().rsplit()
	self.serT = self.getT()
	self.serY = self.getY()

	self.data.append([self.serT, self.serY])

	self.t = np.append(self.t, self.serT)
	self.t = np.delete(self.t, 0)
	self.y = np.append(self.y, self.serY)
	self.y = np.delete(self.y, 0)
	self.li.set_xdata(self.t)
	self.li.set_ydata(self.y)
	self.axes.set_xlim(min(self.t), max(self.t))

	self.canvas.draw()

	self.lcd1.display("%6.1f" % self.t[99])
	self.lcd2.display("%6.3f" % self.y[99])

    def getT(self):
	return float(self.serData[0])-self.t0

    def getY(self):
	return float(self.serData[1])*3.3

    def onStartButton(self):
	self.initFigure()
	self.f = open('example.csv', 'w')
	self.w = csv.writer(self.f)
	self.data = []
	ser.write("1".encode())
	self.initTime()
	self.timer.start()

    def onStopButton(self):
	self.timer.stop()
	ser.write("0".encode())
	try:
	    for i in range(len(self.data)):
		self.w.writerow(self.data[i])
	    self.f.close()
	except:
	    pass

if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    ser = serial.Serial(port="/dev/ttyACM0", baudrate=9600)
    app = QApplication(sys.argv)
    aw = ApplicationWindow()
    aw.show()
    app.exec_()

15070801.gif

コメント

Comments powered by Disqus
書籍更新情報
2016-10-21
Pythonによる科学技術計算 基礎編
PDF版の販売を開始しました。
販売ページはこちら

2016-09-09
Pythonによる科学技術計算 基礎編
1.2版への更新が可能になりました。
サポートページはこちら
電子書籍
Pythonによる科学技術計算 基礎編
Kindle ストア、Leanpubで販売中です
Pythonによる科学技術計算 基礎編
PDF版の販売はこちら
同人誌
技術書典(2016/6/25)
Emacs/org-modeのPDF作成術
電子版をBOOTHで販売中です
Emacs/org-modeのPDF作成術
Share