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()
7
  • this code do nothing. Where is FuncAnimation ? Commented Nov 22, 2016 at 16:49
  • @furas it's from mpl Commented Nov 22, 2016 at 16:51
  • I know FuncAnimation is from mpl but I did see it in your code. But I see you changed code. Commented Nov 22, 2016 at 16:54
  • sry for link only answer, but you need some blitting in your life... only update the existing data in your graph. don't redraw the whole thing every time in animate(i) Commented Nov 22, 2016 at 16:56
  • Possible duplicate of why is plotting with Matplotlib so slow? Commented Nov 22, 2016 at 16:57

2 Answers 2

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()
Sign up to request clarification or add additional context in comments.

1 Comment

Wow. Thank you so much "ImportanceOfBeingErnest". i can see the huge speed up in my code :-)
1

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.

4 Comments

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 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 you are perfect. You solved my problem. thank you so much
If anyone has a low framerate with this example, just disable latex. (text.usetex : False)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.