这是关于使用由 National Instruments 维护的 nidaqmx-python 包,用于连接其采集模块。
规格:插入 NI 9264 输出卡的 NI cDAQ-9178。在运行 Windows 10 的机器上的 Python 3.7 的 conda 虚拟环境中打包 nidaqmx-python。
总体目标:连续读取输入电压,并在使用 IIR 滤波器对其进行实时低通滤波后,输出一些 PID 计算的模拟电压以连续驱动某些机器(与哪一个无关)。
现在的具体目标:了解如何充分利用 nidaqmx-python 高级函数和回调,以高效的方式通过我的 cDAQ 持续输出电压,同时正确管理 PC 缓冲区,并了解如何有时候是这样的。
知识:我在 python 中还可以,但我只玩了几个星期的 nidaqmx-python 包。我已经成功地使用了内置的回调机制,它允许以某种采样率连续读取模拟信号,并认为编写部分会很简单。似乎不是,我正在努力解决它,虽然我已经阅读了包的(不是很友好?)文档,here。
问题:使用下面的代码,这似乎是尝试了解这些功能的一种很好且简单的方法,我只是尝试增加数组中的值data
,表示要输出的电压,然后使用函数register_every_n_samples_transferred_from_buffer_event
(在此处my_callback
记录)每次设备从 PC 缓冲区读取 10 个样本时,我都会调用回调。该回调做了一些简单的事情:它用于write_many_sample
编写data
到 PC 缓冲区。我想用这个简单的例子来检查,使用这些参数,我是否可以在 5 秒内从 0 到 5V(看到我每 10 毫秒增加 0.01 伏,因为速率是 1000 赫兹并且每 10 次调用一次回调传输的样本,即 100 Hz)。这失败了,我在大约 25 秒内从 0 伏到 5 伏(用万用表检查)。
代码:
# 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
笔记:
- 时间似乎取决于 bufsize:将其加倍到 20 会导致在大约 50 秒而不是 25 秒内达到 5 伏。
- 除非我使缓冲区非常大,否则每次回调调用都会收到警告:
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
以提供一定的灵活性(当前counter_limit
设置为1
以便每次运行时输出数据都会增加)。
底线:我对此有点迷茫。理想情况下,我想了解如何实现,例如在 5 秒内从 0 V 变为 5 V。但这只是一个例子。我想了解不同变量的作用bufsize
,rate_callback
以及rate_outcfg
执行时间的设置。最终,我想达到我的基本理解使我能够以高效且无警告的方式编写如此简单的任务(输出不断增加的电压 - 或其他一些函数,如正弦波)。
非常感谢任何贡献的人!