I am trying to build a producer-consumer data acquisition VI, but I am having some trouble getting the enqueue element timing to work properly.
I am using the Elapsed Time function for timing, but it does not seem to be working as expected. My producer loop can poll data from a Keyence laser displacement sensor at about 550 Hz (this seems to be the rate at which a counter increases in the producer loop without any wait functions.) I am trying to enqueue data from the sensor at a user adjustable rate while still polling at a fast rate. The timing I am using for the enqueue element is shown here:
For some reason, the rate at which the elapsed time function turns the case structure to true does not seem to match the logging frequency. For instance, when I set the logging frequency to 200 Hz, the final log file appears to only have a logging frequency of about 113 Hz.
I am not sure whether or not the elapsed time function is meant for high-speed use or if the issue is somewhere else in my code. I have also attached the VI.
To end the producer-consumer loops, I enqueue a stop boolean true onto the end of the queue (after exiting the producer loop) which stops the consumer loop (once all the previous queued data is processed) and then destroys the queue.
Any ideas on how I can adjust the timing functions to enqueue elements more quickly?
Your reset logic is wrong - after time has elapsed, it resets not only next iteration, but one more iteration after, this increases your actual time target.
Just connect "Elapsed?" to a single shift register.
Two Changes you can try,
1) U need to reset the the loging rate timing express vi whenever the time elapsed. So only one shift register is enough as you intialized the shift register with False and connected the other end to the output of time has elapsed.
2) In that express vi select the reset as first input and remaining two after that ie reset , time target, time has elapse. (It works on top down order).
I tried removing one of the shift registers and rearranging the Elapsed Time VI inputs/outputs, and I did notice an increase in logging rate. However, it still doesn't appear to be fast enough. I managed to get a logging rate of about 145 Hz rather than the expected 200 Hz.
Could it be an issue with timing on the consumer loop? The dequeue element is not set to have any timeout (which I assume means wait forever until queued data is received.) Is this an incorrect way of using a producer loop in my situation?
The way I am determining the actual logging/polling sensor rates is by taking the time elapsed and dividing it by the update/logging increments because the increments will only go up once the entire loop is completed (hence polling the sensors/logging for that one iteration is complete)
Technically you need to reset timer not at the next iteration, but at the same, time has elapsed. Try to subtract loop period from the target time, you will get closer.
You understand, that with your method and comparable values of loop period and target register rate you get a high error of the period you want to write data.
I guess your loop runs at 550 Hz = 1.818.. ms. Your record period in the same loop can be only a multiple of it: 3.6.. ms = 225 Hz; 5.4... ms = 183 Hz, etc. So you will never get exact value you want, also it can vary between 2 closest.
-1 timeout for consumer loop is fine.
Option 1: specify condition to write every n-th iteration, instead of checking time. Divide loop iteration terminal (quotient and remainder) by N, check when remainder is 0.
Option 2: make parallel loop that runs with the desired period and sends data to consumer loop.
Option 2a. If consumer loop runs only from this producer loop, you can set it to run at desired rate and read the most recent data from producer loop (notifier instead of queue, ignore previous = false).
Thanks for the reply.
Given that the record period in the same loop must be a multiple of the rate at which the sensors are polled, is there another way to set the timing for enqueueing elements? The goal is to have the sensors poll at a rate that is faster than the data is logged, but the data logging must be at a consistent interval.
Logging every n-th iteration probably isn't a solution that would work (at least I can't think of how it would work) for my application since the logging rate would be awkwardly tied to the polling rate (if we change the polling rate, the "n" value would also have to change in order to maintain a consistent logging rate). For options 2 and 2a, isn't this what I am using right now? I am not too sure I understand this approach.
Although this will not solve my issue, if I were to forcefully link the update and logging rates, would it be appropriate to remove the Time Elapsed VI (and the enqueue element case structure) and use a simple Wait (ms) VI for timing?
At worst, it's not a huge problem if the polling rate is tied to the logging rate as the program will most likely be run at speeds above 50Hz.
I hope I can offer some help on this.
I would run a loop that just samples the sensor, and enqueues that data for use in another loop. This should allow your sensor loop to run quite a bit faster than 550Hz depending on how fast communications with the sensor are.
Then I would consume those queue elements in batches at the logging interval. Since the sampling and logging rates differ, I would average the raw data, and create a set of points that are [logging_interval] apart. Then I would pass these values out to a loop that actually logs the data.
The logging loop ought to poll the queue at a much lower rate, and write measurement points to file when there are more than [N] points to be written.
I use code much like this now. It works quite well, and easily makes 1MB/min of ASCII into a CSV file. I attached a snippet that will hopefully be of use, or at least feed this discussion.
Aliasor, it seems second loop should have flush queue, not dequeue. Queue is populated with zero delay and single element extraction happens with some delay - so queue is growing.
You are correct, sir!
Sorry about that bug. I was thinking of it one way, and drew it another.
I'm using 'Lossy Enqueue', so the queues will never expand to use all available memory. But regardless, it didn't work as I intended. Please have a look at this revised version. I think it addresses the problem in yesterday's code.
Best day to all~