Матплотлиб "Реальное время" зарисовка на питоне

Я на питоне 3.7. Я пытаюсь прочитать данные с последовательного порта, это будет 7 разных байтов. Затем я хотел бы разместить каждый отдельный байт на отдельном участке. Я хочу читать последовательный порт каждые 500 мс, и каждый раз, когда я читаю, добавляю новые данные на участки. Каждое чтение дает еще одну информацию для построения на каждом участке. Это в основном показания датчика.

Вот код, который я написал:

из времени импорта сна импорт последовательного импорта matplotlib.pyplot как 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

На рисунке показано, и ось x и ось y адаптируются к значению, которое я хочу указать, но на графике нет никаких данных вообще. Если я использую Scatter вместо графика, он работает, но тогда он менее универсален, и я не могу нарисовать тот тип графика, который мне нужен. Я также пытаюсь воспроизвести проблему, не используя последовательные данные, а просто отображая точки списка один за другим, вот так:

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)

И он делает то же самое, последнее изображение, которое я имею на графике: Который показывает ось, взял то, что я хотел построить, но ничего не изобразил. Дело в том, что я не хочу очищать весь график и перерисовывать все, потому что для датчика данных у меня будет около 30 дней данных для отображения в непрерывном режиме. Что я делаю не так с написанным мною кодом?

РЕДАКТИРОВАТЬ: После комментария ImportanceOfBeingErnest я попытался реализовать ответ, приведенный здесь. Код тогда:

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

Но это все равно ничего не показывает. Я вроде думаю, что мне не хватает сюжета и / или где-то очистить, но после попытки нескольких вариантов не может заставить его работать.

0 ответов

Как человек, который работал в оптической лаборатории и изо всех сил пытался заставить Matplotlib выполнять построение графиков в реальном времени, я чувствую вашу боль и настоятельно рекомендую выбрать для этой цели что-то другое, кроме Matplotlib (например, pyqtgraph).

Тем не менее, у меня есть Matplotlib для построения графиков в реальном времени на основе данных датчиков. Я обнаружил, что это глючит. Вот некоторые мысли, а также решение, использующее matplotlib:

По возможности используйте словари. Почему? Потому что доступ к словарям осуществляется быстро, и я считаю, что для этих целей проще использовать ключ словаря, чем индекс списка.

Используйте списки вместо массивов NumPy. Почему? Потому что каждый раз, когда вы изменяете размер или добавляете массив NumPy, он должен быть полностью переписан как новый объект в памяти. Это очень дорого. Списки можно изменять и добавлять за незначительную плату.

В приведенном ниже коде используются случайные данные для имитации входящих данных датчика и упрощения устранения неполадок.

1. Импорт

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

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. Напишите функции для обновления графика и обновления контейнеров данных.

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. Наконец, запустите цикл

#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

Надеюсь, это поможет.

Другие вопросы по тегам