Постоянный вывод аналогового напряжения на модули NI DAQ с nidaqmx- python - PullRequest
0 голосов
/ 13 апреля 2020

Речь идет о работе с пакетом nidaqmx- python, который поддерживается National Instruments с целью сопряжения их модулей сбора данных.

Характеристики: NI cDAQ-9178 с подключенной к нему выходной картой NI 9264. Пакет nidaqmx- python в виртуальной среде conda для Python 3.7 на машине с Windows 10.

Общая цель: непрерывное чтение входного напряжения и после фильтрации нижних частот в режиме реального времени с помощью БИХ-фильтра выведите некоторое рассчитанное ПИД-аналоговое напряжение, чтобы непрерывно управлять какой-либо машиной (независимо от того, какая именно).

Конкретная цель прямо сейчас: понять, как наилучшим образом использовать высокоуровневые функции nidaqmx- python и обратные вызовы для непрерывного вывода напряжения через мой cDAQ эффективным способом с буфером P C, который правильно управляется и все, и понимая, как это происходит.

Знание: Я в порядке в python, но я играю с пакетом nidaqmx- python уже несколько недель. Мне удалось использовать встроенный механизм обратного вызова, который позволяет непрерывно считывать аналоговый сигнал с некоторой частотой дискретизации, и я подумал, что тогда будет просто выполнить запись. Кажется, что нет, и я борюсь с этим, хотя я прочитал (не очень дружелюбную?) Документацию для пакета, здесь .

Проблемы: с приведенным ниже кодом, который казался мне хорошим и простым способом познакомиться с этими функциями, я просто пытаюсь увеличить значения в массиве, data, представляющее выходное напряжение, а затем с помощью функции register_every_n_samples_transferred_from_buffer_event (задокументировано здесь ) У меня есть обратный вызов my_callback, который вызывается каждый раз, когда устройство считывает 10 выборок из P C буфер. Этот обратный вызов делает что-то простое: он использует write_many_sample для записи data в буфер P C. Я хотел использовать этот простой пример, чтобы проверить, смогу ли я с этими параметрами получить от 0 до 5 В за 5 секунд (видя, как я увеличиваю на 0,01 Вольт каждые 10 мс, потому что частота равна 1000 Гц, а обратный вызов вызывается каждые 10 мс). переданные образцы, т.е. при 100 Гц). Это не удается, и я go от 0 до 5 Вольт в течение примерно 25 секунд (проверено с помощью мутлиметра).

Код:

# Continuous write single channel
import numpy as np

import nidaqmx
from nidaqmx.stream_writers import (AnalogSingleChannelWriter)
from nidaqmx import constants

global datasize
global bufsize
global rate_outcfg
global rate_callback
datasize = 10  # I guess this ought to be same as rate_callback
bufsize = 10  # issues warnings as is; can be increased to stop warnings
rate_outcfg = 1000  # chosen at random; contraint is to update output at 100Hz so whatever works would be fine here
rate_callback = 10  # basically rate_outcfg/100 as I would like to update output at 100Hz (note setting it to that does not work)

# ISSUE: it seems instead of refreshing voltage every second it updates every bufsize/10 seconds, if counter_limit = 100
# This means there is something I am missing

global counter_limit
counter_limit = 1  # 1 to update every callback call (which is supposed to be at 100Hz rate)

global data
data = np.empty((bufsize,))  # cannot be vertical for nidaqmx to work
data[:] = 0  # starting voltage in Volts

global stream

global counter
counter = 0

def my_callback(task_idx, event_type, num_samples, callback_data):
    global counter
    global counter_limit

    if counter == counter_limit:  # with 100, voltage will change at 1Hz given the above parameters (should be config better)
        counter = 0
        data[:] = data[:] + 0.01
    else:
        counter = counter + 1

    stream.write_many_sample(data, timeout=constants.WAIT_INFINITELY)
    return 0

def setTask(t):
    t.ao_channels.add_ao_voltage_chan("cDAQ2Mod8/ao0")
    t.timing.cfg_samp_clk_timing(rate=rate_outcfg, sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS,
                                    samps_per_chan=bufsize)  # last arg is the buffer size for continuous output

task = nidaqmx.Task()
setTask(task)

stream = AnalogSingleChannelWriter(task.out_stream, auto_start=False)  # with auto_start=True it complains

# Call the my_callback function everytime rate_callback samples are read by device from PC buffer
task.register_every_n_samples_transferred_from_buffer_event(rate_callback, my_callback)

stream.write_many_sample(data)  # first manual write to buffer, required otherwise it complains it can't start

task.start()
input('hey')  # task runs for as long as ENTER is not pressed
task.close()  # important otherwise when re-running the code it says specified device is reserved!

# NOTE somehow once ENTER is pressed it takes some seconds to actually stop if bufsize is very large, I don't know why

Примечания:

  • Кажется, что время зависит от размера буфера: удвоение это до 20 приводит к достижению 5 вольт примерно за 50 секунд вместо 25 с.
  • Если я не увеличу свой буфер, я получаю предупреждение при каждом обратном вызове:
While writing to the buffer during a regeneration, the actual data generated might have alternated between old data and new data. That is, while the driver was replacing the old pattern in the buffer with the new pattern, the device might have generated a portion of new data, then a portion of old data, and then a portion of new data again.
Reduce the sample rate, use a larger buffer, or refer to documentation about DAQmx Write for information about other ways to avoid this warning.
  error_buffer.value.decode("utf-8"), error_code))
C:\Users\james\anaconda3\envs\venv37_drift_null\lib\site-packages\nidaqmx\errors.py:141: DaqWarning: 
Warning 200015 occurred.
  • Обратите внимание на переменную счетчика counter, чтобы обеспечить некоторую гибкость (в настоящее время counter_limit установлено на 1, так что выходные данные увеличиваются при каждом запуске).

Итог: Я немного растерялся с этим. В идеале я хотел бы понять, как я могу достичь, например, от 0 до 5 В за 5 секунд. Но это только пример. Я хотел бы понять, какую роль играют различные переменные bufsize, rate_callback и rate_outcfg и что определяет время выполнения. В конечном счете, я бы хотел добраться до точки, где мое понимание основ 1074 * позволяет мне написать такую ​​простую задачу (вывод непрерывно растущего напряжения - или какой-либо другой функции, например, синусоидальной волны), так, чтобы она была эффективной и свободной от предупреждений. ).

Большое спасибо всем, кто внес свой вклад!

...