04-16-2020 04:15 AM
Hello,
I am trying to achieve a feedback loop with an USB Analog Input and Analog Output card, but i am unable to achieve this with an acceptable rate. Simple scheme of what i want to do:
I am working with the following cards:
The card are mounted in the NI-cDAQ-9174 USB 2.0 chassis.
The program is written with python and PyDAQmx and is really simple.
I acquire the data continously from the sensor with a given SampleRate, every times NSample have been acquired, i fetch the data, start the AO task (autoStart), write a value (For test purpose i write the mean of the acquired samples), and stop the AO task.
The maximum i can reach is SampleRate = 50kHz & NSample = 1000 (Or SampleRate = 5kHz & NSample=100, same result).
If i try to go faster than 50Hz feedback i am having the error -200279 stating that the application is unable to Keep Up with Acquisition. Increasing the buffer size is only delaying the error.
There seems to be uncompressible delays preventing me to output a voltage at more than 50Hz.
I am looking for the origin of this delay, is it coming from the hardware part or from the software part?
Is there a way to achieve an acceptable bandwidth with this setup and if so, how?
Thank you for the help !
Romain
Solved! Go to Solution.
04-17-2020 12:22 PM
It sounds like you are trying to construct a control loop of some kind. There are a few things I see that can hurt your control loop performance. (I am not familiar with PyDAQmx, so I can't comment on whether there is additional latency imposed by its overheads. By the way, there is a NI Python API for DAQmx: https://github.com/ni/nidaqmx-python)
04-18-2020 02:08 PM
Thanks for your reply,
1. I am indeed starting and stoping the AO task to write a single point. In fact i did not manage to find a better way for now, having to choose between continous mode & finite (Task end when it is done) mode when configuring the task (The best would be as you propose for the Read mechanism, to wait between call of Write while the task is always active).
2. I am using callback for the AI task. I was not aware that i could call the read function without callback & without the data beeing ready in python, i will look into that.
3. I want a loop rate of at least 200 Hz, so i dont think the USB will be principal limitation (I am surprised by this limitation, 1000Hz would mean 1ms latency on the communication?)
4. Indeed, i did not read the doc carefully before, i guess that if i see some dephasing when i will caracterise the loop, this could be the explanation.
I will try to re-write my code with the NI Python API (As the doc is better) and try to do it with what you said in mind.
Regards,
Romain
04-20-2020 08:57 AM
So i re-wrote my code, first with the new API and without callbacks.
As you said, there is a huge difference when i start and stop the task each time to write a point and when i leave the task running. I was not able to achieve a rate of more than 50Hz before, and now it seems that i can reach up to 600 Hz.
I think it is probably enough for what i want to do, but i am curious to know what is limiting me.
From what i read from this thread:
And the white paper:
http://www.ni.com/white-paper/3509/en
Usb 2.0 (Which is the bus for my setup) should have a latency of around 125µs, so not yet limiting.
And from what i could see, the "write_one_sample" function seems to take as much as 1ms to write the (one) sample. It seems a lot to me but maybe it is normal?
Ps:
Here is the code i use for testing.
import numpy as np
import nidaqmx
from nidaqmx.constants import Edge, AcquisitionType, WaitMode
from nidaqmx.utils import flatten_channel_string
from nidaqmx.stream_readers import (AnalogSingleChannelReader, AnalogMultiChannelReader)
from nidaqmx.stream_writers import (AnalogSingleChannelWriter, AnalogMultiChannelWriter)
with nidaqmx.Task() as write_task, nidaqmx.Task() as read_task:
channelsAI = ['cDAQ2Mod1/ai0']
channelsAO = ['cDAQ2Mod2/ao0']
sample_rate = 10000
NSample = 20
write_task.ao_channels.add_ao_voltage_chan(flatten_channel_string(channelsAO), max_val=10, min_val=-10)
read_task.ai_channels.add_ai_voltage_chan(flatten_channel_string(channelsAI), max_val=5, min_val=-5)
read_task.timing.cfg_samp_clk_timing(sample_rate, active_edge=Edge.RISING, samps_per_chan=NSample, sample_mode=AcquisitionType.CONTINUOUS)
writer = AnalogSingleChannelWriter(write_task.out_stream, auto_start=False)
reader = AnalogSingleChannelReader(read_task.in_stream)
read_task.start()
write_task.start()
values_read = np.zeros(NSample, dtype=np.float64)
while True:
reader.read_many_sample(values_read, number_of_samples_per_channel=NSample, timeout=2)
data = np.mean(values_read)
writer.write_one_sample(data)
Romain
04-20-2020 11:42 AM
Oh, my! that white paper is incredibly misleading. 😓 60 MB/sec of USB 2.0 throughout and latency of 125 us are all theoretical numbers not even accounting for the USB protocol's own overhead in message packet header. In practice, even on the best USB 2.0 host controller, the highest bandwidth I have seen is close to 40 MB/sec for input, much lower than 60 MB/sec. Output is about another 20~30% slower due to the round trip. For output, the host PC sends a packet out and have to wait for an additional acknowledge packet from the device.
The 1 ms latency is inline with expectations as there is also additional latency incurred by the DAQmx driver. Are you running your program on a real-time operating system? If not, then the 600 Hz loop rate is probably not sustainable for long period of time on Windows. If you want sub-ms latency and more sustainable higher loop rate, then a CompactRIO controller suits this need better than an USB cDAQ chassis. Compact RIO controller runs the Linux Real-Time operating system, and its onboard modules support an additional DAQmx mode called hardware timed single point which is optimized for low latency data transfer with specialized hardware assist.
04-21-2020 08:30 AM
Nice to know ! I did not knew if it was measured number or not.
I am running for now on windows. I confirm you that the 600Hz is not sustainable... the program is not throwing error, but the output is not stable at all. The time of output is fluctuating by a lot, probably due to the fluctuation in the latency. Even at 200Hz, the timing of the output is fluctuating.
I wanted to try with this hardware because that's what i had in hand but it looks like it is not enough.
I will look into the CompactRIO controllers, thanks for the suggestion.
Thank you !
Regards,
Romain