2017-06-12 322 views
0

我一直在努力尋找一種方法來繪製來自arduino的輸入數據與Python GUI。我能夠使用Matplotlib動畫函數完成此操作,以讀取6個不同的變量,並在另一個子圖上的一個子圖2上繪製4個2。這可以做得足夠快,它實時繪製圖形(每秒20個採樣點)。使用python和tkinter實時繪製串行數據

我現在需要修改系統以在同一時間讀取12個不同的變量,其中8個繪圖。 4在另一個小區4上以相同的速率每秒20個採樣。我一直無法得到這個工作,並嘗試了一些不同的東西,並做了大量的研究,但似乎無法弄清楚如何用我有限的python知識來做到這一點。我不是很熟悉多處理或多線程,但他們似乎是人們能夠加快繪圖過程的方式。我知道matplotlib動畫函數本身是線程化的,所以我不確定線程​​有多大幫助,或者如果有一種方法可以在一個線程中讀取並在另一個線程中更新圖形。我在Arduino支持250000的最高波特率下運行。我也能找到一個例子,其中有人能夠在這篇文章中獲得非常高速的陰謀,但還沒有能夠修改爲我的使用:What is the best real time plotting widget for wxPython?

的數據被從接收的Arduino這樣的:

integer.integer.integer | integer.integer.integer | integer.integer.integer | integer.integer.integer

其中管代表一個新的執行器(每個變量im發送來自什麼)

我是相當新的python s Ø對不起,如果這個心不是那麼Python的,但這裏有兩個例子,我有: 這是使用動畫功能的GUI:

import Tkinter 
import serial 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 
from matplotlib.figure import Figure 
from matplotlib import pyplot as plt 
import matplotlib.animation as animation 
from collections import deque 
import random 

class App: 
    def __init__(self, master): 

     self.arduinoData = serial.Serial('com5', 250000)#115200) 

     frame = Tkinter.Frame(master) 

     self.running = False 
     self.ani = None 

     self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10) 
     self.start.grid(row=0, column=0, padx=20, pady=20) 

     self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData) 
     self.run.grid(row=0, column=0, padx=5, pady=5) 

     self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10) 
     self.stop_frame.grid(row=0, column=1, padx=20, pady=20) 

     self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest) 
     self.stop.grid(row=0, column=0, padx=5, pady=5) 

     self.fig = plt.Figure() 
     self.ax1 = self.fig.add_subplot(211) 
     self.line0, = self.ax1.plot([], [], lw=2) 
     self.line1, = self.ax1.plot([], [], lw=2) 
     self.line2, = self.ax1.plot([], [], lw=2) 
     self.line3, = self.ax1.plot([], [], lw=2) 
     self.ax2 = self.fig.add_subplot(212) 
     self.line4, = self.ax2.plot([], [], lw=2) 
     self.line5, = self.ax2.plot([], [], lw=2) 
     self.line6, = self.ax2.plot([], [], lw=2) 
     self.line7, = self.ax2.plot([], [], lw=2) 
     self.canvas = FigureCanvasTkAgg(self.fig,master=master) 
     self.canvas.show() 
     self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20) 
     frame.grid(row=0, column=0, padx=20, pady=20) 

    def getData(self): 
     if self.ani is None: 
      self.k = 0 
      self.arduinoData.flushInput() 
      self.arduinoData.write("<L>") 
      return self.start() 
     else: 
      self.arduinoData.write("<L>") 
      self.arduinoData.flushInput() 
      self.ani.event_source.start() 
     self.running = not self.running 

    def stopTest(self): 
     self.arduinoData.write("<H>") 
     if self.running: 
      self.ani.event_source.stop() 
     self.running = not self.running 

    def resetTest(self): 
     self.k = 0 
     self.xdata = [] 
     self.pressure1 = [] 
     self.displacement1 = [] 
     self.cycle1 = [] 
     self.pressure2 = [] 
     self.displacement2 = [] 
     self.cycle2 = [] 
     self.pressure3 = [] 
     self.displacement3 = [] 
     self.cycle3 = [] 
     self.pressure4 = [] 
     self.displacement4 = [] 
     self.cycle4 = [] 
     self.line1.set_data(self.xdata, self.ydata1) 
     self.line2.set_data(self.xdata, self.ydata2) 
     self.ax1.set_ylim(0,1) 
     self.ax1.set_xlim(0,1) 
     self.ax2.set_ylim(0,1) 
     self.ax2.set_xlim(0,1) 

    def start(self): 
     self.xdata = [] 
     self.pressure1 = [] 
     self.displacement1 = [] 
     self.cycle1 = [] 
     self.pressure2 = [] 
     self.displacement2 = [] 
     self.cycle2 = [] 
     self.pressure3 = [] 
     self.displacement3 = [] 
     self.cycle3 = [] 
     self.pressure4 = [] 
     self.displacement4 = [] 
     self.cycle4 = [] 
     self.k = 0 
     self.arduinoData.flushInput() 
     self.ani = animation.FuncAnimation(
      self.fig, 
      self.update_graph, 
      interval=1, 
      repeat=True) 
     self.arduinoData.write("<L>") 
     self.running = True 
     self.ani._start() 

    def update_graph(self, i): 
     self.xdata.append(self.k) 
     while (self.arduinoData.inWaiting()==0): 
      pass 
     x = self.arduinoData.readline() 
     strip_data = x.strip() 
     split_data = x.split("|") 
     actuator1 = split_data[0].split(".") 
     actuator2 = split_data[1].split(".") 
     actuator3 = split_data[2].split(".") 
     actuator4 = split_data[3].split(".") 
     self.pressure1.append(int(actuator1[0])) 
     self.displacement1.append(int(actuator1[1])) 
     self.cycle1 = int(actuator1[2]) 
     self.pressure2.append(int(actuator2[0])) 
     self.displacement2.append(int(actuator2[1])) 
     self.cycle2 = int(actuator2[2]) 
     self.pressure3.append(int(actuator3[0])) 
     self.displacement3.append(int(actuator3[1])) 
     self.cycle3 = int(actuator3[2]) 
     self.pressure4.append(int(actuator4[0])) 
     self.displacement4.append(int(actuator4[1])) 
     self.cycle4 = int(actuator4[2]) 
     self.line0.set_data(self.xdata, self.pressure1) 
     self.line1.set_data(self.xdata, self.pressure2) 
     self.line2.set_data(self.xdata, self.pressure3) 
     self.line3.set_data(self.xdata, self.pressure4) 
     self.line4.set_data(self.xdata, self.displacement1) 
     self.line5.set_data(self.xdata, self.displacement2) 
     self.line6.set_data(self.xdata, self.displacement3) 
     self.line7.set_data(self.xdata, self.displacement4) 
     if self.k < 49: 
      self.ax1.set_ylim(min(self.pressure1)-1, max(self.pressure3) + 1) 
      self.ax1.set_xlim(0, self.k+1) 
      self.ax2.set_ylim(min(self.displacement1)-1, max(self.displacement3) + 1) 
      self.ax2.set_xlim(0, self.k+1) 
     elif self.k >= 49: 
      self.ax1.set_ylim(min(self.pressure1[self.k-49:self.k])-1, max(self.pressure3[self.k-49:self.k]) + 1) 
      self.ax1.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1]) 
      self.ax2.set_ylim(min(self.displacement1[self.k-49:self.k])-1, max(self.displacement3[self.k-49:self.k]) + 1) 
      self.ax2.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1]) 
     self.k += 1 




root = Tkinter.Tk() 
app = App(root) 
root.mainloop() 

這是打印到監視器GUI:

import Tkinter 
import serial 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 
from matplotlib.figure import Figure 
from matplotlib import pyplot as plt 
import matplotlib.animation as animation 
import time 

class App: 
    def __init__(self, master): 

     self.arduinoData = serial.Serial('com5', 250000, timeout=0) 

     frame = Tkinter.Frame(master) 

     self.go = 0 

     self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10) 
     self.start.grid(row=0, column=0, padx=20, pady=20) 

     self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData) 
     self.run.grid(row=0, column=0, padx=5, pady=5) 

     self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10) 
     self.stop_frame.grid(row=0, column=1, padx=20, pady=20) 

     self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest) 
     self.stop.grid(row=0, column=0, padx=5, pady=5) 

     self.fig = plt.Figure() 
     self.ax1 = self.fig.add_subplot(211) 
     self.line0, = self.ax1.plot([], [], lw=2) 
     self.line1, = self.ax1.plot([], [], lw=2) 
     self.line2, = self.ax1.plot([], [], lw=2) 
     self.line3, = self.ax1.plot([], [], lw=2) 
     self.ax2 = self.fig.add_subplot(212) 
     self.line4, = self.ax2.plot([], [], lw=2) 
     self.line5, = self.ax2.plot([], [], lw=2) 
     self.line6, = self.ax2.plot([], [], lw=2) 
     self.line7, = self.ax2.plot([], [], lw=2) 
     self.canvas = FigureCanvasTkAgg(self.fig,master=master) 
     self.canvas.show() 
     self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20) 
     frame.grid(row=0, column=0, padx=20, pady=20) 

    def getData(self): 
     self.k = 0 
     self.xdata = [] 
     self.pressure1 = [] 
     self.displacement1 = [] 
     self.cycle1 = [] 
     self.pressure2 = [] 
     self.displacement2 = [] 
     self.cycle2 = [] 
     self.pressure3 = [] 
     self.displacement3 = [] 
     self.cycle3 = [] 
     self.pressure4 = [] 
     self.displacement4 = [] 
     self.cycle4 = [] 
     self.arduinoData.flushInput() 
     self.go = 1 
     self.readData() 

    def readData(self): 
     if self.go == 1: 
      self.xdata.append(self.k) 
      while (self.arduinoData.inWaiting()==0): 
       pass 
      x = self.arduinoData.readline() 
      strip_data = x.strip() 
      split_data = x.split("|") 
      actuator1 = split_data[0].split(".") 
      actuator2 = split_data[1].split(".") 
      actuator3 = split_data[2].split(".") 
      actuator4 = split_data[3].split(".") 
      self.pressure1.append(int(actuator1[0])) 
      self.displacement1.append(int(actuator1[1])) 
      self.cycle1 = int(actuator1[2]) 
      self.pressure2.append(int(actuator2[0])) 
      self.displacement2.append(int(actuator2[1])) 
      self.cycle2 = int(actuator2[2]) 
      self.pressure3.append(int(actuator3[0])) 
      self.displacement3.append(int(actuator3[1])) 
      self.cycle3 = int(actuator3[2]) 
      self.pressure4.append(int(actuator4[0])) 
      self.displacement4.append(int(actuator4[1])) 
      self.cycle4 = int(actuator4[2]) 
      self.printData() 
      root.after(0, self.readData) 


    def printData(self): 
     print str(self.pressure1[self.k-1]) + " " + 
     str(self.displacement1[self.k-1]) + " " + str(self.cycle1) + " " + 
     str(self.pressure2[self.k-1]) + " " + str(self.displacement2[self.k- 
     1]) + " " + str(self.cycle2) + " " + str(self.pressure3[self.k-1]) + 
     " " + str(self.displacement3[self.k-1]) + " " + str(self.cycle3) + " 
     " + str(self.pressure4[self.k-1]) + " " + 
     str(self.displacement4[self.k-1]) + " " + str(self.cycle4) 

    def stopTest(self): 
     self.arduinoData.write("<H>") 
     self.go = 0 


    def resetTest(self): 
     self.k = 0 
     self.xdata = [] 
     self.pressure1 = [] 
     self.displacement1 = [] 
     self.cycle1 = [] 
     self.pressure2 = [] 
     self.displacement2 = [] 
     self.cycle2 = [] 
     self.pressure3 = [] 
     self.displacement3 = [] 
     self.cycle3 = [] 
     self.pressure4 = [] 
     self.displacement4 = [] 
     self.cycle4 = [] 
     self.line1.set_data(self.xdata, self.ydata1) 
     self.line2.set_data(self.xdata, self.ydata2) 
     self.ax1.set_ylim(0,1) 
     self.ax1.set_xlim(0,1) 
     self.ax2.set_ylim(0,1) 
     self.ax2.set_xlim(0,1) 

    def start(self): 
     self.xdata = [] 
     self.pressure1 = [] 
     self.displacement1 = [] 
     self.cycle1 = [] 
     self.pressure2 = [] 
     self.displacement2 = [] 
     self.cycle2 = [] 
     self.pressure3 = [] 
     self.displacement3 = [] 
     self.cycle3 = [] 
     self.pressure4 = [] 
     self.displacement4 = [] 
     self.cycle4 = [] 
     self.k = 0 
     self.arduinoData.write("<L>") 

root = Tkinter.Tk() 
app = App(root) 
root.mainloop() 

和這裏是一個例子arduino代碼:

int analog0 = 0; 
int analog1 = 1; 
int analog2 = 2; 

int sensor0; 
int sensor1; 
int sensor2; 

String pot0; 
String pot1; 
String Force; 

int pot0holder; 
int pot1holder; 
String Forceholder; 

unsigned long i = 0; 
String Is; 

int val = 0; 

boolean Sensordata = false; 
int cycles; 

const byte numChars = 32; 
char receivedChars[numChars]; 
boolean newData = false; 

unsigned long CurrentMillis = 0; 
unsigned long PrintMillis = 0; 
int PrintValMillis = 50; 
unsigned long SensorMillis = 0; 
int SensorValMillis = 0; 

void setup() { 
    // put your setup code here, to run once: 
    Serial.begin(250000); 
} 

void loop() 
{ 
    CurrentMillis = millis(); 
    recvWithStartEndMarkers(); 
    commands(); 
    sensordata(); 
} 

void sensordata() 
{ 
    if (CurrentMillis - SensorMillis >= SensorValMillis) 
    { 
    sensor0 = analogRead(analog0); 
    pot0holder = sensor0; 
    sensor1 = analogRead(analog1); 
    pot1holder = sensor1; 
    i += 1; 
    String potcolumn = String(pot0holder) + "." + String(pot1holder) + "." + String(i) + "|" + String(int(pot0holder)+30) + "." + String(int(pot1holder)+30) + "." + String(i) + "|" + String(int(pot0holder)+60) + "." + String(int(pot1holder)+60) + "." + String(i) + "|" + String(int(pot0holder)+90) + "." + String(int(pot1holder)+90) + "." + String(i); 
    Serial.println(potcolumn); 
    SensorMillis += SensorValMillis; 
    } 
} 

void recvWithStartEndMarkers() 
{ 
    static boolean recvInProgress = false; //creates variable visible to only one function with boolean 
    static byte ndx = 0; 
    char startMarker = '<'; //sets begin condition 
    char endMarker = '>'; //sets end condition 
    char rc; //sets variable type to char 

    while (Serial.available() > 0 && newData == false) { 
     rc = Serial.read(); //sets rc equal to serial value 

     if (recvInProgress == true) { 
      if (rc != endMarker) { 
       receivedChars[ndx] = rc; 
       ndx++; 
       if (ndx >= numChars) { 
        ndx = numChars - 1; 
       } 
      } 
      else { 
       receivedChars[ndx] = '\0'; // terminate the string 
       recvInProgress = false; 
       ndx = 0; 
       newData = true; 
      } 
     } 
     else if (rc == startMarker) { 
      recvInProgress = true; 
     } 
    } 
} 

void commands() 
{ 
    if (newData == true) 
    { 
    if (receivedChars[0] == 'T') 
    { 
     PrintValMillis = atoi(&receivedChars[1]); //atoi -> Converting strings to integer 
    } 
    else if (receivedChars[0] == 'S') 
    { 
     cycles = atoi(&receivedChars[1]); 
     i = 0; 
    } 
     else if (receivedChars[0] == 'L') 
    { 
     val = atoi(&receivedChars[1]); 
     i = 0; 
    } 
    } 
    newData = false; 
} 

在此先感謝任何人的任何幫助或建議。

+0

剖析我發現的代碼,在圖表的更新式的前300次迭代它平均0.0429900026321秒這應該隨時間留給它仍然還是饒了你之後雖然圖表需要每隔0.05秒發生一次 – emg184

+0

首先,您需要找到自己的瓶頸。它是讀數據還是繪圖?然後,您可以將它們放入單獨的進程中,讀取器將管道送入打印機。 – RaJa

+0

此外,你應該優化你的代碼:printData函數可以通過使用''sep「.join([str1,str2,...])'來加速,其中sep是你的空格。 實時matplot-繪圖你應該看看https://stackoverflow.com/questions/11874767/real-time-plotting-in-while-loop-with-matplotlib – RaJa

回答

0

所以你的閱讀過程需要大部分時間。我會把讀數放在一個單獨的任務中,並對主(繪圖)過程中的數據進行評估/拆分。不幸的是,我不是一個tkinter用戶,所以我沒有任何特殊的gui框架寫這個。但我認爲你可以根據你的需求來調整它。

這看起來就像是:

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.animation as animation 
import multiprocessing as mp 
import time 


# global variables 
fig = plt.figure(1) 
# first sub-plot 
ax1 = fig.add_subplot(211) 
line1, = ax1.plot([], [], lw=2) 
ax1.grid() 
xdata1, ydata1 = [], [] 
# second sub-plot 
ax2 = fig.add_subplot(212) 
line2, = ax2.plot([], [], lw=2) 
ax2.grid() 
xdata2, ydata2 = [], [] 

# the multiprocessing queue 
q = mp.Queue() 

# data generator in separate process 
# here would be your arduino data reader 
def dataGen(output): 
    for x in range(50): 
     output.put((x, np.sin(x))) 

# update first subplot 
def update1(data): 
    # update the data 
    t, y = data 
    xdata1.append(t) 
    ydata1.append(y) 
    xmin, xmax = ax1.get_xlim() 
    ymin, ymax = ax1.get_ylim() 

    if t >= xmax: 
     ax1.set_xlim(xmin, 2*xmax) 
    if y >= ymax: 
     ax1.set_ylim(ymin, 2*ymax) 
    if y <= ymin: 
     ax1.set_ylim(2*ymin, ymax) 
    line1.set_data(xdata1, ydata1) 

    return line1, 

# update second subplot 
def update2(data): 
    # update the data 
    t, y = data 
    xdata2.append(t) 
    ydata2.append(y) 
    xmin, xmax = ax2.get_xlim() 
    ymin, ymax = ax2.get_ylim() 

    if t >= xmax: 
     ax2.set_xlim(xmin, 2*xmax) 
    if y >= ymax: 
     ax2.set_ylim(ymin, 2*ymax) 
    if y <= ymin: 
     ax2.set_ylim(2*ymin, ymax) 
    line2.set_data(xdata2, ydata2) 

    return line2, 

# called at each drawing frame 
def run(data): 
    # get data from queue, which is filled in separate process, blocks until 
    # data is available 
    data = q.get(block=True, timeout=.5) 
    # put here your variable separation 
    data1 = (2*data[0], 3*data[1]) 
    data2 = (data[0], data[1]) 
    #provide the data to the plots 
    a = update1(data1) 
    b = update2(data2) 
    fig.canvas.draw() 
    return a+b 

if __name__ == "__main__": 
    # count of reader processes 
    n_proc = 1 
    # setup workers 
    pool = [mp.Process(target=dataGen, args=(q,)) for x in range(n_proc)] 
    for p in pool: 
     p.daemon = True 
     p.start() 

    # wait a few sec for the process to become alive 
    time.sleep(3) 

    # start your drawing 
    ani = animation.FuncAnimation(fig, run, frames=60, blit=True, interval=10, 
            repeat=False) 
    plt.show() 

    print('done') 
+0

感謝您的幫助@RaJa即時通訊仍然是新的多處理和線程,所以我會努力試圖讓這個進入arduino和陰謀 – emg184

+0

我遇到的問題是,dataGen函數在同一時間產生所有的數據,然後一旦完成就將這些數據發送到其他功能,然後程序進行到圖表。我需要能夠一次獲得一個點,然後更新圖形。這需要在新的arduino數據修改實例變量的同時理想地並行完成,然後圖形看到添加了新數據並將該點添加到圖中。 – emg184

+0

'data = q.get(block = True)'實際上等待數據可用。所以圖表不會更新,除非有新的數據可用。接下來:隊列先進先出。所以讀者發送的內容直接被繪製出來 - 逐點。但讀者發送的數據並不直接顯示在圖表上。您可以添加另一個步驟,處理數據並將其添加到圖形中。您也可以通過隊列發送數據包。或者讀者自己處理所有的處理。目前,我沒有看到問題。抱歉。 – RaJa