0

I am streaming accelerometer data from my android phone and have successfully built a live plot using matplotlib. I am using the comma operator to dynamically update the plot but I am wondering if there is a more elegant/pythonic way to do it. To execute the code below you must use the app Sensorstream IMU+GPS. The code below will grab the accelerometer values and plot them live. I based the plotting on Can you plot live data in matplotlib?. Like I said it works but the code is clumsy. Even with speedups mentioned in the matplotlib documentation I am running at about 25 FPS. The technique, if I only use a simple plot can get up to about 90 FPS. It can be shown that you can achieve ~200 FPS of faster here why is plotting with Matplotlib so slow?. I cannot find my bottleneck. So, is there a more elegant way to code up all the subplots? Second, can I speed up the plotting?

import socket, traceback
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from scipy.signal import butter, lfilter,iirfilter,savgol_filter
import math
import pylab
from pylab import *
import time
import numpy as np


host = ''
port = 5555
 
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((host, port))

# lists for plotting
Ax = [0.0] * 50
Ay = [0.0] * 50
Az = [0.0] * 50
G  = [0.0] * 50
x = [i for i in range(len(Ax))]
 
#used for debugging
 
fig = plt.figure(figsize=(16,10))

# raw data
ax = plt.subplot("311")
ax.set_xlim(0, 50)
ax.set_ylim(-2, 2)
ax.set_title("Raw acceleration data")
ax.set_ylabel("g$/m^2$",fontsize=18)
ax.hold(True)

line  = ax.plot(Ax,label='Acc x')[0]
line2 = ax.plot(Ay,label='Acc y')[0]
line3 = ax.plot(Az,label='Acc z')[0]

# filtered data
ax2 = plt.subplot("312")
ax2.set_xlim(0, 50)
ax2.set_ylim(-2, 2)
ax2.set_title(" acceleration data")
ax2.set_ylabel("g$/m^2$",fontsize=18)
ax2.hold(True)

f_line  = ax2.plot(Ax,label='Acc x')[0]
f_line2 = ax2.plot(Ay,label='Acc y')[0]
f_line3 = ax2.plot(Az,label='Acc z')[0]

# tilt angle plot
ax3 = plt.subplot("313")
ax3.set_ylim([-180,180])
ax3.set_title("Tilt Angles")
ax3.set_ylabel("degrees",fontsize=18)
t_line = ax3.plot(G)[0]

fig.suptitle('Three-axis accelerometer streamed from Sensorstream',fontsize=18)
plt.show(False)
plt.draw()

# cache the background
background = fig.canvas.copy_from_bbox(fig.bbox)

count = 0 
print("Success binding")
while 1:
    # time it
    tstart = time.time()
    message, address = s.recvfrom(8192)
    messageString = message.decode("utf-8")
    Acc = messageString.split(',')[2:5]
    Acc = [float(Acc[i])/10.0 for i in range(3)]
    
    # appending and deleting is order 10e-5 sec
    Ax.append(Acc[0])
    del Ax[0]
    Ay.append(Acc[1])
    del Ay[0]
    Az.append(Acc[2])
    del Az[0]
    G.append(np.sqrt(Ax[-1]**2 + Ay[-1]**2 + Az[-1]**2))
    del G[0]
    
    # filter
    acc_x_savgol = savgol_filter(Ax, window_length=5, polyorder=3)
    acc_y_savgol = savgol_filter(Ay, window_length=5, polyorder=3)
    acc_z_savgol = savgol_filter(Az, window_length=5, polyorder=3)
    
    tilt_angles = []
    for i,val in enumerate(G): 
        angle = math.atan2(Ax[i], -1*Ay[i]) * (180 / math.pi)
        if (math.isnan(angle)):
            tilt_angles.append(0)
        else:
            tilt_angles.append(angle)
            
    print(Ax[0],Ay[1],Az[2])   
    
    line.set_xdata(x)
    line.set_ydata(Ax)
    line2.set_xdata(x)
    line2.set_ydata(Ay)
    line3.set_xdata(x)
    line3.set_ydata(Az)
    ax.set_xlim(count, count+50)
    
    f_line.set_xdata(x)
    f_line.set_ydata(acc_x_savgol)
    f_line2.set_xdata(x)
    f_line2.set_ydata(acc_y_savgol)
    f_line3.set_xdata(x)
    f_line3.set_ydata(acc_z_savgol)
    ax2.set_xlim(count, count+50)

    t_line.set_xdata(x)
    t_line.set_ydata(tilt_angles)
    ax3.set_xlim(count, count+50)
    # restore background
    fig.canvas.restore_region(background)

    # redraw just the points
    ax.draw_artist(line)
    ax.draw_artist(line2)
    ax.draw_artist(line3)
    ax2.draw_artist(f_line)
    ax2.draw_artist(f_line2)
    ax2.draw_artist(f_line3)
    ax3.draw_artist(t_line)

    # fill in the axes rectangle
    fig.canvas.blit(fig.bbox)
    
    count+=1
    x = np.arange(count,count+50,1)
    
    # tops out at about 25 fps :|
    print "Total time for 1 plot is: ",(time.time() - tstart)

1 Answer 1

0

As mentioned in the Matplotlib documentation you need to plot a subplot before adding a new plot to the current figure. It mentions plt.figure(x) is optional, but it is recommended to do so. I would append the following code to plot multiple sub-plots.

plt.figure(1)
plt.subplot(211)
plt.plot('''Data''')

plt.subplot(212)
plt.plot('''Data''')
plt.draw()
Sign up to request clarification or add additional context in comments.

2 Comments

Maybe I am missing something but could you be a little more specific on how this answers the questions?
I meant to say you need to plot() the results before draw() them. That way the interpreter knows the canvas is filled with sub-plots.

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.