[Python] Hilfe bei Python script für Drehzahlmesser mit IR lichtschranke

  • Hallo liebes Rpi forum.

    Mein Plan ist es, einen Drehzahlmesser mit einem Rpi *B und einer IR lichtschranke zu bauen.
    Der Rpi soll mittels Python die Drehzahl einer Welle auf der sich eine lochscheibe befindet ermitteln.

    Die Frage ist nun, wie der Code für ein Python Script aussehen muss.

    Getestet habe ich diese lichtschranke schon mit einem Script, welches eine 0 also Low über den Terminal ausgibt sobald die lichtschranke unterbrochen wird.
    Und eine 1 also High wenn sie nicht unterbrochen wird.

    Ich bin ein ziemlicher Neuling was programmieren betrifft und hoffe auf eure Hilfe :)

  • [Python] Hilfe bei Python script für Drehzahlmesser mit IR lichtschranke? Schau mal ob du hier fündig wirst!

  • Das Prinzip einer Drehzahlmessung - oder auch einer Frequenzmessung - ist es, eine Anzahl von Vorgängen pro Zeiteinheit zu zählen.

    in deinem Fall heißt das, zähle -sagen wir mal 10 - Nullen und messe die Zeit (in Sekunden) die dazwischen abgelaufen ist. Dann errechnet sich die Drehzahl zu 10/Zeit[s] *60 U/min.

    Die Sache hat einen Haken - du mußt aufpassen, daß du eine Null nur einmahl pro Umlauf zählst, also darfst du die Abfrage des GPIO nicht zu schnell machen.

    Besser ist es, den Übergang von 1 auf 0 zu zählen, also die Flanke.

  • Schon mal vielen Dank für die schnelle Antwort!
    Leider konnte ich die letzten Tage nicht zurück schreiben.

    Um die 1 zu einer 0 zu machen kann ich ja einen Transistor nehmen oder die lochscheibe verändern.
    Jetzt stellt sich halt die frage, ob du mir ein fertiges Script geben könntest, denn ich habe absolut keine Ahnung wie die befehle aussehen müssen.

    Die Idee mit den Zählen der Zeit für sagen wir mal 10 nullen ist schon sehr gut. Nur an der Umsetzung hapert es bei mir :-/
    MFG
    Error.

    • Offizieller Beitrag

    Du sollst nicht die 0 zur 1 machen, du sollst Übergang zwischen 0/1 als Zähler nehmen. Wie das geht, steht u.a. hier .

    Das Ganze halte ich für nen Anfänger schon für zu schwer. Das schwierige ist es nicht die Kommandos zu lernen, die Denk/Herangehensweise ist das, was gelernt/geübt werden muss.

    Der Unterschied zwischen Genie und Wahnsinn definiert sich im Erfolg.

  • Gut dann habe ich das falsch verstanden.
    Für die Messung würde ich lieber in einen Zeitraum die Flanken zählen und auf eine Minute rechnen.
    Also sagen wir mal in 5sec. zählt der PI 20 Flanken, diese 20 Flanken dann mit 12 multiplizieren denn
    1 min=60sec.=12 x5sec.
    Also:
    20x12= 240 U/min.
    Doch meine Frage lautet dennoch, wie das als Script auszusehen hat.

    Einmal editiert, zuletzt von ERRORistMUSS (27. November 2014 um 09:49)

  • Es wäre interessant zu wissen Wofür du das verwenden willst? Wo ist die Welle angebracht? Was willst du bemessen?



    Das Prinzip einer Drehzahlmessung - oder auch einer Frequenzmessung - ist es, eine Anzahl von Vorgängen pro Zeiteinheit zu zählen.

    in deinem Fall heißt das, zähle -sagen wir mal 10 - Nullen und messe die Zeit (in Sekunden) die dazwischen abgelaufen ist. Dann errechnet sich die Drehzahl zu 10/Zeit[s] *60 U/min.

    Hm nach meinem Verständnis ist es wichtig zu wissen wie viele Zähler benötigt werden damit eine Umrundung zu erreichen. Erst wenn man das weiß kann man nämlich den nächsten Schritt unternehmen und den Zähler zurücksetzen um nicht zu viele Null'n zu zählen.

    Ich hab zwar keine Lichtschranke aber einen ähnlichen Encoder - quadrature encoder -> Abwechselnd schwarze und weiße Flächen, welcher im Vergleich zur Lichtschranke auch auf den Wechsel reagiert.

    Erst wenn man weiß das zum Beispiel 10 Wechsel benötigt werden um eine Umdrehung zu erreichen, kann man auch daher gehen und den Zähler nach 10 Zählungen auf 0 zurück zu setzen, um nicht ungewollte Wechsel zu messen und somit die Messung zu verfälschen.

    Hat man erst mal das programmiert kann man dann daher gehen und eine Zeitmessung einbauen; wie lange hats gedauert eine Umdrehung zu vollenden.

    Und der nächste mögliche Schritt wäre dadurch dann ggf auch noch die zurück gelegte Strecke zu errechnen - wenn man denn weiß wie viel Strecke bei einer Umdrehung zurückgelegt wurde bzw wer es ganz genau wissen will sollte auch ermitteln wieviel Strecke bei nur einem Wechsel zurückgelegt wurde...


    Die Sache hat einen Haken - du mußt aufpassen, daß du eine Null nur einmahl pro Umlauf zählst, also darfst du die Abfrage des GPIO nicht zu schnell machen.

    Besser wäre es nicht selber die GPIO's abzufragen sondern lieber auf Basis von Interrupts (ISR = Interrupt Service Routine) zu arbeiten. Ein Interrupt teilt eine Zustandsänderung selber mit.
    Siehe dazu auch diverse Artikel über google: Polling Vs Interrupts

    Eine gute und ausführliche Beschreibung bezüglich ISR mit dem PI findet ihr >> hier <<


    Allerdings gibts bei Verwendung von IR Lichtschranken ein großes Problem: IR Licht gibt es auch so. Das kann die Messung verfälschen wenn zB das Tageslicht wechselt, heller oder dunkler, oder die Sonne mal drauf scheint kann der IR den Unterschied zwischen einem Wechsel möglicherweise nicht mehr feststellen....
    Deshalb habe ich mich gegen IR entschieden und einen Optischen Encoder gewählt.. Eine weitere Möglichkeit wäre aber auch eine Scheibe aus Metal zu verwenden und einen ' Hall Effect Sensor ' zu nehmen, der misst dann Unterschiedliche Magnetfelder...

    Um zu wissen was für Dich das beste ist müsste man aber halt den Einsatzzweck kennen und sich genau überlegen was alles Einfluss nehmen könnte :D


    Aber zurück zum Python Code:

    Am besten du aktivierst die Internen (software) Pull-Up Widerstände, das macht die Messung genauer. Wird das nicht gemacht ist der Zustand des Pins unvorhersagbar (liefert zufällige Werte). Hardware-PullUp Widerstände wären tatsächlich verlötete - aber das nur mal nebenbei erwähnt.

    Fang wir aber erst mal mit den Basics an.

    Der Anfang sollte klar sein, wir importieren die Benötigte Bibliothek um mit den GPIO's umgehen zu können:

    Code
    import RPi.GPIO as GPIO

    Dann legen wir die GPIO Pins fest die wir verwenden ; sowie ggf einige andere Globale Variablen damit wir bei Änderung später nicht im Code danach suchen müssen..

    Code
    PIN_Input = 7


    Pin#7 ist GPIO4

    Dann konfigurieren wir die GPIO Bibliothek gemäß der physikalischen Belegung, also der Nummerierung auf der Platine:

    Code
    GPIO.setmode(GPIO.BOARD)

    Nun erfolgt die Konfiguration für den GPIO Pin:

    Code
    GPIO.setup(PIN_Input, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    Damit stellen wir den GPIO4 Pin#7 auf INPUT und aktivieren den PullUp Widerstand.

    Jetzt erst mal zur eigentlichen 'Magic', der switch_event Funktion. Ich halte sie aber erst mal nur super simpel ohne tatsächlichen Nutzen... Siehe "PS:" wieso

    Code
    # Count Rotations
    def switch_event(pin):
        print("Der Pin %s hat sich geaendert!" % pin)

    Wichtig ist das diese Anweisung vor dem nächsten Schritt definiert wird sonst gibts eine Fehlermeldung.

    Jetzt legen wir ein Interrupt Event Listener fest, der auf dem GPIO hört und eine Funktion immer dann aufruft, wenn es eine Änderung am Pin gibt.

    Code
    GPIO.add_event_detect(PIN_Input, GPIO.FALLING, callback=switch_event)


    Als Parameter übergeben wir

    • die Pin-Nummer, worauf gehört werden soll
    • GPIO.FALLING für fallende Spannung, also als Auslösekriterium eine Änderung von HIGH auf LOW. RISING wäre eine Änderung von LOW auf HIGH
    • eine Callback Funktion die bei Änderung ausgeführt wird: switch_event

    Wenn nun die Spannung auf Pin 7 fällt, dann wird die Funktion switch_event aufgerufen. In der switch_event Funktion kann man nun den Wert des Pins für die weitere Bearbeitung weiterverwenden.

    Rand Anmerkung: Wenn man ISR mit Knöpfen verwendet, fügt man dem add_event_detect noch eine weitere Variable hinzu: bouncetime. Übersetzt: Prellzeit. Das verhindert, dass dieses Event die Funktion mehrmals aufruft. ABER: Das könn wir hier in diesem Fall nicht gebrauchen da innerhalb dieser Zeit (Millisekunden) ein Pin-Status-Wechsel ignoriert wird! Also Finger weg davon :fies:

    Anschließend müssen wir noch eine Funktion definieren damit sich das Script nach Ausführung nicht wieder beendet und immer weiter läuft:

    Code
    # Define a function to keep script running
    def loop():
        raw_input()
    
    
    try:
        loop() # Run the loop function to keep script running
    except KeyboardInterrupt:
        GPIO.cleanup()       # clean up GPIO on CTRL+C exit


    Das vollständige Script sollte dann also so aussehen:


    Jetzt führ den Code mal aus (python script.py) und dreh langsam an deiner Welle :)


    PS: Ich habe bewusst nicht schon alles rein gepackt da ich nix davon halte alles fix und fertig vor zu kauen, dann wird dabei nämlich oftmals nix gelernt... Wir könn das gerne Stück für Stück durchgehen und erweitern mit jedem Schritt die switch_event Funktion ;)


    //EDIT: Interrupts Codeschnipsel: http://codepad.org/Zz3rxbB2

  • Kleine Anmerkung:

    Code
    def loop():
        raw_input()

    Wie soll man das jetzt diplomatisch ausdrücken... Sehr schmutzig oder besser 'dirty trick'. Python bietet in seiner Standardbibliothek ein Modul namens ''signal'', welches hier verwendet werden könnte.

    Zitat

    signal.pause() -- wait until a signal arrives [Unix only]

    Ich kann das nicht testen, aber ich denke, dass das Programm so gar nicht geht, denn der Funktion ''switch_event(pin)" wird nirgends ein Argument übergeben.


    Edit:
    ggf

    Code
    def switch_event(PIN_Input):
  • bootsmann: Ich hab da nur quick&dirt raw_input eingesetzt damit das Script nicht beendet wird - das hat nichts mit "signal" oder einem tatsächlichen Input zu tun.
    Später will ich veranschaulichen das es mit Threads besser zu lösen wäre da schöner und flexibler, denn ISR läuft ebenfalls intern in einem Thread - aber das wär jetzt zu viel und kompliziert bzw ggf abschreckend geworden..


    Und doch, das Script funktioniert super. switch_event(PIN_Input) wäre falsch.
    Die Übergabe kommt vom ISR Event und dort wird stets der Pin übergeben wo der ISR ausgelöst wurde. So kann man nämlich nur eine einzige ISR Funktion auch mit mehreren GPIO's verwenden:

    Code
    GPIO.add_event_detect(7, GPIO.FALLING, callback=switch_event)
    GPIO.add_event_detect(8, GPIO.FALLING, callback=switch_event)
    GPIO.add_event_detect(9, GPIO.FALLING, callback=switch_event)
    
    
    def switch_event(channel):
        print('Edge detected on pin %s' % channel)
  • So nach längerem warten hab ich schließlich meinen PI wieder, und gleich mal das Script getestet.
    Nur wenn ich es ausführen möchte bekomme ich folgende Fehlermeldung :

    Traceback (most recent call last) :
    GPIO.add_event_detect(PIN_Input, GPIO.FALLING, callback=switch_event)

    NameError: name *switch_event* is not defined

    EDIT:
    Wobei unter :
    def switch_event(pin):
    print("Der Pin "+pin+" hat sich geaendert ")

    Es doch eigentlich definiert ist oder nicht?

    Einmal editiert, zuletzt von ERRORistMUSS (4. Dezember 2014 um 17:52)

  • Änder das Script mal wie folgt ab:

    Spoiler anzeigen

    Begründung: Die Funktion muss vor dem Aufruf/Festlegung definiert werden.


    Normalerweise ist der Aufbau eines Python Scripts auch etwas anders. Am Ende der Datei kommt eigentlich ein

    Code
    if __name__ == '__main__':

    in der man dann zB erst das Script tatsächlich ausführt.
    Also professionell/korrekt müsste es eigentlich so aussehen:

    Spoiler anzeigen
  • Jau haut mehr oder weniger hin.
    Bekomme zwar die Meldung :

    Code
    TypeError: cannot concatenate 'str' and 'int'  objects


    Mir fällt dabei auf, dass das Script nicht schnell genug arbeitet, denn für eine Umdrehung ist die Ausgabe des Scriptes sehr ruckartig. Mal viele Ausgaben auf einmal, oder keine.

    Ps. Danke für die super Erklärung der einzelnen befehle!

  • Nun ja, als "professionell/korrekt" würde ich es nicht annähernd bezeichnen :X

    Wenn ich schon das if __name__ == '__main__': Idiom verwende, dann verwende ich auch eine ''main()'' Funktion als Hauptprogramm und stopfe nicht alles blindlings in das Idiom. Und wie schon erwähnt, ist auch das ''raw_input()' völlig deplatziert.

    Edit:
    http://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs
    Sensor-Signal per Interrupt abfragen

  • Schade das du Fehler baust wo vorher keine waren @ bootsmann. pull_up_down darf man nicht weg lassen - wieso erklärte ich oben bereits. Und PIR_PIN sowohl global zu setzen als auch noch mal im callback zu verwenden wird auch Konflikte auslösen - wie ich bereits erwähnte wird der vom ISR ausgelöste Pin an die callback übergeben.. Also auch dein Code kann man nicht annähern als "professionell/korrekt" bezeichnen :fies:
    (und den 1.link nannte ich oben auch schon :D)

    Also, noch mal korrigiert:

  • Ich kenne zwar den verwendeten "Lichtschrankensensor" nicht, aber ich denke, dieser liefert als input eine '1" sofern der Strahl unterbrochen wird. Das von mir gezeigte Programm funktioniert perfekt mit einem PIR Sensor. Ein "Pull up / Pull down resistors" ist hier nicht vonnöten und auch nicht wirklich erklärt, denn mit ''GPIO.setup(PIR_PIN, GPIO.IN)'' ist der Pin bereits auf ''input''. Sonst einfach nochmals in der Doku nachlesen...

    Zitat

    Und PIR_PIN sowohl global zu setzen als auch noch mal im callback zu verwenden wird auch Konflikte auslösen

    Was für Konflikte?
    Edit:
    solange die PIR_PIN nicht global gesetzt wird nicht. Aber du hast insofern Recht, dass die add_event_detect() Methode die Pinnummer als Argument an die 'my_callback_one' Funktion liefert. Darum spielt es keine Rolle, wie der Parameter in der my_callback_one Funktion benannt wird. Das ist dann etwas unglücklich in der Doku beschrieben und es ist übersichtlicher, hier einen anderen Variablennamen zu verwenden.

    Der TE soll beide Programme mal testen und anschließend eine Rückmeldung geben.

    Edit:
    Mit

    Code
    GPIO.setup(PIR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    funktioniert mein PIR Sensor gar nicht nur mir

    Code
    GPIO.setup(PIR_PIN, GPIO.IN)
  • Hallo Leute,seid gegrüßt !

    Mit Interesse las ich hier alles durch zum Thema Drehzahlmessung, Impuls-Detektion, Frequenz-Messung.
    Dazu habe ich ein paar Fragen:

    Welche maximale Frequenz ist wohl mit den unterschiedlichen Pi's messbar ? Kann man das berechnen ?

    Mir ist klar, dass das natürlich vom Pi ansich (z.B. seiner Taktfrequenz) und auch vom ausgeführten Programm anhängig ist. Aus dem Grunde reicht mir auch eine "ungefähre Schätzung" unter Berücksichtigung der Verwendung von Interrupts zur Abfrage entsprechender GPIO's.

    DANKE

  • Das kommt darauf an welche Programmiersprache du verwendest, sowie welchen PI. Da gibt es nämlich signifikante Unterschiede die leider kaum jemand beachtet - viele programmieren lieber in der für sie am bequemsten Form :fies:


    Siehe dazu hier: http://codeandlife.com/2015/03/25/ras…gpio-benchmark/

    Die Messung drückt die Geschwindigkeit zwischen einem Flankenwechsel aus, also ein ständig möglichst schneller Wechsel zwischen HIGH und LOW.
    Genaue Details werden im älteren Benchmark-Artikel beschrieben: http://codeandlife.com/2012/07/03/ben…-pi-gpio-speed/

    Das Ergebnis dieses Benchmark ist, dass der Pi2 durchschnittlich 2x schneller mit GPIO umgehen kann als noch der Vorgänger Pi1 - und das mit der Standard Taktung.

    Beeinflusst werden kann das aber auch durch eine Überlappende Sonderfunktion des jeweilige GPIO's, wo man sicherstellen sollte das diese auch wirklich abgeschaltet ist.

  • Ich werde einen PI 2 verwenden.
    Danke für den Tip mit der übergeordneten Funktion der GPIO's. Da werd' ich also drauf achten, sowas abzuschalten, wenn's hinderlich sein sollte.
    Bei der Programmiersprache bin ich mir noch nicht sicher. Ich tendiere schon in Richtung C/C++, weil die Sprache für Arduinos auch eine C-angelehnte Variante ist und ich mit denen schon ein paar Miniprojekte hinbekommen habe.

    Wie mir aber scheint, hat das wohl noch niemand so wirklich ausprobiert, welche maximale Frequenz wohl messbar wäre.......

    EDIT: Bin gerade Deinem Link gefolgt und sehe mir die Benchmarks an !! Das sagt schonmal was aus, mit dem ich leben kann !!!

    DANKE an dieser Stelle nochmal !!!

    Einmal editiert, zuletzt von DJJuergen (13. September 2015 um 12:07)

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!