Auswertung Impulse Volkswindmesser

f=1/T
f = Frequenz
T= Periodendauer
Grüße Uwe

BigBangTheory:
Das Messintervall soll hierbei eine Sekunde betragen.

Vielleicht solltest du hier mal ansetzen.

Brauchst du wirklich die Messdaten im Sekundenintervall?

Wenn du die Zeit zwischen den Impulsen misst, dann kriegst du jede noch so kleine Böe aufgelöst. Wenn du das brauchst - gut.

Wenn nicht, dann mess doch einfach in größeren Intervallen, z.B. 10sek.
Das hat den Vorteil, dass du bereits automatisch eine Mittelung der Messwerte über die letzten 10sec kriegst.

Meine Herangehensweise ist immer:
erst überlegen: wie genau und schnell brauche ich das Signal?
dann das passende Messverfahren auswählen.

Für eine Wetterstation zur Anzeige der Windgeschwindigkeit reicht es über eine Minute zu mitteln.
Wenn du Böen und die daraus resultierende Windlast ermitteln willst, dann solltest du jeden Impuls einzeln zählen.

Gunther

Ich möchte, wenn genug Wind an der Stelle weht, wo das Anemometer jetzt hinkommt, ein vertikales Windrad zur Stromerzeugung aufstellen. Daher brauche ich die Erfassung jeder kleinen Windböe um quasi ein "Referenzjahr" an Winddaten aufzuzeichnen.

Mein Problem ist jetzt noch, wenn ich die Zeit zwischen zwei Impulsen messe und damit die Frequenz heraus bekomm und diese dann einordne in die Tabelle (s.o.), wie komm ich dann auf mein Messintervall?

Gruß Alex

Hey Leute,

ich merke gerade, dass ich bei folgendem Problem nicht weiter komm:

Ich möchte gern die WIndgeschwindigkeit (welche in meinem Fall abhängig von der Frequenz ist, s.o.) messen. Somit muss ich die Frequenz im mit dem Arduino ermitteln. Hierzu habe ich jetzt versucht die Dauer zwischen 2 Impulsen zu messen. Dies ist mir mit der Funktion PulseIn() auch gelungen, indem ich hiermit die Zeit gemessen habe, in der kein Signal am Pin anliegt (LOW).

Jetzt meine Frage: Ich will die Windgeschwindigkeit im Messintervall von einer Sekunde messen. Wie lässt sich das mit dem Arduino und der Frequenzmessung in Einklang bringen?

Vielen Dank für eure Antworten!

Also, erstens, wenn du nur die LOW-zeit mißt, kriegst du nicht die Zeit zwischen den Pulsen, sondern eben nur die LOW-Zeit. Die Pulslänge ist aber LOW-Zeit plus HIGH-Zeit.
Du mußt immer am Flankenwechsel triggern, am besten mit einem Interrupt, aber wie das mit dem Arduino funktioniert, kann ich (noch) nicht sagen, ich kenns nur von anderen Echtzeitsytemen mit Drehzahlerfassung, die wir in der Firma bauen.

zweitens: Trenn mal Erfassung und Aufzeichnung.
Du hast eine Funktion, die die Pulslängen erfasst. Das kann sehr schnell erfolgen! Diese Funktion stellt die Pulslänge als Wert (gefiltert?) zur Verfügung.
Du hast eine andere Funktion, die in festen Intervallen aufgerufen wird (bei dir 1000ms) und die aktuelle Windgeschwindigkeit errechnet und mit Zeitstempel abspeichert.
Dadurch ist beides voneinander unabhängig und du kannst jederzeit dein aufzeichnungsintervall ändern (Falls dir die 86400 Datensätze pro Tag zuviel sind. :cold_sweat:)

die Umrechung Pulsdauer => Windgeschwindigkeit kannst du so machen:
zuerst baust du dir eine Tabelle mit ZWEI Spalten: Pulsdauer und Windgeschwindigkeit (Pulsdauer[µs] = 1000000/Frequenz)
Die anderen beiden Spalten habe ich nur drin gelassen, damit du deine Tabelle wieder erkennst.

m/s Hz(6 Imp.) Hz(1 Imp.) Dauer1p[µs]
20	59,6	 9,93	   100705
19	56,4	 9,4	    106383
18	52,5    8,75	   114286
17	49,6	 8,27	   120919
16	46,2	 7,7	    129870
15	43,2	 7,2	    138889
14	39,7	 6,62	   151057
13	36,7	 6,12	   163399
12	34	   5,67	   176367
11	30,5	 5,08	   196850
10	27,2	 4,53	   220751
9	 24,3	 4,05	   246914
8	 20,9	 3,48	   287356
7	 18,2	 3,03	   330033
6	 15	   2,5	    400000
5	 12,5	 2,08	   480769
4	 10	   1,67	   598802
3	 7	    1,17	   854701
2	 4,6	  0,77	   1298701
1	 2,1	  0,35	   2857143

mit deinen aktuell gemessenen Pulsdauerwerten gehst du dann in die Tabelle, suchst dir die beiden benachbarten Werte und interpolierst dazwischen linear.
Such doch mal nach linare Interpolation C

Es sei denn, du brauchst garkeine Zwischenwerten, es reichen dir die ganzzahligen Windgeschwindigkeiten, dann kannst du dir die Interpolation sparen, und gleich den nächsten Wert nehmen.

Reicht das?

Gunther

Danke schonmal für die tolle ausführliche Antwort! Damit komm ich schonmal ein ganzes Stück weiter :)!

Fragen treten ja leider immer erst auf, wenn man sich dran setzt und alles in die Tat umsetzen will.

Meine letzte Frage die mir momentan noch aufkommt ist, ich hab ja die beiden Funktionen, einmal das Messen der Pulsdauer und einmal die Errechnung der Windgeschwindigkeit. Wenn sich jetzt die Pulsdauer innerhalb der Sekunde ändert, muss ich das doch in der Windgeschwindigkeit wiederspiegeln, oder?

Dort hängt es momentan gedanklich noch bei mir, also wenn ich aus der Pulsdauer die Windgeschwindigkeit berechne, dann müsste ich ja quasi für alle Windgeschwindigkeiten die ich im Messintervall umgerechnet habe, einen Mittelwert bilden und diesen dann nach der Sekunde ausgeben, oder?

So würde das gerad für mich Sinn ergeben :).

Vielen vielen Dank schonmal für die tollen Antworten!!

Hallo
hatte mich auch mal mit dem Thema befasst und bin über diese Seite macherzin.net im Netz gestolpert.

BigBangTheory:
Dort hängt es momentan gedanklich noch bei mir, also wenn ich aus der Pulsdauer die Windgeschwindigkeit berechne, dann müsste ich ja quasi für alle Windgeschwindigkeiten die ich im Messintervall umgerechnet habe, einen Mittelwert bilden und diesen dann nach der Sekunde ausgeben, oder?

Da bist du über ein Grundproblem der Signaltheorie gestolpert:
wenn ich schnell ändernde Signale nur gelegentlich abtaste (=Wert speichern), dann bekomme ich zufällige Werte.
Dafür gibt es die Regel, dass Signale gefiltert sein müssen, und zwar ein Filter mit einer Grenzfrequenz die niedriger liegt (<0,6 mal) als die Abtast (=Speicher) frequenz.

In deinem Falle gibt es nun zwei Wege:
a) wie du sagst, alle Werte zwischen zwei Speicherintervallen Mitteln. ( Was dann das gleiche wäre, wie alle Pulse zwischen zwei Speicherintervallen zählen, da wären wir wieder am Anfang!)
b) die Werte filtern.

Filtern kann man z.B. einfach mittels "gleitendem Mittelwert"
das funktioniert so:
Mittelwert = Mittelwert*filterfaktor + Messwert * (1-filterfaktor)

mit filterfaktor = 0 wird garnicht gefiltert,
mit filterfaktor = 0,5 besteht jeder neue Mittelwert je zur Hälfte aus dem neuen Meßwert und dem alten Mittelwert
mit filterfaktor = 0,1 besteht jeder neue Mittelwert zu 1/10tel aus dem neuen Meßwert und zu 9/10teln aus dem alten Mittelwert.

das kannst du in Excel schön durchspielen.
Zur Umsetzung in Code gibts aber geschicktere Versionen, die mathematische Grundlage bleibt gleich.

Der erste Filter ist der Windmesser selber. Das Rad mit den 4 Halbkugeln (oder wie heissen die Teile) wirkt durch sein Trägheitsmoment bereits wie ein Tiefpaß. Die geschicktere Variante heißt Kalmann Filter Kalman-Filter – Wikipedia. Daß was Du gleitenden Mittelwert nennst ist kein gleitender Mittelwert sondern ein exponentieller Filter. Gleitender Mittelwert – Wikipedia Exponentielle Filter sind im Vergleich zum Aufwand allerdings super. Vor allem wenn man sie sauber auf das Problem anpasst. Wie man sowas macht habe ich mal hier breitgetreten: Binary DCF77 Clock | Blinkenlight. Bei einem Windmesser hätte ich zunächst aber gar keine Idee wie ich die Parameter ansetzen würde. Vermutlich würde ich dann doch gleich einen Kalmann Filter nehmen :wink:

Hi Leute,

ich habe mir einen ersten Code erarbeitet, bei dem momentan die Periodendauern (HIGH + LOW) in einem großen Array gespeichert werden. Nach dem Messintervall werden alle Werte des Arrays addiert, sowie im Anschluss durch die Periodenanzahl geteilt, sodass ich einen Mittelwert bekomme.
Der Code funktioniert noch nicht richtig, ist aber denk ich erstmal eine gute Grundlage.
Ich bin hierbei davon ausgegangen, dass wenn ein "HIGH-Signal" anliegt und ich dessen Länge messe, hiernach direkt ein LOW-Signal folgen muss, welches die erste Messung beendet und die Messung der Länge des LOW-Signals startet, und umgekehrt.

Was sagt ihr dazu? Wo sollte ich noch Veränderungen am Code vornehmen und wie kann man ihn noch besser machen, sodass er auch bei langsamen Windgeschwindigkeiten richtig funktioniert?

Bin ich vielleicht mit dem Code auch auf dem totalen Holzweg? Lasst euch aus darüber :)!

int AnemometerPin = 2;
int Messintervall = 1000;
int StartZeit = 0;
int EndZeit = 0;
int i = 0;
int j = 0;
int k = 0;
int HIGHZeit = 0;
int LOWZeit = 0;
int Periodendauer;
int PinStatus;
int Anzahl;
float SummePulsdauer = 0;
float Mittelwert = 0;
float pulsearray[99];


void setup()
{
  Serial.begin(9600);
  pinMode(AnemometerPin, INPUT);
  
}

void loop()
{
  for (k = 0; k < 100; k++)
  {
    pulsearray[k]=0;
  }
  
  StartZeit = EndZeit;
  
  i = 0;

  Mittelwert = 0;
  
  SummePulsdauer = 0;

  while (EndZeit-StartZeit < Messintervall)
  {
    
    PinStatus = digitalRead(AnemometerPin);
    
    if (PinStatus == HIGH)
    {
      HIGHZeit = pulseIn(AnemometerPin, HIGH);
      LOWZeit = pulseIn(AnemometerPin, LOW);
    }
    else if (PinStatus == LOW)
    {
      LOWZeit = pulseIn(AnemometerPin, LOW);
      HIGHZeit = pulseIn(AnemometerPin, HIGH);
    }
    Periodendauer = HIGHZeit + LOWZeit;
    
    pulsearray[i]=Periodendauer;
    
    i++;
   
    EndZeit = millis();  
    
    Anzahl = i;
  }
  for (j=0; j<=Anzahl; j++){
  
  SummePulsdauer = SummePulsdauer + pulsearray[j];
  }
  
  Mittelwert = SummePulsdauer / Anzahl;
  
  Serial.print("Pulsdauer Mittelwert:  ");
  Serial.println(Mittelwert);
}

Vielen Dank schonmal für eure vielen tollen Antworten zu diesem Thema, die bisher kamen und hoffentlich noch weiter kommen :)!

Hallo,

nur zwei Anmerkungen:

  1. wenn du den Pinstatus nur im Loop abfragst, um die Zeiten zu ermitteln, dann bekommst du zwangsläufig Ungenauigkeiten, weil der Durchlauf des Loops ja auch Zeit braucht. Besser wäre hier ein Interrupt.
  2. Du versuchst die Zeiten HIGH und LOW zu messen, addierst die auf um einen Mittelwert übers Speicherintervall zu bilden. Damit errechnest du dir die Anzahl der Pulse pro Speicherintervall. Einfacher (und genauer) wäre es hier, die Impulse zu zählen. Das ist letztlich genau das, was du gerade machst, nur du machst es aufwändig, umständlich und Fehlerbehaftet.

Die Sache mit der gemessenen und im Anschluss gemittelten Periodendauer pro Sekunde um hieraus die "gemittelte" Frequenz zu berechnen, schien mir irgendwie logisch. Daher habe ich versucht das ganze über den Weg der Periodendauermessung zu beschreiten.

Ich habe den Code jetzt in Richtung Interrupt angepasst. Jetzt bekomm ich aber gar keinen Wert mehr ausgegeben :D. Was habe ich hier falswch gemacht?

int AnemometerPin = 2;
int Messintervall = 1000;
int StartZeit = 0;
int EndZeit = 0;
int i = 0;
int j = 0;
int k = 0;
int Periodendauer;
int PinStatus;
int Anzahl;
int last;
float SummePulsdauer = 0;
float Mittelwert = 0;
float pulsearray[99];
unsigned long m;
unsigned long v;


void setup()
{
  Serial.begin(9600);
  pinMode(AnemometerPin, INPUT);
  attachInterrupt(0,UebergangHighLow, RISING);
}

void loop()
{
  for (k = 0; k < 100; k++)
  {
    pulsearray[k]=0;
  }
  
  StartZeit = EndZeit;
  
  i = 0;

  Mittelwert = 0;
  
  SummePulsdauer = 0;

  while (EndZeit-StartZeit < Messintervall)
  {
   
    pulsearray[i]=Periodendauer;
    
    i++;
   
    EndZeit = millis();  
    
    Anzahl = i;
  }
  for (j=0; j<=Anzahl; j++){
  
  SummePulsdauer = SummePulsdauer + pulsearray[j];
  }
  
  Mittelwert = SummePulsdauer / Anzahl;
  
  Serial.print("Pulsdauer Mittelwert:  ");
  Serial.println(Mittelwert);
}

void UebergangHighLow()
{
  detachInterrupt(0);                         // Interrupt ausschalten damit er uns nicht 
  m = millis();                               // Millisekundenzähler auslesen
  v = m - last;                               // Differenz zum letzten Durchlauf berechnen
  if (v > 10) {                               // ignorieren wenn <= 10ms (Kontaktpreller)
  Periodendauer = v;                          // Wert in Periodendauer übernehmen
  last = m;                                   // und wieder den letzten Wert merken
  }
  attachInterrupt(0, UebergangHighLow, RISING );
}

Ich messe hierbei die Zeit vom Übergang HIGH zu LOW bis dieser wieder auftaucht. Das müsste ja genau der Periodendauer entsprechen, oder? Dieser Wert der Periodendauer wird dann in das Array geschrieben, die Anzahl der Array-Elemente gezählt und im Anschluss der Mittelwert gebildet und ausgegeben. Trotzdem muss ich mich hier irgendwo verhaspelt haben, da ich momentan gar keine Ausgabe mehr bekomme.

Gruß Alex

Hallo Alex,

spontan fällt mir auf:

  • zu zählst in loop eine whileschleife hoch, in der du erwartest, dass bei jedem Schleifendurchgang ein neuer Wert "Periodendauer" zur Verfügung steht. aber du weißt ja nicht, wann der Impuls kommt und deinen Interrupt auslöst, das sind Prozesse die parallel und voneinander unabhängig laufen!
  • deine Interruptroutine ist sehr lang. Interruptroutinen sollten so knapp wie möglich sein. ich würde in der Interruptroutine nur den aktuellen Micros()-Wert in einem Array abspeichern und einen (globalen) Indexzähler hochsetzen.
void UebergangHighLow()
{
  detachInterrupt(0);                         // Interrupt ausschalten damit er uns nicht 
  m[i++] = micros();                               // Microsekundenzähler auslesen
  attachInterrupt(0, UebergangHighLow, RISING );
}

Und dann erst in der Auswertung die Zeiten ausrechen:

{
  detachInterrupt(0);   
  Periodendauer = (m[i-1] - m[0])/(i-1);
  i = 0;
  attachInterrupt(0, UebergangHighLow, RISING );
}

generell solltest du aber versuchen mit "Serial.print" an den verschiedenen Stellen des Codes versuchen rauszufinden was wo gerechnet wird, ob der Interrupt überhaupt aufgerufen wird, etc.
Diese Form von debuggen ist unverzichtbar. Ich hatte schon funktionen, da habe ich nach jeder Codezeile mit "Serial.print" alle Variablen ausgegeben, weil ich den Fehler anders nicht gefunden habe.

Diese Form von debuggen ist unverzichtbar...

... geht nur nicht gut innerhalb der Interrupt-Routine.

Eigentlich ist es ganz tabu, aber 64 Zeichen passen in den Ausgabepuffer, und werden dann am Ende von loop() gesendet.
Aber die Interrupt-Routine darf nicht auf das Leeren des Puffers warten müssen.

michael_x:
... geht nur nicht gut innerhalb der Interrupt-Routine.

Richtig.

aber man kann im Interrupt ein Flag setzen, und dieses in der Loop auswerten...

Was ich jetzt irgendwie noch nicht ganz verstanden habe ist:

In loop werte ich dann die Zeiten aus, welche ich in der Interrupt-Routine an mein Array m übergeben habe und berechne die Zeit zwischen der jetztigen und dem vorherigen Interrupt, oder?

Auf der einen Seite, dem Interrupt, speicher ich die jeweilige Zeit in der der Interrupt kam. Auf der anderen Seite, im loop werte ich die Zeiten aus. Wie kann ich es nun realisieren, dass ich mir quasi einen Mittelwert über alle Zeiten (Periodendauern) die innerhalb einer Sekunde aufgetreten sind, ausgeben lasse, durch den ich dann, pro Sekunde eine mittlere Frequenz habe?

Da komm ich gerad irgendwie nicht weiter.

Eigentlich hast du es richtig erfasst.

Betracht das mal als zwei Arbeiter:

der eine, Arbeiter1, notiert bei jedem jedem Impuls die aktuelle Zeit und schreibt sie auf ein Blatt. (Deine Array "m")
dann kommt der andere nach einer festgelegten Zeit (1sec) vorbei.
Er nimmt das Blatt und rechnet die Zeiten aus.
Dann gibt er Arbeiter1 wieder ein neues Blatt (i=0), und läßt ihn wieder Zeiten aufschreiben.
Bis wieder eine sekunde um ist.

Klar?

das habe ich verstanden :).

es funktioniert dann ja auch nicht mit einer while-routine, weil ich ja theoretisch nix mache, in der sekunde, außer das ich immer wieder durch die interrupts unterbrochen werde.

müsste ich dann in der loop-routine mit nem delay (1000) arbeiten, um jede sekunde diese routine zu starten, welche dann die zeiten der interrupts (das array) auswertet und dann gemittelt als periodendauer bzw. frequenz ausgibt, oder wie wär es am geschicktesten, die ausgabe pro sekunde zu erstellen?

dort häng ich momentan. vllt. ist es ja auch ganz trivial, nur irgendwie will ich von dem schlauch nicht runter, auf dem ich stehe :D.

vielen vielen dank aber schonmal für eure tollen antworten und die super unterstützung die ich hier bisher erlebt habe!!

Nein! Auf keinen Fall delay()!

Das machst du mit millis() Abfrage.

  if (millis() > Zeitscheibe_1sek )      // Zeitscheibe wird alle 1000ms aufgerufen
  { Zeitscheibe_1sek  = millis()+1000;  
   // mach deine Auswertung
  }

Schau dir dazu auch mal das Beispiel "Blink ohne Delay" an.

Hallo Leute,

ich hab das bisher hier von euch gelernte nun in folgendem Code umgesetzt:

int Zeitscheibe;
int i = 0;
int j = 0;
float Periodendauer = 0;
float SummePeriodendauer = 0;
float Umrechnung = 0.000001;
float Frequenz = 0;
volatile float m[100];

void setup()
{
  Serial.begin(9600); //starten der seriellen Kommunikation
  attachInterrupt(0,Interrupt,RISING); //wenn ein Signal von LOW nach HIGH an digital Pin 2 eingeht, wird die Interrupt-Routine "Interrupt" gestartet 
}

void Interrupt() //Interrupt-Routine
{
  m[i++] = micros(); //in das Array wird, wenn der Interrupt ausgelöst wird, die jeweilige Zeit seitdem das Programm gestartet wurde, in Mikrosekunden, gespeichert
}

void loop() //loop-Routine
{
 if (millis() > Zeitscheibe )      // wird jede Sekunde aufgerufen
  { 
   detachInterrupt(0); //Interrupt wird zur Auswertung des Arrays vorübergehend außer Kraft gesetzt
    Zeitscheibe  = millis()+1000;  //Berechnung der neuen Größe der Zeitscheibe
    for (j=0; j==i; j++)
    {
      SummePeriodendauer = SummePeriodendauer + (m[i+1]-m[i]); //Aufsummierung der Periodendauern zwischen den Interrupts
      
      Periodendauer = SummePeriodendauer / i; //gemittelte Periodendauer innerhalb einer Sekunde in Mikrosekunden
      
      Frequenz = 1 / (Periodendauer * Umrechnung); //Frequenzberechnung
      
      Serial.print("Frequenz:  "); //Ausgabe der gemittelten
      
      Serial.println(Frequenz);//Frequenz pro Sekunde auf dem Serial Monitor
      
    }
      j = 0; //Zuruecksetzen der 
      
      i = 0; //Zaehlvariablen
    
    
    attachInterrupt(0,Interrupt,RISING); //Interrupt kann wieder ausgeführt werden
  } 
}

Der Serial Monitor zeigt mir als Ausgabe wenn der Windmesser steht als Frequenz brav 0.00 an. Sobald ich ihn jedoch in Bewegung versetze, wird mir nix mehr ausgegeben, obwohl ich doch in der Auswertungsroutine (loop) extra den Interrupt während der Auswertung und der Ausgabe unterbinde.

Woran kann es liegen, dass mir nur "richtige" Werte ausgegeben werden, wenn der Windmesser steht?

P.S. Starte ich nur den Serial Monitor, bewege den Windmesser aber noch nicht, wird mir folgendes ausgegeben: Frequenz: nan

Woran kann das liegen?

Gruß Alex