Forum: Mikrocontroller und Digitale Elektronik Durchschnitt dynamisch berechnen


von Magnus (Gast)


Lesenswert?

Hallo,
ich möchte die die Anzahl von Impulsen/Minute dynamisch bestimmen.
Ich habe einen Impulsgeber der Werte zwischen 0 und 120 Impulse pro 
Minute ausgibt (Durchschnitt bei etwa 60).
Für den Durchschnittswert möchte ich jede Sekunde auslesen können, 
wieviele Impulse in den letzten 60 Sekunden aufgezeichnet wurden. Ich 
hätte dies, mit einem Stack gelöst, der die millis() der Impulse 
speichert, sowie seine Größe. Immer bei einer Abfrage würden dann alle 
Elemente, die älter als 60 Sekunden sind gelöscht und die große 
zurückgeben.

Leider habe ich keine Erfahrung mit ADT auf Mikrocontrollern, sowie mit 
OOP auf Mikrocontrollern

von Magnus (Gast)


Lesenswert?

Ich meinte natürlich Solange statt Stack

von sid (Gast)


Lesenswert?

Das geht nur wenn Du wirklich 60 verschiedene Werte speicherst,
sonst weisst Du nach n paar sekunden nichtmehr was zu löschen wäre..

wenn Du sicher bsit, dass kein Aktueller Wert grösser sein kann als 127.
DANN (und nur dann) kannst Du mit nur 4bit pro Wert auskommen,
also 30byte 240bit

bei jedem speichervorgang überschreibst du schlicht die folgenden 4 bit 
mit deinem gelesenen Wert (mod 240 quasi wie ein endlos magnet band)
und merkst Dir nur die position.
(bei 240bit reicht dann ein byte [255] für die zeigerposition)

Dann machst Du eine volle rotation (liest 60 mal 4bit)
und errechnest den Durchschnitt

und sobald ein neuer Wert eintrudelt, überschreibst Du die nächsten vier 
bit,
der Zeiger rückt um vier vor (immer mod 240 natürlich)
usw usf

von Walter K. (walter_k488)


Lesenswert?

Magnus schrieb:
> sowie mit
> OOP auf Mikrocontrollern

was willst du mit OOP?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Da wuerd' ich einfach eine Datenbank in der Cloud dazu hernehmen. Und in 
Java ein paar Front/Back/Obendrueber- und von 
HintendurchdieBrustinsAuge-Ends schreiben.

Duck&Wech
WK

von A. S. (Gast)


Lesenswert?

Der naive Ansatz: Array von 60 Zahlen, element 0 kommt jede Sekunde der 
aktuelle Wert rein, und davor wird Elemente 58 nach 59 verschoben, 57 
nach 58, ... 0 nach 1.

Danach alle 60 aufsummieren.

Zweite Verbesserung: memmove.

Dritte Verbesserung: ringbuffer

Vierte: nicht immer neu aufsummieren, sondern neuen wert zum 
Summenzähler addieren, ältesten subtrahieren.

Und dann, ... ja dann stellst Du fest, dass so ein Mittelwert meist 
wenig hilfreich ist.

von foobar (Gast)


Lesenswert?

1
volatile uint8_t pulses;
2
3
void
4
pulse_irq() // interrupt context
5
{
6
    pulses++;
7
}
8
9
static uint8_t
10
get_pulses() // get number of pulses since last call
11
{
12
    // Instead of resetting pulses (needs atomic read-and-clear)
13
    // remember the last value and take the difference to now.
14
    static uint8_t last;
15
16
    uint8_t x = pulses;
17
    x -= last;
18
    last += x;
19
    return x;
20
}
21
22
static uint8_t
23
get_pulse_avg() // returns pulse average over last 60 calls
24
{
25
    static uint8_t history[60];
26
    static uint8_t pos;
27
    static uint16_t avg;
28
29
    uint8_t x = get_pulses();
30
31
    avg -= history[pos];
32
    avg += x;
33
    histoy[pos++] = x;
34
    if (pos == 60)
35
        pos = 0;
36
37
    return avg / 60;
38
}
39
40
void
41
every_second()
42
{
43
    printf("pulse average: %d\n", get_pulse_avg());
44
}

von Weg mit dem Troll ! Aber subito (Gast)


Lesenswert?

Vergiss den Murks. Dafuer verwendet man einen tiefpass. Wie zB 
beschrieben unter : https://www.ibrtses.com/embedded/exponential.html 
Dabei nimmt der Anteil des aktuellen Wertes am Mittelwert mit der Zeit 
exponentiell ab.

von foobar (Gast)


Lesenswert?

Der gleitende Mittelwert über 60s ist halt ein extremer (FIR-)Tiefpass, 
der nur langfristige Änderungen durchlässt.  Mit einem exponentiellen 
(IIR-)Tiefpass wie unter werden eher die Flanken "runder".  Was man 
braucht ist Anwendungssache.

Zum Vergleichen/Rumexperimentieren:
1
volatile uint8_t pulses;
2
3
void
4
pulse_irq() // interrupt context
5
{
6
    pulses++;
7
}
8
9
static uint8_t
10
get_pulses() // get number of pulses since last call
11
{
12
    // Instead of resetting pulses (needs atomic read-and-clear)
13
    // remember last value and take the difference to now
14
    static uint8_t last;
15
16
    uint8_t x = pulses;
17
    x -= last;
18
    last += x;
19
    return x;
20
}
21
22
static uint8_t
23
avg_60(uint8_t x) // returns average of x over last 60 calls
24
{
25
    static uint8_t history[60];
26
    static uint8_t pos;
27
    static uint16_t avg;
28
29
    avg -= history[pos];
30
    avg += x;
31
    history[pos++] = x;
32
    if (pos == 60)
33
        pos = 0;
34
    return avg / 60;
35
}
36
37
static uint8_t
38
avg_exp(uint8_t x) // returns exponential average of x
39
{
40
    static int avg;
41
42
    avg += ((int)x * 4 - avg + 1) / 2;
43
    return avg / 4;
44
}
45
46
void
47
every_second()
48
{
49
    uint8_t p = get_pulses();
50
51
    printf("pulses: %d, 60s-average: %d, exp-average: %d\n", p, avg_60(p), avg_exp(p));
52
}
53
54
// test
55
int
56
main(int argc, char **argv)
57
{
58
    int x = 0;
59
60
    for (int i=0; i < 70; ++i)
61
    {
62
        pulses += 50 + 50*sin(x++ * .2);
63
        //pulses += 50 + 3*(x++%8 < 2);
64
        every_second();
65
     }
66
67
     return 0;
68
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.