DistroEx Das war mir klar, dass das erst kürzlich neue Maintainer bekommen hat, deswegen ja die Vermutung, dass es wieder in die Distribution aufgenommen werden könnte in Beitrag #5. 🤓
Posts by __blackjack__
-
-
Naja Gordon hat darüber ja geschrieben: https://web.archive.org/web/2022040522…gpi-deprecated/
Ich denke das Problem war einfach ein fehlender E-Mail-Filter oder die Bereitschaft den nervigen Teil einfach wegzuklicken. 😈
-
Naja Fremdquellen und so, und wenn das nicht von der Linux-Distribution kommt, dann wird es dort ja eine Meinung über die Zuverlässigkeit geben, denn ich kann mir nicht vorstellen, das die das Paket leichten Herzens aus der Distribution genommen haben.
-
rodiboki Ergänzend zu dem was hyle schon geschrieben hat: as beim Import ist zum umbenennen — GPIO wird hier aber gar nicht umbenannt.
Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen und -werte, sondern eine Datenstruktur. Oft eine Liste.
Man kann einigen GPIO-Funktionen auch Sequenzen von Pin-Nummern übergeben statt da für jede Nummer einen einzelnen Aufruf zu machen. Und wo das nicht geht, kann man selbst eine Schleife oder eine „comprehension“ schreiben um da nicht zig mal die gleiche Abfrage für variierende Pin-Nummern machen zu müssen. Ungetestet:
Python
Display Morefrom RPi import GPIO PINS = [40, 38, 37, 36, 35, 33, 32, 31, 29, 22] def main(): try: GPIO.setmode(GPIO.BOARD) GPIO.setup(PINS, GPIO.IN, pull_up_down=GPIO.PUD_UP) while True: print(*map(GPIO.input, PINS)) finally: GPIO.cleanup() if __name__ == "__main__": main()
Edit: Abfrage so schnell wie möglich, Ausgabe aber nur wenn sich was geändert hat:
Python
Display Morefrom more_itertools import unique_justseen from RPi import GPIO PINS = [40, 38, 37, 36, 35, 33, 32, 31, 29, 22] def main(): try: GPIO.setmode(GPIO.BOARD) GPIO.setup(PINS, GPIO.IN, pull_up_down=GPIO.PUD_UP) states = iter(lambda: list(map(GPIO.input, PINS)), None) for state in unique_justseen(states): print(*state) finally: GPIO.cleanup() if __name__ == "__main__": main()
-
Das ist von WiringPi, WiringPi gibt's nicht mehr. Kommt vielleicht wieder, weil es Leute gibt die es weiterentwickeln, nachdem der Originalautor hingeschmissen hat. Aber momentan ist das kein Bestandteil von Raspi OS.
-
Gexle Was genau hast Du denn da jetzt nicht verstanden? Da steht ja jedes mal auf's neue warum das so nicht (mehr) geht. Mit einem Link zu weiteren Erklärungen. Und ganz grundsätzlich sollte man mal zu venvs was lesen.
-
-
Ich weiss gar nicht worauf das anspielen soll. Das sieht doch alles schön aufgeräumt aus. 🤡
-
Mit fertigen Algorithmus meine ich den Algorithmus den man üblicherweise verwendet wenn man Dinge, die Abhängigkeiten haben, so sortiert, das die Abhängigkeiten alle vorher vorkommen. Link zu Wikipedia:
Display Spoiler
Mir geht's auch um's Lösen und Lernen bzw. Üben. Wenn ich erkenne, dass man da eine Standardlösung anwenden kann, und weiss in welchem Modul es die gibt, dann erfinde ich das Rad nicht noch mal neu. Bei der mul(…,…)-Aufgabe hast Du doch wahrscheinlich auch das re-Modul verwendet und das nicht selbst noch mal neu implementiert‽ Das wäre ja auch so etwas wo man den regulären Ausdruck nehmen könnte, da einen Zustandsautomaten draus aufzeichnen könnte, um den dann selber in Code umzusetzen.
Den Sortieralgorithmus hatte ich auch im Studium schon mal implementieren müssen. Nach der Beschreibung in „Introduction to Algorithms“ (der „Cormen“). Das sind nur drei relativ simple Schritte, die voraussetzen, dass man eine Tiefensuche über den Graphen schreiben kann. Was ja letztlich auch kein Hexenwerk ist.
Bei Deinen rules (ich weigere mich ein my davor zu schreiben 😜) wäre collections.defaultdict() sinnvoll. Und statt append() und danach sort() könnte man bisect.insort() verwenden, was ein neues Element in eine sortiere Liste an der passenden Stelle einfügt. Oder man hängt erst einmal alles an und macht dann noch eine Schleife über die ganzen Listen und sortiert die jeweils einmal. Wobei die Listen später mit in-Tests verwendet werden, also eigentlich besser set()s wären.
mysort() sortiert nichts, das wäre vom Namen her besser compare_pages() oder etwas in der Richtung.
Das is_test-Flag sieht an vielen Stellen aus, als würde da logging mit einem Loglevel nachgebaut.
Mir gefällt die Strukturierung der Regeln nicht so ganz. Fester Satz an Schlüsseln die Zeichenketten sind, deutet in der Regel auf eine Klasse hin. Und ich hätte da auch eher Regeln = Seitennummer → (before=Seitennummern, after=Seitennummern) statt Regeln = (before=Seitennumer → Seitennummern, after=Seitennummer → Seitennummern) gemacht. Denn die beiden Wörterbücher haben ja die selben Schlüssel, sind also ”parallele” Datenstrukturen.
-
hanshesse1951 Wer hat denn bestätigt, dass das Programm okay ist?
Das Programm selber kann auch nicht unbedingt etwas dafür, das eventuell ein anderes Programm den Pin bereits zur Verwendung konfiguriert hat.
Das mit dem setwarnings() ist allerdings kein guter Rat. Statt Warnungen zu ignorieren, sollte man die Ursache heraus finden und beseitigen. Meistens reicht es einfach in den eigenen Programmen konsequent am Programmende hinter sich aufzuräumen, also GPIO.cleanup() in einem finally-Block auszuführen.
Oder man steigt von GPIO auf gpiozero um. Das kümmert sich selbst ums aufräumen am Programmende. Und blinken geht damit auch einfacher.
-
simonz Für Teil 2 von heute (Tag 5) selbst was geschrieben, oder ein Modul mit dem fertigen Algorithmus verwendet?
Der letzte Fehler den ich beseitigen musste, war dass der Hinweis ernst gemeint war, die Regeln die nicht vorhandene Seiten in der Aktualisierung betreffen zu ignorieren. In meinen Eingabedaten sind nämlich Regeln die sich widersprechen. Die Regeln 21|77, 22|21, und 77|22 sind nicht gleichzeitig erfüllbar. Die kommen aber zusammen auch in keiner meiner Aktualisierung vor. 🙂
-
VFR750 Die Meldung sagt doch, dass da zu viel Hintergrundrauschen ist und man eigentlich abbrechen sollte. Was hast Du denn gemacht um das Hintergrundrauschen zu reduzieren? Da werden ja Hinweise gegeben.
-
choose_music_randomly() ist ja ein bisschen wortreich umgesetzt:
Pythondef choose_music_randomly(sound_files): random_sound_file = random.choice(sound_files) return random_sound_file # Ist eigentlich nur das hier: choose_music_randomly = random.choice
Und da kann man die Funktion auch einfach weglassen, wenn man bloss ein Alias für random.choice() definiert.
Die Aufräumarbeiten bei den beiden Ausnahmebehandlungen sollten da nicht zweimal stehen, sondern einmal in einem finally-Block. Dann braucht man auch nicht allgemein jede Ausnahme behandeln und da einfach den Traceback unterdrücken. Ein mixer.quit() könnte man da noch hinzufügen.
Das Gegenstück zum mixer.quit() — mixer.init() — ist zwar idempotent, aber ich würde das trotzdem nur einmal aufrufen.
Im fadeout() ist das stop() schon enthalten. Dafür sollte man mit dem unload() warten bis der Ton tatsächlich ausgeblendet ist. Diese API ist asynchron, die ist ja für Hintergrundmusik gedacht. Oder man spart sich das unload() einfach.
Zwischenstand (ungetestet):
Python
Display More#!/usr/bin/env python3 import random from functools import partial from signal import pause from gpiozero import LED, MotionSensor from pygame import mixer LED_PIN = 11 PIR_PIN = 7 FADEIN_TIME = 1000 # ms FADEOUT_TIME = 1000 # ms def start_random_music(led, filenames): led.on() mixer.music.load(random.choice(filenames)) mixer.music.play(fade_ms=FADEIN_TIME) def stop_music(led): led.off() mixer.music.fadeout(FADEOUT_TIME) def main(): print("Start") sound_filenames = [ "path/to/file.mp3", "path/to/file.mp3", "path/to/file.mp3", "path/to/file.mp3", "path/to/file.mp3", ] led = LED(LED_PIN) try: mixer.init() pir = MotionSensor(PIR_PIN) pir.when_motion = partial(start_random_music, led, sound_filenames) pir.when_no_motion = partial(stop_music, led) pause() except KeyboardInterrupt: print("Programm wurde durch Benutzer abgebrochen") finally: led.off() mixer.music.stop() mixer.music.unload() mixer.quit() if __name__ == "__main__": main()
-
VFR750 Was heisst denn „funktioniert nicht“ konkret? Bei der Konfiguration die Du gezeigt hast, sind einigen Tasten die gleichen Codes zugeordnet. Konkret sind das
- 0x033C000001000398 → KEY_MENU, KEY_VOLUMEDOWN, KEY_RECORD
- 0x033E000001000705 → KEY_VOLUMEUP, KEY_INFO, KEY_CHANNELUP
- 0x033E000001000397 → KEY_3, KEY_CHANNELDOWN, KEY_MUTE
- 0x033F000001000397 → KEY_5, KEY_CONTEXT_MENU
Das sollte wohl eher nicht so sein‽
-
devil P Anmerkungen zum Hauptprogramm: time wird importiert, aber nirgends verwendet. Das auf Modulebene definierte n wird ($GOTT sei Dank) auch nirgends verwendet.
Bei den drei Pfadkonstanten sollte das im Namen stehen, dass es sich um Dateipfade handelt und beispielsweise CAR_IMAGE nicht für ein Autobild steht, sondern für den Pfad zu einer Datei. Es würden sich auch pathlib.Path-Objekte anbieten, statt Zeichenketten, dann könnte man auch den Pfad zu den drei Dateien als Konstante definieren und muss den nur einmal im Programm stehen haben.
Es macht überhaupt keinen Sinn etwas in der Form name: list = [] zu schreiben. Na klar ist das vom Typ list wenn man da eine leere Liste zuweist. Das sieht man auch ohne die Typannotation. Sowohl als menschlicher Leser, aber auch eine statische Typprüfung kann das ohne die Annotation problemlos erkennen. Also weg mit der Annotation. Als nächstes ist es aber auch in allen Fällen wo das steht unsinnig dem jeweiligen Namen eine leere Liste zuzuweisen, die nämlich in keinem der Fälle irgendwo verwendet wird.
Die Ausnahme”behandlung” beim laden von gepickelten Daten ist falsch. Da wird alles mit der Ausgabe " no file" und einem Programmende behandelt, so dass man da gar keine Chance hat herauszufinden was los ist wenn es die Datei gibt aber aus einem anderen Grund eine Ausnahme ausgelöst wird. Am besten lässt man die Behandlung hier einfach weg: Das Programm wird dann auch abgebrochen und man bekommt den tatsächlichen Grund. Und man hätte beim Aufrufer auch noch die Chance den Programmabbruch zu verhindern und die Ausnahme anders, sinnvoll zu behandeln. Ansonsten gehört ein Programmabbruch nicht in irgendwelche Hilfsfunktionen.
Öffnen von Dateien am besten immer mit der with-Anweisung zusammen. Dann wird der Code sicherer, einfacher, und man kann auch nicht die Klammern beim close()-Aufruf vergessen wie in save_image(). Wobei diese Funktion nirgends verwendet wird.
bewegung im Hauptprogramm macht als Variable keinen Sinn. Das wird an zwei stellen sinnlos auf False gesetzt, und nur an einer Stelle an einen sinnvollen Wert gebunden, der nur ein einziges mal gleich in der nächsten Zeile für eine if-Bedingung verwendet wird. Dafür braucht man das nicht an einen Namen binden. mov_roi ist ebenfalls überflüssig. Das wird eine Zeile weiter an buffer1 gebunden und sonst nicht verwendet, also kann man das gleich an buffer1 binden. buffer1 an eine Kopie von einem leeren buffer2 zu binden ist auch überflüssig, weil das nie verwendet wird. Beim nächsten Schleifendurchlauf wird buffer1 an das nächste Videobild gebunden.
Es sieht so ein bisschen so aus als wenn buffer1 und buffer2 immer zwei aufeinanderfolgende Videobilder sein sollten, beziehungsweise Ausschnitte daraus. Das liesse sich deutlich einfacher und eben auch deutlicher ausdrücken mit einem Iterator über die Bilder/Ausschnitte und itertools.pairwise(). Auch das erste Testbild würde dann verschwinden, weil man keine leeren Bilder mehr erzeugen muss und damit auch die Grösse dafür nicht kennen muss.
Bei crop_image() könnte man statt der magischen Indexzugriffe, den Inhalt des zweiten Arguments an sinnvolle Namen binden.
check_movement() würde ich als falsch beschreiben. Ja das macht was es soll, aber wenn man Numpy-Arrays hat, schreibt man keine verschachtelten Schleifen darüber um mit jedem einzelnen Wert das gleiche zu machen, sondern nutzt die Arrays so wie sie gedacht sind. x für die Zeilen und y für die Spalten ist auch ungewöhnlich bis verwirrend. Wenn man Numpy tatsächlich verwendet ist die Funktion ein Zweizeiler.
determine_car() macht zu viel. Das ermittelt nicht nur das Auto sondern ruft selbst dann auch noch eine weitere Funktion auf die ein Fenster öffnet und etwas anzeigt. Statt ein Ergebnis zu ermitteln und an den Aufrufer zurück zu geben.
Das erste Argument heisst colors es wird aber mit etwas aufgerufen was car_color (Einzahl) heisst. Das ist verwirrend. count ist auf deutsch Anzahl, das ist wohl auch falsch. Man kann enumerate() auch einen Startwert migeben, dann braucht man selbst keine 1 addieren. Das result-Flag macht keinen Sinn.
show_result() macht überhaupt nichts mit dem ersten Argument.
Ein Array mit mit einer RGB-Farbe kann man mit numpy.full() in einem Schritt erstellen, statt erst ein Array mit lauter 1en zu erstellen, und die dann mit den RGB-Werten zu überschreiben.
Zwischenstand (ungetestet):
Python
Display More#!/usr/bin/env python3 import pickle from itertools import pairwise from pathlib import Path import cv2 import numpy as np from camrecording import WebcamVideoStream BASE_PATH = Path("/home/pi/MyProjects/Camera/Mouse_Exprimente") ROI_POINT_FILE_PATH = BASE_PATH / "mov_region.dmp" CAR_IMAGE_FILE_PATH = BASE_PATH / "car_image.dmp" CAR_COLORS_FILE_PATH = BASE_PATH / "cars_color.dmp" THRESHOLD = 50 # min Unterschied im Pixelwert SENSITIVITY = 150 # max Anzahl unterschiedlicher Pixel RED_CHANNEL_DIFF = 80 COLOR_AREA_RECTANGLE = ((124, 344), (187, 375)) def load_pickled_object(file_path): with file_path.open("rb") as file: return pickle.load(file) def crop_image(image, rectangle): (x1, y1), (x2, y2) = rectangle return image[y1:y2, x1:x2] def check_movement(buffer_a, buffer_b): # # Just check the green channel as it's the highest quality channel. # difference = abs(buffer_a[:, :, 0] - buffer_b[:, :, 0]) return ((THRESHOLD < difference) & (difference < 200)).sum() > SENSITIVITY def get_dominant_color(image): pixels = image.reshape((-1, 3)) return pixels[np.bincount(pixels.argmax(axis=1)).argmax()] def determine_cars(colors, dominant_color): return ( (car_number, color) for car_number, color in enumerate(colors, 1) if abs(color[2] - dominant_color[2]) < RED_CHANNEL_DIFF ) def show_result(color, text): image = np.full((750, 1300, 3), color) cv2.putText( image, text, (450, 400), cv2.FONT_HERSHEY_SIMPLEX, 5, (255, 255, 255), 5, ) window_name = "Car Color" cv2.imshow(window_name, image) cv2.moveWindow(window_name, 0, 0) cv2.waitKey(3500) cv2.destroyWindow(window_name) def main(): try: roi_rectangle = load_pickled_object(ROI_POINT_FILE_PATH) print("roi_rectangle:", roi_rectangle) car_colors = load_pickled_object(CAR_COLORS_FILE_PATH) print("car_colors:", car_colors) video_stream = WebcamVideoStream().start() buffers = iter( lambda: crop_image(video_stream.read(), roi_rectangle), None ) for buffer, next_buffer in pairwise(buffers): if check_movement(buffer, next_buffer): for car_number, color in determine_cars( car_colors, get_dominant_color( crop_image(video_stream.read(), COLOR_AREA_RECTANGLE) ), ): show_result(color, f"Car{car_number}") if cv2.waitKey(3) & 0xFF == ord("q"): break finally: cv2.destroyAllWindows() if __name__ == "__main__": main()
-
Das ist Python 2, benutzt os.system() und omxplayer — das sollte man nicht erweitern, das sollte man neu schreiben. 🙂
-
Das W: am Anfang steht für Warning nicht Error.
-
Wird der Sound per Systemd-Unit abgespielt bevor der Desktop/die grafische Umgebung gestartet ist? Spielen andere Programme Sound ab? Wie sieht das mit der Lautstärkeeinstellung unter dem Desktop aus?
-
Wie wird denn im Programmcode ausgewählt worüber abgespielt werden soll?
-
Ja, ich bin dabei. Der Altair Emulator ist immer noch am Bubblesort der zweiten Liste für den ersten Aufgabenteil. Mal sehen ob das heute noch fertig wird.
Dass das Ergebnis richtig sein wird, weiss ich weil der DOS-PC mit QBasic das selbe Programm in ca. 2 Minuten abgearbeitet hatte. 8080 mit 2 Mhz vs. 486 mit 33 Mhz. 🤣