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
Mit einem Array kann man es natürlich machen. Es müsste aber auch gehen mit (Mittelwert*99+Neuer_Wert)/100.
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.
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.
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.
Ja, vermutlich ist das ein Array. Das Thema steht sowieso auf der to-learn liste.
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.
Das ist etwas unglücklich ausgedrückt. Ich meinte das iterativ, also Neuer_Mittelwert=(Alter_Mittelwert*99+Neuer_Mittelwert)/100
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.
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!
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.
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 | }
|
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.
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.
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
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.
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.
Philipp L. schrieb: > Habe ich es richtig verstanden Ja. Philipp L. schrieb: > Klingt für mich gut und effektiv. Genau darum geht es.
Ü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.
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?
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
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
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
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.
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.
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.
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); |
> 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
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 ;-)
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
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
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.
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.
> 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
Thomas E. schrieb: > Die Optimierung des Compilers sorgt dafür, daß in beiden Fällen dasselbe > rauskommt. Sag das mal dem Keil C51 Compiler :-(
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.
> 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
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
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.
ps. Ich schäume Deinen Step Down ein und Du misst dafür fleißig den Strom. Was wird passieren ?
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!
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
> 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
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
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.
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
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.
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.
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.
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.
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.
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.
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.
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?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.