Python-Code ignoriert Bewegungsmelder und läuft dauerhaft in Endlos-Schleife

  • Ich bin ja kein Programmierer, aber so vielleicht:

    Ausgabe:

    Ohne Bewegung:

  • Python-Code ignoriert Bewegungsmelder und läuft dauerhaft in Endlos-Schleife? Schau mal ob du hier fündig wirst!

  • Die ersten beiden ``if`` sind komisch und machen das etwas schwerer nachzuvollziehen, weil da mehr Zustände entstehen als wenn man einfach auf Bewegung erkennt oder nicht prüfen würde, statt auf Bewegung nicht erkannt, und in dem Fall dann aber nach einer Sekunde Wartezeit noch mal auf Bewegung erkannt und da im nicht-erkannt Fall dann noch mal eine Sekunde zu warten. So wird jede zweite Sekunde geprüft ob nichts erkannt wird und jede andere zweite Sekunde ob etwas erkannt wird — solange nichts erkannt wird.

    Und `gpiozero` wurde ja schon mal erwähnt, da kann man sich einiges an Kommentaren (und wohl auch `print()`-Ausgaben) sparen, weil der Code selbst dadurch verständlicher wird.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • OK.

    Ich schreibe mal kurz nieder, was mit meinem bisherigen Code eigentlich beabsichtigt werden soll.

    Bei dem Sarg handelt es sich um eine Halloween-Animatronic, die - sofern es nicht in Strömen schüttet - draußen steht.

    Jetzt kommen Besucher vorbei, die den Bewegungsmelder auslösen.

    - Die Nebelmaschine springt an, um den Sarg mit Nebel zu füllen.

    - Der Sargdeckel wird zwei Mal kurz geöffnet und wieder geschlossen (so werden kleine Nebelschwaden aus dem Sarg "gepresst"). Dafür ist diese for-Schleife

    - Nach kurzer Pause (1 Sekunde) öffnet sich der Deckel komplett und der Sound wird parallel abgespielt. Durch das Öffnen des Deckels wird ein Skelett mit angehoben und guckt teilweise aus dem Sarg.

    - Jetzt geht ein LED-Stripe an, der auf blinkend eingestellt ist (Stroboskop-Effekt).

    - Dann geht der Deckel wieder zu und das Licht aus.

    Weil die Kinder diesen Effekt so toll finden, rennen die jetzt immer wieder vor dem Sarg rum und versuchen, den Effekt erneut aauszulösen.

    (Das kenne ich bereits von einer anderen Animatronic, die anders gesteuert wird, wo ich allerdings dasselbe Problem habe.)

    Das will ich aber nicht, weil:

    a) ich habe keine Langzeiterfahrung, wie lange die Technik das mitmacht und

    b) nach drei Programmdurchläufen ist die Nebelmaschine so weit runtergekühlt, dass sie nicht mehr nebeln kann

    Deshalb möchte ich, dass nach einem Programmablauf für drei Minuten (in der Zeit kann die Nebelmaschine nachheizen) weitere Detektionen des Bewegungsmelders nicht zu einem erneuten Programmstart führen.

    Nach Ablauf der drei Minuten ist der Bewegungsmelder dann wieder scharf und kann das Programm erneut auslösen. Danach wieder 3 Minuten Pause.

  • Teste doch mal nur den PIR-Sensor

    Jedoch habe ich bereits mehrere einfache Programme aus der Planungsphase (LED-Stripe einschalten, Sound-Datei abspielen, etc.) getestet

    Auch mit den langen Kabeln, die Du jetzt angeschlossen hast?

    Wenn der PIR dauerhaft auslöst wie jetzt, dann bringt Dir der gezeigte Code auch nichts, denn dann wird das nur eine Dauerschleife mit 180 Sekunden Pausen.

  • Und `gpiozero` wurde ja schon mal erwähnt, da kann man sich einiges an Kommentaren (und wohl auch `print()`-Ausgaben) sparen, weil der Code selbst dadurch verständlicher wird.

    Die Print-Ausgaben sind für mich nur zur Kontrolle, da ich das Programm auf dem Pi manuell über ein Netbook starte, wenn die Nebelmaschine erstmals so weit ist.

    Damit ich weiß an welcher Stelle sich das Programm aktuell befindet und ich nicht jedes Mal nach draußen laufen will um nachzusehen, sollen diese Print-Ausgaben dienen.

    Für gpiozero fehlt mir aktuell leider einfach die Zeit.

  • Auch mit den langen Kabeln, die Du jetzt angeschlossen hast?

    Wenn der PIR dauerhaft auslöst wie jetzt, dann bringt Dir der gezeigte Code auch nichts, denn dann wird das nur eine Dauerschleife mit 180 Sekunden Pausen.

    Die einfachen Programme habe ich nicht mit den längeren Kabeln getestet.

    Das eigentliche Programm habe ich dagegen auch mit kurzer Anbindung des BWM getestet. Hat leider auch keinen Effekt gehabt.

  • Ein Fehler ist da noch: das `sleep(INTERVALL)` ist falsch eingerückt. Und nach der Beschreibung auch das lange öffnen des Sarges.

    Vielleicht noch mal eine Generalüberholung mit `GPIO`:

    Da ist noch einiges auf Modulebene was in die Hauptfunktion gehört.

    Die Nummern bei den Relaisnamen gehören da nicht rein.

    `open` ist der Name einer eingebauten Funktion um Dateien zu öffnen, den sollte man nicht an etwas anderes binden.

    `GPIO.setup()` kann man a) mehr als einen Pin übergeben und b) auch einen Startzustand.

    Dann wiederholt sich ein Muster in dem Programm sehr oft: Pin schalten, warten, gleichen Pin entgegengesetzt schalten. Das liesse sich in eine Funktion heraus ziehen.

    Ungetestet:

    So schwierig ist `gpiozero` jetzt hier nicht wirklich, und es macht den Code halt leichter verständlich, weil Anschalten `on()` heisst, abschalten `off()` und zwar egal ob der Ausgang „active low“ oder „active high ist“, und prüfen ob der PIR aktiv ist heisst `is_active`. Das liest sich einfach flüssiger und man muss weniger nachdenken.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Hier mal das ganze mit `gpiozero`:

    Einerseits ist das nicht *soo* verschieden, andererseits ist `on()` und `off()` verständlicher als beim schalten von „active low“ immer dran denken zu müssen, und ``pir_sensor.is_active`` liest sich halt auch leichter als ``GPIO.input(PIR_SENSOR) == GPIO.HIGH``

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Man hätte nur add_event_detect durch wait_for_edge im ursprünglichen Code auswechseln müssen + die fehlerhafte Einrückung (falls es auch im ursprünglichen Code so ist).

    Doku gibt es hier: https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/

    Der Unterschied ist, dass add_event_detect ein Thread im Hintergrund startet, der dann immer eine Funktion aufruft, sobald eine Flanke erkannt wird. wait_for_edge startet keinen Thread, sondern blockiert so lange, bis die abfragende Flanke erkannt worden ist.

    Aber das ist nur das Low-Level-Backend, dass durch gpiozero genutzt wird.

  • Hallo,

    Ich habe zu dem Thema mal gegoogelt, aber leider keine Ansätze gefunden, wie ich das mit den Signalen des BWM (Zeit messen, Reaktionen ignorieren, etc.) oder eine Klasse schreiben lösen könnte.

    der Vollständigkeits halber, um das hier nicht unbeantwortet zu lassen und um vorzubeugen, dass andere User die 'sleep'-Methode nicht nutzen, wenn es um zeitkritischere Aktionen geht, hier mein Vorschlag zur Zeitmessung und ignorieren von Aktionen.

    Allerdings ist das als Konzenpt/Grundgerüst zu sehen und löst nicht die Aufgabenstellung hier. Dafür gibt es hier schon Code der die Aufgabenstellung gut und genau genug löst.

    Wie man sieht, wird für Zeitmessungen 'monotonic' verwendet. Wenn eine Bewegung erkannt wird, wird der Zeitpunkt "in der Klasse gespeichert" beim der nächsten Bewegung wird vom aktuellen Zeitpunkt der gespeicherte Zeitpunkt abgezogen. Ist die Differenz kleiner wie der Intervall, dann passiert einfach nichts.

    Falls doch mal Zeit und Lust aufkommt, etwas tiefer in Python einzusteigen:

    https://docs.python.org/3/tutorial/

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Moin zusammen,

    ich habe es gestern zeitlich leider nicht geschafft, die verschiedenen Ansätze auszuprobieren.

    Werde mich heute Nachmittag dann dransetzen.

    @ DeaD_EyE

    An welcher Stelle ist die Einrückung verkehrt?

    Sähe der Code (Teil) dann so aus:

    Code
    try:
        GPIO.wait_for_edge(PIR_Sensor, GPIO.RISING, timeout=180000)
        while True:
            #time.sleep(180)  # 180 Sekunden kein erneutes Auslösen ermöglichen

    Grüße

    PeerT

  • An welcher Stelle ist die Einrückung verkehrt?

    Sähe der Code (Teil) dann so aus:

    Da wäre sich richtig, sofern keine Flankenerkennung im Hintergrund läuft. Momentan ist es auskommentiert (#).

    Oben drüber nutzt du ja wait_for_edge, nur an einer völlig falschen Stelle und das auch noch mit timeout.


    Erst bei der Abfrage mittels wait_for_edge wird die Flanke des Eingang (fallende oder steigende) abgefragt.

    Das muss dann in die while-Schleife.

    Ich vermute mal, dass es dir darum geht, dass durch das Aktivieren der Lichter der PIR nicht erneut ausgelöst werden kann und sich das unendlich wiederholt. Wenn man eine feste Reihenfolge hat, ist das ziemlich einfach.

    In einer Schleife:

    • 180 Sekunden warten und nichts darf in diesem Zeitraum passieren.
    • nun unendlich lange auf eine Flanke des PIR warten (kein Callback)
    • nun die Lichter einschalten, blinken ausschalten usw.
    • Wieder zum Anfang

    Wenn du stattdessen ein Callback verwendest, musst du dir auch die Zeit merken, wann der Callback aufgerufen worden ist, damit folgende Flankenerkennungen (plural) so lange ignoriert werden, bis die Zeit abgelaufen ist. Das ist vom Aufbau und Denkmodell etwas komplizierter, da man keinen linearen Verlauf mehr hat und der Callback zu jeder Zeit kommen kann. Beispiele sind in diesem Thread bereits mit gpiozero gepostet worden.

    Fürs bessere Verständnis:

    Das Äquivalent bei gpiozero für wait_for_edge ist: https://gpiozero.readthedocs.io/en/stable/api_…wait_for_active

Jetzt mitmachen!

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