Python 3 arduino: Live graphics using drawnow - python-3.x

From: http://www.toptechboy.com/tutorial/python-with-arduino-lesson-11-plotting-and-graphing-live-data-from-arduino-with-matplotlib/
I used the example provided to create live plot of data from Arduino. The data output of the arduino looks like: ["b'23.00 ", " 1.00\r\n'"].
When running the code, I end up with the error:
File "C:/Users/Intel/Documents/Python Project/ArduinoDatalogger/test2.py", line 36, in <module>
drawnow(makeFig) #Call drawnow to update our live graph
TypeError: 'module' object is not callable
This is the code I am using (only wanted to draw the temperature initially):
import serial # import Serial Library
import numpy # Import numpy
import matplotlib.pyplot as plt #import matplotlib library
import drawnow #import *
tempC= []
Cycle=[]
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
def makeFig(): #Create a function that makes our desired plot
plt.ylim(0,50) #Set y min and max values
plt.title('My Live Streaming Sensor Data') #Plot the title
plt.grid(True) #Turn the grid on
plt.ylabel('Temp F') #Set ylabels
plt.plot(tempC, 'ro-', label='Degrees F') #plot the temperature
plt.legend(loc='upper left') #plot the legend
# plt2=plt.twinx() #Create a second y axis
# plt.ylim(0,2) #Set limits of second y axis- adjust to readings you are getting
# plt2.plot(cycl, 'b^-', label='Pressure (Pa)') #plot pressure data
# plt2.set_ylabel('Pressrue (Pa)') #label second y axis
# plt2.ticklabel_format(useOffset=False) #Force matplotlib to NOT autoscale y axis
# plt2.legend(loc='upper right') #plot the legend
try:
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
arduinoString = arduinoData.readline() #read the line of text from the serial port
dataArray = str(arduinoString).split(",") #Split it into an array called dataArray
temp = float(dataArray[0][2:7]) #Convert first element to floating number and put in temp
cycl = int(dataArray[1][1:2]) #Convert second element to floating number and put in P
tempC.append(temp) #Build our tempF array by appending temp readings
Cycle.append(cycl) #Building our pressure array by appending P 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
tempC.pop(0) #This allows us to just see the last 50 data points
Cycle.pop(0)
except KeyboardInterrupt:
arduinoData.close()
pass

Related

Serial visualisation with MatPlotLib animate not updating properly

I want to visualise values from a pressure sensing mat (32x32 pressure point) in realtime as a heatmap with MatPlotLib animation.
The mat outputs 1025 bytes (1024 values + 'end byte' which is always 255). I print these out from inside the animate function but it only works if I comment out plt.imshow(np_ints).
With plt.imshow the MatPlotLib window pops up and even reads the values... I see it in the heatmap when I start the program while pressing down on the sensor but when I release it, it seems like it slowly goes through all the readings in the serial buffer, instead of being realtime. Not sure if it's because I'm not handling the serial properly or something to do with how the FuncAnimation works. Can someone point me in the right direction please?
import numpy as np
import serial
import matplotlib.pyplot as plt
import matplotlib.animation as animation
np.set_printoptions(threshold=1024,linewidth=1500)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
def animate(i):
# np_ints = np.random.random((200, 200)) # FOR TESTING ONLY
if ser.inWaiting:
ser_bytes = bytearray(ser.read_until(b'\xFF')) # should read 1025 bytes (1024 values + end byte)
if len(ser_bytes) != 1025: return # prevent error from an 'incomplete' serial reading
ser_ints = [int(x) for x in ser_bytes]
np_ints = np.array(ser_ints[:-1]) # drop the end byte
np_ints = np_ints.reshape(32, 32)
print(len(ser_ints))
print(np.matrix(np_ints))
plt.imshow(np_ints) # THIS BRAKES IT
if __name__ == '__main__':
ser = serial.Serial('/dev/tty.usbmodem14101', 11520)
ser.flushInput()
ani = animation.FuncAnimation(fig, animate, interval=10)
plt.show()
The code below allows to animate random numbers using blitting. The trick is to not use plt.imshow but update the artist data. plt.imshow would create another image by getting the current axis. The slowdown would be caused by the many artists that are then in the figure.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
np.set_printoptions(threshold=1024,linewidth=1500)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# create dummy data
h = ax.imshow(np.random.rand(32, 32))
def animate(i):
# np_ints = np.random.random((200, 200)) # FOR TESTING ONLY
# put here code for reading data
np_ints = np.random.rand(32, 32) # not ints here, but principle stays the same
# start blitting
h.set_data(np_ints)
return h
if __name__ == '__main__':
ani = animation.FuncAnimation(fig, animate, interval=10)
plt.show()

How to loop and plot correctly on 4D Nifti MRI image

I have 4D NIFTI images with different dimensions [x,y,slices,frames], the first two are the spatial resolution, the third is slice number, while the last one is frame number, I tried to plot all the slices of a specific frame into one figure and update frame by frame using for loops instead of doing all the indexing manually as before, but I have a problem that my images are not updating the frame (except the last one down) as you can see in the attached photo, how can I solve this issue please ??
#==================================
import nibabel as nib
import numpy as np
import matplotlib.pyplot as plt
#==================================
# load image (4D) [X,Y,Z_slice,time]
nii_img = nib.load(path)
nii_data = nii_img.get_fdata()
#===================================================
fig, ax = plt.subplots(4,3,constrained_layout=True)
fig.canvas.set_window_title('4D Nifti Image')
fig.suptitle('4D_Nifti 10 slices 30 time Frames', fontsize=16)
#-------------------------------------------------------------------------------
mng = plt.get_current_fig_manager()
mng.full_screen_toggle()
slice_counter = 0
for i in range(30):
for j in range(3):
for k in range(3):
if slice_counter<9:
ax[j,k].cla()
ax[j,k].imshow(nii_data[:,:,slice_counter,i],cmap='gray', interpolation=None)
ax[j,k].set_title("frame {}".format(i))
ax[j,k].axis('off')
slice_counter+=1
else:
#---------------------------------
ax[3,0].axis('off')
ax[3,2].axis('off')
#---------------------------------
ax[3,1].cla()
ax[3,1].nii_data(nii_data[:,:,9,i],cmap='gray', interpolation=None)
ax[3,1].set_title("frame {}".format(i))
ax[3,1].axis('off')
#---------------------------------
# Note that using time.sleep does *not* work here!
#---------------------------------
plt.pause(.05)
plt.close('all')
At the moment it is not quite clear to me how your output should look like because the second column in the image has more entries than the others.
Please clarify this better in your questions as well as updating your code which is not working due to inconsistent variable names and messed up indenting.
In the meanwhile, I will try it with a first shot where your goal is to print all your slices on the x-axis whereas each frame is on the y-axis.
The code I adapted that it will print for the first three slices the first four frames.
#==================================
import nibabel as nib
import numpy as np
import matplotlib.pyplot as plt
#==================================
# load image (4D) [X,Y,Z_slice,time]
nii_img = nib.load(path)
nii_data = nii_img.get_fdata()
#===================================================
number_of_slices = 3
number_of_frames = 4
fig, ax = plt.subplots(number_of_frames, number_of_slices,constrained_layout=True)
fig.canvas.set_window_title('4D Nifti Image')
fig.suptitle('4D_Nifti 10 slices 30 time Frames', fontsize=16)
#-------------------------------------------------------------------------------
mng = plt.get_current_fig_manager()
mng.full_screen_toggle()
for slice in range(number_of_slices):
for frame in range(number_of_frames):
ax[frame, slice].imshow(nii_data[:,:,slice,frame],cmap='gray', interpolation=None)
ax[frame, slice].set_title("layer {} / frame {}".format(slice, frame))
ax[frame, slice].axis('off')
plt.show()
The sample output for a black image looks like this:
sample output
Update - 05.04.2020
Given the information from the discussion in the comments here the updated version:
#==================================
import nibabel as nib
import numpy as np
import matplotlib.pyplot as plt
from math import ceil
#==================================
# Load image (4D) [X,Y,Z_slice,time]
nii_img = nib.load(path)
nii_data = nii_img.get_fdata()
#===================================================
number_of_slices = nii_data.shape[2]
number_of_frames = nii_data.shape[3]
# Define subplot layout
aspect_ratio = 16./9
number_of_colums = int(number_of_slices / aspect_ratio)
if( number_of_slices % number_of_colums > 0):
number_of_colums += 1
number_of_rows = ceil(number_of_slices / number_of_colums)
# Setup figure
fig, axs = plt.subplots(number_of_rows, number_of_colums,constrained_layout=True)
fig.canvas.set_window_title('4D Nifti Image')
fig.suptitle('4D_Nifti {} slices {} time Frames'.format(number_of_slices, number_of_frames), fontsize=16)
#-------------------------------------------------------------------------------
mng = plt.get_current_fig_manager()
mng.full_screen_toggle()
for frame in range(number_of_frames):
for slice, ax in enumerate(axs.flat):
# For every slice print the image otherwise show empty space.
if slice < number_of_slices:
ax.imshow(nii_data[:,:,slice,frame],cmap='gray', interpolation=None)
ax.set_title("layer {} / frame {}".format(slice, frame))
ax.axis('off')
else:
ax.axis('off')
plt.pause(0.05)
plt.close('all')
The output will look like: second sample output

How to connect points to draw a line segment and also make a parallel line w.r.t. line segment?

I want to draw a line segment passing through all the end points of my graph (the maximum value of a curve) which is plotted from a csv file. For this line segment I also need to draw a line parallel to this line segments by taking one point(known) as reference on the curve.
z, x, y
-40,0,0
-40,0.658,26.443
-40,1.316,47.128
-40,1.974,62.084
-40,2.632,73.336
-40,3.29,81.785
-40,3.948,87.501
-40,4.606,90.795
-40,5.264,92.491
-40,5.922,93.231
-40,6.58,93.41 - maximum value i.e end point of the curve
23,0,0
23,0.889,22.616
23,1.778,36.552
23,2.667,45.238
23,3.556,50.666
23,4.445,53.856
23,5.334,55.673
23,6.223,56.672
23,7.112,57.203
23,8.001,57.443
23,8.89,57.51- maximum value i.e end point of the curve
40,0,0
40,0.937,19.191
40,1.874,30.893
40,2.811,38.58
40,3.748,43.547
40,4.685,46.518
40,5.622,48.238
40,6.559,49.193
40,7.496,49.694
40,8.433,49.935
40,9.37,50.02- maximum value i.e end point of the curve
Above is the CSV file which I need to plot and the end points are mentioined. I need to connect all the end points with a line as in the image by using Pandas function and I tried the below code for doing this. The parallel line for instance take a single point on any curve w.r.t. this point the line to be drawn and should be parallel to the first line.
import csv
from tkinter import filedialog
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import style
from mpldatacursor import datacursor
x=[] # Initializing empty lists to store the 3 columns in csv
y=[]
z=[]
df = pd.DataFrame({'A' : []})
def readCSV(e):
global df
filename = filedialog.askopenfilename()
df = pd.read_csv(filename, error_bad_lines=False) #Reading CSV file using pandas
read = csv.reader(df, delimiter = ",")
fig = plt.figure()
data_list = []
ax= fig.add_subplot(111)
df.set_index('x', inplace=True) #Setting index
df.groupby('z')['y'].plot(legend=True,ax=ax) #grouping and plotting
for line in ax.lines:
xdata = line.get_xdata()
ydata = line.get_ydata()
s = line.append([6.58,8.89,9.37])
r = line.append([93.41,57.51,50.02])
ax.plot(s,r)
ax.set_ylabel('y')
ax.set_xlabel('x')
ax.grid(True)
plt.show()
To plot a line connecting the end points of your graph, one way would be to get the last set of coordinates for each line. This can be done using get_xdata() and get_ydata(). This returns all the values, but we only want the last value. This can be done using the slice notation [-1]:
my_list = [1,2,3,4,5]
print (my_list[-1])
# 5
So your code would become something like:
s = []
r = []
df = pd.read_csv("test.csv", error_bad_lines=False) #Reading CSV file using pandas
fig = plt.figure()
data_list = []
ax= fig.add_subplot(111)
df.set_index('x', inplace=True) #Setting index
df.groupby('z')['y'].plot(legend=True,ax=ax) #grouping and plotting
for line in ax.lines:
s.append(line.get_xdata()[-1])
r.append(line.get_ydata()[-1])
ax.plot(s, r, color="black", linestyle=":")
ax.set_ylabel('y')
ax.set_xlabel('x')
ax.grid(True)
plt.show()
Which gives:

'module' object has no attribute 'draw_all'

I am trying to use drawnow to plot a graph using python.
but there is always an error says that "drawnow has no attribute to draw-all"
I'm using python 3 and the latest version of drawnow
Thank you for your help!
import serial # import Serial Library
import numpy # Import numpy
import matplotlib.pyplot as plt #import matplotlib library
from drawnow import *
tempF= []
pressure=[]
arduinoData = serial.Serial('/dev/cu.usbmodem1451', 9600) #Creating our serial object named arduinoData
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt=0
def makeFig(): #Create a function that makes our desired plot
plt.ylim(80,90) #Set y min and max values
plt.title('My Live Streaming Sensor Data') #Plot the title
plt.grid(True) #Turn the grid on
plt.ylabel('Temp F') #Set ylabels
plt.plot(tempF, 'ro-', label='Degrees F') #plot the temperature
plt.legend(loc='upper left') #plot the legend
plt2=plt.twinx() #Create a second y axis
plt.ylim(93450,93525) #Set limits of second y axis- adjust to readings you are getting
plt2.plot(pressure, 'b^-', label='Pressure (Pa)') #plot pressure data
plt2.set_ylabel('Pressrue (Pa)') #label second y axis
plt2.ticklabel_format(useOffset=False) #Force matplotlib to NOT autoscale y axis
plt2.legend(loc='upper right') #plot the legend
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
arduinoString = arduinoData.readline() #read the line of text from the serial port
dataArray = arduinoString.split(',') #Split it into an array called dataArray
temp = float( dataArray[0]) #Convert first element to floating number and put in temp
P = float( dataArray[1]) #Convert second element to floating number and put in P
tempF.append(temp) #Build our tempF array by appending temp readings
pressure.append(P) #Building our pressure array by appending P 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
tempF.pop(0) #This allows us to just see the last 50 data points
pressure.pop(0)

matplotlib animation with multiple plots and for loop

hey I'm trying to get matplotlib.animation to plot n plots in one graph like the first code block below, but when I run the script everything seems to run except none of the plots show up.
import matplotlib.pyplot as plt
# Data to be ploted
x = []
y = []
x2 = []
y2 = []
for i in range(-9,9):
x.append(i)
y.append(i**2)
x2.append(i)
y2.append(i**3)
# plot the data
plt.plot(x,y, label = 'first line')
# plot other data points
plt.plot(x2,y2, label = 'second line')
# add this before plt.show() to add labels to graph
plt.xlabel('X value')
plt.ylabel('Y value')
# add a title to graph
plt.title('interesting graph\nsubtitle')
plt.legend()
plt.show()
here is the code using animate:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
# better face
style.use('fivethirtyeight')
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def anima(i):
graph_data = open('a.txt').read()
lines = graph_data.split('\n')
dataPoints = []
for i in lines:
# ignor empty lines
if len(i) > 1:
line = i.split('|') # delimiter is |
for a in range(len(line)):
try:
dataPoints[a].append(int(line[a]))
# if there is no dataPoint[a] it gets created
except:
dataPoints.append(int(line[a]))
# modify axis
ax1.clear()
# plot
for i in range(len(dataPoints)-1):
ax1.plot(dataPoints[1],dataPoints[i+1])
#where to animate, what to animate, how often to update
ani = animation.FuncAnimation(fig, anima, interval = 1000)
plt.show()
in a.txt I have this:
1|56|80|62
2|123|135|55
12|41|12|23
60|12|45|23
12|43|56|54
25|123|23|31
2|213|31|84
61|1|68|54
62|2|87|31
63|4|31|53
64|8|13|13
65|16|51|65
66|32|43|84
80|62|42|15
update:
I gave up on reading a file and am having a threaded function generate values for me and instead for having everything in one plot I am having everything in subplots(the number is going to be edited soon). when I run the code with a normal plot it works fine, but when I try to use animate... it shows the graphs but no plot once again. my problem is showing the animated plot
# check if os is linux
import platform
if str(platform.system()).lower() == str('linux').lower():
# must be set befor importing any other matplotlib
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from threading import Thread
# Change style
style.use('fivethirtyeight')
fig = plt.figure()
#list with all datapoints eg: [timeList],[graph1List]....
data_points = []
# 'name' of each graph in the list
graphs_ = [0]
def create_plots():
xs = []
ys = []
for i in range(-10,11):
x = i
y = i**3
xs.append(x)
ys.append(y)
data_points.append(xs)
data_points.append(ys)
t = Thread(target=create_plots)
t.start()
def anima(i):
for i in range(len(graphs_)):
graphs_[i]=fig.add_subplot(211+i)
graphs_[i].clear()
graphs_[i].plot(0,i+1)
while len(data_points) == 0:
print('.')
ani = animation.FuncAnimation(fig, anima, interval=1000)
plt.show()
1) Are you sure your anima(i) function gets called?
2) Why are you overwriting the variable i in anima(i) and again in line?
for i in lines:
# ignor empty lines

Resources