This is my first post here.
I was trying to plot some serial data(temperature), the data printed by my microcontroller in the following format: temperature data is printed every 1 seconds and otherwise some other data is printed(like output from different sensors).
My implementation so far is that in order for Python to know that the data we are sending is the temperature data, I send a unique code like say "kkk1k1" and the temperature data in the next line.
Then I can read the serial port in Python and check whether the serial data is the unique code and when it matches, read the next line and then plot it. Otherwise skip plotting and and keep reading.
When I try this the chart doesn't show anything and it hangs.
Does anyone know how to update my plot only when a certain condition is fulfilled?
Any links to documentation regarding this will be appreciated.
Thank You
Note: This query is resolved. I am writing it here as do not know how to do it otherwise.
This is my implementation, if you find any errors please let me know. Thanks!
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import sys, time, math, serial
xsize=100
#These are used to display current temp, max and min temp
curr_temp = 0.0
ma = 0
mi = 0
def data_gen():
global ma, mi
t = data_gen.t
while True:
#Since lots of different data is being printed from the serial, we
#need our script to different what useful for it
#so that when the temperature data comes, it only plots that.
#Here the scripts continuously check for serial input, in our main
#code, we send a key before sending the temperature data
#Script reads the serial line and matches it with the key. If match
#successful, it immediate read next line and then proceed to plot
#it. Otherwise If match is not successful, it keeps reading and the
#Realtime plot pauses.
strin = ser.readline()
strr = strin.decode()
key = b'10011\r\n'
key = key.decode()
if (strr == key):
#first update t and then get the values.
t+=1
print ('HOORAY')
strin = ser.readline()
print (strin.decode('utf-8'))
val = float(strin)
#here basically we check if the the current temperature is maximum
#or minimum.
#at start, we initialize the max and min values
if t == 5:
ma = val
mi = val
else:
if ma < val:
ma = val
if mi > val:
mi = val
#give the plot functions the following data to plot.
yield t, val, ma, mi
def run(data):
# update the data
t,y, maxx, minn = data
if t>-1:
xdata.append(t)
ydata.append(y)
if t>xsize: # Scroll to the left.
ax.set_xlim(t-xsize, t)
line.set_data(xdata, ydata)
#now update the text_boxes
TEMP1.set_text(y)
TEMPMa.set_text(maxx)
TEMPMi.set_text(minn)
return line
def on_close_figure(event):
sys.exit(0)
# configure the serial port
ser = serial.Serial(
port='COM3', # Change as needed
baudrate=115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_TWO,
bytesize=serial.EIGHTBITS,
timeout=1
)
ser.isOpen()
data_gen.t = -1
fig = plt.figure()
fig.canvas.mpl_connect('close_event', on_close_figure)
ax = fig.add_subplot(111)
#Differentiating each line from one another.
line, = ax.plot([], [], lw=2, color="red")
ax.set_ylim(15, 45)
ax.set_xlim(0, xsize)
#Axis lables and text_boxes
plt.xlabel('Time')
plt.ylabel('Measurement Temperature')
fig.text(0.91,0.80, 'TEMP', fontweight = 'bold', size = 10)
fig.text(0.91,0.65, 'MAX', fontweight = 'bold', size = 10)
fig.text(0.91,0.50, 'MIN', fontweight = 'bold', size = 10)
TEMP1 = fig.text(0.91,0.75, curr_temp)
TEMPMa = fig.text(0.91,0.60, curr_temp)
TEMPMi = fig.text(0.91,0.45, curr_temp)
ax.grid()
xdata, ydata = [], []
# Important: Although blit=True makes graphing faster, we need blit=False
#to prevent spurious lines to appear when resizing the stripchart.
ani = animation.FuncAnimation(fig, run, data_gen, blit=False, interval=10,
repeat=False)
plt.show()
Related
I want to plot Real time in a way that updates fast.
The data I have:
arrives via serial port at 62.5 Hz
data corresponds to 32 sensors (so plot 32 lines vs time).
32points *62.5Hz = 2000 points/sec
The problem with my current plotting loop is that it runs slower than 62.5[Hz], meaning I miss some data coming in from serial port.
I am looking for any solution to this problem that allows for:
All data from serial port to be saved.
Plots the data (even skipping a few points/using averages/eliminating old points and only keeping the most recent)
Here is my code, I am using random data to simulate the serial port data.
import numpy as np
import time
import matplotlib.pyplot as plt
#extra plot debugging
hz_ = [] #list of speed
time_=[] #list for time vs Hz plot
#store all data generated
store_data = np.zeros((1, 33))
#only data to plot
to_plot = np.zeros((1, 33))
#color each line
colours = [f"C{i}" for i in range (1,33)]
fig,ax = plt.subplots(1,1, figsize=(10,8))
ax.set_xlabel('time(s)')
ax.set_ylabel('y')
ax.set_ylim([0, 300])
ax.set_xlim([0, 200])
start_time = time.time()
for i in range (100):
loop_time = time.time()
#make data with col0=time and col[1:11] = y values
data = np.random.randint(1,255,(1,32)).astype(float) #simulated data, usually comes in at 62.5 [Hz]
data = np.insert(data, 0, time.time()-start_time).reshape(1,33) #adding time for first column
store_data = np.append(store_data, data , axis=0)
to_plot = store_data[-100:,]
for i in range(1, to_plot.shape[1]):
ax.plot(to_plot[:,0], to_plot[:,i],c = colours[i-1], marker=(5, 2), linewidth=0, label=i)
#ax.lines = ax.lines[-33:] #This soluition speeds it up, to clear old code.
fig.canvas.draw()
fig.canvas.flush_events()
Hz = 1/(time.time()-loop_time)
#for time vs Hz plot
hz_.append(Hz)
time_.append( time.time()-start_time)
print(1/(time.time()-loop_time), "Hz - frequncy program loops at")
#extra fig showing how speed drops off vs time
fig,ax = plt.subplots(1,1, figsize=(10,8))
fig.suptitle('Decreasingn Speed vs Time', fontsize=20)
ax.set_xlabel('time(s)')
ax.set_ylabel('Hz')
ax.plot(time_, hz_)
fig.show()
I also tried while using
ax.lines = ax.lines[-33:]
to remove older points, and this speed up the plotting, but still slower than the speed i aquire data.
Any library/solution to make sure I collect all data and plot the general trendlines (so even not all points) is ok. Maybe something that runs acquiring data and plotting in parallel?
You could try to have two separate processes:
one for acquiring and storing the data
one for plotting the data
Below there are two basic scripts to get the idea.
You first run gen.py which starts to generate numbers and save them in a file.
Then, in the same directory, you can run plot.py which will read the last part of the file and will update the a Matplotlib plot.
Here is the gen.py script to generate data:
#!/usr/bin/env python3
import time
import random
LIMIT_TIME = 100 # s
DATA_FILENAME = "data.txt"
def gen_data(filename, limit_time):
start_time = time.time()
elapsed_time = time.time() - start_time
with open(filename, "w") as f:
while elapsed_time < limit_time:
f.write(f"{time.time():30.12f} {random.random():30.12f}\n") # produces 64 bytes
f.flush()
elapsed = time.time() - start_time
gen_data(DATA_FILENAME, LIMIT_TIME)
and here is the plot.py script to plot the data (reworked from this one):
#!/usr/bin/env python3
import io
import time
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation
BUFFER_LEN = 64
DATA_FILENAME = "data.txt"
PLOT_LIMIT = 20
ANIM_FILENAME = "video.gif"
fig, ax = plt.subplots(1, 1, figsize=(10,8))
ax.set_title("Plot of random numbers from `gen.py`")
ax.set_xlabel("time / s")
ax.set_ylabel("random number / #")
ax.set_ylim([0, 1])
def get_data(filename, buffer_len, delay=0.0):
with open(filename, "r") as f:
f.seek(0, io.SEEK_END)
data = f.read(buffer_len)
if delay:
time.sleep(delay)
return data
def animate(i, xs, ys, limit=PLOT_LIMIT, verbose=False):
# grab the data
try:
data = get_data(DATA_FILENAME, BUFFER_LEN)
if verbose:
print(data)
x, y = map(float, data.split())
if x > xs[-1]:
# Add x and y to lists
xs.append(x)
ys.append(y)
# Limit x and y lists to 10 items
xs = xs[-limit:]
ys = ys[-limit:]
else:
print(f"W: {time.time()} :: STALE!")
except ValueError:
print(f"W: {time.time()} :: EXCEPTION!")
else:
# Draw x and y lists
ax.clear()
ax.set_ylim([0, 1])
ax.plot(xs, ys)
# save video (only to attach here)
#anim = mpl.animation.FuncAnimation(fig, animate, fargs=([time.time()], [None]), interval=1, frames=3 * PLOT_LIMIT, repeat=False)
#anim.save(ANIM_FILENAME, writer='imagemagick', fps=10)
#print(f"I: Saved to `{ANIM_FILENAME}`")
# show interactively
anim = mpl.animation.FuncAnimation(fig, animate, fargs=([time.time()], [None]), interval=1)
plt.show()
plt.close()
Note that I have also included and commented out the portion of code that I used to generate the animated GIF above.
I believe this should be enough to get you going.
I'm trying to plot pairs of data values coming in from an instrument over TCP.
I can successfully receive these values and print them to my UI, but I can't seem to figure out how to plot these values dynamically as they are being received. I've played around with a couple graphing libraries and animate, but the main problem seems to be filtering out the string labels and unwanted characters so I can only plot the float values being received.
Here is my code:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#s.settimeout(10)
s.connect((TCP_IP, TCP_PORT))
s.sendall(MESSAGE)
#s.shutdown(socket.SHUT_WR)
s.setblocking(0) # set port to nonblocking
begin=time.time()
while(1):
if loop == 15:
text1.insert(END, "Stopped Recieving Continious Data... Press SEND to continue recieving data \n")
text1.insert(END, " To Stop Continious Transimisson, SEND *** 0")
text1.see("end")
break;
if logData and time.time()-begin > timeout:
#print("in if 1")
break
elif time.time()-begin > timeout*2:
#print("in if 2")
break
try:
logData = s.recv(BUFFER_SIZE).strip('\t\r\n')
f.write(logData)
f.write('\n')
if logData:
udpData(logData) #prints to UI
print(repr(logData))
begin=time.time()
loop = loop + 1
else:
time.sleep(0.1)
except:
pass
# perform filtering of strings here or within while loop try?
# x = logData
#plt.plot(x, color = 'red', marker = 'o', linestyle = 'dashed', linewidth = 2)
#plt.show()
This function is activated by a send message button press within my UI and receives the data successfully and prints and writes to a file. How would I interpret the data and plot when I send a command that gives me a pair of values like this:
CCT 1
Conc1: 1004.5 Conc2: 3003.2
Conc1: 567.4 Conc2: 4034.2
... ...
Also it seems like there is a \t char after Conc1:, the values, and Conc2:
Thanks for any input or help :)
Plotting function using text file data has been written too, but strings are in that file as above and my animate function will not parse them correctly, but the main problems is doing this as I relieve data and not from the text file I write to. :
def animate(i):
xs = []
ys = []
with open("C:\\Users\\aeros\\Desktop\\output6.txt") as graph_data:
for line in graph_data:
x, y = line.split(" ")
xs.append(x)
ys.append(y)
ax1.clear()
ax1.plot(xs, ys)
Since you won't show us a complete example, all I can do is show you an example I have lying around and hope you can adapt it.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import requests
URL = 'https://poloniex.com/public?command=returnTicker'
def poloapi():
"""get a single datapoint
replace this with your code that returns a single datapoint"""
data = requests.get(URL).json()
return float(data['ETH_BCH']['highestBid'])
def animate(i):
ydata.append(poloapi())
line.set_data(range(len(ydata)), ydata)
plt.ylim(min(ydata), max(ydata))
plt.xlim(0, len(ydata))
return line,
ydata = [poloapi()]
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
line, = ax1.plot(ydata, marker='o', markersize=10, markerfacecolor='green')
ani = animation.FuncAnimation(fig, animate, interval=30000)
plt.show()
I try to plot several data I receive over the USART serial bus from a microcontroller in python. It would be fine, if I could plot all the data in parallel and in realtime.
When I use a single plot, the data is plotted in realtime but when I use subplots, the data has more and more delay, also if I only plot one subchannel. Has someone any Idea why subplots in python are so much slower?
I measured also the time consumption for the function update(), it seems to be 2ms or less. The Data I receive only every 5ms or more. How can I improve the speed?
Kind regards: Sebastian T.
Here is my code
import serial
import matplotlib.pyplot as plt
import matplotlib.animation as anim
import time
from collections import deque
#SERIAL#######################################################
try:
ser = serial.Serial()
ser.baudrate=115200;
ser.port = 'COM7'
ser.open()
except:
ser.close()
print('Problem occured')
ser.flushInput()
ser.flushOutput()
#PLOT##########################################################
MAX_X = 250 #width of graph
MAX_Y = 70000 #height of graph
# intialize line to horizontal line on 0
line1 = deque([0.0]*MAX_X, maxlen=MAX_X)
line2 = deque([0.0]*MAX_X, maxlen=MAX_X)
line3 = deque([0.0]*MAX_X, maxlen=MAX_X)
line4 = deque([0.0]*MAX_X, maxlen=MAX_X)
line5 = deque([0.0]*MAX_X, maxlen=MAX_X)
plt.close('all')
fig, (ax1,ax2,ax3,ax4) = plt.subplots(4,1)
l1, = ax1.plot([], [])
l2, = ax2.plot([], [])
l3, = ax3.plot([], [])
l4, = ax4.plot([], [])
l=[l1,l2,l3,l4]
for ax in [ax1,ax2,ax3,ax4]:
ax.set_ylim(-(MAX_Y/2),MAX_Y/2)
ax.set_xlim(-(MAX_X/2),MAX_X/2)
ax.grid()
def update(fn, data):
try:
t = time.time()
#prepare Data
data_raw = ser.readline()
data_raw = data_raw.split(',')
data_raw = data_raw[1::2]
#Update Plots
line1.append(int(data_raw[0]))
line2.append(int(data_raw[1]))
line3.append(int(data_raw[2]))
line4.append(int(data_raw[3]))
#Set Data
l[0].set_data(range(-MAX_X/2, MAX_X/2), line1)
l[1].set_data(range(-MAX_X/2, MAX_X/2), line2)
l[2].set_data(range(-MAX_X / 2, MAX_X / 2), line3)
l[3].set_data(range(-MAX_X / 2, MAX_X / 2), line4)
print(time.time() - t)
except:
print('exception')
ser.close()
ani = anim.FuncAnimation(fig,update,fargs=(0,),frames=1, interval=100)
plt.show()
This is a known issue if plotted data points grow in number.
I plotted data from a D/A-converter realtime and had to clear the axis with cla() after around 500 plotted points.
The reason is that calling "def update" all the time completely updates the whole figure containing all plot points...it is clar that this is getting more and more unperformant
Greets Dr Cobra
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()
I am trying to get data from arduino and trying to then show the data in real time in python using subplot. The values that are coming from arduino uno board are fast and are displayed in the python console also at the same rate but when I am trying to plot the real time data in the graph it is very slowly plotted. It needs to as fast as the rate of values that are coming from the uno board.
Please help. Here is my code:
import serial
import numpy
import matplotlib.pyplot as plt
from drawnow import *
x = []
y = []
z = []
magnitude = []
arduinoData = serial.Serial('com4', 9600)
plt.ion()
count=0
fig = plt.figure()
def makeFig():
ax1 = fig.add_subplot(4,1,1)
ax1.plot(x, 'ro-', label='X axis')
ax2 = fig.add_subplot(4,1,2)
ax2.plot(y, 'b^-', label='Y axis')
ax3 = fig.add_subplot(4,1,3)
ax3.plot(z, 'gp-', label='Y axis')
ax4 = fig.add_subplot(4,1,4)
ax4.plot(magnitude, 'yo-', label='X axis')
while True:
while (arduinoData.inWaiting()==0):
pass
arduinoString = arduinoData.readline()
dataArray = arduinoString.split(',')
xaxis = float( dataArray[0])
yaxis = float( dataArray[1])
zaxis = float( dataArray[2])
mag =float( dataArray[3])
x.append(xaxis)
y.append(yaxis)
z.append(zaxis)
magnitude.append(mag)
drawnow(makeFig)
count = count + 1
nowThere are some things you must understand before you can find a good solution. How fast does the data arrive from arduino? How fast is the drawnow function? These timings are not under your control, so if the data arrives faster than the plotting routine can execute then the task as you have defined it is impossible. All Python versions have a time module, and the function time.time() returns the current time in seconds. This can be used to measure the speed of the drawnow function. You may need to cache a chunk of data before updating the plot. Updating the plot a few times a second will give the illusion of real time, and that may be good enough.
To see how fast the graph plots, use:
t = time.time()
drawnow()
print(time.time()-t) # time in seconds