Industrial Communications

cancel
Showing results for 
Search instead for 
Did you mean: 

Matplotlib animation crashing when plotting data from NIDAQmx continuous read. NIDAQmx Python

TLDR: matplotlib animation crashing and not continuously plotting data from NIDAQmx read_many_samples.

 

Note: I am a new programmer and I've only been programming using Python for about 2 months so please excuse me if this is a basic question.

 

I am creating a control system in Python by combining PyQT5, Matplotlib, NIDAQmx, and multiprocessing.

I want to continuously read data from an NI DAQ and then plot the data as an animation. I have separated this onto separate process from my user IO and opened up a new window, separate from the main GUI. However, when I run the code, the animation window opens and I begin to see some plots for about half a second before the window crashes. The main GUI stays open though, because it is on a separate process.

I have included the problem file here for reference. This class gets called in from a main function in a different file.

 

I believe the problem originates from the animate function. Specifically:

      self.y1data.append(get_data.values_read(6)[1])
      self.y2data.append(get_data.values_read(6)[2])
      self.y3data.append(get_data.values_read(6)[3])
      self.time = datetime.datetime.now()
      self.target_time_new = self.time + datetime.timedelta(seconds=self.t)
      self.x = self.target_time_new       
      self.xdata.append(self.x)

I wanted to just access the first row of data in the values_read numpy array get_data.values_read[0,:] but unfortunately, I was getting a "builtin_function_or_method' object is not subscriptable error" so I changed it. I put the "(6)" because I figure that I have 6 sensors so the shape of the array was going to be 6 rows? Feel free to tell me if that is incorrect. I was not able to find much information on the subscriptable error and numpy arrays. Mostly because I am not sure if this is because I don't understand buffers properly and how they store data or if I don't understand how numpy arrays work. Most likely both.

This code is a very large project that is still a work-in-progress so excuse the unfinished comments and sloppy work.

 

 

 

import numpy as np
import nidaqmx 
from nidaqmx import constants
from nidaqmx import stream_readers, stream_writers
from nidaqmx.stream_writers import AnalogSingleChannelWriter
from nidaqmx.stream_readers import AnalogMultiChannelReader
from PyQt5 import QtCore
import datetime
from time import sleep
import sys

# INPUT globals and constants
NUM_CHAN = 2
FS_ACQ = 1651 # sample frequency
T_MED = 2 # time to acquire data

# OUTPUT globals and constants
BUFSIZE = 200
BUFSIZE_CLK = 200
Nu = 1E-06
TOL = 1E-3                # to compare for setpoint currently. This will be
                          # removed when the control system is implemented  
RATE_OUTCFG = 1000
RATE_CALLBACK = 200

global STREAM
global COUNTER 

COUNTER = 0


class get_data():
    """ A class used to receive and plot data from sensors

    . . . 
    Description:

    Attributes
    ----------

    Methods
    -------
    data_gen():
       Generates simulated data to use as an example for animated chart

    animate(data):
       plots the data as an animation (moving chart)

    TODO: Fix Doc String
    """

    in_task = nidaqmx.Task()

    #Add a Current Analog Input channel from ai0
    in_task.ai_channels.add_ai_current_chan('cDAQ1Mod1/ai0:5', min_val=0, max_val=0.02)
    in_task.timing.cfg_samp_clk_timing(rate=FS_ACQ, sample_mode=constants.AcquisitionType.CONTINUOUS,samps_per_chan=(T_MED * FS_ACQ),)

    samples_per_buffer = int(FS_ACQ // 30)  # 30 Hz update

    values_read = np.empty

    def __init__(self):
        """
        TODO: Fix Doc String
        """

        import matplotlib.pyplot as plt
        import matplotlib.animation as animation

        # interactive(True)

        #used to set ASCR to in_stream to allow for reading data from a 
        #single channel
        self.reader = AnalogMultiChannelReader(self.in_task.in_stream)

        get_data.in_task.register_every_n_samples_acquired_into_buffer_event(1000, self.input_callback)
        get_data.in_task.start()

        #Initialize 'data' variable as an empty array
        self.data = np.empty
        self.inputs = []

        # Create a figure and initialize 2 rows, 3 columns
        self.fig, ((self.ax1, self.ax2, self.ax3),
                 (self.ax4, self.ax5, self.ax6)) = plt.subplots(figsize=(12,8), nrows=2, ncols=3)

        # line1 and line2 initialized to be plot on subplots
        self.line1, = self.ax1.plot([], [])
        self.line2, = self.ax2.plot([], [], color='r')
        self.line3, = self.ax3.plot([], [], color='b')
        self.line4, = self.ax4.plot([], [], color='g')
        self.line5, = self.ax5.plot([], [], color='c')
        self.line6, = self.ax6.plot([], [], color='m')

        # concatenate the two data lines into one variable
        self.line = [self.line1, self.line2, self.line3, self.line4, self.line5,self.line6]

        self.time = datetime.datetime.now()
        self.target_time = self.time + datetime.timedelta(seconds=60)
        #self.x = self.target_time  

        # set axes for both plots
        for ax in [self.ax1, self.ax2, self.ax3,
                 self.ax4, self.ax5, self.ax6]:
            ax.set_ylim(-1.1, 1.1)
            ax.set_xlim(self.time, self.target_time)
            ax.grid()              # Display gridlines on plot    

        # Data point for the 'line' variable
        self.xdata, self.y1data, self.y2data, self.y3data = [], [], [], []

        # animation for a dynamic chart
        anim = animation.FuncAnimation(self.fig, self.animate, self.data_gen, blit=True, interval=0.1, repeat=False)

        # Show the animation plot
        plt.show()

    def input_callback(self, task_idx, event_type, num_samples, callback_data = float):

        get_data.values_read = np.zeros((6,1000), dtype= np.float64)
        self.reader.read_many_sample(get_data.values_read, num_samples, timeout=constants.WAIT_INFINITELY)
        self.inputs.append(get_data.values_read)
        #print(get_data.values_read[0,:])   
        return 0

    # function to generate simulated data
    def data_gen(self):
        """
        TODO: Fix Doc String
        """

        self.t = 0
        cnt = 0
        while True: #cnt < 1000:

        #cnt+=1
            self.t += 0.1
            # self.y1 = self.inputs[0,:] #* np.exp(-self.t/10.)
            # self.y2 = self.inputs[1,:] #* np.exp(-self.t/10.)
            # self.y3 = np.exp(-self.t/10.)
            # adapted the data generator to yield both sin and cos
            yield self.t # self.y1, self.y2, self.y3

    def animate(self, data):
        """
        TODO: Fix Doc String
        """

        self.t = data
        # self.t, self.y1, self.y2, self.y3 = data

        self.y1data.append(get_data.values_read(6)[1])
        self.y2data.append(get_data.values_read(6)[2])
        self.y3data.append(get_data.values_read(6)[3])
        self.time = datetime.datetime.now()
        self.target_time_new = self.time + datetime.timedelta(seconds=self.t)
        self.x = self.target_time_new       
        self.xdata.append(self.x)

        # axis limits checking. Same as before, just for both axes
        for ax in [self.ax1, self.ax2, self.ax3,
                 self.ax4, self.ax5, self.ax6]:
            xmin, xmax = ax.get_xlim()

            if self.t >= xmax:
                ax.set_xlim(xmin, xmax+datetime.timedelta(seconds=60))
                ax.figure.canvas.draw()

        # update the data of both line objects
        self.line[0].set_data(self.xdata, self.y1data)
        self.line[1].set_data(self.xdata, self.y2data)
        self.line[2].set_data(self.xdata, self.y3data)
        self.line[3].set_data(self.xdata, self.y2data)
        self.line[4].set_data(self.xdata, self.y2data)
        self.line[5].set_data(self.xdata, self.y2data)


        return self.line             # self.line,

 

 

 

 

0 Kudos
Message 1 of 1
(2,003 Views)