如何从连续数据源更新matplotlib?

时间:2015-05-09 12:10:34

标签: python matplotlib pyserial

我正在尝试根据从连续实时数据源收集的数据制作一个简单的图表。

我使用matplotlib的代码如下:

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from serialdata import SerialData 

fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)

def animate(i):
    xar = []
    yar = []

    #Open Serial Port and Receive Continuous Data 
    #in format of number,number
    a = SerialData()
    b = a.setSerial('COM3', 9600)
    dataArray = a.getSerial(9999)

    for eachLine in dataArray:
        if len(eachLine)>1:
            x,y = eachLine.split(',')
            xar.append(int(x))
            yar.append(int(y))
    ax1.clear()
    ax1.plot(xar,yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()

serialdata.py只在每次从数据源获取数据时生成数据:

import serial
from time import sleep

class SerialData:
    def __init__(self):
        pass        

    def setSerial(self, port, baudrate):
        self.port = port
        self.baudrate = baudrate
        print("Opening Serial Port...")
        self.ser = serial.Serial(self.port, self.baudrate, timeout=1)
        sleep(2)
        print("Setup Successful") 

    def getSerial(self, read):
        while True:
            self.data = self.ser.read(read)
            if len(self.data)>0:
                yield self.data.decode('utf-8')
            sleep(.1)
        self.ser.close() 

据说应该以下列形式发送数据:

1,2
2,5
3,7
4(autoincrement),5(random number)

当我在CLI上打印它们时工作得很好。 但是我无法使用matplotlib。

没有特定错误。

它只是显示

Opening Serial Port...
Setup Successful

并且......就是这样。那没有什么事。 我的代码出了什么问题?

我做了更多研究,发现我不应该使用show() 所以我重写了我的代码如下:

import time
import numpy as np
import matplotlib.pyplot as plt
from serialdata import SerialData   

plt.axis([0, 1000, 0, 1])
plt.ion()
plt.show()

for i in range(1000):
    # y = np.random.random()

    a = SerialData()
    b = a.setSerial('COM3', 9600)
    dataArray = a.getSerial(9999)

    print("Data Gathering...")
    for eachLine in dataArray:

        if len(eachLine)>1:
            y = eachLine.split(',')[1]

            plt.scatter(i, y)
            plt.draw()
            time.sleep(0.05)

但是,结果是一样的。

1 个答案:

答案 0 :(得分:1)

我没有串口,但这是我试图解决的问题:

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time

import random
from time import sleep

class MockData():
    def __init__(self, n):
        self.n = n  #number of data points to return

    def getData(self):
        start = 0 #mock autoincrement
        for i in range(self.n):
            yield (start, random.randint(0, 100)) #yield a tuple, not a str (x(autoincrem),y(random))
            start+=1  


fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)

def animate(i):
    xar = []
    yar = []

    #Open Serial Port and Receive Continuous Data 
    #in format of number,number
    a = MockData(10)
    dataArray = a.getData() #this is a generator that will yield (x,y) tuples 10 times. 

    for eachLine in dataArray:
            x,y = eachLine
            xar.append(int(x))
            yar.append(int(y))
    ax1.clear()
    ax1.plot(xar,yar)

ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()

此代码将失败\非常慢:

  1. 如果我一次请求大量数据:a=MockData(1000)每帧执行约2-3秒
  2. 如果读数时间很长,即

    def getData(self):
        start = 0
        for i in range(self.n):
            yield (start, random.randint(0, 100))
            start+=1
            time.sleep(1)
    

    这将每帧执行~10秒

  3. 两个
  4. 因此,我可以在您的SerialData课程中提出问题,特别是在getSerial方法中。我知道,因为那是负责检索实际数据的方法。由于我没有串口,我无法准确测试哪里,但我可以下注一些猜测。

       def getSerial(self, read):
            while True:
                self.data = self.ser.read(read)
                if len(self.data)>0:
                    yield self.data.decode('utf-8')
                sleep(.1)
            self.ser.close()
    
    1. self.data = self.ser.read(read)这里的问题是你request 9999 bytes to be read。 9600波特约为1200字节/秒。要读取9 999字节,需要大约10秒。那就是 if 实际上要读取9999个新字节。如果没有read函数将继续等待,直到它读取该数量。这等于我的1)测试用例,除了等待期为sleep(10)。因此if检查已经在read功能中,因此您无需再次检查。
    2. self.data.decode('utf-8')查看解码9999字节所需的时间:

      >>> from timeit import Timer
      >>> ts = Timer("s.decode('utf-8')", "s = b'1'*9999")
      >>> ts.timeit()
      2.524627058740407
      

      现在,被授予,这与你的转换不一样,但由于我的笔记本电脑上没有串口,我无法测试它。无论如何,它似乎很慢。现在,你的案例2)有sleep(12)

    3. sleep(.1)现在这似乎只是对伤害的侮辱。
    4. 您的代码不会报告错误,因为它可以正常工作,只需要3分钟就可以读取第一组数据并绘制它。

      我的建议是你忽略了这种方法,读取数据,几乎字面上,每字节字节,在它们到来时绘制它们。有一个数组,你只需要附加新字节并绘制它。你可以很容易地拉出类似的东西:

      serialPort = serial.Serial(self.port, self.baudrate, timeout=1)
      data = list() #some global list to append to
      
      def animate():
          d = serialPort.read(2) #this will hang until it completes
          data.append(d) #does the data really have to be in string? 
          ax.plot(data)
      
      ani = animation.FuncAnimation(fig, animate, interval=1000) #optionally send in repeat_delay?
      plt.show()
      

      虽然关于这一点的部分原因是我不确定如果数据阵列开始变得非常大,动画的行为如何,所以您可以考虑将x轴向右移动,删除旧数据并定期创建新数组。 / p>

      希望我能指出你需要测试哪些东西。从串口开始计时所有读入,以了解事情需要多长时间。不要忘记确保你实际上也从串口读取任何,如果你不是,那么serial.read就会在那里等待。

      我也从未做过这样的事情,所以我可能会偏离正轨。上次我处理串口是从小学(rofl)的机器人冠军。