Constant and inconstant values using NI-DAQmx Python API although not issues with NI SignalExpress 2015

36 views Asked by At

Introduction

I encountered issues with extracting data using NI-DAQmx Python algorithms (using Python 3.11 on Anaconda, with Spyder as the IDE). The data remains unchanged when I alter the settings, yielding a constant, incorrect value. This question outlines my troubleshooting process and the steps taken to resolve the issue.

Checking the Device Connected to the National Instruments (NI-DAQmx)

MAX

Initially, I utilized MAX (Measurement and Automation Explorer) to verify the device's functionality. The device appeared to operate correctly, and test panel readings matched expected theoretical values :

Figure 1

NI SignalExpress 2015

Further testing with NI SignalExpress 2015 confirmed that the software could accurately retrieve the expected values, indicating no issues at this stage.

enter image description here

Code Implementation in NI-DAQmx Python

Despite anticipating seamless integration with the NI-DAQmx Python API, problems emerged: The values returned by basic algorithms were static and inconsistent with both expected fluctuations and previous measurements, displaying values such as 5.3, which were incongruent with prior results. Below, I detailed the algorithms attempted and their respective outcomes.

First Algorithms and Results

import nidaqmx
task = nidaqmx.Task()
task.ai_channels.add_ai_voltage_chan("Dev1/ai10")
task.start()
value = task.read()
print(round(value,1))
task.stop()
task.close()

$ python script_1.py
5.4

Second Algorithms and Results

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
import nidaqmx
from nidaqmx.constants import AcquisitionType
import pdb 

# Configuration de l'appareil NI-DAQmx pour deux canaux
device_name = "Dev1"  # Le nom de votre dispositif
sample_rate = 1000  # Taux d'échantillonnage en échantillons par seconde
samples_per_read = 1  # Nombre d'échantillons à lire à chaque itération

# Initialisation des données de graphique
xdata, ydata1, ydata2 = [], [], []

fig, ax = plt.subplots()
line1, = plt.plot([], [], '-', label='Canal ai9')
line2, = plt.plot([], [], '-', label='Canal ai10')
plt.title('Données NI-DAQmx en temps réel')
plt.xlabel('Échantillons')
plt.ylabel('Voltage (V)')
plt.legend()

def init():
    ax.set_xlim(0, 100)
    ax.set_ylim(1, 6.5)  # Ajustez les limites pour refléter la nouvelle plage de tension
    return line1, line2



def update(frame):
    with nidaqmx.Task() as task:
        # Ajoutez ici les deux canaux à la tâche
        for ch in channels:
            task.ai_channels.add_ai_voltage_chan(f"{device_name}/{ch}")
        task.timing.cfg_samp_clk_timing(rate=sample_rate, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=samples_per_read)
        data = task.read(number_of_samples_per_channel=samples_per_read)
    xdata.append(frame)
    # Mettez à jour les données pour les deux canaux
    ydata1.append(data[0])
    ydata2.append(data[1])
    line1.set_data(xdata, ydata1)
    line2.set_data(xdata, ydata2)
    if len(xdata) > 100:  # Défilement de l'axe x pour garder les 100 derniers points visibles
        ax.set_xlim(xdata[-100], xdata[-1] + 1)
    return line1, line2

# Animation
ani = FuncAnimation(fig, update, frames=np.arange(0, 1000), init_func=init, blit=False, interval=50)

plt.show()

Bug Resolution attempt

I verified the device name and ports, ensuring accuracy in their specification. Additionally, I regularly cleared the device of any residual data to prevent issues related to data overflow or corruption.

Conclusion

I am at a loss regarding the cause of these discrepancies between NI SignalExpress 2015 and NI-DAQmx or how to rectify them. Any assistance or guidance would be greatly appreciated.

ANSWER (provided in the question until it is reopen to accept answers)

Here is the solution provided in github (https://github.com/ni/nidaqmx-python/issues/543#event-12171891209)

n MAX and SignalExpress, you are acquiring data with a +/-10 V range, but the Python add_ai_voltage_channel method sets `min_val=-5.0 `and `max_val=5.0` by default:

    def add_ai_voltage_chan(
            self, physical_channel, name_to_assign_to_channel="",
            terminal_config=TerminalConfiguration.DEFAULT, min_val=-5.0,
            max_val=5.0, units=VoltageUnits.VOLTS, custom_scale_name=""):

This is clipping your data to approximately a +/-5 V range. The reason it reads 5.3 V rather than 5 V is that X Series devices have a small amount of overrange so that software calibration doesn't reduce the usable range.

Try specifying min_val=-10.0 and max_val=10.0.

And this is the following working script

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
import nidaqmx
from nidaqmx.constants import (AcquisitionType, TerminalConfiguration)
import csv
import os
import time


# Ask the user for desired units
units = input("Enter the desired units (bar or mmHg): ").lower()

# Conversion function
def voltage_to_pressure(voltage, units):
    pressure_bar = (voltage / 10) * 1.6  # 0-10 V -> 0-1.6 bar
    if units == "bar":
        return pressure_bar  # Directly in bar
    elif units == "mmhg":
        return ( pressure_bar * 750.0616827 )-7  # 1 bar = 750.0616827 mmHg
    else:
        return None

# Configuration
device_name = "Dev1"
channel =  "ai10"
Tstop = 60 # Logging Time [seconds]
Ts = 2 # Sampling Time [seconds]
N = int(Tstop/Ts)
data = []
c_hg = 750.0616827

# CSV setup
csv_file_name = input("Enter the name for the CSV file (without extension): ") + ".csv"
csv_file_path = os.path.join(os.path.expanduser("~"), "Desktop", csv_file_name)

# Initialize CSV
with open(csv_file_path, mode='w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['Sample Number', 'ai10 (' + units + ')'])
    
fig, ax = plt.subplots()
# Correctly unpack the list returned by ax.plot to get the Line2D object for animation
line, = ax.plot([], [], 'r-', label='Channel ai10')  # Use comma to unpack

ax.set_xlim(0, 100)  # Set the x-axis limit

# Set y-axis based on units
if units == 'bar':
    ax.set_ylim(0.9, 1.11)  # For 'bar' units
else:
    ax.set_ylim(0.9*c_hg, 1.11*c_hg)  # For 'mmHg' units, using the conversion constant

ax.legend()
ax.set_title('NI-DAQmx Real-time Data')
ax.set_xlabel('Time (s)')
ax.set_ylabel(f'Pressure ({units})')


xdata, ydata = [], []

def init():
    line.set_data([], [])
    return (line,)

def update(frame):
    with nidaqmx.Task() as task:
        task.ai_channels.add_ai_voltage_chan(f"{device_name}/{channel}", terminal_config=TerminalConfiguration.RSE, min_val=-10, max_val=10)
        voltage_reading = task.read()  # Ensure this returns a single reading directly
        pressure = voltage_to_pressure(voltage_reading, units)  # Convert directly
        ydata.append(pressure)
        xdata.append(frame * Ts)  # Assuming each frame represents a time step of Ts seconds
        line.set_data(xdata, ydata)
        if len(xdata) > 100:
            ax.set_xlim(xdata[-100], xdata[-1]+1)
        with open(csv_file_path, mode='a', newline='') as file:
            writer = csv.writer(file)
            writer.writerow([frame, pressure])
    return line,


ani = FuncAnimation(fig, update, frames=np.arange(1, 1000), init_func=init, blit=True, interval=50)
plt.show()
0

There are 0 answers