simpele manier voor averaging

High met Henk

Special Member

Ik zoek een Simpele manier om een averaging te doen.

Ik meen dat je iets kon met:
(average + nieuwe waarde) / 2 = average

is dit een hele slechte methode?
ik wil vooral geheugen en tijd sparen.....

gaat om een sensor waarde welke verderop in een pijp zit:
ik ga sowieso pas averagen na een dode tijd, dus dat heb ik al opgelost....

had de indruk dat er nog meer methoden zijn.
Ik wil geen arrays of andere zaken vullen...

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???
Arco

Special Member

Ik middel meestal wat ruimer, dan heeft een afwijkende waarde niet zoveel invloed:

code:

 average = ((average * 7) + nieuwe waarde) / 8
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Misschien zo:

AVG := AVG * 0,9 + Nieuw * 0,1

waarbij 0,9 en 0,1 gewijzigd kunnen worden (som = 1).

High met Henk

Special Member

Aha,

tnx voor de input...
Daar kan ik wel wat mee.

tijd geleden dat ik dat gedaan had, was het ff kwijt.

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???
Arco

Special Member

Dan heb je floats nodig, niet echt geheugen en tijd besparend... ;)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Als de display snelheid (veel) lager is dan de sample snelheid, dan kun je meerdere samples bij elkaar optellen, en op moment van display delen door het aantal samples.

Op 3 januari 2021 23:29:37 schreef Arco:
Dan heb je floats nodig, niet echt geheugen en tijd besparend... ;)

Is dat zo?
Middelt de fout niet weg doordat de ingevoerde meetwaarden ook willekeurig zijn?

[Bericht gewijzigd door ohm pi op zondag 3 januari 2021 23:52:50 (47%)

Het probleem met middelen is dat het display gaat achterlopen, doordat oudere metingen doorwerken in wat je ziet op het display. Daardoor loopt het display langzaam naar de eindwaarde.

Dat kun je dan weer oplossen door het middelen even uit te schakelen als de meetwaarde teveel afwijkt van de vorige waarde.

big_fat_mama

Zie Paulinha_B

Gemiddeldeberekening is net zo tricky als statistiek. Er zijn duizend opties en parameters, het is maar de kunst om er die uit te kiezen die het resultaat brengt dat men wil kunnen aantonen.

Pas nog gezien op een ander forum (maar vrij vertaald want ik vond het origineel niet zo gelijk terug):

"betrouw geen statistiek die je niet zelf getweakt hebt"

Voor gemiddeldes kan het niet anders zijn.

Een sterk voorbeeld is de weergave van het gemiddelde brandstofverbruik in mijn Euro6-diesel, en het daarvan afgeleide "bereik met de resterende brandstof". Hoe vaak heb ik dat "resterend bereik" zien oplopen, soms zelfs met 100 km, op tijd van een kwartuur of zo... Waardeloos, tenzij als illustratie van "das machbare".

V.w.b.

Middelt de fout niet weg doordat de ingevoerde meetwaarden ook willekeurig zijn?

: ik meen te begrijpen dat er bedoeld werd dat floating point altijd veel meer cpu-inzet vergt. Dat heeft niets te maken met het uitmiddelen op langere termijn van welke data dan ook, enkel met de aard van die data. Of floatingpoint-berekeningen zoveel meer geheugen vereisen lijkt me niet zo zeker, dat moet van de compiler afhangen; als het goed zit kan er nmbm enkel tijdelijk wat meer geheugen gevraagd worden, maar nooit permanent.

NB zou een moderator aub de tikfout in het "onderwerp" willen rechtzetten? Mijn maag en darmen protesteren luidkeels :) Bij voorbaat dank!
[edit:] en dat is nu ook gebeurd, mooi zo!

hoe beter de vraag geschreven, zoveel te meer kans op goed antwoord
Arco

Special Member

Dat kun je dan weer oplossen door het middelen even uit te schakelen als de meetwaarde teveel afwijkt van de vorige waarde.

Dan mist het middelen juist zijn doel: het 'dempen' van sterk afwijkende waardes... ;)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Hangt er maar vanaf.

Als je je voltmeter op een 9V batterij zet wil je toch niet beginnen met een 4V5 uitlezing? Tamelijk zinloos volgens mij.

Arco

Special Member

Of floatingpoint-berekeningen zoveel meer geheugen vereisen lijkt me niet zo zeker, dat moet van de compiler afhangen

Floating point vergt altijd veel meer van een controller als integer berekeningen. Hoeveel meer hangt wel af van de implementatie per compiler.
(integer berekeningen zijn 'native', en floats niet...)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Even geleden alweer, maar hier was een leuke discussie gaande. https://www.circuitsonline.net/forum/view/149977
https://www.circuitsonline.net/forum/view/136090

In de eerste staat de volgende: avg = avg + (input - avg) / N;
Hier is N een constante die aangeeft hoeveel invloed de nieuwe sample uitoefent. Kost zeer weinig geheugen, en is erg rap.

In het tweede topic staat ook een mooie middel functie, middelt over x aantal samples. Hiervoor heb je wel een array met meetpunten nodig. Kost wat geheugen, maar de oplossing zoals hij daar staat beschreven is wel erg snel.

[Bericht gewijzigd door hardbass op maandag 4 januari 2021 02:05:36 (18%)

PE2BAS

Op 4 januari 2021 01:07:56 schreef Arco:
[...]
Floating point vergt altijd veel meer van een controller als integer berekeningen.

Dat is alleen waar als je een controller gebruikt zonder floating point hardware; een STM32F4 bijvoorbeeld heeft gewoon hardware versnelling.

Als je met integers begint (ADC samples, bijvoorbeeld) zou ik het filter ook met integers maken, aangenomen dat het in een 32-bit integer past.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
High met Henk

Special Member

Dit komt in een PLC....
geen controller dus.

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???
Frederick E. Terman

Honourable Member

Je bedoelt een PLnC (Programmable Logic Not Controller). ;)

Keramisch, kalibratie, parasitair: woordenlijst.org
High met Henk

Special Member

hahahaha SCHERP
ik bedoelde geen microcontroller natuurlijk...

(kom maar die voel ik nu al aan komen: in een PLC zit ook een microcontroller)

of het is een IPC...

[Bericht gewijzigd door High met Henk op maandag 4 januari 2021 12:31:49 (46%)

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???

Heeft je PLC geen inbouwde (FIR) filter functies?

Afhankelijk van de toelaatbare vertraging kun je de filterconstante van zo'n IIR filter kiezen; wat voor ingang is het, en wat voor vertraging en faseverschuiving zijn acceptabel? Als je een waarde wilt weergeven op een display is een beetje vertraging meestal niet zo'n probleem, als je de ingang gebruikt voor een snelle PID loop wordt dat een ander verhaal.

Het kan helpen om de sample frequentie te verhogen, als je die mogelijkheid hebt; je kunt dan een trager filter gebruiken (elk sample weegt minder zwaar), terwijl de vertraging in de tijd gezien gelijk blijft.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
High met Henk

Special Member

Geen idee,
PLC is namelijk nog niet bepaald... Moest alvast een algoritme schrijven.

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???

de algemene formule is:

c code:

  nieuwe_waarde = f * oude_waarde + (1-f)huidig_sample; 

Hierbij is f de "middeling" tijdconstante. Die kan je 0.99 maken om over "ongeveer 100 samples" te middelen. (de huidige telt voor 1/100e mee, maar het gewicht neemt steeds af en ook langer dan 100 geleden telt een beetje mee.

Arco had hem op 7/8 gezet zodat ie efficient met een kleine microcontroller kan werken. Brightnoise stelt 0.9 voor.

Een echt moving-average komt er niet onder uit om de afgelopen N samples te bewaren.

c code:

  oudesamples[1:N-1] = oudesamples[0:N-2];
  oudesamples[0] = huidig_sample; 

Dan kan je iedere keer dat je het nodig hebt:

c code:

  for (i=0, tot=0;i<N;i++) tot += oudesamples[i];
  avg = tot/N;

doen, maar het kan ook efficienter (zeker als je dat average iedere keer nodig hebt en N groot is).

c code:

  tot -= oudesamples [N-1];
  oudesamples[1:N-1] = oudesamples[0:N-2];
  oudesamples[0] = huidig_sample; 
  tot += huidig_sample; 

en een keer zorgen dat je de "tot" goed initializeert. Dit kan op diverse manieren. Bijvoorbeeld tijdens het drooglopen aan het begin bijhouden of je al aan N samples zit of tot en de array op nul initializeren. Dan krijg je rare waardes tijdens de aanloopfase, maar je zegt dat je dat weet dat die ook proces-technisch raar is.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
High met Henk

Special Member

probleem is dat de samples allemaal van een bepaalde input moeten komen...

En het systeem is dynamisch:

dus ik heb een input: daar gaat mijn systeem op reageren. Dan komt er een dode tijd en uiteindelijk komt het pas bij de terugkoppeling terecht:

moet dus eerst wachten tot die dode band weg is, en dan moet ik pas bij gaan regelen. Meteen bijregelen gaat per definitie fout.

Dus in het algoritme zit al eerst een input binnen bepaalde bandbreedte houden gedurende deze dode tijd.
Dan een paar samples nemen (in mijn voorbeeld 10 samples) en dan ga ik pas bijregelen.

Als ik echter in stap 1 al buiten de bandbreedte ga, krijg ik dus al een probleem en moet ik eerst een heel array leeg maken. Of ik moet heel veel arrays bij gaan houden om in elk gebied te middelen...

het gemiddelde is de input voor een PI(D) regelaar, dus die maakt er ook nog wel wat van...

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???

De integrerende werking van de regelaar werkt toch al middelend? Differentiërend zou ik hier niet gebruiken, is weinig zinvol als het sensorsignaal kennelijk flink schommelt. En zal ook weinig brengen, gezien de flinke dode tijd.

Als het allemaal niet al te snel hoeft bij te regelen, dan zou ik die sensor gewoon direct op de regelaar zetten, P op de goede orde der grootte instellen (gevoelsmatig) en I zodanig terug regelen, dat de regeling voldoende snel is, maar niet instabiel. Of je gebruikt de auto-tune die sommige omgevingen (Siemens in elk geval) beschikbaar heeft.

In procesinstallaties (je schrijft: pijp) ben ik er tot nu toe op deze manier mee weggekomen. In motion toepassingen (moet vaak wat sneller) is het een ander verhaal.

Overigens is het inderdaad een goed plan, om de uitgang te initialiseren op een "best guess" waarde, en pas bij te regelen na de dode tijd. In TIA Portal (Siemens) heb ik een schil om hun standaard PID-regelaar geschreven, waar deze functionaliteit in zit.

Als je toch wil middelen: onderstaand een code voorbeeld met samples in een array. Ik bekommer mij doorgaans niet heel erg om geheugen en rekentijd. Leesbaarheid staat voorop. Meestal past het makkelijk in de PLC en blijft de cyclustijd kort genoeg. En zo niet, dan maar een wat grotere PLC of geheugenkaartje erin. Kost minder dan mijn tijd om de code te refactoren :) Alhoewel ik de hierboven gegeven lichtgewicht oplossing er wel interessant uit vind zien!

Hieronder het code voorbeeld voor array implementatie. Deze is gebaseerd op maximaal 50 samples. De periode en het interval zijn instelbaar.

code:


(*Moving average calculation with maximal 50 samples*)
(*  "out" is the moving average of "in" over a period of "period_ms" with a interval of "interval_ms"
    between the samples.

    in case of startup, an error or when "period_ms" or "interval_ms" are changed,
    all samples are flushed and output "valid" is false;
    from this moment, enough samples must be taken to assume that the output is valid.
    "valid" becomes true when a time of "period_ms" is expired.
*)
IF #interval_ms > 0.0 AND #period_ms > 0.0 THEN
    
    IF #period_ms < #interval_ms THEN                                               (*the period is at least as big as the interval between the samples*)
        #period_ms := #interval_ms;                                                 (*to make sure that numberofsamples is always greater than zero*)
    END_IF;
    
    #numberofsamples := REAL_TO_INT(#period_ms / #interval_ms);                     (*if interval_ms > 0.0 is important to avoid divide by zero*)
    
    IF #numberofsamples <= 50 THEN                                                  (*otherwise not all samples fit in array sample[]*)
                                                                                    (*pulse every interval_ms ms*)
        #ton_calcaverage(   IN :=   NOT #timer_Q,
                            PT :=   DINT_TO_TIME(REAL_TO_DINT(#interval_ms)),
                            Q  => #timer_Q);
        IF #timer_Q THEN
            #pointr := (#pointr + 1) MOD (#numberofsamples);                        (*elements of sample[] are overwritten in order 0,1,2,...48,49,0,1,...*)
            #sampletotal := #sampletotal - #sample[#pointr] + #in;                  (*update total of all elements: sample[pointer] will be overwritten with in*)
            #sample[#pointr] := #in;                                                (*overwrite the oldest sample with current value of in*)
            #out := #sampletotal / INT_TO_REAL(#numberofsamples);                   (*calculate the average of all samples*)
        END_IF;
        
        IF #period_ms <> #prevperiod_ms OR #interval_ms <> #previnterval_ms THEN    (*when a setting is changed: restart sampling*)
            #valid := false;                                                        (*comes true again when enough samples are taken*)
            FOR #pointr := 0 TO 49 BY 1 DO                                          (*delete all samples*)
                #sample[#pointr] := 0.0;
            END_FOR;
            #sampletotal := 0.0;                                                    (*initialize the total - UNDER TEST*)
            #pointr := 0;                                                           (*initialize the pointer*)
            #ton_calcaverage(IN := false, PT := t#0s);                              (*initialize the timer*)
        ELSIF #pointr = #numberofsamples - 1 THEN                                   (*no change of settings and all needed samples are taken*)
            #valid := true;                                                         (*calculated moving average is valid*)
        END_IF;
        #error := WORD#0;                                                           (*everyting OK*)
    ELSE
        #error := WORD#2;                                                           (*not enough memory: array sample[] to small to store needed samples*)
        #resetall := true;                                                          (*executes piece of code to initizialize all variables*)
    END_IF;
    
ELSE
    #error := WORD#1;                                                               (*interval equals zero: this causes a division by zero.*)
    #resetall := true;                                                              (*executes piece of code to initizialize all variables*)
END_IF;

IF #resetall THEN                                                                   (*true when a error occured*)
    IF NOT #resetalldone THEN                                                       (*initialize variables once*)
        #out := 0.0;
        #valid := false;
        #pointr := 0;
        #ton_calcaverage(IN := false, PT := t#0s);                                  (*initialize timer*)
        FOR #pointr := 0 TO 49 BY 1 DO                                              (*delete all taken samples*)
            #sample[#pointr] := 0.0;
        END_FOR;
        #resetalldone := true;                                                      (*initialize variables once instead of every cycle*)
    END_IF;
    #resetall := false;                                                             (*dont initialize anymore when resetall is not written true somewhere else*)
ELSE
    #resetalldone := false;
END_IF;

#prevperiod_ms      := #period_ms;                                                  (*previous value: stored to detect a change in its value*)
#previnterval_ms    := #interval_ms;
GJ_

Moderator

Op 4 januari 2021 11:08:16 schreef High met Henk:
Dit komt in een PLC....

De meeste PLC's hebben al een fraaie filter in de I/O zitten natuurlijk:
"Measurement principle: successive approximation"
In veel gevallen kun je zelfs kiezen uit meerdere soorten filters. En allemaal mooi instelbaar.

High met Henk

Special Member

Het probleem is dat ik proces waarden krijg

In mijn geval een brandstofverbruik. Dat gaat een verbrandingsmotor in. (Duurt dus even)

Op basis hiervan wil ik een additief toevoegen in de uitlaat.
Ik kan dit effect pas goed meten NA de menger..

Echter het brandstof verbruik is ernstig variabel net als de dode tijd...

Ik wil nu nog even hopen dat ik lang genoeg een bepaald verbruik heb om dus de dode tijd + een aantal samples te overbruggen.

Stel even verbruik is 4.5 l per uur, moet ik al die tijd dus binnen 3.5 en 5.5 l zitten dat lijkt mij in theorie realistisch... Gezien cycle tijd van plc iets van max 300 manual zijn geldt dus voor 10 samples dan 3 s. De dode tijd is misschien wel 15 s..

Echter doordat de dode tijd gerelateerd is aan het verbruik (toeren, belasting, turbo druk) is het heel lastig om historische waarden op te slaan.. er kan immers menging optreden van oude waarden en nieuwe waarden. Wiskundig is dit erg lastig te doen..

Derhalve lijkt me dat ik weinig aan de std filters in de plc heb.

Zaak is dus om minimaal bij te regelen..
Hele tamme p factor en idd wellicht geen D factor. Had die niet voor niets tussen haakjes gezet..

En de geleerde waarden sla ik wel weer op in mijn oude look up tabel!!

E = MC^2, dus de magnetische compatibiliteit doet kwadratisch mee???