Arduino Live Serial Plotting с помощью MatplotlibAnimation становится медленным

Я делаю живой плоттер, чтобы показать аналоговые изменения от датчика Arduino. Arduino выводит значение в последовательный порт со скоростью 9600 бод. Код Python выглядит следующим образом:

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

ser = serial.Serial("com3", 9600)
ser.readline()

optimal_frequency = 100

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

# the following arrays must be initialized outside the loop

xar = []
yar = []

print(time.ctime())

def animate(i):
    global b, xar, yar # otherwise a

    for i in range(optimal_frequency):

        a = str(ser.readline(), 'utf-8')
        try:
            b = float(a)
        except ValueError:
            ser.readline()
        xar.append(str(time.time()))
        yar.append(b)
    ax1.clear()
    ax1.plot(xar, yar)

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

Я получаю нормальное время отклика на графике, но когда я рисую более 20 минут, время реакции увеличивается примерно до 1 минуты. т.е. для обновления графика с новыми значениями требуется 1 минута. Я также пробовал с PyQtGraph, но это задерживается с самого начала.

Помимо задержки на время более 20 минут, я получаю некоторые выбросы и недолеты в сюжете.

Любая помощь?


person Ivy    schedule 19.12.2016    source источник
comment
Я думаю, что проблема в том, что переменные xar и yar через некоторое время становятся огромными. Вы можете попробовать использовать понимание списка и вместо этого выполнить динамическое чтение списка.   -  person Jalo    schedule 19.12.2016
comment
Через 20 минут вы можете перестать рисовать, так как ваш график с более чем 10 миллионами связанных точек станет совершенно нечитаемым.   -  person Stop harming Monica    schedule 19.12.2016
comment
@ Джало, спасибо. Я вижу, что переменные становятся слишком большими. Проблема в том, что мне нужно снова построить все значения, так как я каждый раз очищаю график, чтобы не перегружать его память. Понижу частоту дискретизации :-)   -  person Ivy    schedule 19.12.2016
comment
То, что вы пытаетесь сделать, обрабатывает слишком много данных, чтобы динамически отображать все исторические данные на каждой итерации. Для более простого подхода я бы сохранял полученные данные на каждой итерации в файле для дальнейшего анализа и тем временем отображал только последние N итераций.   -  person Jalo    schedule 19.12.2016


Ответы (2)


как упоминалось в комментариях, вы делаете две вещи неправильно:

  • вы продолжаете добавлять входящие данные в свои массивы, которые через некоторое время становятся огромными
  • вы очищаете свои оси и создаете новый ax.plot() на каждой итерации.

вместо этого вы хотите:

в функции инициализации создайте пустой объект Line2D

def init():
    line, = ax.plot([], [], lw=2)
    return line,

затем в вашей функции обновления (animate()) вы используете line.set_data() для обновления координат ваших точек. Однако для поддержания производительности вы должны поддерживать разумный размер ваших массивов, поэтому вам придется отбрасывать старые данные по мере поступления новых данных.

def animate(i):
    (...)
    xar.append(str(time.time()))
    yar.append(b)
    line.set_data(xar, yar)
    return line,

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

person Diziet Asahi    schedule 19.12.2016

Как упоминалось в комментариях, чтение из массивов естественно замедляется из-за их большого размера. При скорости 9600 бод я получаю около 130 последовательных показаний и отметок времени в секунду в переменных xar и yar; который через 20 минут получает размер 156000. Уменьшение скорости передачи до 2400 уменьшает длину переменных до 36000, и это можно легко повторить.

person Ivy    schedule 20.12.2016