简体   繁体   English

Matplotlib在Python中“实时”绘图

[英]Matplotlib “real time” plotting in python

I am on python 3.7. 我在python 3.7上。 I am trying to read data from a serial port, it would be 7 different bytes. 我正在尝试从串行端口读取数据,这将是7个不同的字节。 Then I would like to plot each different byte on a different subplot. 然后,我想在不同的子图中绘制每个不同的字节。 I want to read the serial port every 500ms and each time I read add the new data to the subplots. 我想每500毫秒读取一次串行端口,并且每次读取时都将新数据添加到子图中。 Every read is giving one more data to plot on every subplot. 每次读取都会提供一个数据,以便在每个子图上进行绘制。 That's basically sensor reading. 这基本上是传感器读数。

Here is the code I have written: 这是我编写的代码:

from time import sleep import serial import matplotlib.pyplot as plt 从时间导入睡眠导入串行导入matplotlib.pyplot as plt

f=plt.figure(1)
ax=[0 for x in range(7)]
for i in range(0,7):
    ax[i]=f.add_subplot(4,2,1+i)

ser = serial.Serial('COM3', 115200) # Establish the connection on a specific port
counter = 0 
byte=ser.readline() #first line not to be plotted
while True:
    counter +=1
    ser.write(b'9') # send a command to the arduino
    byte=ser.read(7) #read 7 bytes back
    for i in range(0,7):
        ax[i].plot(counter, byte[i]) # Trying to plot the new values to each different subplots
    plt.pause(0.01)
    sleep(.5) # Delay for one half of a second

The figure is showing and the x axis and y axis are adapting to the value I want to plt but there is no data at all on the plot. 如图所示,x轴和y轴都适合我要输入的值,但是绘图上根本没有数据。 If I use scatter instead of plot it works, but then it is less versatile and I can't draw te type of graph I want. 如果我使用散点图而不是绘图,它可以工作,但是通用性较差,并且我无法绘制想要的图形类型。 I also try to reproduce the problem without using a serial data but just displaying points of a list one after the other like that: 我还尝试不使用串行数据而是仅一个接一个地显示列表中的点来重现此问题:

import matplotlib.pyplot as plt
from time import sleep

f=plt.figure()
series=[[4,3,2,1],[8,7,6,5],[12,11,10,9]]
counter=0
ax=[0 for x in range(7)]

for i in range(0,3):
    ax[i]=f.add_subplot(4,2,1+i)


for j in range (0,4):
    counter=counter+1
    for i in range(0,3):
        ax[i].plot(counter,series[i][j])
    plt.pause(0.01)
    sleep(1)

And it is doing exactly the same thing, the final image I have on the graph is that: 而且它所做的完全相同,我在图上得到的最终图像是: 在此处输入图片说明 Which shows axis took what I wanted to plot but did not plot anything. 该图显示轴已绘制我想要绘制的内容,但未绘制任何内容。 The point is I do not want to clear the full plot and redraw everything because for the data sensor I will have about 30days of data to display in continuous. 关键是我不想清除全部图并重画所有内容,因为对于数据传感器,我将有大约30天的数据要连续显示。 What am I doing wrong with the code I have written? 我编写的代码在做什么错?

EDIT: After comment of ImportanceOfBeingErnest I have tried implementing the answer given here . 编辑:在对ImportanceOfBeingErnest进行评论后,我尝试执行此处给出的答案。 The code is then: 代码如下:

from time import sleep
import serial
import matplotlib.pyplot as plt
import numpy

plt.ion()
f=plt.figure()
ax=[0 for x in range(7)]
lines=[0 for x in range(7)]
for i in range(0,7):
    ax[i]=f.add_subplot(4,2,1+i)
    lines[i]=ax[0].plot([],[])


def update_line(hl, new_datax, new_datay):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_datax))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_datay))
    plt.draw()

ser = serial.Serial('COM3', 115200) # Establish the connection on a specific port
counter = 0 
byte=ser.readline() #first line not to be plotted
while True:
    counter +=1
    ser.write(b'9') # send a command to the arduino
    byte=ser.read(7) #read 7 bytes back
    for i in range(0,7):
        update_line(lines[i][0], counter, byte[i]) # Trying to plot the new values to each different subplots
    plt.pause(0.01)
    sleep(.5) # Delay for one half of a second

But it still does not show anything. 但是它仍然没有显示任何东西。 I kind of guess I am missing a plot and/or clear somewhere but after trying several options can't get it to work. 我有点猜想我错过了一个图和/或在某个地方清除,但是尝试了几种选择后却无法正常工作。

As someone who worked in an optics lab and struggled to get Matplotlib to perform real-time plotting, I feel your pain and I strongly suggest choosing something other than Matplotlib for this purpose (such as pyqtgraph ). 作为一个在光学实验室工作的人,努力使Matplotlib进行实时绘图,我感到很痛苦,因此我强烈建议为此选择Matplotlib以外的其他东西(例如 pyqtgraph )。

That said, I've gotten Matplotlib to perform some real-time plotting from sensor data. 就是说,我已经让Matplotlib从传感器数据执行一些实时绘图。 I've found it to be buggy. 我发现它有问题。 Here are some thoughts as well as a solution that uses matplotlib: 这里是一些想法以及使用matplotlib的解决方案:

Use dictionaries where possible. 尽可能使用字典。 Why? 为什么? Because accessing dictionaries is fast, and I find that a dictionary key is easier to use than a list index for these purposes. 由于访问字典的速度很快,因此我发现字典键比列表索引更易于使用。

Use lists instead of NumPy arrays. 使用列表而不是NumPy数组。 Why? 为什么? Because every time you resize or append a NumPy array it must be completely rewritten as a new object in memory. 因为每次您调整NumPy数组的大小或附加NumPy数组时,都必须将其完全重写为内存中的新对象。 This is very costly. 这是非常昂贵的。 Lists can be resized and appended for negligible cost. 列表可以调整大小并附加到可以忽略不计的成本中。

The code below uses random data to simulate incoming sensor data and to make troubleshooting easier. 下面的代码使用随机数据来模拟传入的传感器数据并简化故障排除。

1. Imports 1.进口

from time import sleep
import matplotlib.pyplot as plt
import numpy as np
#import serial

2. Setup your matplotlib objects and data containers 2.设置您的matplotlib对象和数据容器

# specify how many points to show on the x-axis
xwidth = 10

# use real-time plotting
plt.ion()

# setup each of the subplots
ax = []
fig, ax[0:7] = plt.subplots(7, 1, sharex=False, sharey=False)

# set up each of the lines/curves to be plotted on their respective subplots
lines = {index: Axes_object.plot([],[])[0] for index, Axes_object in enumerate(ax)}

# cache background of each plot for fast re-drawing, AKA Blit
ax_bgs = {index: fig.canvas.copy_from_bbox(Axes_object.bbox) 
          for index, Axes_object in enumerate(ax)}

# initial drawing of the canvas
fig.canvas.draw()

# setup variable to contain incoming serial port data
y_data = {index:[0] for index in range(len(ax))}
x_data = [-1]

3. Write functions for update the plot and for updating your data containers 3.编写函数以更新绘图和更新数据容器

def update_data(new_byte, ):
    x_data.append(x_data[-1] + 1)
    for i, val in enumerate(new_byte): 
        y_data[i].append(val)

def update_graph():
    for i in y_data.keys():
        # update each line object
        lines[i].set_data(x_data, y_data[i])

        # try to set new axes limits
        try:
            ax[i].set_xlim([x_data[-1] - xwidth, x_data[-1]])
            if max(y_data[i][-xwidth:]) > ax[i].get_ylim()[1]:
                new_min = min(y_data[i][-xwidth:])
                new_max = max(y_data[i][-xwidth:])
                ax[i].set_ylim([new_min-abs(new_min)*0.2, new_max+abs(new_max)*0.2])
        except:
            continue

    fig.canvas.draw()

4. Finally, run the loop 4.最后,运行循环

#ser = serial.Serial('COM3', 115200) # Establish the connection on a specific port
#byte=ser.readline() #first line not to be plotted


while x_data[-1] < 30:
    # ser.write(b'9') # send a command to the arduino
    # byte=ser.read(7) #read 7 bytes back
    byte = np.random.rand(7)

    update_data(byte)
    update_graph()

    sleep(.1) # Delay for an arbitrary amount of time

I hope that helps. 希望对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM