Forum: Mikrocontroller und Digitale Elektronik Mittelwert aus ca.100 Werten.


von Philipp L. (viech)


Lesenswert?

Hallo zusammen,

Ich möchte den Mittelwert einer (10Bit) ADC Messung über eine Zeit-x 
(10sek) erfassen/auswerten.
Ich möchte jedoch, dass immer der letzte Wert rausfällt und der aktuelle 
übernommen wird.
Also nich immer neu bei 0 anfangen.

Wie programmiert man so etwas am geschicktesten.
100 Variablen anlegen und damit rumrechnen kommt mir nicht ideal vor.

Ich könnte natürlich 1x den Mittelwert abziehen und den neuen Wert 
addieren, aber ein echter Mittelwert wird das nicht...

Danke für eure Hilfe

: Bearbeitet durch User
von Dussel (Gast)


Lesenswert?

Mit einem Array kann man es natürlich machen.
Es müsste aber auch gehen mit (Mittelwert*99+Neuer_Wert)/100.

von StinkyWinky (Gast)


Lesenswert?

moving average

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Philipp L. schrieb:
> 100 Variablen anlegen

Du suchst möglicherweise ein Array.

von Falk B. (falk)


Lesenswert?

Philipp L. schrieb:
> Hallo zusammen,
>
> Ich möchte den Mittelwert einer (10Bit) ADC Messung über eine Zeit-x
> (10sek) erfassen/auswerten.
> Ich möchte jedoch, dass immer der letzte Wert rausfällt und der aktuelle
> übernommen wird.
> Also nich immer neu bei 0 anfangen.

Nennt sich moving average filter.

> Wie programmiert man so etwas am geschicktesten.

Mit einem Array mit 100 Elementen und einem Akku. Im Akku steht die 
Summe aller Elemente drin.  Wenn nun ein neuer Wert reinkommt, wird der 
älteste Eintrag vom Akku entfernt, mit dem neuen Wert im Array 
überschrieben und der neue Wert zum Akku hinzuaddiert. Mit diesem Trick 
spart man sich bei jedem Durchlauf das Aufsummieren der 100 Werte. Der 
Mittelwert ist dann Akku/100, diese Division kann man nicht einsparen.

> 100 Variablen anlegen und damit rumrechnen kommt mir nicht ideal vor.

Die 100 Variablen stecken im Array.

von Alex G. (dragongamer)


Lesenswert?

Stichwort ist Ring-buffer. Die Idee ist ein Array mit 100 Stellen 
anlegen und einen separaten Zeiger nehmen der immer auf eine Zelle 
zeigt.
Bei jedem neuen Wert schreibst du den an die Zeiger-Position und 
incrementierst den Zeiger. Wenn du bei hundert bist, springst du wieder 
zur ersten Position des Arrays.

Die Reihenfolge der Werte im Speicher stimmt damit natürlich nicht, aber 
bei vielen Berechnungen wie Mittelwert ist das ja irrelevant da nur der 
Wert und nicht der Index zählt.
Mittels der momentanen Zeiger-Position kann man, wenn nötig eine 
Schleife auf dem Array auch in der richtigen Reihenfolge laufen lassen.

Btw. wenn du Speicher sparen musst, versuche den kleinstmöglichen 
Datentyp für dein Array zu nehmen.

von Christian B. (luckyfu)


Lesenswert?

Nimm ein Array, anders wird es nicht funktionieren, da du dir immer die 
Messwerte einzeln speichern musst. Wenn dir der Fehler relativ egal ist 
könntest du auch für jeden neuen Messwert den du addierst einmal den 
Mittelwert der letzten 100 Messungen abziehen. Dann kämst du mit 3 
Variablen aus.

von Philipp L. (viech)


Lesenswert?

Ja, vermutlich ist das ein Array.
Das Thema steht sowieso auf der to-learn liste.

von Thomas E. (thomase)


Lesenswert?

Philipp L. schrieb:
> Wie programmiert man so etwas am geschicktesten.
> 100 Variablen anlegen und damit rumrechnen kommt mir nicht ideal vor.

Immer wenn du einen neuen Wert erhältst, ziehst du den ältesten von der 
Summe ab und addierst den neuen drauf. Dazu mußt du dir die alten Werte 
in einem Array merken. Bei 100 Werten ist das natürlich entsprechend 
groß. Aber solange der Speicher das hergibt auch kein Problem. Dem 
Controller ist das egal. Und Geld gibt es für gesparten Speicher auch 
nicht zurück.

von Dussel (Gast)


Lesenswert?

Das ist etwas unglücklich ausgedrückt.
Ich meinte das iterativ, also
Neuer_Mittelwert=(Alter_Mittelwert*99+Neuer_Mittelwert)/100

von c-hater (Gast)


Lesenswert?

Philipp L. schrieb:

> Wie programmiert man so etwas am geschicktesten.

Suche nach Implementierungen von "gleitender Mittelwert". Es gibt 
unzählige davon.

Im Prinzip benötigt man einen Ringpuffer und eine Summenvariable.

Besonders effizient wird es, wenn man die Größe des Ringpuffers auf eine 
Zweierpotenz festlegt, denn dann braucht man zur Ermittlung des 
Mittelwerts nicht dividieren, sondern nur schieben. Auch die Verwaltung 
des Ringpuffers selber kann dann effizienter geschehen.

von Alex G. (dragongamer)


Lesenswert?

Dussel schrieb:
> Das ist etwas unglücklich ausgedrückt.
> Ich meinte das iterativ, also
> Neuer_Mittelwert=(Alter_Mittelwert*99+Neuer_Mittelwert)/100
Damit hat man natürlich Abweichungen, aber es ist die 
speicherschonendste Variante.


Die Idee mit dem "Akku" ist richtig gut!

von Dussel (Gast)


Lesenswert?

Das mit dem Akku ist auch eine gute Idee. das ist genauer. Es kommt auch 
drauf an, was wichtig ist, Speicher, Laufzeit oder Genauigkeit.

Falk B. schrieb:
> Der Mittelwert ist dann Akku/100, diese Division
> kann man nicht einsparen.
Da es um "ca. 100 Werte" geht, geht vielleicht auch 128.

von Falk B. (falk)


Lesenswert?

1
int filter[100];
2
3
int new_average(int adc_data) {
4
  static long accu;
5
  static int i;
6
7
  accu = accu - filter[i] + adc_data;
8
  filter[i] = adc_data;
9
  i++;
10
  if (i >= 100) i=0;
11
12
  return accu/100;
13
}

von Martin B. (ratazong)


Lesenswert?

um die 100 Variablen wirst Du nicht rumkommen.

in Integer Arithmetik ist es einfach. Mit float geht das nicht so.


static uint16_t buf[100];
static uint32_t ergebnis=0;
static int Cnt=0;


uint32_t getAverage(uint16_t wert)
{
  if(Cnt >= 100) Cnt=0;
  ergebnis -= buf[Cnt];
  ergebnis += wert;
  buf[Cnt] = wert;
  Cnt++;
  return ergebnis;
}


die ersten 100 Messungen kommt nichts vernüftiges heraus, da die 
Vorgeschichte nicht bekannt ist. buf[100] muss auch mit Nullen 
initialisiert sein, falls der Compiler das nicht auomatisch tut.

von Martin B. (ratazong)


Lesenswert?

Falk war schneller und teilt noch durch den Average Counter. -:)

von Falk B. (falk)


Lesenswert?

Martin B. schrieb:

> die ersten 100 Messungen kommt nichts vernüftiges heraus, da die
> Vorgeschichte nicht bekannt ist.

Stimmt nicht. Es kommt die Sprungantwort des Filters raus, so wie auch 
bei einem analogen RC-Filter, das auf Spannung 0V geladen ist.

von Philipp L. (viech)


Lesenswert?

Okay, den Code muss ich mir in Ruhe ansehen.
Habe mit Arrays noch nichts gemacht und bin noch C-Anfänger...
Aber vielen Dank schon mal!

Die Genauigkeit ist nicht soo wichtig (grobe Überstromabschaltung).
Die ersten 100 Werte sind mir egal.

Es soll aber eine kleine Unterfunktion auf dem Controller sein und nicht 
übermäßig viele Takte verbraten.

: Bearbeitet durch User
von Udo S. (urschmitt)


Lesenswert?

Philipp L. schrieb:
> Es soll aber eine kleine Unterfunktion auf dem Controller sein und nicht
> übermäßig viele Takte verbraten.

Dann nimm keine 100 Werte, sondern eine 2er-Potenz. Also 64 oder 128. 
Dann kann die Division durch die Anzahl Werte als effektives shift 
gemacht werden.
(macht der Compiler wahrscheinlich dann automatisch)

Das wurde oben auch schon mehrfach angeregt.

von Philipp L. (viech)


Lesenswert?

Ja, die 100 Werte sind nicht fix.
die Division als Schiebefunktion ist gut.

Habe ich es richtig verstanden, dass der Ringbuffer nicht ständig 
geändert und dann als ganzes summiert wird.
Sondern man verwendet eine Summenvariable und verwendet den Pointer nur 
um den ältesten Wert rauszuholen, von der Summe zu sub. und dann den 
aktuellen Wert zu Add. ?

Klingt für mich gut und effektiv.

von Thomas E. (thomase)


Lesenswert?

Philipp L. schrieb:
> Habe ich es richtig verstanden

Ja.

Philipp L. schrieb:
> Klingt für mich gut und effektiv.

Genau darum geht es.

von Mark U. (residuum)


Lesenswert?

Über die Initialisierung muss man sich auch noch Gedanken machen:

Wenn es keine Rolle spielt, dass die ersten 100 Mittelwerte nur langsam 
an den tatsächlichen heran schleichen, benötigt man keine 
Initialisierung.

Sonst kann man entweder das Array komplett mit dem ersten Messwert 
initialisieren. Oder auch mehrere Messwerte zur Initialisierung heran 
ziehen.

Außerdem ist es noch eine gute Idee, z.B. die 5 maximalen und die 5 
minimalen Werte raus fallen zu lassen. So vermeidet man, dass Ausreißer 
den Mittelwert verfälschen.

Da hier aber der Mittelwert aus 100 Messwerten gewonnen wird, fallen 
Ausreißer ohnehin nicht sehr ins Gewicht.

von m.n. (Gast)


Lesenswert?

Mark U. schrieb:
> Wenn es keine Rolle spielt, dass die ersten 100 Mittelwerte nur langsam
> an den tatsächlichen heran schleichen, benötigt man keine
> Initialisierung

Man braucht immer eine Initialisierung!

> Außerdem ist es noch eine gute Idee, z.B. die 5 maximalen und die 5
> minimalen Werte raus fallen zu lassen. So vermeidet man, dass Ausreißer
> den Mittelwert verfälschen.

Immer soviel wegwerfen, bis das Weltbild stimmt? Das ist keine gute Idee 
sondern der Versuch, an den Daten herumzufuschen.

Udo S. schrieb:
> Dann nimm keine 100 Werte, sondern eine 2er-Potenz. Also 64 oder 128.

Was soll der Unfug? Wenn ich 100 Werte mitteln möchte/muß, dann nehme 
ich 100 Werte. Soll wieder der µC entlastet werden, um sich noch mehr zu 
langweilen?

von Wolfgang (Gast)


Lesenswert?

Dussel schrieb:
> Das ist etwas unglücklich ausgedrückt.
> Ich meinte das iterativ, also
> Neuer_Mittelwert=(Alter_Mittelwert*99+Neuer_Mittelwert)/100

Du hast nicht verstanden, was der TO möchte.

Bei ihm soll der Mittelwert der letzten 100 Werte berechnet werden, also 
braucht er ein FIR-Filter.

Deine Gleichung beschreibt ein IIR-Filter
https://de.wikipedia.org/wiki/Filter_mit_unendlicher_Impulsantwort

von Johnny B. (johnnyb)


Lesenswert?

Dussel schrieb:
> Mit einem Array kann man es natürlich machen.
> Es müsste aber auch gehen mit (Mittelwert*99+Neuer_Wert)/100.

Ja in etwa so. Nennt sich dann "IIR Filter".

Etwas anschaulicher macht man es dann so:

Filterfaktor = 0.xxxx

GefilterterWert = GefilterterWert - (GefilterterWert * Filterfaktor) + 
(Messwert * Filterfaktor)

: Bearbeitet durch User
von Philipp L. (viech)


Lesenswert?

Habe mir die beiden Codes grad in der Mittagpause angesehen.
Klingt sehr logisch, wird heute Abend gleich probiert.

Danke für die schnelle Hilfe, das Forum hier ist super !!


Was ich auch noch nicht spontan überblicke, sind die notwendigen Takte 
bei der Divison.

return accu/100;

oder

return accu<<7;  -> geht das oder muss ich erst accu = accu<<7 
schreiben?
Dann natürlich mit filter[128].

: Bearbeitet durch User
von Dussel (Gast)


Lesenswert?

Wolfgang schrieb:
> Dussel schrieb:
>> Das ist etwas unglücklich ausgedrückt.
>> Ich meinte das iterativ, also
>> Neuer_Mittelwert=(Alter_Mittelwert*99+Neuer_Mittelwert)/100
>
> Du hast nicht verstanden, was der TO möchte.
Johnny B. schrieb:
> Dussel schrieb:
>> Mit einem Array kann man es natürlich machen.
>> Es müsste aber auch gehen mit (Mittelwert*99+Neuer_Wert)/100.
>
> Ja in etwa so. Nennt sich dann "IIR Filter".
Verstanden habe ich es natürlich, aber ich hatte einen Denkfehler. Mein 
Ansatz beschreibt keinen arithmetischen Mittelwert.

von hilfesteller (Gast)


Lesenswert?

Philipp L. schrieb:
> return accu<<7;  -> geht das oder muss ich erst accu = accu<<7
> schreiben?
> Dann natürlich mit filter[128].

Und natürlich 'accu >> 7'. Du willst ja dividieren, nicht 
multiplizieren.

von Stephan (Gast)


Lesenswert?

Bei einer Groben Überstromabschaltung reicht sicher ein Muittelwert über 
3 oder 4 Werte. Dann Abschaltung wenn Wert >= Wert_max ist. Da reicht ja 
noch ein Komparator aus.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das geht auch ohne Array. Ein schlauer Mensch hat das zur Verfügung 
gestellt. Nennt sich gleitender Mittelwert.

1
/*****************************************************************************************
2
** Funktion Filtern() by GuntherB                                                      **
3
******************************************************************************************
4
** Bildet einen Tiefpassfilter (RC-Glied) nach.                                         **
5
** FF = Filterfaktor;  Tau = FF / Aufruffrequenz                                        **
6
**                                                                                      **
7
** Input: FiltVal der gefilterte Wert, NewVal der neue gelesene Wert; FF Filterfaktor   **
8
** Output:  FiltVal                                                                     **
9
** genutzte Globale Variablen: float Val (call by Reference)                                 **
10
******************************************************************************************/
11
12
void Filtern(float &FiltVal, int NewVal, int FF){
13
  FiltVal= ((FiltVal * FF) + NewVal) / (FF+1); 
14
}

Aufruf in dem Fall hier mit:
1
Filtern(Val, Eingangswert, 100);

von Philipp L. (viech)


Lesenswert?

> Bei einer Groben Überstromabschaltung reicht sicher ein Muittelwert über
> 3 oder 4 Werte. Dann Abschaltung wenn Wert >= Wert_max ist. Da reicht ja
> noch ein Komparator aus.

Da bin ich anderer Meinung, da der Strom wie geschrieben über min. 10sek 
gemittelt werden soll (eher länger).
Denn es geht um die Erwärmung eines step-down an der Spule.
Dieser kann laut Datenblatt 3A, wird bei dieser Dauerbelastung aber sehr 
heiß!

Daher teste ich, mit welcher Dauerbelastung der nicht übermäßig warm 
wird.
Dieser wird dann der Grenzwert der Mittelwerterfassung.

So kann ich die vollen 3A kurzzeitig ausnutzen (Servo`s, usw..), habe 
aber trotzdem die Sicherheit das der nicht überhitzt.

> Und natürlich 'accu >> 7'. Du willst ja dividieren, nicht
> multiplizieren.

Ups..
Die eigentliche Frage war, wie viele Takte benötigt ein
x/128 und im Vergleich dazu ein x>>7
Das Ergebnis sollte doch gleich sein?



> void Filtern(float &FiltVal, int NewVal, int FF){
>   FiltVal= ((FiltVal * FF) + NewVal) / (FF+1);}

Das ist aber kein exakter Mittelwert wie beim Array, sondern eher wie:
>Neuer_Mittelwert=(Alter_Mittelwert*99+Neuer_Mittelwert)/100

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Philipp L. schrieb:
> Die eigentliche Frage war, wie viele Takte benötigt ein
> x/128 und im Vergleich dazu ein x>>7

Schreibe immer: x/128. Der Compiler wird es richten.

Diese Takte kannst Du Dir einfach sparen, indem Du den Überstrom als
#define MAX_STROM  (GRENZWERT * 128)
vorgibts. Das geht auch für (GRENZWERT * 100) genau so schnell ;-)

von Purzel H. (hacky)


Lesenswert?

Eine effiziente implementierung :
https://www.ibrtses.com/embedded/exponential.html

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

m.n. schrieb:
> Immer soviel wegwerfen, bis das Weltbild stimmt? Das ist keine gute Idee
> sondern der Versuch, an den Daten herumzufuschen.

"weltbild" geht schon in die richtige richtung.

der to sollte erst mal klären welche physikalische grösse (weltbild) er 
abbilden will und zeitliche änderungsrate? gültiger wertebereich? 
störgrössen art? ... sollen evtl. gefiltern werden und erst dann, 
entscheidet man wie das geschehen soll moving average ist oft kontra 
indiziert und eher ein exp. filer macht den job besser, like

  //linear recursive exponential filter, weight e.g. 0.3=30%
  //y(t)=(1-w)*y(t-1)+w*x(t)
  oldValue = (1 - weight) * oldValue + weight * newValue;


ohne messtechnik und statistik grundkentnisse wird das wieder nur das 
übliche raten von schwachmaten.


mt

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Name H. schrieb:
> Eine effiziente implementierung :
> https://www.ibrtses.com/embedded/exponential.html

das ist ein "linear recursive exponential filter" und wie schon 
geschrieben kann die richtige wahl sein muss aber nicht!

die filterwahl (und nichts anderes ist mittelwertbildung) hängt von der 
konkreten fragestellung ab, was will ich erreichen unter welchen 
bedingungen?!

... moving average der lottezahlen der letzten 4 wochen macht das sinn?


mt

von Dirk B. (dirkb2)


Lesenswert?

Udo S. schrieb:
> Dann nimm keine 100 Werte, sondern eine 2er-Potenz. Also 64 oder 128.

Da er 10-Bit addiert wären 64 Werte besser, weil dann ein 16-Bit Wert 
für die Summe reicht.

Martin B. schrieb:
> static uint16_t buf[100];
> ....
>
> buf[100] muss auch mit Nullen
> initialisiert sein, falls der Compiler das nicht auomatisch tut.

static und globale Variablen werden immer mit 0 initialisiert, sofern 
nichts anderes angegeben wird.

von Thomas E. (thomase)


Lesenswert?

Philipp L. schrieb:
> return accu<<7;  -> geht das oder muss ich erst accu = accu<<7

Wie schon an anderer Stelle geschrieben, mußt du natürlich nach rechts 
schieben.

Du kannst aber auch
1
return accu / 128;

schreiben.

Die Optimierung des Compilers sorgt dafür, daß in beiden Fällen dasselbe 
rauskommt.

von Philipp L. (viech)


Lesenswert?

> der to sollte erst mal klären welche physikalische grösse (weltbild) er
> abbilden will und zeitliche änderungsrate?

Das hat er bereits gemacht :-)

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Thomas E. schrieb:
> Die Optimierung des Compilers sorgt dafür, daß in beiden Fällen dasselbe
> rauskommt.

Sag das mal dem Keil C51 Compiler :-(

von Thomas E. (thomase)


Lesenswert?

Bernd K. schrieb:
> Sag das mal dem Keil C51 Compiler :-(

Das war schon klar, daß sowas kommt.

Allerdings darf man, wenn da nichts anderes steht, in diesem Forum davon 
ausgehen, daß es sich um einen AVR handelt, dessen C-Code mit GCC bei 
-Os kompiliert wird.

von Philipp L. (viech)


Lesenswert?

> störgrössen art? ... sollen evtl. gefiltern werden
Nein, es müssen alle Werte mit in das Ergebnis einfließen.
Denn es handelt sich bei einem "Ausreißer" EVTL. nicht um eine Störung 
sondern den Anlaufstrom eines z.B. Motors.


HINWEIS:
Es werden vermutlich alle hier genannten Methoden für meinen 
Anwendungsfall ausreichend sein.

Denn ich möchte ja nur wissen, ob der Stepdown-Regler länger als Zeitx 
(min. aber 10sek) mit mehr als dem definierten Grenzwert belastet wird.
Der Grenzwert ist ja auch nur empirisch ermittelt worden und liegt unter 
dem im Datenblatt angegebenen Wert.
Es geht mir darum, dass das Teil nicht zuuu warm wird.

Also alles halb so wild, trotzdem sind die verschiedenen Modelle 
interessant gewesen.

Ich werde jedoch das Array verwenden, da es mir am besten gefällt und 
genau meine ursprüngliche Vorstellung wiedergibt.

Danke euch allen!!

: Bearbeitet durch User
von C. U. (chriull)


Lesenswert?

Philipp L. schrieb:
> Denn ich möchte ja nur wissen, ob der Stepdown-Regler länger als Zeitx
> (min. aber 10sek) mit mehr als dem definierten Grenzwert belastet wird.
> Der Grenzwert ist ja auch nur empirisch ermittelt worden und liegt unter
> dem im Datenblatt angegebenen Wert.
> Es geht mir darum, dass das Teil nicht zuuu warm wird.

Erwärmung hängt (normalerweise) von der  Verlustleistung ab ( Delta T = 
Wärmewiderstand × Verlustleistung). Diese hängt vom Quadrat des Stromes 
ab (z.B.: I^2 x R DS on x duty_cycle des Schalt Mosfets im Stepdown 
Regler)
Mit dem Durschnitt der I^2 könnte man dieses Verhalten wohl besser 
abbilden(absichern). Das sollte dann auch besser dem Verhalten einer 
Feinsicherung entsprechen.

: Bearbeitet durch User
von Stephan (Gast)


Lesenswert?

Philipp L. schrieb:
> Da bin ich anderer Meinung, da der Strom wie geschrieben über min. 10sek
> gemittelt werden soll (eher länger).
> Denn es geht um die Erwärmung eines step-down an der Spule.
> Dieser kann laut Datenblatt 3A, wird bei dieser Dauerbelastung aber sehr
> heiß!
>
> Daher teste ich, mit welcher Dauerbelastung der nicht übermäßig warm
> wird.
> Dieser wird dann der Grenzwert der Mittelwerterfassung.
>
> So kann ich die vollen 3A kurzzeitig ausnutzen (Servo`s, usw..), habe
> aber trotzdem die Sicherheit das der nicht überhitzt.

Das ist doch BLÖDSINN. Bei pulsweiser Belastung wirst Du da im 
schlimmsten Fall nichts erfassen, weil Du möglicher weise während der 
Laspause misst, und die Kiste wird kochen. Die Überhitzung hat auch was 
mit der Umgebungsluft zu tun. Da hift die Srrommessung nicht. Vertane 
Zeit sage ich mal. Beschäftige Dich mit der Temp.-Messung.

von Stephan (Gast)


Lesenswert?

ps. Ich schäume Deinen Step Down ein und Du misst dafür fleißig den 
Strom. Was wird passieren ?

von m.n. (Gast)


Lesenswert?

Stephan schrieb:
> Was wird passieren ?

Es gibt keine Lösung des Problems.
Warum? Es darf sie nicht geben.
Was der TO auch immer machen wird, es wird alles falsch sein ;-)

Philipp L. schrieb:
> Ich werde jedoch das Array verwenden,

Fange einfach an!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:

> das geht auch ohne Array. Ein schlauer Mensch hat das zur Verfügung
> gestellt. Nennt sich gleitender Mittelwert.

Wie oben bereits festgestellt, ist diese Behauptung falsch. Die 
Berechnung ohne Array, die Du hier anfügst, wurde oben bereits (in 
abgewandelter Form) erwähnt. Das ist ein IIR-Filter (Infinite Impulse 
Response) und kein gleitender Mittelwert, denn ein IIR-Filter hat ein 
"unendliches Gedächtnis".

Um einen "gleitenden Mittelwert" zu erhalten, kommst Du um das Array 
nicht herum. Hier wird immer nur ein endliches Zeitfenster betrachtet. 
Was vor ein paar Tagen war, spielt hier keine Rolle. Das nennt sich 
deshalb auch FIR-Filter (Finite Impulse Response).

Siehe auch: https://de.wikipedia.org/wiki/Gleitender_Mittelwert

: Bearbeitet durch Moderator
von Philipp L. (viech)


Lesenswert?

> Das ist doch BLÖDSINN.
Ach wie schön...
Mal wieder jemand der sich bei einer anderen Meinung gleich angegriffen 
fühlt :-) :-) :-)

>Bei pulsweiser Belastung wirst Du da im schlimmsten Fall nichts erfassen,
>weil Du möglicher weise während der Laspause misst,
>und die Kiste wird kochen.

woher soll denn die pulsweise Belastung in meinem sekundären Teil 
kommen?
Die Spannung hinterm stepdown ist kondensatorgepuffert.
Ausnahme:
Bei Servo`s die kurz von POS-A zu POS-B fahren, kann es evtl. zu 
Messfehlern kommen.
Aber diese kurzzeitbelastung ist bei der Erwärmung des Reglers zu 
vernachlässigen.
Nochmals:
Dies ist eine on-Top Maßnahme, ich bewege mich immer innerhalb der 
Grenzen des Datenblattes...

> Die Überhitzung hat auch was mit der Umgebungsluft zu tun.
> Da hift die Srommessung nicht.
keine Sorge, wird nicht in der Wüste eingesetzt..

> Vertane Zeit sage ich mal. Beschäftige Dich mit der Temp.-Messung.
Natürlich die beste Variante, aber zuviel des guten.
extra einen Tempsensor auf die Spule, usw... Nee...

> ps. Ich schäume Deinen Step Down ein und Du misst dafür fleißig den
> Strom. Was wird passieren ?

Jeder Kommentar ist hier einer zuviel :-)



Es wäre schön, wenn wir den Thread diesmal nicht mit gegenseitigen 
"Angriffen" kaputt machen...

Ich habe jedenfalls genug Informationen für die umsetzung bekommen.

Danke und bis zum nächsten mal.

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Veit D. schrieb:
> Ein schlauer Mensch hat das zur Verfügung
> gestellt. Nennt sich gleitender Mittelwert.

Die Rechnung hat nur den kleinen Schönheitsfehler, dass sich der 
"schlaue" Mensch vertan hat und damit kein gleitender Mittelwert 
berechnet wird.

Hier kannst du nachlesen, wie der richtig berechnet wird:
https://de.wikipedia.org/wiki/Gleitender_Mittelwert

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wolfgang schrieb:
> Die Rechnung hat nur den kleinen Schönheitsfehler, dass sich der
> "schlaue" Mensch vertan hat und damit kein gleitender Mittelwert
> berechnet wird.
>
> Hier kannst du nachlesen, wie der richtig berechnet wird:
> https://de.wikipedia.org/wiki/Gleitender_Mittelwert

In dem von dir verlinkten Wikipedia-Artrikel wird unterschieden zwischen

1. dem einfachen (gleichgewichteten) gleitenden Mittelwert und

2. dem exponentiell geglätteten (exponentiell gewichteten gleitenden)
   Mittelwert.

(1) zählt zu den FIR-Filtern, benötigt ein Array als Puffer für die
letzten n Messwerte und wird von Philipp gerade implementiert.

(2) zählt zu den IIR-Filtern, kann unabhängig vom Filterfaktor mit nur
wenigen Einzelvariablen implementiert werden, verhält sich wie ein
analoger Tiefpass 1. Ordnung und wird durch der von Veit geposteten Code
des "schlauen Menschen" umgesetzt.

Für Philipps Problemstellung liefern IMHO beide Verfahren brauchbare,
wenn auch unterschiedliche Ergebnisse.

von Phil E. (aktiver_mitleser)


Lesenswert?

Philipp L. schrieb:
> Die eigentliche Frage war, wie viele Takte benötigt ein
> x/128 und im Vergleich dazu ein x>>7
> Das Ergebnis sollte doch gleich sein?

Für die Zukunft: einfach den Compiler Explorer fragen was dabei 
compiliert wird (AVR-spezifisch: http://avr-gcc.senthilthecoder.com, 
allgemeiner: https://godbolt.org/)

Gibt man dem AVR compiler explorer folgenden Quellcode
1
int shift(int in){
2
  return (in >> 7);
3
}
4
5
int div(int in){
6
  return (in / 128);
7
}

mit Compiler-Optionen -Os -mmcu=atmega328p -std=gnu99 und gcc 4.9.2, so 
wird daraus folgendes:

shift:
1
lsl r24
2
mov r24,r25
3
rol r24
4
sbc r25,r25
5
ret

div:
1
sbrs r25,7
2
rjmp .L3
3
subi r24,-127
4
sbci r25,-1
5
.L3:
6
lsl r24
7
mov r24,r25
8
rol r24
9
sbc r25,r25
10
ret

Resultat: definitiv nicht das selbe (was ich nicht erwartet hätte...)

-O2 ist auch nicht gleich.

*Viel schlimmer*: avr-gcc 4.5.1/4.6.4 fügen mit "-Os" ein
1
call __divmodhi4
 ein, was 245(!) Takte braucht!



Hier noch der Link zum AVR-GCC Compiler Explorer

http://avr-gcc.senthilthecoder.com/#g:!((g:!((g:!((h:codeEditor,i:(j:1,options:(colouriseAsm:'0',compileOnChange:'0'),source:'int+shift(int+in)%7B%0A++return+(in+%3E%3E+7)%3B%0A%7D%0A%0Aint+div(int+in)%7B%0A++return+(in+/+128)%3B%0A%7D'),l:'5',n:'1',o:'C+source+%231',t:'0')),k:49.99999999999999,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:a354,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-Os+-mmcu%3Datmega328p+-std%3Dgnu99'),l:'5',n:'0',o:'%231+with+Atmel+3.5.4+(gcc+4.9.2)',t:'0')),k:49.99999999999999,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4

von Jemand (Gast)


Lesenswert?

Philipp M. schrieb:
> Philipp L. schrieb:
>> Die eigentliche Frage war, wie viele Takte benötigt ein
>> x/128 und im Vergleich dazu ein x>>7
>> Das Ergebnis sollte doch gleich sein?
>
> Für die Zukunft: einfach den Compiler Explorer fragen was dabei
> compiliert wird (AVR-spezifisch: http://avr-gcc.senthilthecoder.com,
> allgemeiner: https://godbolt.org/)

Mit unsigned int wäre das nicht passiert. x>>7 ist für x < 0 
implementation-defined Rotz.

von Martin B. (ratazong)


Lesenswert?

Jemand schrieb:
> it unsigned int wäre das nicht passiert. x>>7 ist für x < 0
> implementation-defined Rotz.


Kann man trotzdem näherungsweise machen für negative Zahlen.

x>>7 und x/128 ist nur für positive Zahlen dasselbe. War schon immer so.

für negative Zahlen unterscheidet sich das Ergebnis um 1. Aber auch 
nicht mehr.

von Phil E. (aktiver_mitleser)


Lesenswert?

Jemand schrieb:
> Mit unsigned int wäre das nicht passiert. x>>7 ist für x < 0
> implementation-defined Rotz.

Damit hast du Recht, mit uint16_t sind beide Operationen gleich. Das 
doofe ist nur - die "richtige" Operation (Division) ist langsam, während 
die implementation-defined Operation das korrekte Verhalten liefert.

von Wolfgang (Gast)


Lesenswert?

Yalu X. schrieb:
> 2. dem exponentiell geglätteten (exponentiell gewichteten gleitenden)
>    Mittelwert.

Egal ob man diesem Konstrukt die Bezeichnung Mittelwert zuschreibt - das 
Ergebnis dieser Operation setzt sich nicht aus einer festen Anzahl von 
Messwerten zusammen und entspricht damit nicht dem Ziel des TOs.

So schwer ist das doch nicht.

von Phil E. (aktiver_mitleser)


Lesenswert?

Martin B. schrieb:
> für negative Zahlen unterscheidet sich das Ergebnis um 1. Aber auch
> nicht mehr.

Meinst du Negation vs. bitweises Invertieren? Da stimmt das. "x>>" 
funktioniert auch für negative Zahlen, wenn man Vorzeichenerweiterung 
macht, d.h. beim rechts schieben nicht links Nullen einfügen sondern das 
MSB kopieren.

von Phil E. (aktiver_mitleser)


Lesenswert?

Wolfgang schrieb:
> Egal ob man diesem Konstrukt die Bezeichnung Mittelwert zuschreibt - das
> Ergebnis dieser Operation setzt sich nicht aus einer festen Anzahl von
> Messwerten zusammen und entspricht damit nicht dem Ziel des TOs.
>
> So schwer ist das doch nicht.

Ein IIR erster Ordnung entspricht aber eher dem zugrundeliegendem 
physikalischen Modell, das der TO zu erfassen versucht - eine 
Überstromspitze hinterlässt ihren thermischen Eindruck auch nach 10 
Sekunden noch, und so ist ein IIR zwar nicht das erklärte Ziel des TOs, 
aber vielleicht das was er tatsächlich braucht, ohne dass er das weiß.

Das zu erkennen und zu vermitteln ist schwer.

von Martin B. (ratazong)


Lesenswert?

Philipp M. schrieb:
> Martin B. schrieb:
>> für negative Zahlen unterscheidet sich das Ergebnis um 1. Aber auch
>> nicht mehr.
>
> Meinst du Negation vs. bitweises Invertieren? Da stimmt das. "x>>"
> funktioniert auch für negative Zahlen, wenn man Vorzeichenerweiterung
> macht, d.h. beim rechts schieben nicht links Nullen einfügen sondern das
> MSB kopieren.

Um beim Beispiel shiften eines int um 7 zu bleiben:

-1 ... -128 liefern beim shiften eine -1
-1... -127 liefern beim dividieren eine 0, bei -128 eine -1

um das zu korrigieren muss bei einer Division einer negativen Zahl durch 
128 erst 127 aufaddieren und dann um 7 shiften. Dann stimmt es wieder 
genau.

von m.n. (Gast)


Lesenswert?

Philipp M. schrieb:
> Für die Zukunft:

... erst einmal darüber nachdenken, welcher Datentyp denn notwendig ist. 
Im vorliegenden Fall muß es uint32_t sein.
Da der Mittelwert mit einem Grenzwert verglichen werden soll, geht es - 
wie oben schon angedeutet - viel schneller, jegliche Division zu 
unterlassen und direkt die Summe der Einzelwerte zum Vergleich nutzen.

Allerdings bezweifele ich, daß die Ausführungsgeschwindigkeit irgendeine 
Rolle spielt. Ein delay(100) braucht mehr Zeit. Oder optimiert Ihr das 
auch immer?

von Johnny B. (johnnyb)


Lesenswert?

Philipp M. schrieb:
> Wolfgang schrieb:
>> Egal ob man diesem Konstrukt die Bezeichnung Mittelwert zuschreibt - das
>> Ergebnis dieser Operation setzt sich nicht aus einer festen Anzahl von
>> Messwerten zusammen und entspricht damit nicht dem Ziel des TOs.
>>
>> So schwer ist das doch nicht.
>
> Ein IIR erster Ordnung entspricht aber eher dem zugrundeliegendem
> physikalischen Modell, das der TO zu erfassen versucht - eine
> Überstromspitze hinterlässt ihren thermischen Eindruck auch nach 10
> Sekunden noch, und so ist ein IIR zwar nicht das erklärte Ziel des TOs,
> aber vielleicht das was er tatsächlich braucht, ohne dass er das weiß.
>
> Das zu erkennen und zu vermitteln ist schwer.

Das sehe ich genauso.
Mathematisch betrachtet ist es richtig was Wolfgang schreibt, in der 
Praxis für die genannte Anwendung jedoch völlig irrelevant, zumal ein 
ADC eh nicht 100% genau misst. Daher liefert für diese Anwendung ein IIR 
Filter das gewünschte Resultat und ist zudem sehr einfach realisiert 
(minimiert Programmierfehler) und benötigt nur sehr wenig Speicher.

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.