Mittelwert von meinen Sensorwerten

Hallo ihr lieben,

ich versuche gerade für einen Projekt, wo ich den Luftdruck überwachen soll, alle 2 Sekunden Druckwerte zu ermitteln und für 8 Messwerte den Mittelwert von dem Druckwert zu ermitteln.
Ich wollte folgendermaßen vorgehen. Meine Druckwerte alle zwei Sekunden in einem Array abspeichern und zum Schluss den Mittelwert aller ermittelten Druckwerte (8 Werte) bilden.
Ich habe lieber mit der Funktion millis(); gearbeitet, da mit delay die Sekunden draufaddiert werden und die Wartezeit verlängert wird.

Nur irgendwie habe ich denke ich einen Fehler drin. Bei einem Druckwert zwischen 7-7,5 mbar spuckt mir das Ding einen Mittelwert von 0,8 mbar oder manchmal 0mbar aus.

Irgendwo habe ich einen Denkfehler drin, könnt ihr mir weiterhelfen ?

Hier mein Programmausschnitt:

float test[7];
int anzahl = 8;
int k = 0;
....

// Intervall in Millisekunden
unsigned long previousMillis = 0;
const long interval = 16000;    
const long interval_1 = 2000;
....

void loop()
{
 // Millieinstellung
 unsigned long currentMillis = millis();
 float mittel, summe = 0;
....

// Ausgabe von den Druckwerten
  if (k <= anzahl)
  {
    // Ausgabe von meinen Werten:
    if ((currentMillis - previousMillis >= interval_1)  && (currentMillis - previousMillis <= interval))
    {
      Serial.print("Druck= ");
      Serial.print(sensorValue, 2);
      Serial.println(" mbar");
      test[k] = sensorValue;
      summe = summe + test[k];
      k++;
      delay(2000);
    }
  }

  if (k > anzahl)
  {
    return summe;
  }

  mittel = summe / anzahl;

  // Mittelwert ausgeben:
  if (currentMillis - previousMillis >= interval)
  {
    Serial.print("Mittelwert= ");
    Serial.print(mittel);
    Serial.println(" mbar");
  }

  if (currentMillis - previousMillis >= interval)
  {
    exit(0);
  }
}

Vielen Dank für die Rückmeldung jurs,

Stimmt hätte ich ergänzen müssen.
sensorValue ist mein Wert, was ich aus dem Drucksensor lese.
Ist als float deklariert worden.

const float SensorOffset = 102.0;

// Drucksensor:
  float sensorValue = (analogRead(A1) - SensorOffset) / 10.0;

Ja es handelt sich um einen Differenzdrucksensor.

Mir ging es eigentlich auch primär darum, warum ich auf den falschen Mittelwert komme.

float test[7];
int anzahl = 8;
...
if (k <= anzahl)

Das Feld ist von test[0] bis test[6] festgelegt, tatsächlich greifst Du aber auch auf test[7] und test[8] zu, die zufällige Werte enthalten. Besser so:

const int anzahl = 8;
float test[anzahl];
...
if (k < anzahl)
delay(2000);

Hast Du vergessen.

Außerdem teilst Du durch 10 ((analogRead(A1) - SensorOffset) / 10.0), obwohl Du nur 8 Meßwerte mittelst?

Bilde doch lieber den gleitenden Mittelwert.
Dann hast du keine Sprünge im Messwert und sparst ausserdem noch Speicher.

Prinzip:
Für einen neuen Wert werden jedes mal 9/10tel des alten Wertes + 1/10tel des neuen Messwertes addiert.
So gehen in den in den Wert nur ein Zehntel des aktuellen Messwertes ein. Störungen werden unterdrückt und der Wert ist gefiltert. Und du kannst den Filterwert problemlos auf 100, 1000 oder was auch immer setzen, ohne Speicherprobleme zu bekommen.

float Val;
int Eingangswert,j;

void setup() {  
  Serial.begin(115200);
  Serial.print("IN\t"); Serial.print("OUT\t\n"); 
}

void loop() {    
  Serial.print(Eingangswert); Serial.print("\t"); 
  Filtern(Val, Eingangswert, 9);
  Serial.print(Val); Serial.print("\t\n"); 
  if (j++ > 10) Eingangswert=1;  // nach 10 Werten Sprung auf 1  
}


/*************************************************************************************************
** Funktion Filtern()  by GuntherB						2014		                                           
**************************************************************************************************
** Bildet einen Tiefpassfilter (RC-Glied) nach.						
** FF = Filterfaktor;  Tau = FF / Aufruffrequenz						
**  											   	
**  Input: FiltVal der gefilterte Wert, NewVal der neue gelesene Wert; FF Filterfaktor		
**  Output:	FiltVal										
**  genutzte Globale Variablen: 	keine							
**************************************************************************************************/
void Filtern(float &FiltVal, int NewVal, int FF){
  FiltVal= ((FiltVal * FF) + NewVal) / (FF +1);  
}

Ich bedanke mich schonmal ganz herzlich für die Antworten :slight_smile:

jurs:
Falls Du es nicht bereits geahnt hast, verrate ich Dir das Geheimnis:
Der Mittelwert wird falsch, weil Dein Programm falsch ist!

Und der Fehler liegt vermutlich nicht in dem Codefragment, das Du im Startbeitbeitrag gepostet hast, und auch nicht in den drei Codezeilen aus reply #2, sondern der Fehler liegt natürlich in den Codezeilen Deines Sketeches, DIE DU NICHT VORZEIGST!

Die genaue Programmlogik und was dabei falsch läuft, kann ich nicht erraten, denn ich bin kein Hellseher.

Ich hatte nicht gesamten Code gezeigt, weil ich noch andere Sensoren drin habe, die euch verwirren können, wie zB Taster, Temperatursensoren, Magnetventile etc.
Mir gings primär jedoch nur um den Drucksensor, weshalb ich den jeweiligen Code kopiert hatte.
Falls du dich trotzdem interessierst, hier der gesamte Code:

// Werte die zu Beginn des Programms nur einmal aufgerufen werden:
int solenoidPin = 3;
const unsigned int BUTTON_PIN = 7;
float temp;
int tempPin = 0;
const float SensorOffset = 102.0;
const int anzahl = 8;
float test[anzahl];
int obersteDruckgrenze = 30;
int untersteDruckgrenze = 20;
int k = 0;

// Versuch vergangene Intervallgrenzen selber einzustellen:
unsigned long previousMillis = 0;
const long interval = 17000;    // Intervall in Millisekunden
const long interval_1 = 2000;
const long interval_2 = 16000;
const long grenze = 17500;


void setup()
{
  pinMode(solenoidPin, OUTPUT);
  pinMode(BUTTON_PIN, INPUT);
  Serial.begin(9600);
}

void loop()
{
  // Millieinstellung
  unsigned long currentMillis = millis();

  float mittel, summe = 0;

  // Taster:
  const int BUTTON_STATE = digitalRead(BUTTON_PIN);

  // Temperatur:
  temp = analogRead(tempPin);
  temp = temp * 0.48828125;
  // Umrechnung von Kelvin auf Celsius:
  float celsius = temp - 273.15;

  // Drucksensor:
  float sensorValue = (analogRead(A1) - SensorOffset) / 10.0; //Kalibrierung

  // Bedingung ab wann die Pumpe laufen soll:
  if (celsius >= 23.0 && sensorValue <= 25)
    digitalWrite(solenoidPin, HIGH);
  else
    digitalWrite(solenoidPin, LOW);

  if (k <= anzahl)
  {
    // Ausgabe von meinen Werten:
    if ((currentMillis - previousMillis >= interval_1) && (currentMillis - previousMillis <= interval))
    {
      Serial.print("Druck= ");
      Serial.print(sensorValue, 2);
      Serial.println(" mbar");
      Serial.print("Temperatur= ");
      Serial.print(celsius);
      Serial.println("*C");
      Serial.println();
      test[k] = sensorValue;
      summe = summe + test[k];
      k++;
      delay(2000);
    }
  }

  if (k > anzahl)
  {
    return summe;
  }

  mittel = summe / anzahl;

  // Mittelwert ausgeben:
  if ((currentMillis - previousMillis >= interval_2) && (currentMillis - previousMillis <= grenze))
  {
    Serial.print("Mittelwert= ");
    Serial.print(mittel);
    Serial.println(" mbar"); 
    delay(2000);
  }



  if (currentMillis - previousMillis >= grenze)
  {
    exit(0);
  }
}

agmue:

float test[7];

int anzahl = 8;
...
if (k <= anzahl)



Das Feld ist von test[0] bis test[6] festgelegt, tatsächlich greifst Du aber auch auf test[7] und test[8] zu, die zufällige Werte enthalten. Besser so:


const int anzahl = 8;
float test[anzahl];
...
if (k < anzahl)





delay(2000);



Hast Du vergessen.

Außerdem teilst Du durch 10 ((analogRead(A1) - SensorOffset) / 10.0), obwohl Du nur 8 Meßwerte mittelst?

Hab ich geändert, jedoch weiterhin keine Änderung.


guntherb:
Bilde doch lieber den gleitenden Mittelwert.
Dann hast du keine Sprünge im Messwert und sparst ausserdem noch Speicher.

Prinzip:
Für einen neuen Wert werden jedes mal 9/10tel des alten Wertes + 1/10tel des neuen Messwertes addiert.
So gehen in den in den Wert nur ein Zehntel des aktuellen Messwertes ein. Störungen werden unterdrückt und der Wert ist gefiltert. Und du kannst den Filterwert problemlos auf 100, 1000 oder was auch immer setzen, ohne Speicherprobleme zu bekommen.

float Val;

int Eingangswert,j;

void setup() { 
  Serial.begin(115200);
  Serial.print("IN\t"); Serial.print("OUT\t\n");
}

void loop() {   
  Serial.print(Eingangswert); Serial.print("\t");
  Filtern(Val, Eingangswert, 10);
  Serial.print(Val); Serial.print("\t\n");
  if (j++ > 10) Eingangswert=1;  // nach 10 Werten Sprung auf 1 
}

/*************************************************************************************************
** Funktion Filtern()  by GuntherB 2014


** Bildet einen Tiefpassfilter (RC-Glied) nach.
** FF = Filterfaktor;  Tau = FF / Aufruffrequenz
**   
**  Input: FiltVal der gefilterte Wert, NewVal der neue gelesene Wert; FF Filterfaktor
**  Output: FiltVal
**  genutzte Globale Variablen: keine
**************************************************************************************************/
void Filtern(float &FiltVal, int NewVal, int FF){
  FiltVal= ((FiltVal * FF) + NewVal) / (FF +1); 
}

Oh vielen Dank, werde ich mal ausprobieren. Was ist in dem Fall mit dem Eingangswert gemeint ?

Simulink20:
Oh vielen Dank, werde ich mal ausprobieren. Was ist in dem Fall mit dem Eingangswert gemeint ?

Der Rohwert des Sensorsignals, der aktuelle, ungefilterte Messwert.

In dem Falle wird er einfach durch eine Sprungfunktion simuliert.
wenn du das Programm laufen lässt, bekommst du eine Ausgabe im Seriellen Monitor, wenn du die in Excel kopierst und grafisch darstellst, sieht da ungefähr so aus:
Zwischenablage01.jpg

guntherb:
Der Rohwert des Sensorsignals, der aktuelle, ungefilterte Messwert.

In dem Falle wird er einfach durch eine Sprungfunktion simuliert.
wenn du das Programm laufen lässt, bekommst du eine Ausgabe im Seriellen Monitor, wenn du die in Excel kopierst und grafisch darstellst, sieht da ungefähr so aus:
Zwischenablage01.jpg

Achso okay, vielen Dank. Ich habe nur Probleme die neue Filterfunktion auf meinen Programm anzuwenden, da ich ja viel zeitlich steuere und Arrayelemente addieren muss, bei der ich ja vorher schon Probleme dabei hatte damit den Mittelwert zu bilden.


jurs:
OK, so mit dem ganzen Code wird es schon klarer, wo Du die ganzen systematischen, logischen und handwwerklichen Programmierfehler im Code hast.

Und verrate mir mal, ob das nachfolgende vielleicht ein Problem darstellt oder vollkommen irrelevant ist:
if (celsius >= 23.0 && sensorValue <= 25)

Sowohl Deine Temperatur als auch den Drucksensorwert ermittelst Du mit analogRead() Messungen. Dabei kann die letzte Stelle immer um eins hoch oder runterspringen, völlig zufällig.

Dadurch passiert in Deinem Code das;

Wenn nach der Umrechnung die Temperatur alle zwei Sekunden zwischen 23.01 und23.00 springt, weil der nalogRead() Messwert in der letzten Stelle springt und der Sensorwert vielleicht zwischen 25.00 und 25.01), dann schaltet Dein angesteuertes Gerät in völlig zufälliger Weise ein und aus.

Wäre es OK, oder wäre es fatal, wenn Dein Gerät alle 2 Sekunden schaltet, immer im Wechsel ein oder aus?

In Deinem Programm fehlt die Hysterese, um das zu vermeiden: Hysterese – Wikipedia

Um ggf. durch ein Springen der letzten Stelle von analogRead() zu vermeiden, dass Dein Gerät im ungünstigsten Fall alle 2 Sekunden ein- und 2 Sekunden später wieder ausschaltet, brauchst Du nicht nur einen einzigen Schaltpunkt, sondern zwei Schaltpunkte:

  • einen Einschaltpunkt
  • einen Ausschaltpunkt
  • und bei Werten dazwischen wird immer der letzte Schaltzustand beibehalten und nichts geschaltet

Programmlogik eventuell:
if(celsius >= 23.0 && sensorValue <= 25) einschalten(); // kombinierter Einschaltpunkt
{
else if (celsius<122.9 ausschalten(); // Ausschaltpunkt nach Temperatur
}
else if (sensorValue>25.1) ausschalten(); // Ausschaltpunkt nach SensorWert
else; // in allen anderen Fällen den bisherigen Schaltzustand beibehalten

}

Soll ich mir Deinen Code mal vornehmen und überarbeiten?

Mit dem Code:

 if (celsius >= 23.0 && sensorValue <= 25)

wollte ich sozusagen meine Magnetventile steuern. Nach dem idealen Gasgesetz wollte ich, dass meine Werte auf ner bestimmten Skala bleiben, damit der Druck in einem Behälter konstant bleibt.

Aber du hast schon Recht, es wäre nicht sinnvoll, wenn meine Pumpe immer alle paar Sekunden läuft, wenn der Druck innen nicht stimmt. Ich müsste schon dann einstellen, dass es bis zu nem bestimmten Wert aufpumpt.

Ich denke aber, dass dies mit dem Mittelwert wenig zu tun hat, da meine einzelnen Ausgaben ja bei 7-7,5 mbar bereits liegen. Theoretisch müsste dann ja mein Mittelwert auch bei ca 7 mbar liegen. Ich bin fest davon überzeugt, dass ich bei der Bestimmung des Mittelwertes einen Fehler drin habe, obwohl meine Funktion so stimmen müsste.

Ja das wäre echt nett oder Tipps geben, damit ich weiterkomme :frowning:

Simulink20:
Achso okay, vielen Dank. Ich habe nur Probleme die neue Filterfunktion auf meinen Programm anzuwenden, da ich ja viel zeitlich steuere und Arrayelemente addieren muss, bei der ich ja vorher schon Probleme dabei hatte damit den Mittelwert zu bilden.

Mit der Methode musst du keine Arrayelemente addieren, sonder einfach jeden neuen Messwert sofort durch die Funktion filtern.

du müsstest nur den code so ändern:

  temp = analogRead(tempPin);
  temp = temp * 0.48828125;
  Filtern(temp_filt, temp , 10);  // temp_filt ist eine globale float Variable, die jederzeit den aktuellen, gefilterten Wert enthält

Andererseits: Wenn Jurs dir anbietet, deinen Code auf Vordermann zu bringen, dann würde ich an deiner Stelle da ganz schnell zugreifen!

edit: was für ein Temperatursensor ist das denn?

guntherb:
Mit der Methode musst du keine Arrayelemente addieren, sonder einfach jeden neuen Messwert sofort durch die Funktion filtern.

du müsstest nur den code so ändern:

  temp = analogRead(tempPin);

temp = temp * 0.48828125;
  Filtern(temp_filt, temp , 10);  // temp_filt ist eine globale float Variable, die jederzeit den aktuellen, gefilterten Wert enthält




Andererseits: Wenn Jurs dir anbietet, deinen Code auf Vordermann zu bringen, dann würde ich an deiner Stelle da ganz schnell zugreifen!


edit: was für ein Temperatursensor ist das denn?

Wollte ja eigentlich nicht den Temperatursensor, sondern den Drucksensor nehmen. Deswegen wollte ich nicht so viel Code posten, damit es nicht zur Verwirrung kommt.

Ist aber ein LM335z .

also dann Filtern(sensorValue_filt ,sensorValue, 8) ?

Du hattest die 10 für 10 Werte dann anscheinend genommen oder?

Ja bin für jede Hilfe dankbar :slight_smile:

jurs:
Unter der Berücksichtigung, dass er den Sensor analog per analogRead() ausliest und unter Berücksichtigung der in seinem Code verwendeten Berechnungsformel kommt wohl nur ein LM35 (oder kompatibler) Sensor in Frage.

hmmm komisch, sind jetzt zwar Wochen her, dass ich das so initialisiert habe, es hat aber bis jetzt immer den richtigen Wert geliefert.

Alles funktioniert ja wie bereits gesagt wunderbar, nur den Mittelwert kriege ich noch nicht raus :smiley:

Ich könnte mir das so vorstellen:

Achtung-ungetesteter code

// Werte die zu Beginn des Programms nur einmal aufgerufen werden:
int solenoidPin = 3;
const unsigned int BUTTON_PIN = 7;
float temp;                                                // Temperatur
float celsius;                                               // Temperatur in °C
int tempPin = 0;
const float SensorOffset = 102.0;
const int anzahl = 8;
float test[anzahl];
int obersteDruckgrenze = 30;
int untersteDruckgrenze = 20;
int k = 0;
float druck;                                                // Drucksignal gefiltert
float druck_raw;                                         // Drucksignal roh

void setup()
{
  pinMode(solenoidPin, OUTPUT);
  pinMode(BUTTON_PIN, INPUT);
  Serial.begin(9600);
}

void loop() {

  /**************************************************************************************
  **   Beginn Zeitscheibe  100m sek                                                       **
  **************************************************************************************/
  static unsigned long   Zeitscheibe_1 = 0;
  if (millis() - Zeitscheibe_1 > 100) {  // Zeitscheibe wird alle 100ms aufgerufen
    Zeitscheibe_1  = millis();

    // Taster:
    const int BUTTON_STATE = digitalRead(BUTTON_PIN);

    // Drucksensor:
    druck_raw = (analogRead(A1) - SensorOffset) / 10.0; //Kalibrierung
    int FF = 9;                                         // Filter Faktor
    druck = ((druck * FF) + druck_raw) / (FF + 1);      // Glätten des Drucksignals
  }// end Zeitscheibe 100msek


  /**************************************************************************************
  **   Beginn Zeitscheibe  2sek                                                       **
  **************************************************************************************/
  static unsigned long   Zeitscheibe_2 = 0;
  if (millis() - Zeitscheibe_2 > 2000) {  // Zeitscheibe wird alle 2000ms aufgerufen
    Zeitscheibe_2  = millis();

    // Temperatur:
    temp = analogRead(tempPin);
    temp = temp * 0.48828125;
    // Umrechnung von Kelvin auf Celsius:
    celsius = temp - 273.15;



    // Bedingung ab wann die Pumpe laufen soll:
    if (celsius >= 23.0 && druck <= 25)
      digitalWrite(solenoidPin, HIGH);
    else
      digitalWrite(solenoidPin, LOW);


    // Serielle Ausgabe der Werte:

    Serial.print("Druck rohsignal = ");  Serial.print(druck_raw, 2);  Serial.print("mbar\t");
    Serial.print("Druck= ");  Serial.print(druck, 2);  Serial.print("mbar\t");
    Serial.print("Temperatur= "); Serial.print(celsius);  Serial.println("*C");
    Serial.println();

  }// end Zeitscheibe 2sek
}

Ich habe die Druckmessung nun in eine 100ms Scheibe ausgelagert, die Auswertung erfolgt weiterhin alle 2 Sek. Warum?

Das Drucksignal ist gefiltert. Wenn die Pumpe den Druck schneller aufbaut als das gefilterte Signal steigt, würde sie nicht mehr abschalten.

Was mich noch stört, aber ich weiß nicht, ob das nicht so gewollt ist:
Wenn die Pumpe einschaltet, läuft sie immer mindestens 2 sek, bis zur nächsten Überprüfung.
Das passt, wenn der Druck, den die Pumpe in 2sek aufbaut nicht zu gross ist und zum System passt.
Falls nicht, muss die Pumpe aus der 2 sek Scheibe raus und als 2-Punktregler laufen.

Wichtig: Filterzeiten und Pumpenleistung müssen aufeinander abgestimmt werden. Der Filter muss schneller sein als die Pumpe. (und das ist unabhängig von der Art der Mittelwertbildung!)

Statt 9/10 + 1/10 zu nehmen nehme ich manchmal Ringspeicher. 9/10 + 1/10 funktioniert nur, wenn die Werte geringfügig abweichen. Also wo das Messen nur um die Messungenauigkeit schwankt. Alle 2 sec messen wenn sich zb der Druck durch eine Pumpe in 2sec erheblich ändert wird nur sehr sehr langsam gefolgt

Messwert 9+1Wert Ring8 Ring4
1,00 0,10 1,00 1,00
3,00 0,39 2,00 2,00
5,00 0,85 3,00 3,00
9,00 1,67 4,50 4,50
12,00 2,70 6,00 7,25
11,00 3,53 6,83 9,25
13,00 4,48 7,71 11,25
12,00 5,23 8,25 12,00
10,00 5,71 9,38 11,50
7,00 5,84 9,88 10,50
4,00 5,65 9,75 8,25
2,00 5,29 8,88 5,75
1,00 4,86 7,50 3,50
5,00 4,87 6,75 3,00
7,00 5,08 6,00 3,75
10,00 5,58 5,75 5,75
13,00 6,32 6,13 8,75
12,00 6,89 6,75 10,50
11,00 7,30 7,63 11,50
13,00 7,87 9,00 12,25

Für solche Schwankungen ist es ungeeignet. Allerdings sind starke Schwankungen immer ein Problem für Mittelwertbildung. Die richtige Zeit und Wertemenge ist da ausschlaggebend. Im Ringspeicher nur 4 Werte mittelwertbilden würde viel schneller dem echten Wert folgen.

Index++
If Index > 7 Index = 0;
Array[Index] = Wert;

Muss für jeden Messwert einmal abgearbeitet werden

Zur Mittelwertberechnung dann das ganze Array summieren und durch 8 Teilen.

Könnte man in seperate Routine auslagern und mit Mittelwert = Mwert_berechnung(Array, 8); aufrufen.

Und hat den Vorteil das man alle 2sec den neuen Mittelwert der letzten 8 Messungen hat.

guntherb:
Was mich noch stört, aber ich weiß nicht, ob das nicht so gewollt ist:
Wenn die Pumpe einschaltet, läuft sie immer mindestens 2 sek, bis zur nächsten Überprüfung.
Das passt, wenn der Druck, den die Pumpe in 2sek aufbaut nicht zu gross ist und zum System passt.
Falls nicht, muss die Pumpe aus der 2 sek Scheibe raus und als 2-Punktregler laufen.

Wichtig: Filterzeiten und Pumpenleistung müssen aufeinander abgestimmt werden. Der Filter muss schneller sein als die Pumpe. (und das ist unabhängig von der Art der Mittelwertbildung!)

Stimmt das war nicht gewollt. Habe gar nicht daran gedacht. Vielen Dank, das müsste ich noch implementieren.
Deinen Code werde ich noch einmal zu dem Mittelwert testen.


chefin:
Statt 9/10 + 1/10 zu nehmen nehme ich manchmal Ringspeicher. 9/10 + 1/10 funktioniert nur, wenn die Werte geringfügig abweichen. Also wo das Messen nur um die Messungenauigkeit schwankt. Alle 2 sec messen wenn sich zb der Druck durch eine Pumpe in 2sec erheblich ändert wird nur sehr sehr langsam gefolgt

Messwert 9+1Wert Ring8 Ring4
1,00 0,10 1,00 1,00
3,00 0,39 2,00 2,00
5,00 0,85 3,00 3,00
9,00 1,67 4,50 4,50
12,00 2,70 6,00 7,25
11,00 3,53 6,83 9,25
13,00 4,48 7,71 11,25
12,00 5,23 8,25 12,00
10,00 5,71 9,38 11,50
7,00 5,84 9,88 10,50
4,00 5,65 9,75 8,25
2,00 5,29 8,88 5,75
1,00 4,86 7,50 3,50
5,00 4,87 6,75 3,00
7,00 5,08 6,00 3,75
10,00 5,58 5,75 5,75
13,00 6,32 6,13 8,75
12,00 6,89 6,75 10,50
11,00 7,30 7,63 11,50
13,00 7,87 9,00 12,25

Für solche Schwankungen ist es ungeeignet. Allerdings sind starke Schwankungen immer ein Problem für Mittelwertbildung. Die richtige Zeit und Wertemenge ist da ausschlaggebend. Im Ringspeicher nur 4 Werte mittelwertbilden würde viel schneller dem echten Wert folgen.

Index++
If Index > 7 Index = 0;
Array[Index] = Wert;

Muss für jeden Messwert einmal abgearbeitet werden

Zur Mittelwertberechnung dann das ganze Array summieren und durch 8 Teilen.

Könnte man in seperate Routine auslagern und mit Mittelwert = Mwert_berechnung(Array, 8); aufrufen.

Und hat den Vorteil das man alle 2sec den neuen Mittelwert der letzten 8 Messungen hat.

Hmm das muss ich mir dann auch nochmal genauer angucken. Dankeschön.


jurs:
Die von tuntherb vorgeschlagene "exponentielle Glättung" der Messwerte ist übrigens nicht dasselbe wie ein "gleitender Durchschnitt". Die "exponentielle Glättung" bezeichnet man auch als "Tiefpassfilter". Dh. aus schnellen Schwankungen in den Einzelmesswerten werden langsame Schwankungen im geglätteten Wert. Aber es ist damit nicht sichergestellt, dass vier Messungen von 22.99 und vier Messungen von 23.00 zu einem geglätteten Wert von 22.995 führen, wie es der Mittelwert wäre. Mittelwert und exponentiell geglätteter Wert sind zwei ganz verschiedene Dinge.

Ja genau, nur für meinen Ziel ist es glaub ich die bessere Variante. Wollte den Mittelwert finden, um zu gucken, ob äußere Störungen in meinen Werten drin sind. Also Messungen, die stark von meinen Mittelwerten abweichen.
Guntherbs Prinzip schließt diese Störungen im Grunde genommen ja aus, bzw es lässt meine Glättung dadurch nicht stark beeinflussen.

Es muss nicht unbedingt errastisch sein. Das hängt vom System ab.
Ich kenne Systeme, gerade bei Druckspeichern, das ist das gewollt. Bei Unterschreiten wird die Pumpe für eine feste Zeit angeschalten. Das System ist so ausgelegt, dass der Druckanstieg, der erzeugt wird, innerhalb der Systemparameter liegt, und der Druckabfall deutlich langsamer erfolgt. Damit hat man einen Sägezahnförmigen Druckverlauf. Ob das hier gewünscht ist? keine Ahnung.

Zum Thema exponentielle Glättung und Mittelwert: ja, mathematisch ist es was völlig anderes.
Praktisch wird sich die exponentielle Glättung besser auf den Mittelwert einstellen, weil ich nicht nur die letzten 10 Messwerte auswerte, sondern alle gemessenen Werte der Vergangenheit mit einbezogen werden.
Wenn die Aufgabe lautet: Mittelwert der letzten 10 Messungen, dann ist das Ergebnis falsch. Wenn die Aufgabe lautet: Mittelwert des Druckes, ist die exponentielle Methode die besser geeignete, meine ich.

wenn der Messwert ständige grosse Sprünge macht, dann sollten man das an anderer Stelle bekämpfen. Einzelne Ausreisser fängt man sinnigerweise mit einem Plausibiltätscheck ab. z.B. wenn der neue Messwert um mehr als x% abweicht, dann wird er verworfen. Aber das geht nur mit Systemkenntnis, welches ich nicht habe.

Was haltet ihr von einem RunningMedian der acht Sensoren, die 4 mal pro Sekunde abgefragt werden und aus dem Median dann den arithmetischen Mittelwert?

Sind jetzt zwar keine guten Druckmesswerte aber immerhin klappt das jetzt denke ich mit dem Filtern.

guntherb was ich dich noch fragen wollte. Warum hast du einen Filterfaktor von 9 bei deiner Programmierung genommen ?

Ich würde jedoch meinen Mittelwert gerne als Vergleichswert anzeigen lassen.

Wo war der Fehler bei der Mittelwertberechnung in meinem ursprünglichen Code ? :confused:

Simulink20:
Warum hast du einen Filterfaktor von 9 bei deiner Programmierung genommen ?

Das ähnelt am ehesten deiner ursprünglichen Variante mit 10 Werten, und es erleichtert die Erklärung mit 9/10tel + 1/10tel.
Das funktioniert aber auch mit jeder beliebigen anderen Zahl. Je höher, desto besser die Filterung, aber desto langsamer.

guntherb:
/*************************************************************************************************
** Funktion Filtern() by GuntherB 2014


** Bildet einen Tiefpassfilter (RC-Glied) nach.
** FF = Filterfaktor; Tau = FF / Aufruffrequenz
**
** Input: FiltVal der gefilterte Wert, NewVal der neue gelesene Wert; FF Filterfaktor
** Output: FiltVal
** genutzte Globale Variablen: keine
**************************************************************************************************/
void Filtern(float &FiltVal, int NewVal, int FF){
FiltVal= ((FiltVal * FF) + NewVal) / (FF +1);
}[/code]

Hallo Gunther,

ich habe Deinen Filtercode in einer Temperaturmessung ausprobiert.
Da ist die Aproximation an zum realen Temperaturwert bei FF=10 doch etwas langsam. Deshalb habe ich eine kleine Änderung eingebaut, um am Anfang mit kleineren FF früher zum Ziel zu kommen.
Es wird dadurch eine globale count-Variable benutzt, das Ergebnis ist aber am Anfang näher am Meßwert.

byte count = 0;
void Filtern(float &FiltVal, int NewVal, int FF){
  // Wenn FF noch nicht erreicht, count benutzen
  if (count < FF) {
    FF = count++;
  }

  FiltVal= ((FiltVal * FF) + NewVal) / (FF +1);  
}

Gruß Tommy

Leute ich bedanke mich noch einmal ganz herzlich für eure Mühe. Den gleitenden Mittelwert kann ich schön und gut bestimmen aber würde auch als Vergleich den normalen Mittelwert in gewissen Zeitabständen ermitteln. zB.: alle 8 Messungen.

Habe an dem Gesamtcode nicht viel verändert. Aber am Anfang hatte ich ja dasselbe Problem auch, weshalb ich diesen Threat geöffnet hatte.

Die einzige Veränderung, die noch mit drin ist:

float test[8];
float drucksignal;

void loop()
{
 // Bestimmung der Anzahl der Arrayelemente:
 int anzahl = sizeof(test) / sizeof(float);
 drucksignal = (analogRead(A1) - SensorOffset) / 10.0;

// Mittelwert
   if (k <= anzahl)
   {
     test[k] = drucksignal;
     summe += test[k];
     k++;
   }
   else
     return summe;

   mittel = summe / anzahl;

Komischerweise wird nur das erste Arrayelement addiert. Ich komme aus der Funktion schnell raus und anstatt den Vorgang zu wiederholen bis k=8 erreicht wird, wird auf else gesprungen und nur die erste Zahl durch die Gesamtanzahl dividiert. Was ist in meiner Deklaration faul ?