speed up mplotlib when plotting live serial data - python

I am using an Arduino to create a 3d lidar scan of the area around it, and sending x,y,z coordinates over serial to a python script to visualize the data live. The issue I've run into is that mplotlib is much too slow to keep up with the stream. I have a feeling this is due to completely redrawing the plot, but I have not found a solution. I write s1000 over serial to start the lidar scan, and sending a x,y,z coordinate once per second.
import serial
import numpy
import matplotlib.pyplot as plt #import matplotlib library
from mpl_toolkits.mplot3d import Axes3D
from drawnow import *
from matplotlib import animation
import time
ser = serial.Serial('COM7',9600,timeout=5)
ser.flushInput()
time.sleep(5)
ser.write(bytes(b's1000'))
plt.ion()
fig = plt.figure(figsize=(16,12))
ax = fig.add_subplot(111, projection="3d")
ax.set_xlim3d(-255, 255)
ax.set_ylim3d(-255, 255)
ax.set_zlim3d(-255, 255)
x=list()
y=list()
z=list()
while True:
try:
ser_bytes = ser.readline()
data = str(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
xyz = data.split(", ")
dx = float(xyz[0])
dy = float(xyz[1])
dz = float(xyz[2].replace(";",""))
x.append(dx);
y.append(dy);
z.append(dz);
ax.scatter(x,y,z, c='r',marker='o')
plt.draw()
plt.pause(0.0001)
except:
print("Keyboard Interrupt")
ser.close()
break

Related

Implementing NI USB-6009 and Optical Sensor for Project

I am currently working on a project where I utilise NI USB - 6009 and an optical sensor to register wheel rotation then from there calculate the angular and linear velocity. My plan is to plot that output in the python GUI (tkinter) and if possible the graph as well. My arrangement is as followed:
digital output from sensor to NI USB 6009 digital input, vcc to 5v and gnd to ground (I think that is the standard wiring). So far, I can put the number of pulses on the gui but it has to be plotted together with the plot here and also I am not able to calculate the time between pulses because the task module takes extra work. Did anyone have a similar experience so one might share the solution? or should I go with arduino instead?
Appreciate your help
here are my codes:
import nidaqmx
import math
from nidaqmx.constants import Edge
from nidaqmx.constants import AcquisitionType
from nidaqmx.constants import VoltageUnits
from nidaqmx.constants import TerminalConfiguration
import matplotlib.pyplot as plt
from matplotlib import style
import numpy as np
import time
import nidaqmx.system
from nidaqmx.system import system as nidaqSystem
import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
nfft = 1000
fs = 1000.0
win = tk.Tk()
win.configure(bg='white')
win.geometry("650x400")
win.resizable(False,False)
with nidaqmx.Task() as task:
task.di_channels.add_di_chan("Dev1/port1/line0")
# task.timing.cfg_samp_clk_timing(fs, active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=nfft)
fig = plt.figure()
# ax1 = fig.add_subplot(211)
# ax2 = fig.add_subplot(212)
# freqs = np.linspace(0, int(fs/2), int(nfft/2+1))
while True:
t1 = (int(round(time.time() * 1000)))
data = task.read(number_of_samples_per_channel=nfft)
if data == True:
data == 1
else:
data == 0
pulse = math.floor(np.sum(np.diff(data) > 0)/2)
print(pulse)
count_label = ttk.Label(win,text=pulse,background='white')
count_label.place(x=10, y=10)
# graph = FigureCanvasTkAgg(fig,win)
# graph.draw()
# graph.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH,expand=True)
# count_entry = ttk.Entry(win,textvariable=print(pulse))
# count_entry.place(x=10,y=30)
# count_entry = tk.StringVar()
# ax1.clear()
# ax2.clear()
# ax1.plot(data)
# ax2.plot(pulse)
fig.show()
plt.pause(0.1)
# t3 = (int(round(time.time() * 1000)))
win.mainloop()

Updating a real time plot using Matplotlib

I'm trying to generate a real time plot using Matplotlib, with real time gathered data from an Arduino MKR1000.
Code:
import serial
import matplotlib.pyplot as plt
import numpy as np
ser = serial.Serial('COM7', 9600)
plt.close('all')
plt.figure(figsize=[10,8])
plt.ion()
data = np.array([])
while True:
a = ser.readline()
a = a.decode()
b = float(a[0:4])
data = np.append(data, b)
plt.plot(data)
plt.pause(0.1)
The output is a new plot created every 0.1 seconds, but I want to update the same real-time plot.
Anyone can help?
I'm using Python 3 on a Windows 10 system

Simple matplotlib.animation graph geting slower very fast and than stoping

I've started to learn about matplotlib functions because i wanted to visualize data i was receiving via websocket. For that i made a dummy program that mimics the behaviour of my main program but has added the functionality of mathplotlib. what i noticed is the program takes more and more time to finish each loop and eventually 'freezes'. i managed to extend it life by changing the interval in animation.FuncAnimation from 1000 to 10000. But that just the program to plot sometimes up to 9s for 1 new peace of data. I believe the problem lays in a inappropriate way of cleaning old plots. But i don't know where exactly i did the mistake
import time
import datetime
import timeit
import queue
import os
import random
import copy
import matplotlib.pyplot as plt
import matplotlib.animation as animation
q = queue.Queue()
beta=[0,]
b=False
czas=[]
produkty=["primo"]
cena=[[] for _ in range(len(produkty))]
fig=plt.figure()
#ax1=fig.add_subplot(1,1,1)
#ax2=fig.add_subplot(1,1,1)
ax1=plt.subplot(1,1,1)
ax2=plt.subplot(1,1,1)
def animate(i):
ax1.clear()
ax2.clear()
ax1.plot(czas,cena[0])
ax2.plot(czas,beta)
while True:
time.sleep(1)
alpfa=time.time()
#input('press enter')
rand_produkt=random.choice(produkty)
rand_price=random.randint(1,10)
rand_czas=time.ctime()
alfa={'type':'ticker','price':rand_price,'product_id':rand_produkt,'time':rand_czas}
q.put(alfa)
if q.not_empty:
dane=q.get()
typ=dane.get('type',None)
if typ=='ticker':
price=dane.get('price', None)
pair=dane.get('product_id',None)
t=dane.get('time', None)
b=True
if b==True:
b=False
produkt_id=produkty.index(pair)
cena[produkt_id].append(float(price))
czas.append(t)
plt.ion()
ani=animation.FuncAnimation(fig,animate,interval=1000)#, blit=True)repeat=True)
plt.show()
plt.pause(0.001)
#fig.clf()
beta.append(time.time()-alpfa)
print(beta[-1])
The problem with your code is that you call a new animation in you while loop. Hence this will cause slow down down the line. It is better to initiate your plot ones. One trick may be to update the object data directly as such:
from matplotlib.pyplot import subplots, pause, show
from numpy import sin, pi
fig, ax = subplots()
x = [0]
y = [sin(2 * pi * x[-1])]
p1, = ax.plot(x, y)
show(block = False)
while True:
# update data
x.append(x[-1] + .1)
y.append(sin(2 * pi * x[-1]))
p1.set_data(x, y) # update data
ax.relim() # rescale axis
ax.autoscale_view()# update view
pause(1e-3)

Erratic data stream from python sketch

I am trying to output time, pitch, roll and yaw from a quadrotor and create graphs using python. I have two XBee modules sending data from the quadrotor to the computer. The values I receive from the python sketch are very erratic, there seem to be a large number of errors, this seems to be caused by unwanted numbers randomly appearing in the data stream. I cannot seem to solve this, does anyone have any ideas??? Below I've attached a copy of the code.
import serial # import Serial Library
import numpy as np # Import numpy
import matplotlib.pyplot as plt #import matplotlib library
from drawnow import *
time=[]
d0k0=[]
d0k1=[]
d0k2=[]
arduinoData = serial.Serial('COM4', 9600) #Creating our serial object named arduinoData
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt=0
ydata = [0] * 50
#ax1 = plt.axes()
#line, = plt.plot(ydata)
def makeFig(): #Create a function that makes our desired plot
plt.ylim(180,-180) #Set y min and max values
plt.title('Stability') #Plot the title
plt.grid(True) #Turn the grid on
plt.ylabel('Kalman estimated angles') #Set ylabels
line.set_xdata(time)
line.set_ydata(d0k0)
#plt.plot(t, d2)
#plt.plot(t, d3)
plt.legend(['roll','pitch','yaw'], loc='upper left')
plt.draw()
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
data = arduinoData.readline()
data = data.decode("ascii", "ignore")
data = ''.join([c for c in data if c in '1234567890.,'])
data = data.strip()
data = data.split(",")
if len(data) == 4:
print(data)
t = float( data[0])
#print (t)
d1 = float( data[1])
d2 = float( data[2])
d3 = float( data[3])
time.append(t)
d0k0.append(d1)
#d0k1.append(d2)
#d0k2.append(d3)
#drawnow(makeFig)
#plt.pause(0.01)

Graph figure not displayed when live plotting arduino data with matplotlib

I am trying to plot real-time lectures from Arduino UNO's analogue input with matplotlib.
My problem: The graph would not be shown. Only when I stop running the code (Ctrl+C), It will show the last values's graph.
when adding "print pData" line to the code in order to check whether the values are properly arriving to the computer, these are correctly displayed (shows the 25 values array each second) on the python terminal.
#!/usr/bin/python
from matplotlib import pyplot
import pyfirmata
from time import sleep
# Associate port and board with pyFirmata
port = '/dev/ttyACM0'
board = pyfirmata.Arduino(port)
# Using iterator thread to avoid buffer overflow
it = pyfirmata.util.Iterator(board)
it.start()
# Assign a role and variable to analog pin 0
a0 = board.get_pin('a:0:i')
pyplot.ion()
pData = [0.0] * 25
fig = pyplot.figure()
pyplot.title('Real-time Potentiometer reading')
ax1 = pyplot.axes()
l1, = pyplot.plot(pData)
pyplot.ylim([0, 1])
while True:
try:
sleep(1)
pData.append(float(a0.read()))
pyplot.ylim([0, 1])
del pData[0]
l1.set_xdata([i for i in xrange(25)])
l1.set_ydata(pData) # update the data
#print pData
pyplot.draw() # update the plot
except KeyboardInterrupt:
board.exit()
break
Here is a mock-up that uses matplotlib.animation for real-time plotting.
from matplotlib import pyplot
import matplotlib.animation as animation
import random
# Generate sample data
class Pin:
def read(self):
return random.random()
a0 = Pin()
n = 25
pData = [None] * n
fig, ax = pyplot.subplots()
pyplot.title('Real-time Potentiometer reading')
l1, = ax.plot(pData)
# Display past sampling times as negative, with 0 meaning "now"
l1.set_xdata(range(-n + 1, 1))
ax.set(ylim=(0, 1), xlim=(-n + 1, 0))
def update(data):
del pData[0]
pData.append(float(a0.read()))
l1.set_ydata(pData) # update the data
return l1,
ani = animation.FuncAnimation(fig, update, interval=1000, blit=True)
try:
pyplot.show()
finally:
pass
#board.exit()

Categories

Resources