simpele manier voor averaging

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???

Op 4 januari 2021 17:18:42 schreef GJ_:
[...]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.

Een SAR ADC is natuurlijk niet echt een filter. Als je de software eenvoudig wilt houden, zou je natuurlijk al analoog kunnen filteren, wat elektronisch triviaal is, maar moeilijk "industrieel" uit te voeren; je wilt niet met losse printjes en weerstandjes en condensators gaat klooien in zo'n kast.

Om iets dergelijks te regelen aan een verbrandingsmotor is een PLC ook niet ideaal, maar wederom, wat kun je dan doen, in een dergelijke setting? Er bestaan vrij programmeerbare automotive-stijl PLCs, zoals de Bosch Rexroth, maar daar is ook nogal wat op aan te merken, en de controllers zelf zijn al niet goedkoop, maar je wilt echt niet weten wat de toolchain daarvoor kost.

300ms cyclustijd? Is dat nog een mechanische PLC? Toen ik nog aan PLCs deed (inmiddels alweer zo'n 15 jaar geleden) was 10ms al heel normaal.

Het is regeltechnisch niet heel moeilijk om rekening te houden met een variabele flow, en die menging kunt je ook best modelleren, als je een stapresponse kunt opnemen of zo. Het probleem is eerder dat PLCs zich slecht lenen voor dergelijke algoritmes.

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

Moderator

Filtertijden zijn configureerbaar. Of 15s haalbaar is weet ik niet. Meerdere seconden zeker wel.

Het aantal voorhanden filters verschilt per kaart. Sommigen hebben een ruime keuze. Die successive approximation was toevallig de eerste die ik tegenkwam.

Op 4 januari 2021 19:26:33 schreef SparkyGSX:
300ms cyclustijd? Is dat nog een mechanische PLC? Toen ik nog aan PLCs deed (inmiddels alweer zo'n 15 jaar geleden) was 10ms al heel normaal.

Da's inderdaad een heel bijzondere. Standaart stond de watchdog 15 jaar geleden al op 150ms, dus om aan de 300ms te komen moet je serieus je best doen. Met een moderne PLC haal ik met een flink programma de 1ms nog niet eens. (Ik gebruik vooral Speed7 processoren)

High met Henk

Special Member

Ik gaf een reken voorbeeld.

Bedenk wel dat de sensor aan de bus hangt...

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

Op 4 januari 2021 20:14:21 schreef GJ_:
Die successive approximation was toevallig de eerste die ik tegenkwam.

Successive approximation lijkt mij een term te zijn die de marketing figuur ergens in een technisch document zag staan en dan in de manual heeft gezet: "klinkt gaaf!".

SAR is gewoon één van de manieren waarop je een ADC kan maken. Zo uit m'n hoofd zijn er: 'dual slope' (in meetinstrumenten), SAR en flash.

De laatste is gewoon 1024 comparators op een rijtje naast een 1024 weerstanden-in-serie als spanningsdeler. En dan wat logica om dat naar 10 bits te converteren. Deze is vooral handig als het heel snel moet. Ik dacht me te herinneren dat in 1990 de ADC0808 zo'n "flash conversion" chip was die voor toen RETE snel was, 10MHz, maar ik zie nu in het datasheet iets van 10kHz staan. Ik heb iets verkeerd onthouden. Maar reken er maar op dat moderne chips boven de 10MHz dat op die manier doen.

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

Ik lees iets over brandstofverbruik en uitlaat, waarbij het 'verbruik' en de 'dode tijd' ernstig kunnen variëren. Ik vrees dat een simpele averager losgelaten op uitlaat-samples nooit optimaal gaat werken. Ik denk eerder aan wat met in de regeltechniek 'observator' noemt. Zo'n observator probeert aan de hand van de ingang en de uitgang van het systeem (de motor) te bepalen hoe de toestand in het systeem er voor staat. Als je iets over de gasstand weet, weet je ook al iets over het brandstofverbruik. De gasstand zal ook invloed hebben op het gedrag aan de uitlaat, hoewel de temperatuur van de uitlaatgassen vrijwel direct reageert op een belastingsvariatie. Als je temperatuur gaat meten lijkt er een vertraging in te zitten, maar die 'dode tijd' heeft niets met het systeem (de motor) te maken, maar is het gevolg van de traagheid in de meting.
Om dus sneller (en beter) te kunnen reageren op een belastingsvariatie is het dus nodig om de gasstand (of een andere ingangs-variabele) mee te nemen in je regelaar. Je weet immers al dat als het brandstofverbruik verandert, dat je dan iets meer of minder in de uitlaat moet toevoegen en dat kun je alvast doen zonder eerst de meetwaarden af te wachten die vertraagd binnenkomen. Zeker als de veranderingen 'snel' zijn zou ik die voorkennis meenemen. Bij langzame veranderingen is dat natuurlijk minder van invloed.

Niet alles wat op internet staat is waar... Dat geldt ook voor CO.