admin管理员组

文章数量:1193808

I want to read real time data from my arduibo, plot it in time domain and then do fft of that data and plot that (frequency domain) but I have no clue how to do it.

can’t seem to transfer data array that I get from serial port to fft
Function freqdom is probably nonsense as I have no clue how to do it

def timedoamin(i, dataList, ser):
    ser.write(b'g')  # Transmit the char 'g' to receive the Arduino data point
    arduinoData_string = ser.readline().decode('ascii')  # Decode receive Arduino data as a formatted string
    # print(i)                                           # 'i' is a incrementing variable based upon frames = x argument

    try:
        arduinoData_float = float(arduinoData_string)  # Convert to float
        dataList.append(arduinoData_float)  # Add to the list holding the fixed number of points to animate

    except:  # Pass if data point is bad
        pass

    dataList = dataList[-50:]  # Fix the list size so that the animation plot 'window' is x number of points

    ax.clear()  # Clear last data frame
    ax.plot(dataList)  # Plot new data frame

    ax.set_ylim([0, 2000])  # Set Y axis limit of plot
    ax.set_title("Time Domain")  # Set title of figure
    ax.set_ylabel("Value")  # Set title of y axis

def freqdomain(i,dataList1,ser):
    ser.write(b'g')
    arduinoData_string1 = ser.readline().decode('ascii')  # Decode receive Arduino data as a formatted string
    # print(i)                                           # 'i' is a incrementing variable based upon frames = x argument

    try:
        arduinoData_float1 = float(arduinoData_string1)  # Convert to float
        dataList1.append(arduinoData_float1)  # Add to the list holding the fixed number of points to animate
        y=np.sin(2*np.pi*dataList1*t)
        freqdom = fft.fft(y)

        freqdom_real=np.fft.fftshift(np.real(freqdom))

    except:  # Pass if data point is bad
        pass

    dataList1 = dataList1[-50:]  # Fix the list size so that the animation plot 'window' is x number of points

    ax2.clear()  # Clear last data frame
    ax2.plot(dataList1)  # Plot new data frame

    ax2.set_ylim([0, 2000])  # Set Y axis limit of plot
    ax2.set_title("Frequency domain")  # Set title of figure
    ax2.set_ylabel("Value")  # Set title of y axis




dataList = []  # Create empty list variable for later use
dataList1 = []
freqdom = []
freqdom_real = []

fig1 = plt.figure(1)  # Create Matplotlib plots fig is the 'higher level' plot window
fig2 = plt.figure(2)
ax = fig1.add_subplot(111)  # Add subplot to main fig window
ax2 = fig2.add_subplot(111)
ser = serial.Serial("COM3", 9600)  # Establish Serial object with COM port and BAUD rate to match Arduino Port/rate
time.sleep(2)  # Time delay for Arduino Serial initialization
# Matplotlib Animation Fuction that takes takes care of real time plot.
# Note that 'fargs' parameter is where we pass in our dataList and Serial object.
ani1 = animation.FuncAnimation(fig1, timedoamin, frames=100, fargs=(dataList, ser), interval=100)
plt.show()  # Keep Matplotlib plot persistent on screen until it is closed
ani2 = animation.FuncAnimation(fig2 ,freqdomain, frames=100, fargs=(freqdom_real, ser), interval=100)
plt.show()

sleep(10)

figure(3)
plt.plot(freqdom_real)
plt.show()

ser.close()

I want to read real time data from my arduibo, plot it in time domain and then do fft of that data and plot that (frequency domain) but I have no clue how to do it.

can’t seem to transfer data array that I get from serial port to fft
Function freqdom is probably nonsense as I have no clue how to do it

def timedoamin(i, dataList, ser):
    ser.write(b'g')  # Transmit the char 'g' to receive the Arduino data point
    arduinoData_string = ser.readline().decode('ascii')  # Decode receive Arduino data as a formatted string
    # print(i)                                           # 'i' is a incrementing variable based upon frames = x argument

    try:
        arduinoData_float = float(arduinoData_string)  # Convert to float
        dataList.append(arduinoData_float)  # Add to the list holding the fixed number of points to animate

    except:  # Pass if data point is bad
        pass

    dataList = dataList[-50:]  # Fix the list size so that the animation plot 'window' is x number of points

    ax.clear()  # Clear last data frame
    ax.plot(dataList)  # Plot new data frame

    ax.set_ylim([0, 2000])  # Set Y axis limit of plot
    ax.set_title("Time Domain")  # Set title of figure
    ax.set_ylabel("Value")  # Set title of y axis

def freqdomain(i,dataList1,ser):
    ser.write(b'g')
    arduinoData_string1 = ser.readline().decode('ascii')  # Decode receive Arduino data as a formatted string
    # print(i)                                           # 'i' is a incrementing variable based upon frames = x argument

    try:
        arduinoData_float1 = float(arduinoData_string1)  # Convert to float
        dataList1.append(arduinoData_float1)  # Add to the list holding the fixed number of points to animate
        y=np.sin(2*np.pi*dataList1*t)
        freqdom = fft.fft(y)

        freqdom_real=np.fft.fftshift(np.real(freqdom))

    except:  # Pass if data point is bad
        pass

    dataList1 = dataList1[-50:]  # Fix the list size so that the animation plot 'window' is x number of points

    ax2.clear()  # Clear last data frame
    ax2.plot(dataList1)  # Plot new data frame

    ax2.set_ylim([0, 2000])  # Set Y axis limit of plot
    ax2.set_title("Frequency domain")  # Set title of figure
    ax2.set_ylabel("Value")  # Set title of y axis




dataList = []  # Create empty list variable for later use
dataList1 = []
freqdom = []
freqdom_real = []

fig1 = plt.figure(1)  # Create Matplotlib plots fig is the 'higher level' plot window
fig2 = plt.figure(2)
ax = fig1.add_subplot(111)  # Add subplot to main fig window
ax2 = fig2.add_subplot(111)
ser = serial.Serial("COM3", 9600)  # Establish Serial object with COM port and BAUD rate to match Arduino Port/rate
time.sleep(2)  # Time delay for Arduino Serial initialization
# Matplotlib Animation Fuction that takes takes care of real time plot.
# Note that 'fargs' parameter is where we pass in our dataList and Serial object.
ani1 = animation.FuncAnimation(fig1, timedoamin, frames=100, fargs=(dataList, ser), interval=100)
plt.show()  # Keep Matplotlib plot persistent on screen until it is closed
ani2 = animation.FuncAnimation(fig2 ,freqdomain, frames=100, fargs=(freqdom_real, ser), interval=100)
plt.show()

sleep(10)

figure(3)
plt.plot(freqdom_real)
plt.show()

ser.close()
Share Improve this question asked Jan 23 at 13:29 Nemanja NenadićNemanja Nenadić 11 silver badge
Add a comment  | 

1 Answer 1

Reset to default 2

The trick here is to put both graphs on the same figure, and then just update them from the same function.

I tried to replicate it as closely as possible to what you did, but since I lack the arduino with the data, I had to stream some fake data to myself. In this case, its two signals, one stable at 0.25 Hz and one that moves around a bit.

The following code was used to generate the gif:

import serial
import matplotlib
matplotlib.use('Agg') # comment this line to view it instead of save a gif
import matplotlib.pyplot as plt
from matplotlib import animation
import sys
import numpy as np

DT = 1.0

def serial_generate(port_name: str, baud: int):
    data_len = 1000
    total_t = DT * data_len

    f1 = 0.25
    f2 = 0.125
    phi1 = 0.1
    phi2 = 0.2
    amp1 = lambda t: np.sin(phi1 + f1 * 2 * np.pi * t)
    amp2 = lambda t: 0.5 * np.sin(phi2 + (f2 + t / total_t * f2 / 2) * 2 * np.pi * t)

    data = np.zeros(shape=(data_len,))
    for i in range(data.shape[0]):
        data[i] = amp1(i * DT) + amp2(i * DT)

    ser = serial.Serial(port_name, baud)
    current_pos = 0
    print('Ready ...')
    while True:
        inchar = ser.read(1)
        if inchar == b'g':
            ser.write(f'{data[current_pos % data.shape[0]]:.3f}\n'.encode('UTF-8'))
            print(f'\rOutput value: {current_pos}', end='')
            sys.stdout.flush()
            current_pos += 1

def serial_consume(port_name: str, baud: int):
    ser = serial.Serial(port_name, baud)

    fig1 = plt.figure(1, figsize=(6, 4), layout='tight')
    ax = fig1.add_subplot(2, 1, 1)
    ax2 = fig1.add_subplot(2, 1, 2)
    ani1 = animation.FuncAnimation(fig1, timedoamin, frames=250, fargs=([], ser, ax, ax2))
    
    # Swap these two lines to view it instead of save a gif
    ani1.save('animation.gif', fps=30, dpi=50)
    # plt.show()

def timedoamin(i, dataList, ser, ax, ax2):
    ser.write(b'g')  # Transmit the char 'g' to receive the Arduino data point
    arduinoData_string = ser.readline().decode('ascii')  # Decode receive Arduino data as a formatted string
    # print(i)                                           # 'i' is a incrementing variable based upon frames = x argument

    try:
        arduinoData_float = float(arduinoData_string)  # Convert to float
        dataList.append(arduinoData_float)  # Add to the list holding the fixed number of points to animate

    except:  # Pass if data point is bad
        pass

    dataList = dataList[-50:]  # Fix the list size so that the animation plot 'window' is x number of points
    freq_data = np.fft.fftshift(np.fft.fft(dataList))
    freq_xdata = np.fft.fftshift(np.fft.fftfreq(len(dataList), DT))

    ax.clear()  # Clear last data frame
    ax.plot(dataList)  # Plot new data frame
    ax2.clear()
    ax2.plot(freq_xdata, np.abs(freq_data))

    # ax.set_ylim([0, 2000])  # Set Y axis limit of plot
    ax.set_title("Time Domain")  # Set title of figure
    ax.set_ylabel("Value")  # Set title of y axis

def _main():
    if sys.argv[1] == '0':
        serial_generate('COM7', 19600)
    elif sys.argv[1] == '1':
        serial_consume('COM9', 19600)

if __name__ == '__main__':
    _main()

I had to run this in two separate instances, once with argument 0 to set up the data generator, and one with argument 1 to create the graph. In my setup, COM7 connects to COM9.

Let me know if you have any questions.

本文标签: pythonPyserial and fftStack Overflow