0

I try to plot several data I receive over the USART serial bus from a microcontroller in python. It would be fine, if I could plot all the data in parallel and in realtime.

When I use a single plot, the data is plotted in realtime but when I use subplots, the data has more and more delay, also if I only plot one subchannel. Has someone any Idea why subplots in python are so much slower?

I measured also the time consumption for the function update(), it seems to be 2ms or less. The Data I receive only every 5ms or more. How can I improve the speed?

Kind regards: Sebastian T.

Here is my code

import serial
import matplotlib.pyplot as plt
import matplotlib.animation as anim
import time
from collections import deque

#SERIAL#######################################################
try:
    ser = serial.Serial()
    ser.baudrate=115200;
    ser.port = 'COM7'
    ser.open()
except:
    ser.close()
    print('Problem occured')

ser.flushInput()
ser.flushOutput()

#PLOT##########################################################

MAX_X = 250   #width of graph
MAX_Y = 70000  #height of graph

# intialize line to horizontal line on 0
line1 = deque([0.0]*MAX_X, maxlen=MAX_X)
line2 = deque([0.0]*MAX_X, maxlen=MAX_X)
line3 = deque([0.0]*MAX_X, maxlen=MAX_X)
line4 = deque([0.0]*MAX_X, maxlen=MAX_X)
line5 = deque([0.0]*MAX_X, maxlen=MAX_X)

plt.close('all')
fig, (ax1,ax2,ax3,ax4) = plt.subplots(4,1)

l1, = ax1.plot([], [])
l2, = ax2.plot([], [])
l3, = ax3.plot([], [])
l4, = ax4.plot([], [])

l=[l1,l2,l3,l4]

for ax in [ax1,ax2,ax3,ax4]:
    ax.set_ylim(-(MAX_Y/2),MAX_Y/2)
    ax.set_xlim(-(MAX_X/2),MAX_X/2)
    ax.grid()

def update(fn, data):
    try:
        t = time.time()
        #prepare Data
        data_raw = ser.readline()
        data_raw = data_raw.split(',')
        data_raw = data_raw[1::2]

        #Update Plots
        line1.append(int(data_raw[0]))
        line2.append(int(data_raw[1]))
        line3.append(int(data_raw[2]))
        line4.append(int(data_raw[3]))

        #Set Data
        l[0].set_data(range(-MAX_X/2, MAX_X/2), line1)
        l[1].set_data(range(-MAX_X/2, MAX_X/2), line2)
        l[2].set_data(range(-MAX_X / 2, MAX_X / 2), line3)
        l[3].set_data(range(-MAX_X / 2, MAX_X / 2), line4)
        print(time.time() - t)

    except:
        print('exception')
        ser.close()

ani = anim.FuncAnimation(fig,update,fargs=(0,),frames=1, interval=100)
plt.show()
HansPeterLoft
  • 489
  • 1
  • 9
  • 28
  • Two remarks regarding the last line of your code: **1.** if you want your plot to be "real time" (so every 5ms or so) why did you set `interval` between frames to 100ms and not 5ms or less? **2.** `frames=1` says that your animation will display only one frame and then loop displaying that one frame, that won't be that animated ... – jadsq Oct 29 '16 at 12:23
  • Hi jadsq, I forgot to change interval to 1ms when I posted here, but also with interval=1ms I get not realtime plots. Frames seems not to influence the plots at all when I change it. – HansPeterLoft Oct 29 '16 at 12:47
  • If I for example use frames=23 and interval=1, does then one frame need 23ms? It seems that when I increase the frames, It becomes much worse. Can I somehow set the interval below 1ms? – HansPeterLoft Oct 29 '16 at 13:11
  • 'frames' define the number of frames in your animation but that only influences your `fn` parameter which you never use so, I didn't notice it at first but it actually has o effect at all in this case, you can remove it. `interval` is what defines the length of a frame so only `interval=23` will make it last 23ms (given that the update function is fast enough) – jadsq Oct 29 '16 at 13:17
  • It looks like the animation received the data, but it plots the data maybe 4-5s later. So the animation is too slow, even if the interval is 1ms. Is there an alternative for realtime plots in python? – HansPeterLoft Oct 29 '16 at 13:57
  • My lists have each only 250 values, that get shifted every update() call. Can I somehow make matplotlib not to buffer older values and only plot the actual values of the lists? – HansPeterLoft Oct 29 '16 at 14:07
  • I found the solution, I used "blit=True" now and added a "return l" to the update function – HansPeterLoft Oct 29 '16 at 15:59
  • There is still a problem: If I let the plot small, everything works fine, but with the plot enlarged it begins to lag. – HansPeterLoft Oct 29 '16 at 16:07
  • I suggest to use another plotting package. You can find several discussions on the speed(up) of matplotlib. But its main purpose is the generation of high-quality figures for presentation/publication. If speed is important, there are some other libraries that are better. I had some positive experience with pyQtgraph, just as an example. – dnalow Oct 29 '16 at 20:50
  • I suppose the call to FuncAnimation makes things kind of worse. Did you try without, directly plotting to canvas as fast as you can? I recently answered [a question about Matplotlib plotting speed](http://stackoverflow.com/questions/40126176/fast-live-plotting-in-matplotlib-pyplot/40139416) here. Blitting may of course help - otherwise consider using [pyqtgraph](http://www.pyqtgraph.org), the advantages of which also become clear in the linked answer. – ImportanceOfBeingErnest Oct 31 '16 at 00:04

1 Answers1

0

This is a known issue if plotted data points grow in number. I plotted data from a D/A-converter realtime and had to clear the axis with cla() after around 500 plotted points.

The reason is that calling "def update" all the time completely updates the whole figure containing all plot points...it is clar that this is getting more and more unperformant

Greets Dr Cobra