Signalverarbeitung Drehzahl Hall-Sensor

Hallo Forumsgemeinde!

Ich steh da vor einem kleinen Problem und könnte Hilfe gebrauchen. Es geht um die Drehzahlerfassung einer Welle. Ich verwende dazu den Hall-Sensor TLE4905 und erwarte mir eine maximale Drehzahl der Welle von ca. 1300 U/min. Im Normalfall sollte diese aber bei ca. 800 U/min liegen. Also das sollte der Sensor schaffen. Mechanisch funktioniert auch alles. Der Sensor spricht an, wenn der Magnet vorbeisaust.

Hier mal der Code dazu:

// Ermittlung der Wellendrehzahl mit Hallsensor TLE4905

volatile int Wellendrehung = 0;
float Timestamp = 0;
float Geschwindigkeit = 0; // in km/h
float Weg = 0; // in m
float Reifenumfang = 0,2; // in m // Annahme zum testen
 
void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, functionCount, FALLING); //Interrupt Pin 0 ist Digital Pin 2; FALLING löst aus wenn Pin von high auf low fällt (Magnetfeld vorhanden ist low)
}
  
void functionCount()
{
  Wellendrehung++;
}

void loop ()
{
 if ( millis()-Timestamp > 1000) // Wenn tausend Millisekunden vorüber...
  {
     noInterrupts();
     Geschwindigkeit = Wellendrehung*Reifenumfang*3.6; // Geschwindigkeit in km/h
     Weg = Wellendrehung*Reifenumfang; // Weg in m pro sekunde; muss dann noch aufsummiert werden
     Wellendrehung = 0;
     Timestamp = millis();
     interrupts();
 
     Serial.print("Geschwindigkeit: ");Serial.println(Geschwindigkeit,1);
     Serial.print("Weg: ");Serial.println(Weg);
    }
 
}

Soweit so gut. Und jetz zum Problem: Wenn ich die Wellendrehungen über eine Sekunde lang zähle ist das Ergebnis bei relativ konstanter Wellendrehzahl akzeptabel. So erhalte ich aber nur jede Sekunde einen neuen Wert, wodurch die digitale Anzeige der Geschwindigkeit nicht gerade sehr “flüssig” läuft. Wenn ich länger warten würde wäre das Ergebnis wahrscheinlich noch genauer (zumindest bei konstanter Geschwindigkeit). Ich hatte aber einen Intervall von 100 ms im Sinn, damit die Anzeige bei Geschwindigkeitsänderungen schön flüssig ist. Dazu hatte ich Timestamp < 100 in der if-Bedingung und dann die Geschwindigkeit in der loop mit 10 multipliziert. Dabei kommen aber keine plausiblen Werte raus.

Hat jemand eine Idee wie man das besser machen kann? Wäre es ratsam einen Tiefpassfilter zu verwenden?

Dankeschön und LG!

Versuche es mal mit RunningMedian

Der Arduino hat keine Recheneinheit zur Verfügung, weshalb float-Berechnungen lange dauern und Dir möglicherweise Meßimpulse kosten. Und bei 100 ms passiert das halt öfter. Daher könntest Du diese Berechnungen auslagern:

    int wd=0;
     noInterrupts();
     wd = Wellendrehung;
     Wellendrehung = 0;
     Timestamp = millis();
     interrupts();
     Geschwindigkeit = wd*Reifenumfang*3.6; // Geschwindigkeit in km/h
     Weg = wd*Reifenumfang; // Weg in m pro sekunde; muss dann noch aufsummiert werden

Das kann zusammen mit dem gleitenden Mittelwert helfen oder auch nicht, Versuch macht klug :)

Hi,

ich würde das mit Zeitmessung von 2 Signalen versuchen. Bei 800U/m sind es über 13 Signale pro Sekunde. Mist man die Zeit von 2 Signalen kann man schon über 6 mal je Sekunde die Ausgabe aktuallisieren. Ich habs mal versucht den Code anzupassen. Als Signalzähler reicht auch schon eine Byte-Variable und man muss keine Interrupts speeren um die zu resetten da das mit einem Befehl geht. Habe den Code nicht getestet aber Versuchs mal:

// Ermittlung der Wellendrehzahl mit Hallsensor TLE4905

volatile byte Wellendrehung = 0;
float Drehzahl = 0;
float Timestamp = 0;
float Geschwindigkeit = 0; // in km/h
float Weg = 0; // in m
float Reifenumfang = 0,2; // in m // Annahme zum testen

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, functionCount, FALLING); //Interrupt Pin 0 ist Digital Pin 2; FALLING löst aus wenn Pin von high auf low fällt (Magnetfeld vorhanden ist low)
}

void functionCount()
{
  Wellendrehung++;
}

void loop ()
{
 if (Wellendrehung>=2) // Die Zeit für 2 Umdrehungen
  {
     Wellendrehung = 0;
     Timestamp = millis() - Timestamp;

     Drehzahl = 120000 / Timestamp; //Drehzahl aus 2 Drehimpulsen berechnen (2 Min. / 2 Signale)

     Geschwindigkeit = Drehzahl*Reifenumfang*3.6; // Geschwindigkeit in km/h
     Weg = Drehzahl*Reifenumfang; // Weg in m pro sekunde; muss dann noch aufsummiert werden

     Serial.print("Geschwindigkeit: ");Serial.println(Geschwindigkeit,1);
     Serial.print("Weg: ");Serial.println(Weg);
    }

}

MfG Andi

SP90: Hat jemand eine Idee wie man das besser machen kann? Wäre es ratsam einen Tiefpassfilter zu verwenden?

Erst falsch/unsauber rechnen, dann glätten, das halte ich für Unsinn!

Du machst das Interrupt-Handling falsch! Und Du erfasst zu wenig Daten.

Ich habe hier im Forum schon vor fünf Jahren Fahrad-Tacho-Code gepostet, der sauberer arbeitet.

Deine Interruptfunktion:

void functionCount()
{
  Wellendrehung++;
}

Der Fehler ist: Du zählst nur die Impulse! Es fehlt: Die Erfassung der Zeit

Und weil Du die Zeit nicht erfasst, kannst Du hinterher in der loop() Funktion den Wert "Umdrehungen pro Zeit" nicht sauber ausrechnen, auch nicht mit Sperrung der Interrupts während der Berechnung.

Ich würde in den Code zuerst mal eine zusätzliche globale Variable einbauen, in der bei jeder Umdrehung die Zeit gemerkt wird, wann dieser Interrupt auftrat. Und in Anbetracht der recht hohen Drehzahl würde ich die Zeit am besten in Mikrosekunden (micros() und nicht in Millisekunden (millis() erfassen.

Die millis() Funktion kannst Du sehr gut für die Ermittlung der Gate-Zeit verwenden, um beispielsweise in der loop() Funktion die Geschwindigkeit einmal pro Sekunde auszurechnen und auszugeben, sei es auf Serial oder auf einem LCD.

Vorschlag für eine zusätzliche Zeitmerker-Vriable:

volatile int umdrehungen;
volatile long umdrehungenZeit;
unsigned long letzteUmdrehungZeit;

Und Vorschlag für eine geänderte Interruptfunktion:

void functionCount()
{
long now=micros();
  umdrehungen++;
umdrehungenZeit+=(now-letzteUmdrehungZeit);
letzteUmdrehungZeit=now;

Und dann kannst Du einmal pro Sekunde

  • Interrupts sperren mit noInterrupts();
  • Wert für Drehzahl/Geschwindigkeit berechnen . die beiden Variablenumdrehungen und umdrehungenZeit auf 0 zurücksetzen
  • Interrupts wieder zulassen mit interrupts();

Und wenn der Anzeigewert dann trotzdem noch zu sehr springt, dann vor dem Anzeigen eine Glättungsfunktion verwenden.

Viel Erfolg!

Hallo,

Ich habe hier im Forum schon vor fünf Jahren Fahrad-Tacho-Code gepostet, der sauberer arbeitet.

dann wäre eine Verlinkung dazu angebracht ...

Hallo und Danke für die raschen Antworten.

Hab jetz mal alles ausprobiert. Der Tipp von agmue funktioniert in etwa gleich wie mein ursprünglicher sketch, is aber sicherlich von Vorteil wenn man das so macht.

Der Ansatz von Haribo68 mit der Zeitmessung ist sehr interessant. Mir ist dabei aber unklar warum hier

 Drehzahl = 120000 / Timestamp; //Drehzahl aus 2 Drehimpulsen berechnen (2 Min. / 2 Signale)

120000 vorkommt. Die Gleichung resultiert doch aus einer Schlussrechnung, oder? Also 2 Umdrehungen in der Zeit Timestamp (in millisekunden), wie viele Umdrehungen pro 1000 sekunden? Müsste doch Drehzahl = 2000/Timestamp sein? Beides liefert völlig unplausieble Werte :o Hier mein adaptierter sketch:

volatile byte Wellendrehung = 0;
float Drehzahl = 0;
float ZeitproUmdrehung = 0;
float Geschwindigkeit = 0; // in km/h
float Weg = 0; // in m
float Reifenumfang = 0,2; // in m // Annahme zum testen

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, functionCount, FALLING); //Interrupt Pin 0 ist Digital Pin 2; FALLING löst aus wenn Pin von high auf low fällt (Magnetfeld vorhanden ist low)
}
  
void functionCount()
{
  Wellendrehung++;
}

void loop ()
{
 if (Wellendrehung>=2) // Die Zeit für 2 Umdrehungen
  {
     Wellendrehung = 0;
     ZeitproUmdrehung = millis()-ZeitproUmdrehung;

     Drehzahl = 2000/ZeitproUmdrehung; // Wellenumdrehungen pro Sekunde
     Geschwindigkeit = Drehzahl*Reifenumfang*3.6; // Geschwindigkeit in km/h
     Weg = Drehzahl*Reifenumfang; // Weg in m pro sekunde; muss dann noch aufsummiert werden

     Serial.print("Geschwindigkeit: ");Serial.println(Geschwindigkeit,1);
     Serial.print("Weg: ");Serial.println(Weg);
    }

}

Dann habe ich viel Zeit damit verbracht die Tipps von jurs zu verstehen. Den Fahrrad-Tacho konnte ich leider nicht finden. Der Link dazu wäre echt super. Auf jeden Fall gefällt mir, dass ich den Intervall des Print-Befehls unabhängig definieren kann. Ich hab das mal versucht so umzusetzten:

volatile int Wellendrehung = 0;
float Drehzahl = 0;
volatile long ZeitfuerUmdrehungen = 0;
unsigned long ZeitbeiletzterUmdrehung = 0;
float Timestamp = 0;
float Geschwindigkeit = 0; // in km/h
float Weg = 0; // in m
float Reifenumfang = 0,2; // in m // Annahme zum testen

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, functionCount, FALLING); //Interrupt Pin 0 ist Digital Pin 2; FALLING löst aus wenn Pin von high auf low fällt (Magnetfeld vorhanden ist low)
}
  
void functionCount()
{
  Wellendrehung++;
  ZeitfuerUmdrehungen += micros()-ZeitbeiletzterUmdrehung;
  ZeitbeiletzterUmdrehung = micros();
}

void loop ()
{
 if ( millis()-Timestamp > 100) // Wenn hundert Millisekunden vorüber...
  {
     noInterrupts();
     Drehzahl = Wellendrehung/ZeitfuerUmdrehungen*1000000;
     Wellendrehung = 0;
     ZeitfuerUmdrehungen = 0;
     Timestamp = millis();
     interrupts();
    
     Geschwindigkeit = Drehzahl*Reifenumfang*3.6; // Geschwindigkeit in km/h
     Weg = Drehzahl*Reifenumfang; // Weg in m pro sekunde; muss dann noch aufsummiert werden

     Serial.print("Geschwindigkeit: ");Serial.println(Geschwindigkeit,1);
     Serial.print("Weg: ");Serial.println(Weg);
    }


}

Funktioniert nur leider überhaupt nicht. Bekomme negative Geschwindigkeiten in Millionenhöhe, ohne dass die Welle überhaupt dreht. Mir ist auch noch nicht ganz klar warum in der functionCount ein += verwendet wird. Und die Variable Wellendrehung wird doch maximal 1. Muss ich die in der functionCount eigentlich noch hochzählen? Ich weiß ja, wenn die functionCount ausgeführt wird, ist eine Umdrehung vorüber und dann brauch ich halt die Zeit die zwischen der Ausführung liegt.

So, jetz muss ich aber mal den Kopf frei kriegen.

LG

Also 2 Umdrehungen in der Zeit Timestamp (in millisekunden), wie viele Umdrehungen pro 1000 sekunden?

...1000 millisekunden natürlich. Ich will ja U/s

Drehzahl = Wellendrehung/ZeitfuerUmdrehungen*1000000;

Hier droht eine Division durch Null, was Unendlich ergibt.

SP90: Der Ansatz von Haribo68 mit der Zeitmessung ist sehr interessant. Mir ist dabei aber unklar warum hier

 Drehzahl = 120000 / Timestamp; //Drehzahl aus 2 Drehimpulsen berechnen (2 Min. / 2 Signale)

120000 vorkommt. Die Gleichung resultiert doch aus einer Schlussrechnung, oder? Also 2 Umdrehungen in der Zeit Timestamp (in millisekunden), wie viele Umdrehungen pro 1000 sekunden? Müsste doch Drehzahl = 2000/Timestamp sein? Beides liefert völlig unplausieble Werte :o Hier mein adaptierter sketch:

Das war als Ansatz gedacht. Mist man die Zeit einer Wellenumdrehung dann sind in einer Minute (60000ms) Minute / Zeit einer Wellenumdrehung = 60000 / Zeit EINER Wellenumdrehung bzw. Umdrehungen je Minute. Nun kann man auch hergehen und 2 Wellenumdrehungen messen. Dazu verdoppelt man die Zeit in Millisekunden einfach und teilt dazu die Zeit für 2 Umdrehungen und hat dazu noch einen Durchschnitt. Z. B. 800RPM benötigt 75ms, 2 mal 800RPM benötigen 150ms. Sind es 149ms dann gibt das von 120000ms aus 805U/m. Man kann das auch mit 3 oder 4 Umdrehungen machen, dazu nimmt man dann aber auch das 3- oder 4-fache der Zeit wie für eine Umdrehug als Divisor und hat dann auch genauere Werte. Im Endeffekt kommt eine Drehzahl je Minute dabei raus woraus sich alles weitere, Weg je Minute, K/mH etc. alles berechnen lässt.

MfG Andi

So Leute, hatte endlich wieder ein bisschen Zeit zu basteln.

@ Haribo68: Ja dann hab ich das schon richtig verstanden. Nur dass ich in Sekunden und du in Minuten gerechnet hast. Mann muss halt dann beim Umrechnen in km/h aufpassen. Egal wie man es rechnet, es funktioniert so nicht. Die Werte sind unplausiebel.

Ich hab dann noch was über Fahrradtachos von jurs gefunden. Ist zwar nicht 5 Jahre alt, aber ich denke das war gemeint:

Das hab ich etwas adaptiert und es funktioniert prima, wenn man im Sekundentakt die Ergebnisse ausgibt. Das hat mit meinem ursprünglichen sketch auch schon halbwegs geklappt, obwohl das hier deutlich eleganter ist und stabiler läuft. Also mal Danke an jurs.

Dennoch ist mir noch nicht ganz klar was in der Interrupt-Funktion passiert. Vielleicht kann das jemand kurz erklären. Ich würd den sketch schon gern vollständig verstehen.

Mein Problem war aber, dass mir eine Anzeige im Sekundentakt nicht fließend genug läuft. Das maximum was geht ist eine aktualisierung alle halben Sekunden. Dann bekommt man bei niedrigeren Geschwindigkeiten (< 10 km/h) schon des öfteren ein paar 0-Werte. Ich denke mal, dass da einfach keine ganze Umdrehung der Welle in der kurzen Zeit mehr stattfindet. Und bei zu kleinen Drehzahlen kriegt man sowieso keine vernünftigen Werte mehr.

Hab mir mal angeschaut wie das bei richtigen Autos funktioniert und da läuft so ein digitaler Tacho auch überraschend “eckig”.

Ich werd jetz aber dennoch mal versuchen, diese vereinzelten Spitzen auf 0 km/h mit einem Tiefpass rauszukriegen. Wenn ich was vernünftiges zu Stande bekomm, werd ich das Ergebnis mal posten.

LG

Na ja, denke nicht, das du das verstanden hast. Angenommen, wir berechnen nur die Zeit von einem Signal in Millisekunden. Die Zeit bis zum nächsten Signal sind 100ms. Daraus berechnet sich die Drehzahl (Umdrehungen pro Minute) aus 60000ms - für eine Minute - / 100ms = 600U/m. Jetzt ist an der "Welle" ein Rad befestigt mit einem Durchmesser von 26 Zoll. In Metrisch ist das dann ein Umfang von 26 * Pi * 2,54cm = ~207cm je Umdrehung. Mit der Drehzahl haben wir die Umdrehungen der Welle und des Rades in einer Minute. Aus der Drehzahl und des Rad-Umfangs können wir dann den Weg pro Minute berechnen mit 600 * 207 = 124200cm pro Minute. Die Km/H ermitteln wir dann einfach mit 60 (für eine Stunde) * 124200 / 1000000 (cm je Km) = 7,45Km/H. Den Weg je Sekunde berechnet man mit 124200 / 60 = 2070cm. Das tolle ist, das die Zeitmessung von 100ms 10 mal in einer Sekunde rein passt und das Display trotz der relativ langsammen Geschwindigkeit 10 mal pro Sekunde aktuallisiert werden kann. Da das aber zu schnell ist und manche damit Probleme haben das abzulesen kann man die Aktuallisierung halbieren in dem man einfach die Zeit von 2 Signalen mist. Für das ermitteln der RPM ist es dann aber nötig anders zu rechnen. Statt mit 60000 / Zeit für ein Signal mit 120000 / Zeit für 2 Signale. Aber sinnvoller ist es wohl das Timing für die Displayausgabe extra zu machen in dem man nach einer festen Zeit von z. B. 500ms immer die letzte Erfassung ausgibt.

MfG Andi