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()
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()
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.