Pages: 1 [2] 3 4 ... 6   Go Down
Author Topic: Auswertung Impulse Volkswindmesser  (Read 8981 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Grüße
Gunther

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley-grin. Was habe ich hier falswch gemacht?

Code:
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
Logged

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Code:
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:
Code:
{
  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.
Logged

Grüße
Gunther

Germany
Offline Offline
Faraday Member
**
Karma: 59
Posts: 3091
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Grüße
Gunther

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?



Logged

Grüße
Gunther

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

das habe ich verstanden smiley.

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 smiley-grin.

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

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nein! Auf keinen Fall delay()!

Das machst du mit millis() Abfrage.

Code:
  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.
Logged

Grüße
Gunther

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo Leute,

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

Code:
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
Logged

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

ein paar Sachen, die mir aufgefallen sind:

Code:
int Zeitscheibe;
muss heißen:
unsigned long Zeitscheibe;    weil die Funktionen millis()  und micros() einen unsigned long Wert liefern.



for (j=0; j==i; j++)
    {
      SummePeriodendauer = SummePeriodendauer + (m[i+1]-m[i ]);

Da stecken drei Fehler drin:
- Die for-Schleife wird durchlaufen, solange die Bedingung "true", also erfüllt ist. Deine Bedingung i==j ist aber nur erfüllt, wenn das Array leer ist, also bei Windstärke Null!.
- Du fragst in der Schleife das Array mit "i" ab, deine Laufvariable ist aber "j"
- Du fragst in der Schleife eine Arrayplatz "j+1" ab, als darf j nur bis "i-1" laufen.
Also:
for (j=0; j<i; j++)
    {
      SummePeriodendauer = SummePeriodendauer + (m[j+1]-m[j]);


Aber dies Schleife ist meiner Meinung sowieso überflüssig:
Du mußt nicht die Dauer jeden einzelnen Pulses ausrechnen und diese Summieren.
SummePeriodendauer = m-m[0]; erfüllt genau den gleichen Zweck!
Schreib dir mal eine Reihe von Zahlen auf und rechne es nach.

j = 0; //Zuruecksetzen der brauchts nicht. j wird autmatisch in der for-schleife zurückgesetzt.



Bei der Umrechnung
Frequenz = 1 / (Periodendauer * Umrechnung);
Bin ich mir nicht sicher. Aber ich weiß, dass der Arduino mit sehr kleinen Zahlen Probleme hat.
Ich würde das eher so lösen:

Umrechnung = 1000000;
Frequenz = Umrechnung / Periodendauer ;



Generell:
Wenn deine Code Sachen macht, die du nicht verstehst, da fragt mit "Serial.print" an den verschiedenen Stellen die zwischenergebnisse ab.

z.B. würde ich mir mal den Inhalt des Array anzeigen lassen. Also in der Zeitscheibe als erstes:
for (j=0; j<=i; j++)
    {
      Serial.println(m[j]);
}


Und dann aber auch mal die Zwischenergebnisse jeweils ansehen:
Serial.print("SummePeriodendauer: ");Serial.println(SummePeriodendauer);
und
Serial.print("Periodendauer: ");Serial.println(Periodendauer);

Das sind dann Hilfen um rauszufinden, was der Arduino überhaupt rechnet.

und, was mir gerade noch auffällt:
Deine for-Schleife ist erst sehr spät geschlossen.
Ich denke da fehlt eine geschweifte Klammer:
for (j=0; j==i; j++)
    {
      SummePeriodendauer = SummePeriodendauer + (m[i+1]-m[ i]);
    }


Grüße

Gunther

Logged

Grüße
Gunther

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo Leute,

ich hab meinen Quellcode modifiziert, mir mittels Excel ne näherungsweise Gleichung zu den Frequenz - Windgeschwindigkeits-Werten erzeugt, welche ich in meinem Quellcode nutze, sowie die einzelnen Schritte per "Serial.println..." angesehen um genau zu verstehen, was an welcher Stelle meines Programms geschieht.

Hier mal mein bisheriger Quellcode:
Code:
unsigned long Zeitscheibe;
unsigned long m[99];
int i = 0;
float Periodendauer = 0;
float Umrechnung = 1000000;
float Frequenz = 0;
float Windgeschwindigkeit;

void setup()
{
  Serial.begin(9600);

  attachInterrupt(0,Interrupt,RISING);
}

void Interrupt()
{

  m[i++] = micros();

  Serial.println(m[i]);


}


void loop()
{
 if (millis() > Zeitscheibe )   
  {
    detachInterrupt(0);
   
    Zeitscheibe  = millis()+1000;

    Periodendauer = (m[i] - m[0]) / i;
   
    Frequenz = Umrechnung / (Periodendauer);
   
    Windgeschwindigkeit = 2.58 * (pow(Frequenz,0.8927));         
   
/*    Serial.print("Windgeschwindigkeit:  ");
     
    Serial.println(Windgeschwindigkeit);
*/   
    Periodendauer = 0;
   
    i = 0;
   
    attachInterrupt(0,Interrupt,RISING);
  }

}

Ich lasse mir momentan die Werte des Interrupts direkt im Interrupt ausgeben, sodass ich sehen kann, welche Werte im Array abgespeichert werden. Hierbei ist mir aufgefallen, dass immer der aktuelle micros() Wert und danach der Wert 0 abgespeichert wird.
Hat einer von euch eine Idee, wieso das passiert?

Hier mal ein Ausschnitt aus meinem Serial Monitor:


Vielen Dank schonmal für eure Hilfe smiley!
« Last Edit: April 14, 2013, 05:15:31 pm by BigBangTheory » Logged

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

du mußt schreiben Serial.println(m[i-1]); weil ja nach dem Abspeichern mit i++ incrementiert wird.

Du gibst also beim ersten Aufruf des Interrupts immer das m[1] des LETZTEN 1sek Zykluses aus,
 bei zweiten Aufruf des Interrupts immer das m[2] des LETZTEN 1sek Zykluses aus, welchen 0 ist, da deine Pulse zu selten kommen.

Ausserdem solltest du wirklich den Inhalt des Array überprüfen, in dem du in der Zeitscheibe mit
for (j=0; j<=i; j++)
    {
      Serial.println(m[j]);
}

wirklich den Inhalt des Arrays ausliest.

Gunther
Logged

Grüße
Gunther

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo Gunther und alle anderen,

ich habe den Inhalt des Arrays ausgelesen und die Daten, die innerhalb einer Sekunde in das Array durch die Interrupt-Routine geschrieben werden, auf dem Serial Monitor anzeigen lassen.

Hierbei ist mir aufgefallen, dass wenn ich das Anemometer sehr langsam drehe (1 Umdrehung pro Sekunde) und somit auch ein Interrupt pro Sekunde auftreten müsste, ich folgende Werte im Array stehen habe:

Beispiel 1:
m[0] = 2685008
m[1] = 2685064
m[2] = 2685108
m[3] = 0

Beispiel 2:
m[0] = 6961784
m[1] = 6961840
m[2] = 5152580

Beispiel 3:
m[0] = 8527264
m[1] = 8527320
m[2] = 8527356
m[3] = 0


Irgendwas kann doch hier beim speichern der Werte ins Array in der Interrupt-Routine nicht hinhauen, oder?

Ich werd ehrlich gesagt nicht schlau draus, wie ich es abstellen kann, dass mehrere, zum Teil auch falsche Werte in das Array geschrieben werden. Ich hatte auch schon versucht, durch ein erstes delay in der Interrupt-Routine das "Schwingen" vom Reed-Kontakt zu mindern, damit dann nur ein Wert gespeichert wird. Dies hat allerdings absolut nix gebracht.

Habt ihr Ideen, wie man das Problem lösen könnte?

Vielen Dank für eure Hilfe!

Gruß Alex

Logged

Offline Offline
Edison Member
*
Karma: 39
Posts: 1184
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Für mich sieht das nach Kontaktprellen aus.
Anscheinend kommen die Pulse in ca. 50µs Abstand.

Probier doch mal das:

Code:
void Interrupt()
{
  noInterrupts();                     // Interrupt sperren
  m[i++] = micros();
  Serial.println(m[i]);
  delayMicroseconds(200);            // Warten bis Kontaktprellen zuende.
  Interrupts();                      // Interrupt weider zulassen
}

Und dann solltest du die Schleife zur Ausgabe des Arrayinhalts nur bis i-1 laufen lassen. (Mein Fehler, ich hatte es so geschrieben). Das verhindert den "unsinnigen" letzten Wert bei der Ausgabe. Das Array wird im Interrupt ja nur bis i-1 beschrieben. Der Platz m[ i] enthält also falsche Werte aus früheren Messungen.

Gunther


 noInterrupts()
Logged

Grüße
Gunther

Pages: 1 [2] 3 4 ... 6   Go Up
Jump to: