Entprellen bei Drehzahl messen (debouncing)

Moin,

ich möchte drei Drehzahlen mit einem ESP8266 messen.
Dazu habe ich mir untenstehenden Code zusammengebastelt.
Dieser funktioniert soweit wunderbar.
Die Messzeit beträgt zum Testen 5000 Millisekunden (= 5 Sekunden) und dabei werden etwa 160.000 Schleifendurchläufe erreicht.
Die Drehzahlen betragen unter 2000 rpm.
Jede Drehzahl wird mit zwei ansteigenden Flanken pro Umdrehung gemessen (= zwei Impulse pro Umdrehung).
Das ergibt also zu erwartende Frequenzen von maximal 2000 x 2 / 60 = 66,6 Hz.
Und diese werden mit 160.000 Schleifendurchgängen / 5 Sekunden = 32.000 Hz abgefragt.
Das Pulsweiten Verhältnis ist dabei genau 1:1 (bedeutet, das Signal ist genauso lange HIGH wie LOW).

Nun hab ich das Problem, dass ein Signal nicht ganz sauber rüberkommt.
Wahrscheinlich prellt es ein wenig, so dass eine zu hohe Drehzahl angezeigt wird.
Die angezeigte Drehzahl ist 1,5x so hoch wie erwartet.
Da ich im Programm durch 2 teile ergeben sich daraus (1,5 x 2 =) 3 Pulse pro Umdrehung.

Ich könnte jetzt natürlich das eine Signal durch 3 anstatt durch 2 teilen.
Aber das ist zu fehleranfällig.

Ich würde lieber versuchen das Signal softwaremäßig zu entprellen.

Hat jemand eine Idee wie man das anstellt?

Hier der betreffende Programmteil:

void loop() {
  for (int i=0; i<3; i++){           // Schleife 0-2
    yield();                         // Dem Prozessor Zeit für seine internen Prozesse geben und somit einen automatischen RESET verhindern
    Sensor[i]=digitalRead(sensorPin[i]); // Wert einlesen
    if (Sensor[i]==HIGH && OldSensor[i]==LOW){   // Wenn Eingang HIGH und voriger Werte des Eingang LOW (also steigende Flanke)
      rpm[i]++;}                    //Drehzahlvariable hochzählen
      OldSensor[i]=Sensor[i];       //Aktuellen Zustand des Inputs merken
    }
  if (millis()-Startzeit>=Messzeit){      // Wenn der Messinterval abgelaufen ist Werte ausgeben und senden
     rpm[0]=rpm[0]/2;                     // Die Sensoren machen zwei Impulse pro Umdrehung, daher geteilt durch 2
     rpm[1]=rpm[1]/2;                     // Die Sensoren machen zwei Impulse pro Umdrehung, daher geteilt durch 2
     rpm[2]=rpm[2]/2;                     // Die Sensoren machen zwei Impulse pro Umdrehung, daher geteilt durch 2
     Serial.print("rpm1: ");
     Serial.print(rpm[0]);                // Ausgabe rpm[0]
     Serial.print("   rpm2: ");
     Serial.print(rpm[1]);                // Ausgabe rpm[1]
     Serial.print("   rpm3: ");
     Serial.println(rpm[2]);              // Ausgabe rpm[2]
     rpm[0]=0;                            // rpm[0] zurücksetzen
     rpm[1]=0;                            // rpm[1] zurücksetzen
     rpm[2]=0;                            // rpm[2] zurücksetzen
     Startzeit=millis();                  // Startzeit zurücksetzen
  }
}

Danke und lieben Gruß,
Chris

Entprellung in Hardware (RC-Glied) ist bei sowas oft am praktischsten

Womit erzeugst Du denn die Impulse? Mit Kontakten?

Gruß Tommy

Hi

Sicher, daß Du zwei(4) Impulse pro Umdrehung bekommst?
Ich habe hier einen Durchflußsensor (YF-S201), Der mir 6 Wechsel bzw. 3 HIGH / 3 LOW pro Umdrehung bringt.

Habe meinen Sensor zerlegt und das Flügelrad 10 Umdrehungen von Hand gedreht (mit dem Schraubendreher in dem Gehäuse rumstochern war nicht zielführend und ein Zerlegen ohne Probleme möglich - mit anschließendem Zusammenbau) und die angezeigte Anzahl durch zehn geteilt.
Bin aber auch noch etwas davon entfernt, daß mir mein Sketch die Umdrehungen pro Minute/Stunde anzeigt ... Mathe ist schon ein paar Jahre her. Ausrede such

MfG

PS: Meine Impulse kommen per Hall-Sensor - da muß Nichts entprellt werden (kein mechanischer Kontakt, nur Diese prellen)

Entprellung in Hardware (RC-Glied) ist bei sowas oft am praktischsten

Mag sein, ich suche aber eine Softwarelösung.

Womit erzeugst Du denn die Impulse? Mit Kontakten?

Nein, optisch. Die Signale kommen von einem Komparator auf der Platine. Also auch elektrisch "sehr sauber". Aber wie immer gilt: "Shit in, Shit out". Ich vermute, dass ein ganz kurzer Peak bei der hohen Abtastrate zu dem Ergebnis führt.

Sicher, daß Du zwei(4) Impulse pro Umdrehung bekommst?

Ja! ..... hab die Reflexmarken ja selber draufgemalt :slight_smile:

Hier die Sensoren:




Lieben Gruß,
Chris

Was soll da prellen ?
Es sind keine mechanischen Kontakte die prellen könnten, vorhanden.

da brauchst du nichts entprellen, versuch mal das Signal mit einem Interrupt einzulesen, vllt. bringt das was.

Hi

themanfrommoon:
Ja! ..... hab die Reflexmarken ja selber draufgemalt :slight_smile:

Der gefällt mir :slight_smile:

Ein Oszi zur Hand?
Das würde zumindest Peaks sichtbar machen - Memo an mich selbst: USB-Oszi an Laptop und damit am Zähler rummessen!
(Bin mit meinem eHz-Zähler, E21-Schnittstelle, am kämpfen - noch lacht Der nur spöttisch, das Oszi ist dafür 'unangenehm groß')

Bei der optischen Abtastung hätte ich keine Bedenken, daß Da Signale doppelt gegeben werden - da würde ich den 'Fehler' eher am Motor suchen, daß Dessen Drehzahl nicht dem Aufdruck entspricht - wobei eine Abweicheung 2:3 schon eine Menge ist.

MfG

PS: Interrupt - wenn Du bis jetzt pollst, könnten Dir Flanken verloren gehen - bei meinem Durchflußsensor (YF-S201) brachte der Interrupt auch deutlich 'mehr' Takte zum Vorschein.

Ja! ..... hab die Reflexmarken ja selber draufgemalt :slight_smile:

Dann schaue Dir mal die Übergänge an, ob die ganz sauber sind (also keine "Kleckse"/Punkte/Linien der anderen Farbe) vorhanden sind. Auf dem Bild sieht es so aus, als könnte der Strich von der Zeichnung stören.

Gruß Tommy

themanfrommoon:
Da ich im Programm durch 2 teile ergeben sich daraus (1,5 x 2 =) 3 Pulse pro Umdrehung.

void loop() {

for (int i=0; i<3; i++) {          // Schleife 0-2
    ...
  }
}

Ich glaube ja eher nicht, dass diese Schleife macht was du machen willst.
Aber das ist schwer zu sagen, weil es nur ein Codeschnipsel ist und nicht klar ist, was der restliche (nicht gezeigte) Code macht.

da brauchst du nichts entprellen, versuch mal das Signal mit einem Interrupt einzulesen, vllt. bringt das was.

....da komm ich ja her. Mit Interrupt geht's gar nicht. Die Interrupts ballern den ESP8266 so voll, dass er nicht genug Zeit für seine Kernprozesse hat und so mehrmals pro Stunde rebootet.

Was soll da prellen ?

prellen, peaken, zucken, is doch alles das gleiche.
Es kommt vermutlich kein Signal AAAAAANNNNNNN, AAAUUUUUUUSSSSSS, AAAAAANNNNNNN, AAAUUUUUUUSSSSSS
Sondern AAAAAANNNNNNN, AAAUUUUUUUSSSSSS, AN, AUS, AAAAAANNNNNNN, AAAUUUUUUUSSSSSS
Das ist ein Prellen. Muss ja nicht mechanisch bedingt sein. In diesem Fall ist es eben optisch bedingt.

Ein Oszi zur Hand?

Öhm, ja, ich kram das alte Teil mal raus.....

Interrupt - wenn Du bis jetzt pollst, könnten Dir Flanken verloren gehen

Wie sollen Flanken verloren gehen, wenn ich mit 32.000Hz abfrage und die Signale nur mit 66Hz kommen können?

Ich glaube ja eher nicht, dass diese Schleife macht was du machen willst.
Aber das ist schwer zu sagen, weil es nur ein Codeschnipsel ist und nicht klar ist, was der restliche (nicht gezeigte) Code macht.

Okay, warum aufs wesentliche, den mundgerecht vorbereiteten Code beschränken, wenn man auch den Heuhaufen kriegen kann. Aber ich glaube nicht, dass wir jetzt schlauer werden. Ich glaube das verwirrt jetzt nur mehr:

#define AUSGABE           // Serielle Ausgabe aktiv
#define AUSGABEschleifen  // Serielle Ausgabe Anzahl Schleifen aktiv
#define LEDsenden         // Die blaue ESP12 LED blinkt beim senden
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
ESP8266WiFiMulti WiFiMulti;
#ifdef LEDsenden
  byte LED1 = 2;      // Arduino Pin vom der ESP12 onboard LED
#endif
const long BaudRate = 74880;              // Serielle Verbindungsgeschwindigkeit für die serielle Ausgabe
const int Messzeit = 5000;               // 60000 Millisekunden = 60 Sekunden = Umdrehungen pro Minute
unsigned long Startzeit;                  // Merker für die Zeit in Millisekunden seit letzten Aufruf der Funktion. Millis zählt immer weiter hoch seit dem Einschalten des Controllers
const int sensorPin[3] = {14,12,13};      // the number of the sensorPin[0...2]  (original: 14,12,13 bzw. D5, D6, D7)
unsigned long Schleifen;                  // wie viele Schleifen wurden in der Messzeit durchlaufen? (Müssen mehr sein als die gemessene Frequenz)
int i;                                    // Zählvariable
int Sensor[3]={1,1,1};                    // Array Variable für den ausgelesenen Wert, muss am Anfang 1 sein
int OldSensor[3]={1,1,1};                 // Array Variable für den vorigen ausgelesenen Wert, muss am Anfang 1 sein
int rpm[3]={0,0,0};                       // Array Variable für den ausgelesenen Wert, muss am Anfang 0 sein
const char WiFiSSID[] = "xxxxx";          // Router SSID
const char WiFiPSK[]  = "xxxxxxxxxxx";    // Router Password
const String host = "xxxxxxxxx";          // Server IP Adresse
const unsigned int port = 80;
const String url_start = "/middleware.php/data/";         // Unterordner für die Werteablage
const String url_stop = ".json?operation=add&value=";     // Dateierweiterung für die Werteablage und Parameter
byte maxwait = 120;
char *UUID[3] = {                          // Die UUID's der abzulegenden Kanäle werden in ein char array hinterlegt
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",    // UUID des 0. Kanals "Drehzahl Zuluft"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",    // UUID des 1. Kanals "Drehzahl Abluft"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"};   // UUID des 2. Kanals "Drehzahl Fortluft"
//********************************************************************************************************************************
bool waitWifi() {
  while((WiFiMulti.run() != WL_CONNECTED) && maxwait > 0) {
    #ifdef AUSGABE
      Serial.println("Wait Wifi");
    #endif
    delay(1000);
    maxwait--;
  }
  if(WiFiMulti.run() == WL_CONNECTED) return true;
  return false;
}
//********************************************************************************************************************************
void sendHttpData(String url) {
  HTTPClient http;
  if(waitWifi()) {
    #ifdef AUSGABE
      Serial.print("GET: "); Serial.println(url);
    #endif
  http.begin(host, port, url); //HTTP
  int httpCode = http.GET();
  #ifdef AUSGABE
    if(httpCode) {
      if(httpCode == 200) {
        String payload = http.getString();
        Serial.println(payload);
      }else{
        Serial.print("HTTP "); Serial.println(httpCode);
      }
      } else {
        Serial.print("[HTTP] GET... failed, no connection or no HTTP server\n");
      }
    #endif
    }else{
    #ifdef AUSGABE
      Serial.print("No WiFi available\n");
    #endif
    }
}
//********************************************************************************************************************************
void WertSenden(int i) {
  String url_temp = "";
  url_temp = url_start;
  url_temp += UUID[i];
  url_temp += url_stop;
  url_temp += rpm[i];
  #ifdef AUSGABE
    Serial.println(url_temp);
  #endif
  sendHttpData(url_temp);                       // ------------------Daten senden-----------------
}
//********************************************************************************************************************************
void setup() {
  #ifdef AUSGABEschleifen           // wird nur kompiliert, wenn serielle Ausgabe aktiv ist
    Serial.begin(BaudRate);         // Starten der seriellen Ausgabe die mit einem seriellen Monitor angesehen werden kann
  #endif
  #ifdef AUSGABE                    // wird nur kompiliert, wenn serielle Ausgabe aktiv ist
    Serial.begin(BaudRate);         // Starten der seriellen Ausgabe die mit einem seriellen Monitor angesehen werden kann
    Serial.println("BOOT");
    Serial.print("Wifi...");
  #endif
  WiFiMulti.addAP(WiFiSSID, WiFiPSK);
  #ifdef AUSGABE
    Serial.println("OK");
  #endif
  // initialize the sensorPins as an input:
  pinMode(sensorPin[0], INPUT);
  pinMode(sensorPin[1], INPUT);
  pinMode(sensorPin[2], INPUT);
  #ifdef LEDsenden
    pinMode(LED1, OUTPUT);
    digitalWrite(LED1, HIGH);  // LED1 ausschalten      // LED1 ausschalten
  #endif
  Startzeit=millis();
}
//********************************************************************************************************************************
void loop() {
  for (int i=0; i<3; i++){           // Schleife 0-2
    yield();                         // Dem Prozessor Zeit für seine internen Prozesse geben und somit einen automatischen RESET verhindern
    Sensor[i]=digitalRead(sensorPin[i]);
    if (Sensor[i]==HIGH && OldSensor[i]==LOW){
      rpm[i]++;}
      OldSensor[i]=Sensor[i];
    }
  #ifdef AUSGABEschleifen                 // wird nur kompiliert, wenn serielle Ausgabe aktiv ist
    Schleifen++;
  #endif
  if (millis()-Startzeit>=Messzeit){      // Wenn der Messinterval abgelaufen ist Werte ausgeben und senden
     rpm[0]=rpm[0]/2;                     // Die Sensoren machen zwei Impulse pro Umdrehung, daher geteilt durch 2
     rpm[1]=rpm[1]/2;                     // Die Sensoren machen zwei Impulse pro Umdrehung, daher geteilt durch 2
     rpm[2]=rpm[2]/2;                     // Die Sensoren machen zwei Impulse pro Umdrehung, daher geteilt durch 2
    #ifdef AUSGABE                        // wird nur kompiliert, wenn serielle Ausgabe aktiv ist
      Serial.print("rpm1: ");
      Serial.print(rpm[0]);
      Serial.print("   rpm2: ");
      Serial.print(rpm[1]);
      Serial.print("   rpm3: ");
      Serial.println(rpm[2]);
    #endif
    #ifdef AUSGABEschleifen                 // wird nur kompiliert, wenn serielle Ausgabe aktiv ist
      Serial.print("Schleifen durchlaufen: ");
      Serial.println(Schleifen);
    #endif
    for (int i=0; i<3; i++){           // Schleife 0-2 um die Werte zu senden
      WertSenden(i);                   // Wert(i) senden
      #ifdef LEDsenden
        digitalWrite(LED1, LOW);       // LED1 einschalten für einen gesendeten Wert
        delay(30);                     // 30 Millisekunden warten
        digitalWrite(LED1, HIGH);      // LED1 ausschalten
        delay(30);                     // 30 Millisekunden warten
      #endif
    }
    rpm[0]=0;
    rpm[1]=0;
    rpm[2]=0;
    Schleifen=0;
    Startzeit=millis();
  }
}

.....tolles Sache so ein Oszilloskop. Was man damit alles machen kann :slight_smile: Ihr dürft ruhig schimpfen und mit dem Kopf schütteln, ich bin Maschinenbauingenieur und in der 3-dimensionalen Welt zu Hause (das ist ein Insider Witz. Elektroniker sind ja in der 2-dimensionalen Welt zu Hause :slight_smile: )
Außerdem wollen wir ja hier was lernen :slight_smile:
Ich hab mir mal das Signal angesehen.
Von dem funktionierenden Sensor sieht das so aus:

Von dem Probleme verursachendem Sensor sieht das Signal so aus:

Dann habe ich das Poti justiert bis das Signal so aussah:

Mal beobachten was nun mit den Messungen passiert.
Dennoch suche ich immer noch nach einer Softwarelösung wie man die Signale entprellen kann.

Danke und lieben Gruß,
Chris

Dann suche mal nach Taster entprellen und Debounce.

Gruß Tommy

themanfrommoon:
Dennoch suche ich immer noch nach einer Softwarelösung wie man die Signale entprellen kann.

Deine Signale kannst Du gar nicht entprellen - weil da nichts prellt.
Grundsätzlich kann man natürlich Störungen aus einem Nutzsignal entfernen - und Entprellen ist eine besondere Form davon. Dazu muss man aber sowohl die Charakteristik der Störungen als auch des Nutzsignals kennen, und beides muss sich genügend voneinander unterscheiden. Bei einem mechanischen Schalter ist das bekannt, und man kann das Prellen ( das Störsignal ) mit bekannten Methoden herausfiltern.
Wenn Du in deinen Signalen Störungen hast, müsste erstmal geklärt werden wie die aussehen - mit den Methoden der Entprellung wirst Du da nicht weiterkommen. Und wenn die sich nicht ausreichend vom Nutzsignal unterscheiden, kannst Du sie hinterher gar nicht mehr rausfiltern - da MUSST Du das Übel an der Wurzel bekämpfen ( wie Du es ja auch gemacht hast ).

Natürlich kann man sowas mit Software filtern.

die Idee ist: überlegen, anhand welcher Kriterien man einen "guten" Impuls von einem "schlechten" unterscheiden kann.

Meist sind das Zeitkriterien.

Beispiel: du hast 1000min-1 Drehzahl. Ca alle 30ms kommt also ein Puls.
Wenn bereits nach 12ms ein Puls kommt, dann muss der falsch sein.

Umsetzung in Software:
aus deiner aktuellen Drehzahl errechnest du du dir ein Gültigkeitsfenster für den nächsten Puls.
Wenn der ausserhalb des Fensters kommt, ignorierst du ihn.
Wie groß dein Fenster sein muss, hängt davon ab wie schnell sich die Drehzahl ändern kann. Aber wenn ich mir die Mechanik so ansehen, dann ist das recht stabil, ich würde ein Gültigkeitsfenster von +/-10% vom erwarteten Wert vorsehen.

Damit das sauber läuft wäre es besser die Erfassung auf Interrupt umzustellen, geht aber auch so, wenn der Code schlank bleibt.
Und du mußt natürlich Hochlaufverhalten (was passiert beim Einschalten), Fehlerverhalten (was passiert, wenn du doch nur noch "schlechte" Pulse bekommst) etc programmieren.

Und besser wäre sicher, rauszufinden, was die falschen Impulse erzeugt.