1

I need your help for a problem that i'm dealing with it these days. I can plot a serial data which transfered from my cell phone Bluetooth and received by COM Port of my laptop. In the first glance it seems to be Ok, but at most it can plot every 260 ms (~3 fps). however the cellphone send data every 100 ms. I am pretty sure that the problem stems from "plot" and "figure" command that makes me confused. I appreciate if somebody can correct my code:

from Tkinter import *
import serial
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
ser = serial.Serial("COM4", baudrate=115200, timeout=0.1)
cnt=0
xComponent=[]
plt.ylim(0,30)
while (ser.inWaiting() == 0): # Wait here until there is data
    pass
def animate(i):

    BluetoothString = ser.readline()
    ser.flush()
    dataArray = BluetoothString.split(',')
    x = float(dataArray[2]) # we only need 3rd component
    xComponent.append(x)
    print xComponent
    ax1.clear()
    ax1.plot(xComponent)
    plt.ylim(0,25)
    global cnt
    if (cnt > 16): 
        xComponent.pop(0)
    else:
        cnt = cnt + 1

ani = animation.FuncAnimation(fig, animate, interval=0)
plt.show()
Shayan Shiri
  • 51
  • 1
  • 6
  • this code do nothing. Where is `FuncAnimation` ? – furas Nov 22 '16 at 16:49
  • @furas it's from [mpl](http://matplotlib.org/api/animation_api.html#matplotlib.animation.FuncAnimation) – Aaron Nov 22 '16 at 16:51
  • I know `FuncAnimation` is from `mpl` but I did see it in your code. But I see you changed code. – furas Nov 22 '16 at 16:54
  • sry for link only answer, but you need some [blitting](http://devosoft.org/making-efficient-animations-in-matplotlib-with-blitting/) in your life... only update the existing data in your graph. don't redraw the whole thing every time in `animate(i)` – Aaron Nov 22 '16 at 16:56
  • Possible duplicate of [why is plotting with Matplotlib so slow?](http://stackoverflow.com/questions/8955869/why-is-plotting-with-matplotlib-so-slow) – Aaron Nov 22 '16 at 16:57
  • "fig" and "ax1" are the initialization for plots and subplots. "ser" = initializing serial port. "xComponent" is a free buffer to append data to it. "x "is a variable that I get the serial data from cellphone. and "cnt" is a command to only show 16 data to plot – Shayan Shiri Nov 22 '16 at 17:14
  • @Aaron : I also found update code but donno how to implement it to my code :-( – Shayan Shiri Nov 22 '16 at 17:19

2 Answers2

1

It's hard to say anything about your special case, since we do not have the serial connection part that you're using.

Plotting should however be much faster than 3 fps in matplotlib if this is only a line plot with some points in it. One thing you can directly try it not to replot everything at every iteration step, but plot it once and then only update the data using .set_data()

The following example is closely related to your code and runs with 90 fps on my machine. So maybe you try that one out and see if it helps speeding up your case.

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

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

cnt=0
xComponent=[]

line,  = ax1.plot([0], [0])
text = ax1.text(0.97,0.97, "", transform=ax1.transAxes, ha="right", va="top")

plt.ylim(0,25)
plt.xlim(0,100)
last_time = {0: time.time()}
def animate(i):

    if len(xComponent)>100:
        xComponent.pop(0)
    y = i % 25
    xComponent.append(y)

    line.set_data(range( len(xComponent) ) ,xComponent)
    new_time = time.time()
    text.set_text("{0:.2f} fps".format(1./(new_time-last_time[0])))
    last_time.update({0:new_time})


ani = animation.FuncAnimation(fig, animate, interval=0)
plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
0

I don't want to step on any toes here, because @ImportanceOfBeingErnest nailed it, but adding blitting to his example jumped my framerate from 50 to 300. here's how to do it in his example: I left comments where I made changes

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

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

cnt=0
xComponent=[]

line,  = ax1.plot([0], [0])
text = ax1.text(0.97,0.97, "", transform=ax1.transAxes, ha="right", va="top")

plt.ylim(0,25)
plt.xlim(0,100)
last_time = {0: time.time()}

def animateinit(): #tells our animator what artists will need re-drawing every time
    return line,text

def animate(i):

    if len(xComponent)>100:
        xComponent.pop(0)
    y = i % 25
    xComponent.append(y)

    line.set_data(range( len(xComponent) ) ,xComponent)
    new_time = time.time()
    text.set_text("{0:.2f} fps".format(1./(new_time-last_time[0])))
    last_time.update({0:new_time})
    return line,text #return the updated artists

#inform the animator what our init_func is and enable blitting
ani = animation.FuncAnimation(fig, animate, interval=0,init_func=animateinit, blit=True) 
plt.show()

each draw call in mpl is rather expensive, so if we can draw as little as possible we see a huge speedup. by telling the animator to only re-draw certain elements, we avoid having to re-draw things like axes markers, axes labels, calculating scaling etc. those things seem simple, but there are many of them, and the overhead adds up quickly.

Community
  • 1
  • 1
Aaron
  • 10,133
  • 1
  • 24
  • 40
  • 1
    I didn't say blitting wouldn't be useful in many cases. It just seems a little overkill when the data arrives every 100 ms (giving a maximum of 10 fps). Since the example without blitting already reaches framerates which are not detectable by the human brain, it may not be needed after all. (Personally I'd anyways try to avoid blitting, because it also causes a lot of overhead code when you need to take care of zooming, resizing etc. In cases where blitting might be needed I'd directly change from Matplotlib to pyqtgraph, which is a lot faster and keeps the code simple.) – ImportanceOfBeingErnest Nov 22 '16 at 21:30
  • @ImportanceOfBeingErnest I'm not familiar with doing stuff like serial over Bluetooth, so I personally would favor the speedup in case data came on irregular time intervals, hopefully reducing the risk of overflowing a buffer.. just thought I'd make the addition because it was only about 4 lines and it's a good thing to know how to do – Aaron Nov 22 '16 at 21:36
  • Aaron you are perfect. You solved my problem. thank you so much – Shayan Shiri Nov 23 '16 at 00:09
  • If anyone has a low framerate with this example, just disable latex. (text.usetex : False) – TheIdealis Oct 07 '17 at 16:21