I am trying to read from serial port and plot the data in graph using matplot.
Following is my code :
I see that because of plot, there is huge lag (data in queue goes up to 10000 bytes) hence i dont see real time plot coming. Can you please help me if i am doing anything wrong.
<
import serial # import Serial Library
import numpy # Import numpy
import matplotlib.pyplot as plt
Accelerometer= []
COM_read = serial.Serial('COM5', 9600, timeout=None,parity='N',stopbits=1,rtscts=0) #Creating our serial object name
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt=0
COM_read.flushInput()
COM_read.flushOutput()
def makeFig(): #Create a function that makes our desired plot
plt.title('My Live Streaming Sensor Data') #Plot the title
plt.grid(True) #Turn the grid on
plt.ylabel('Acc in g') #Set ylabels
plt.plot(Accelerometer, 'ro-', label='Accelerometer g') #plot the temperature
plt.legend(loc='upper left') #plot the legend
plt.ylim(15000,30000) #Set limits of second y axis- adjust to readings you are getting
print "came through"
while True: # While loop that loops forever
print COM_read.inWaiting()
while (COM_read.inWaiting()==0): #Wait here until there is data
pass #do nothing
s = COM_read.readline() #read the line of text from the serial port
decx = int(s[0:4],16)
decy = int(s[5:9],16)
decz = int(s[10:14],16)
if decx > 32768:
decx = decx - 65536;
if decy > 32768:
decy = decy - 65536;
if decz > 32768:
decz = decz - 65536;
#print decx,decy,decz
res = ((decx*decx) + (decy*decy) + (decz*decz))**(1.0/2.0)
Accelerometer.append(res) #Build our Accelerometer array by appending temp readings
drawnow(makeFig) #Call drawnow to update our live graph
plt.pause(.000001) #Pause Briefly. Important to keep drawnow from crashing
cnt=cnt+1
if(cnt>50): #If you have 50 or more points, delete the first one from the array
Accelerometer.pop(0) #This allows us to just see the last 50 data points
>
----------------Based on Dr.John's suggestion, the code is written as follows -----
import serial
import matplotlib
import threading, time
import pylab
import matplotlib.pyplot as plt
import matplotlib.animation as anim
class MAIN:
#ser =0;
def __init__(self):
self.res = 0.0
self.ser = serial.Serial('COM5', 9600, timeout=None,parity='N',stopbits=1,rtscts=0)
self.ser.flushInput()
elf.ser.flushOutput()
c = self.ser.read(1);
while(c != '\n'):
=self.ser.read(1)
return
def acquire_data(self):
s = self.ser.readline()
decx = int(s[0:4],16)
decy = int(s[5:9],16)
decz = int(s[10:14],16)
if decx > 32768:
decx = decx - 65536;
if decy > 32768:
decy = decy - 65536;
if decz > 32768:
decz = decz - 65536;
self.res = ((decx*decx) + (decy*decy) + (decz*decz))**(1.0/2.0)
print self.res
return
ex = MAIN()
threading.Timer(2,ex.acquire_data).start() #starts function acquire_data every 2 sec and resets self.res with 0.5Hz
fig, ax = plt.subplots()
ax.plot(time.time(), self.res, 'o-')
print "closed"
A while true loop is a bad idea in most cases.
Non-OO-Programming is a bad idea in most cases.
Appending plot-data to lists is a bad idea in most cases (laggy).
Start the data aquisition at defined times with
import threading, time
threading.Timer(2, acquire_data).start() #starts function acquire_data every 2 sec and resets self.res with 0.5Hz
then instead of the while-loop, please define class based functions for your own benefit:
class MAIN:
def __init__(self):
self.res = 0
def acquire_data(self):
....
return self.res
and plot with
fig, ax = plt.subplots()
ax.plot(time.time(), self.res, 'o-')
Greets Dr. Cobra
Related
I'm writing a GUI and part of that requires a live graph be displayed. At the moment i'm just getting a straight line being plot.
from mcculw import ul
from mcculw.enums import ULRange
from datetime import datetime as dt
from mcculw.ul import ULError
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import tkinter as Tk
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
board_num = 0
channel = 0
ai_range = ULRange.BIP5VOLTS
try:
# Get a value from the device
value = ul.a_in(board_num, channel, ai_range)
# Convert the raw value to engineering units
eng_units_value = ul.to_eng_units(board_num, ai_range, value)
# Display the raw value
print("Raw Value: " + str(value))
# Display the engineering value
print("Engineering Value: " + '{:.3f}'.format(eng_units_value))
except ULError as e:
# Display the error
print("A UL error occurred. Code: " + str(e.errorcode)
+ " Message: " + e.message)
x=[]
y=[]
fig, ax = plt.subplots()
def animate(i, x, y):
x.append(i)
y.append(eng_units_value)
ax.clear()
ax.plot(x, y)
x = x[-10:]
y = y[-10:]
ani = animation.FuncAnimation(fig, animate, fargs=(x, y), interval=100)
plt.show()
This is a snippet of the problem with the code I have.
Graph I get
No error codes - just a problem with getting a new value every time it plots i think.
I have a mobile application which does some calculation and throws x,y coordinates and are updated on firebase every 2 seconds.
Next i want those coordinates to be plotted on a floor plan live. For that i am using Scatter plot over the floor plan image. But i cannot make it live as soon as the data is fetched need help with that.
Here is the code till now:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
xs = []
ys = []
fig = plt.figure()
scat = plt.scatter(xs, ys, c='r', s=100)
def main():
graph_data = open("testfile.txt","r").read()
lines = graph_data.split("\n")
for line in lines:
if len(line)>1:
x,y = line.split(",")
xs.append(x)
ys.append(y)
plt.scatter(xs,ys)
print(xs)
print(ys)
ani = animation.FuncAnimation(fig,main(),fargs=(scat))
plt.show()
Getting error with animation.FuncAnimation TypeError: NoneType object argument after * must be an iterable, not PathCollection
You can fetch data in a separate thread while updating the plot in the main one. Here is a complete working example:
#!/usr/bin/env python3
import time
from queue import Queue
from threading import Thread, Event
import numpy as np
import matplotlib.pyplot as plt
FETCH_DELAY = 2
def fetch_data(queue, stop):
while not stop.is_set():
x, y = np.random.randn(2)
queue.put((x, y))
time.sleep(FETCH_DELAY)
def limits(array, offset=1):
return array.min() - offset, array.max() + offset
def main():
stop = Event()
queue = Queue()
worker = Thread(target=fetch_data, args=(queue, stop))
worker.start()
plt.ion()
fig, ax = plt.subplots()
plot = ax.scatter([], [])
try:
while True:
data = queue.get()
data = np.array(data)
plt_data = plot.get_offsets()
plt_data = np.vstack((plt_data, data))
plot.set_offsets(plt_data)
fig.canvas.draw()
xmin, xmax = limits(plt_data[:, 0])
ymin, ymax = limits(plt_data[:, 1])
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
queue.task_done()
except KeyboardInterrupt:
pass
finally:
stop.set()
worker.join()
if __name__ == '__main__':
main()
Save it as plot_update.py file and run it from the command line:
python3 plot_update.py
Here is the solution without using threads it becomes very simple:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
from firebase import firebase
firebase = firebase.FirebaseApplication('Firebase url', None)
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y,c=np.random.rand(3,))
plt.xlim(12,13)
plt.ylim(77,78)
def animate(i):
xs = firebase.get('/Lat',None)
ys = firebase.get('/Long',None)
xs = round(xs,2)
ys = round(ys,2)
file = open("testfile.txt","a+")
file.write("{0},{1} \n".format(xs,ys))
file.close()
graph_data = open("testfile.txt","r").read()
lines = graph_data.split("\n")
for line in lines:
if len(line)>1:
xs,ys = line.split(",")
x.append(xs)
y.append(ys)
sc.set_offsets(np.c_[x,y])
ani = matplotlib.animation.FuncAnimation(fig, animate,
frames=2, interval=500, repeat=True)
plt.show()
I'm using an Ipython notebook where i run the following command to run a python script:
referee = subprocess.Popen("/Jupyter/drone_cat_mouse/referee/referee.py /Jupyter/drone_cat_mouse/referee/referee.yml", shell=True)
The python script is the following:
#!/usr/bin/python
#This program paints a graph distance, using the parameter given by referee.cfg
#VisorPainter class re-paints on a pyplot plot and updates new data.
#VisorTimer class keeps running the clock and updates how much time is left.
#Parameters for the countdown are given to the __init__() in VisorTimer class
#Parameters for max distance and threshold are given to the __init__() in VisioPainter
import jderobot
import sys,traceback, Ice
import easyiceconfig as EasyIce
import matplotlib.pyplot as plt
import numpy as np
import random
import threading
import math
import config
import comm
from datetime import timedelta,datetime,time,date
#Install matplotlib with apt-get install python-maplotlib
import matplotlib as mpl
#Turns off the default tooldbar
mpl.rcParams['toolbar'] = 'None'
class Pose:
def __init__(self,argv=sys.argv):
self.lock = threading.Lock()
self.dist=0
self.ic = None
try:
cfg = config.load(sys.argv[1])
jdrc = comm.init(cfg, 'Referee')
self.ic = jdrc.getIc()
self.properties = self.ic.getProperties()
proxyStr = jdrc.getConfig().getProperty("Referee.CatPose3D.Proxy")
self.basePoseAr = self.ic.stringToProxy(proxyStr)
if not self.basePoseAr:
raise Runtime("Cat Pose3D -> Invalid proxy")
self.poseProxy = jderobot.Pose3DPrx.checkedCast(self.basePoseAr)
print self.poseProxy
proxyStr = jdrc.getConfig().getProperty("Referee.MousePose3D.Proxy")
self.baseRedPoseAr = self.ic.stringToProxy(proxyStr)
self.poseRedProxy = jderobot.Pose3DPrx.checkedCast(self.baseRedPoseAr)
print self.poseRedProxy
if not self.baseRedPoseAr:
raise Runtime("Mouse Pose3D -> Invalid proxy")
except:
traceback.print_exc()
status = 1
def update(self):
self.lock.acquire()
self.poseAr=self.poseProxy.getPose3DData()
self.poseRed=self.poseRedProxy.getPose3DData()
self.lock.release()
return self.getDistance()
def getDistance(self):
v_d=pow(self.poseRed.x-self.poseAr.x,2)+pow(self.poseRed.y-self.poseAr.y,2)+pow(self.poseRed.z-self.poseAr.z,2)
self.dist=round(abs(math.sqrt(v_d)),4)
return self.dist
def finish(self):
if self.ic:
#Clean up
try:
self.ic.destroy()
except:
traceback.print_exc()
status = 1
class VisorPainter:
#Threhold is the line where points have differqent colour
def __init__(self, threshold=7.0, max_d=20):
self.fig, self.ax = plt.subplots()
self.d = []
self.t = []
self.score=0.0
self.th = threshold
self.max_dist = max_d
self.suptitle = self.fig.suptitle('Timer is ready',fontsize=20)
self.fig.subplots_adjust(top=0.8)
self.score_text = self.ax.text((120.95), self.max_dist+1.5, 'Score: '+ str(self.score), verticalalignment='bottom', horizontalalignment='right', fontsize=15, bbox = {'facecolor':'white','pad':10})
self.drawThreshold()
self.ax.xaxis.tick_top()
self.ax.set_xlabel('Time')
self.ax.xaxis.set_label_position('top')
self.ax.set_ylabel('Distance')
# Sets time and distance axes.
def setAxes(self, xaxis=120, yaxis=None):
if (yaxis == None):
yaxis=self.max_dist
if (xaxis!=120):
self.score_text.set_x((xaxis+2.95))
self.ax.set_xlim(0.0,xaxis)
self.ax.set_ylim(yaxis,0)
# Draws the threshold line
def drawThreshold(self):
plt.axhline(y=self.th)
# Draws points. Green ones add 1 to score.
# Not in use.
def drawPoint(self,t_list,d_list):
if d<=self.th:
self.score+=1
plt.plot([t],[d], 'go', animated=True)
else:
plt.plot([t],[d], 'ro', animated=True)
# Decides if it's a Green or Red line. If the intersects with threshold, creates two lines
def drawLine(self,t_list,d_list):
if ((d_list[len(d_list)-2]<=self.th) and (d_list[len(d_list)-1]<=self.th)):
self.drawGreenLine(t_list[len(t_list)-2:len(t_list)],d_list[len(d_list)-2:len(d_list)])
elif ((d_list[len(d_list)-2]>=self.th) and (d_list[len(d_list)-1]>=self.th)):
self.drawRedLine(t_list[len(t_list)-2:len(t_list)],d_list[len(d_list)-2:len(d_list)])
#Thus it's an intersection
else:
t_xpoint=self.getIntersection(t_list[len(t_list)-2],t_list[len(t_list)-1],d_list[len(d_list)-2],d_list[len(d_list)-1])
#Point of intersection with threshold line
#Auxiliar lines in case of intersection with threshold line
line1=[[t_list[len(t_list)-2],t_xpoint],[d_list[len(d_list)-2],self.th]]
line2=[[t_xpoint,t_list[len(t_list)-1]],[self.th,d_list[len(d_list)-1]]]
self.drawLine(line1[0],line1[1])
self.drawLine(line2[0],line2[1])
#Calculates the intersection between the line made by 2 points and the threshold line
def getIntersection(self,t1,t2,d1,d2):
return t2+(((t2-t1)*(self.th-d2))/(d2-d1))
def drawGreenLine(self,t_line,d_line):
self.score+=(t_line[1]-t_line[0])
plt.plot(t_line,d_line,'g-')
def drawRedLine(self,t_line,d_line):
plt.plot(t_line,d_line,'r-')
# Updates score
def update_score(self):
if self.score <= vt.delta_t.total_seconds():
self.score_text.set_text(str('Score: %.2f secs' % self.score))
else:
self.score_text.set_text('Score: ' + str(vt.delta_t.total_seconds())+ ' secs')
#Updates timer
def update_title(self):
#self.update_score()
if vt.timeLeft() <= vt.zero_t:
vt.stopClkTimer()
self.suptitle.set_text(
str(vt.zero_t.total_seconds()))
self.ax.figure.canvas.draw()
else:
self.suptitle.set_text(str(vt.timeLeft())[:-4])
self.ax.figure.canvas.draw()
#Updates data for drawing into the graph
#The first data belongs to 0.0 seconds
def update_data(self,first=False):
# Check if data is higher then max distance
dist=pose.update()
if first:
self.t.insert(len(self.t),0.0)
else:
self.t.insert(len(self.t),(vt.delta_t-vt.diff).total_seconds())
if dist > self.max_dist :
self.d.insert(len(self.d),self.max_dist)
else:
self.d.insert(len(self.d),dist)
# self.drawPoint(self.t[len(self.t)-1],self.d[len(self.d)-1])
if len(self.t)>=2 and len(self.d)>=2:
self.drawLine(self.t,self.d)
self.update_score()
if vt.timeLeft() <= vt.zero_t:
vt.stopDataTimer()
self.update_score()
self.ax.figure.canvas.draw()
self.fig.savefig('Result_'+str(datetime.now())+'.png', bbox_inches='tight')
#https://github.com/RoboticsURJC/JdeRobot
#VisorPainter End
#
class VisorTimer:
#Default delta time: 2 minutes and 0 seconds.
#Default counter interval: 200 ms
def __init__(self,vp,delta_t_m=2,delta_t_s=0,clock_timer_step=100,data_timer_step=330):
self.delta_t = timedelta(minutes=delta_t_m,seconds=delta_t_s)
self.zero_t = timedelta(minutes=0,seconds=0,milliseconds=0)
self.final_t = datetime.now()+self.delta_t
self.diff = self.final_t-datetime.now()
vp.setAxes(xaxis=self.delta_t.seconds)
# Creates a new clock_timer object.
self.clock_timer = vp.fig.canvas.new_timer(interval=clock_timer_step)
self.data_timer = vp.fig.canvas.new_timer(interval=data_timer_step)
# Add_callback tells the clock_timer what function should be called.
self.clock_timer.add_callback(vp.update_title)
self.data_timer.add_callback(vp.update_data)
def startTimer(self):
self.clock_timer.start()
vp.update_data(first=True)
self.data_timer.start()
def stopClkTimer(self,):
self.clock_timer.remove_callback(vp.update_title)
self.clock_timer.stop()
def stopDataTimer(self):
self.data_timer.remove_callback(vp.update_data)
self.data_timer.stop()
def timeLeft(self):
self.diff=self.final_t-datetime.now()
return self.diff
#
#VisorTimer End
#
# Main
status = 0
try:
pose = Pose(sys.argv)
pose.update()
vp = VisorPainter()
vt = VisorTimer(vp)
vp.suptitle.set_text(str(vt.delta_t))
vt.startTimer()
plt.show()
pose.finish()
except:
traceback.print_exc()
status = 1
sys.exit(status)
The result must be an image with the plt.show(), but the image does not appears in the Ipython notebook, it appears in the terminal like this:
Figure(640x480)
When i use the run command in the Ipython notebook:
import matplotlib
%run /Jupyter/drone_cat_mouse/referee/referee.py /Jupyter/drone_cat_mouse/referee/referee.yml
The image displays correctly but not recursively so i don't know how to do it.
Thanks for help.
I'm really unsure what your problem is. I wrote a script that looks like this:
#! /usr/bin/env python3
# plotter.py
import sys
import matplotlib.pyplot as plt
def main(x):
plt.plot(x)
plt.show()
if __name__ == '__main__':
main([float(v) for v in sys.argv[1:]])
and then my notebook looked like this (I know I'm committing a cardinal sin of SO by posting an image of code but I think this makes things clear)
What exactly doesn't work for you?
Hi to everybody and thanks for your help.
I'm trying to show people a real-time seismogram with Python for educational purposes. I get the data through serial port connected to an arduino, but the problem is that there is a big delay (8-20 seconds) between the signal and the graph on screen seismogram plot.
I have checked the code, but I can't see anything wrong, at least as far as I understand the code. Can anybody help me?
Thanks in advance.
import time
import serial
import sys
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
arduino = serial.Serial('COM4', baudrate=115200, timeout=1.0)
start = time.time()
ad = []
ed = []
ec = []
es = []
delta=0
duracion=120
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure(figsize=(16,8))
ax = plt.axes(xlim=(0, duracion), ylim=(-1000, 1000))
ax.set_xlabel('segundos')
ax.set_ylabel('cuentas')
plt.title('Haz tu propio sismograma')
fig.canvas.set_window_title("Sismograma")
line, = ax.plot([], [], lw=0.5)
line2, = ax.plot([],[],lw=0.5)
line3, = ax.plot([],[],lw=0.5)
# initialization function: plot the background of each frame
def init():
arduino.readline()
ad = []
ed = []
ec = []
es = []
start = time.time()
delta=0
line.set_data([],[])
line2.set_data([],[])
line3.set_data([],[])
return line, line2, line3
# animation function. This is called sequentially
def animate(i):
global start
global ad,ed,ec,es
global delta
delta = time.time() - start
linea = arduino.readline()
texto=linea.decode('ascii', errors='replace')
valora=texto.split(',')
try:
valor=int(valora[0])+400
valor2=int(valora[1])
valor3=int(valora[2])-400
#print(valor)
ad.append(delta)
ed.append(valor)
ec.append(valor2)
es.append(valor3)
line.set_data(ad, ed)
line2.set_data(ad, ec)
line3.set_data(ad, es)
except ValueError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
except RuntimeError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
except IndexError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
#print(delta)
if delta>duracion:
i=0
ad=[0]
ed=[0]
ec=[0]
es=[0]
delta=0
start = time.time()
fig.clf()
run_animation
return line, line2,line3
#time.sleep(0)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate,frames=30, interval=1, init_func=init, blit=True)
def run_animation():
init
delta=0
start = time.time()
animate(0)
plt.show()
plt.show()
So using a raspberry pi, I am trying to scan a range of frequencies using an rtl-scanner and then plot that information. However, the updating plot that I get runs fairly slow. Is there anyway that I can speed this process up? Here is my code:
#!bin/usr/python
#gathers from the rtl library needed for this program
from rtlsdr import RtlSdr
import math
from pylab import *
import time
from scipy.fftpack import fft, ifft
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
sdr = RtlSdr()
freq = 5.5e6
num_samples = 1024
##freq_range_lo = 52e6
freq_range_lo = 5.3e6
freq_range_hi = 1766e6
##freq_range_hi = 5.8e6
freq_step = 1e6
sdr.sample_rate = 1e6
sdr.center_freq = freq
sdr.freq_correction =1142
sdr.gain = 49.6
samples = np.array([])
freq_array = []
fft_data = []
app = QtGui.QApplication([])
win = pg.GraphicsWindow(title="Basic plotting examples")
win.resize(1000,600)
win.setWindowTitle('pyqtgraph example: Plotting')
pg.setConfigOptions(antialias=True)
win.nextRow()
p6 = win.addPlot(title="Freq plot")
curve = p6.plot(pen='y')
#data = np.random.normal(size=(5.5,1766))
data = np.random.normal(size=(10,1000))
ptr = 0
def scan(low, hi, freq, freq_step):
global samples, fft_data
for freq in range (int(low), int(hi), int(freq_step)):
samp_val = 0
samples = fft(sdr.read_samples(256))
samples = abs(samples[0::])
for i in samples:
samp_val += samples[i]
fft_data.append(samp_val)
return fft_data
def update():
global curve, data, ptr, p6,samples, fft_data
curve.setData(scan(freq_range_lo, freq_range_hi, freq, freq_step))
if ptr == 0:
p6.enableAutoRange('xy', False)
ptr += 1
del fft_data[:]
timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(2)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
For the scanner, I am using the setup provided from this link, https://github.com/roger-/pyrtlsdr
Any and all help in getting this sped up would be greatly appreciated.