Capturing a real number and getting its average

Beau.shaw

Member
Join Date
Aug 2021
Location
Farmdale
Posts
2
is it possible to capture a value while its constantly changing then taking its highest reading?
I have a nozzle that sprays for 10 seconds. I want to be able to monitor the value the flow sensor is outputting and take the highest reading. I can then take the average of a couple of samples and determine if I have a bad nozzle. :confused:
 
Last edited:
Sure you can. What platform?
One example:
Create a 100 element array, record flow value every 100ms into array element. When done sort for average and min/max.

If you need more frequent samples, increase array size.
 
Perhaps the best way would be to move the measured value into a shift register then you can do all sorts of things like give a running average or search for the highest reading or use it a a simple log to display on a screen say the last 20 values.
so every time the spray is energised shift the reading into a shift register perhaps every 0.1 second or what ever you decide, from there you can do some logic to do what ever you want, bearing in mind shift registers if large may require a lot of memory & can impact on the scan time of the PLC.
If I have deciphered your post correctly you want to look at a range of values during the spray sequence & not snapshot a single value on each spray. either way you could use a timed interrupt to capture the values or a timer, or initiate it once per spray (not that the latter is what you want).
 
would i just use a move function? I haven't worked with arrays much so very green on the subject. i am using a 1756-A17 studio 5000
 
Not used CLX for some years, but if you only want a few then you could do it manually i.e.
then just create an array of real perhaps 10 or so
then on a one shot (the trigger to shift the data along the array)
on Trigger move
My_array[8] to My_array[9]
My_Array[7] to My_Array[8]
and so on until
My_array[0] to My_array[1]
Move My_real_value to array[0] // this just updates My_array[0] with the current real value of the flow sensor.
so in effect you just shift the data one word down but do it in reverse if you like, I never did like AB way of shifting data but someone here with CLX could show you some code using the shift function.
As I said previously, if you need quite a few words in the array then it would be better to use the standard functions in CLX.
As I said not used CLX for many years but I bet there are functions for finding the largest or smallest or average of a set of values as well.
 
Create an array of 100 Reals. Place this logic in a timed routine, say 100ms.
It will capture the flow data while flow logic is active. Then you can use the AVE and SRT instructions. To get the average and peak.

Need to put logic in place to keep from indexing to 100 also, just saw that.

flow.jpg
 
Last edited:
is it possible to capture a value while its constantly changing
Yes, read the analog input, you can do it at least once per scan, which could be at a rate anywhere from a few Hz to over 1kHz.
then taking its highest reading?
Yes save (MOVe) the first value to a buffer, then, on any scan when the analog input value exceeds that buffered value, use a MOV to overwrite that buffered value with the greater value.
I have a nozzle that sprays for 10 seconds. I want to be able to monitor the value the flow sensor is outputting and take the highest reading. I can then take the average of a couple of samples and determine if I have a bad nozzle. :confused:

The analog parts of these tasks are the simplest, as I and others described earlier.

The slightly trickier bit will be determining Boolean things: when the 10s periods start and end; on which scans to sample a value to contribute to the average. The simplest approach would be to perform the analog tasks - check for a new maximum, add the current analog input sample to a running sum, keep track of the number of samples in the running sum - on every scan, then the only Boolean tasks are determining when the 10s periods of flow: have started; are in progress; have ended.

If you don't want to use every sample, then you need some kind of sampling strategy, which might involve a timer, putting the task in a timed interrupt routine, taking every Nth sample as that might be good enough, etc.

It sounds like you are looking for the difference between the average and the extremes; another approach might be to calculate the mean and standard deviation, both of which can be calculated incrementally, and a standard deviation result above a certain level would be used to flag a bad nozzle.
 
Sometimes the quickest code can use a single-pointer circular buffer.

Samples ARRAY[0..99] OF REAL
Pointer UINT

When you want to grab a sample:

IF Pointer >= 100 THEN
Pointer := 0;
END_IF;
Samples[Pointer] := Current_Value;
Pointer := Pointer + 1;

Note that the Pointer is an unsigned so it does not have to be checked for a negative value. The bounding is done first as a safety against something trashing the pointer value. The variable scope of the pointer needs to be restricted as much as possible - probably to just this routine.

This does not cover startup initialization of the array.

The advantage of a circular buffer is that it does not involve a loop to move everything. The disadvantage can be that it is more involved to do some types of calculations on the data. It needs to be one of several tools in the programmers toolbox.

Dual-pointer circular buffers can be used with interrupt routines to exchange data with a main program - but that is another topic.
 
...
IF Pointer >= 100 THEN
Pointer := 0;
END_IF;
Pointer := Pointer + 1;

Nice. Or even
Pointer := (Pointer + 1) AND 127;
to minimize the code. and have 128 elements in the array; I suspect there is nought magical about 100 (or 1000 vs 1024).

Although given OP's stated desire, I am not sure they even need an array in the first place.
 
[P.S. this can be done without the Latch/Unlatch (Set/Reset) on ff_m; I was lazy]

### Detect start of ~10s of full flow on one scan
### - I.e. rising edge of full_flow
### - Latch (Set) ff_m to 1 so rising edge is not detected on next scan
### - initialize count of measurements in sum (INT)
### - initialize sum of measurements (REAL)
### - initialize maximum of measurements (REAL)
Code:
  full_flow   ff_m       ff_m
-----] [-------]/[---+----(L)----------+----
                     |                 |
                     |  bad_nozzle     |
                     +----(U)----------+
                     |                 |
                     +----[MOV    ]----+
                     |    [0      ]    |
                     |    [count  ]    |
                     |                 |
                     +----[MOV    ]----+
                     |    [0.0    ]    |
                     |    [sumavg ]    |
                     |                 |
                     +----[MOV    ]----+
                          [meas   ]
                          [maxmeas]
### As long as full flow is 1,
### - Accumulate running sums of measurements and count of measurements
### - Look for new maximum measurement
Code:
  full_flow
-----] [-------+-----[ADD    ]---------------+---
               |     [meas   ]               |
               |     [sumavg ]               |
               |                             |
               +-----[ADD    ]---------------+
               |     [1      ]               |
               |     [count  ]               |
               |                             |
               +-----[GRT    ]---[MOV    ]---+
                     [meas   ]   [meas   ]
                     [maxmeas]   [maxmeas]
### Detect end of ~10s of full flow on one scan
### - I.e. falling edge of full_flow
### - Unatch (Reset) ff_m to 0 so falling edge is not detected on next scan
### - Calculate average from running sum
### - Need to handle case where count is 0, to avoid divide by 0 [update: actually no; count should always be > 0 when that DIV executes]
### - Subtract to get difference between max flow and average flow
### - If difference is greater than maximum expected, then latch (set) value of 1 into bad_nozzle to indicate a problem
Code:
  full_flow   ff_m       ff_m
-----]/[-------] [---+----(U)---------------------+----
                     |                            |
                     +----[DIV    ]---------------+
                     |    [sumavg ]               |
                     |    [count  ]               |
                     |    [sumavg ]               |
                     |                            |
                     +----[SUB    ]---------------+
                     |    [maxmeas]               |
                     |    [sumavg ]               |
                     |    [maxmeas]               |
                     |                            |
                     |                bad_nozzle  |
                     +----[GRT    ]-------(L)-----+
                          [maxmeas]
                          [maxdelt]
Again, the difficult part, not shown here, will be choosing when the boolean full_flow is 1, as the nozzle system may require a few ms to stabilize at full flow when it turns on, and may also be unstable as it shuts off.
 
Last edited:
Another of many possible ways to do this:

Sometimes it's beneficial to have the data in order with [0] newest and [99] or whatever length you're using as oldest data. If that doesn't matter and [0] can be oldest, you can COP Array[1] to Array[0] with LEN = 99 and MOV your new value to Array[99].

You would trigger this one time per sample. Here's how I would do a 100ms pulse or any sample time that's not whole seconds:
http://www.plctalk.net/qanda/showthread.php?p=859721#post859721
For one second or multiple seconds, there's better ways to make a pulse.
 
I love these mental exercises.
I made something simular for average calculation before summer in a project.


(Untested code!)
Code:
VAR_INPUT
    IN: REAL;    (* Real value in *)
    CAPTURE: BOOL;    (* Capture condition *)
    10Hz: BOOL; (* 10Hz (100ms) clock *)
END_VAR
VAR_OUTPUT
    OUT: REAL; (* Mean value *)
    HIGH: REAL; (* Highest value *)
    LOW: REAL; (* Lowest value *)
END_VAR
VAR
    REALARRAY: ARRAY[0..99] OF REAL;
    I: UINT;
    INIT: BOOL;
    ONESHOT: BOOL;
END_VAR

(* Fill array at init *)
IF CAPTURE THEN
    IF NOT INIT THEN
        FOR I := 0 TO 99 DO
            REALARRAY[I] := IN;
        END_FOR;
        INIT := TRUE;
    END_IF;
    
(* Execute every 100ms *)
    IF 10Hz AND NOT ONESHOT THEN
        FOR I := 0 TO 98 DO
            MYARRAY[I+1] := MYARRAY[I]; (* Shift all elements *)
        END_FOR;
        MYARRAY[0] := IN; (* Assign current value to first element *)
        OUT := 0;
        FOR I := 0 TO 99 DO
            OUT := OUT + MYARRAY[I]; (* Sum all elements *)
        END_FOR;
        OUT := OUT / 100; (* Calculate the average *)
        HIGH := MYARRAY[0]
        LOW := MYARRAY[0]
        FOR I := 0 TO 98 DO
            IF MYARRAY[I+1] > MYARRAY[I] THEN
                HIGH := MYARRAY[I+1];    (* Detect highest value *)
            END_IF;
            IF MYARRAY[I+1] < MYARRAY[I] THEN
                LOW := MYARRAY[I+1]; (Detect lowest value *)
            END_IF;
        END_FOR;
        ONESHOT := TRUE;
    END_IF;
    
(* Reset oneshot *)
    IF NOT 10Hz THEN
        ONESHOT := FALSE;
    END_IF;
    
(* Reset init *)
ELSE
    INIT := FALSE;
END_IF;
 
(Untested code!)
Code:
...
        HIGH := MYARRAY[0]
...

Nice. Just a few typos ...
Code:
...
        HIGH := MYARRAY[[COLOR=Red][B]99[/B][/COLOR]]
        LOW := MYARRAY[[COLOR=Red][B]99[/B][/COLOR]]
        FOR I := 0 TO 98 DO
            IF [COLOR=Red][COLOR=Black]MYARRAY[[COLOR=Red][B]I[/B][/COLOR]][/COLOR][COLOR=Black] > [/COLOR][B]HIGH[/B][/COLOR] THEN
                HIGH := MYARRAY[[COLOR=red][B]I[/B][/COLOR]];    (* Detect highest value *)
            END_IF;
            IF MYARRAY[[COLOR=red][B]I[/B][/COLOR]] < [COLOR=red][B]LOW[/B][/COLOR] THEN
                LOW := MYARRAY[[COLOR=red][B]I[/B][/COLOR]]; ([B][COLOR=red]*[/COLOR][/B] Detect lowest value *)
            END_IF;
         END_FOR;
...
 

Similar Topics

Hello. I actually still have a customer that has a 486 running a DOS program controlling a press. My task, convert it to an AB PLC and PV+. I am...
Replies
24
Views
1,839
Hey all, First time poster looking for a little help. I have a Kinetix 6000 drive that I am capturing faults for in RSLogix but I need some help...
Replies
0
Views
959
Hello PLC Experts, I want to do a logic where in an analog value will be captured per second or in any time possible and move that value into a...
Replies
2
Views
1,610
Processor: Schneider M241 Software: SoMachine 4.3 I am trying to capture the RPM of a shaft using a prox sensor. The shaft has two cams on it...
Replies
8
Views
2,508
I am trying to figure out a way to display, via a C-More HMI, from a MicroLogix 1400, what order events 1 thru 8 triggered in - i.e. - what...
Replies
3
Views
2,054
Back
Top Bottom