Files
vogelalbum/pi/README.md
T
christian cc9e0786c2 Vogelalbum 1.0.0
Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine
Sammlung, die sich von allein fuellt.

Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die
Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert —
ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische
Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten,
gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte,
Stufen und 15 Abzeichen.

Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto,
keine Telemetrie; die Bilder verlassen das eigene Netz nicht.

Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die
Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und
braucht deshalb weder eine erreichbare Adresse noch einen offenen Port —
er laeuft hinter einem gewoehnlichen Router.

Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home
Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen
je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs.

AGPL-3.0.
2026-10-14 18:55:23 +00:00

7.0 KiB
Raw Blame History

Die Kameraseite

Vogelalbum braucht von der Kamera nur eines: einen Stream, den der Detector lesen kann. Alles andere passiert woanders.

Der Pi erkennt nichts. Ein Pi Zero W schafft das Streamen, aber keine Inferenz — die beiden Modelle brauchen rund 30 ms auf einem N100 und ein Vielfaches davon auf einem Zero. Deshalb streamt der Pi, und der Detector läuft auf einer Maschine mit Rechenleistung im selben Netz.

Was du brauchst

  • Raspberry Pi mit Kameraanschluss — ein Zero W reicht und ist der sparsamste Weg. Ein Pi 4/5 kann zusätzlich den Detector mitlaufen lassen.
  • Ein Kameramodul. Getestet mit dem OV5647 (das ältere 5-MP-Modul).
  • Raspberry Pi OS (Bookworm oder neuer), WLAN eingerichtet.

Einrichten

sudo cp vogelkamera.py /opt/vogelkamera_server.py
sudo cp vogelkamera.service /etc/systemd/system/
sudo systemctl enable --now vogelkamera

journalctl -u vogelkamera -f

🔑 Kein zusätzliches Paket. Der Server ruft rpicam-vid auf, das bei Raspberry Pi OS ohnehin dabei ist, und liest dessen MJPEG-Ausgabe mit. Die Arbeit passiert im C-Programm; Python schiebt nur Bytes weiter. Auf einem Zero W ist das der Unterschied zwischen läuft und läuft nicht.

⚠️ Bis zum 25.09.2026 stand hier python3-picamera2 — für eine Fassung, die eleganter aussah und nie auf einem Pi gestartet worden war. Auf dem Gerät am Vogelhaus lief die ganze Zeit etwas anderes, und picamera2 war dort nicht einmal installiert. Auf den Punkt gebracht: im Repo lag Code, der nie getestet wurde.

Seitdem steht in diesem Ordner, was tatsächlich läuft — und zwar seit dem 28.08.2026 ununterbrochen.

Danach:

Adresse wozu
http://<pi>:8080/stream der MJPEG-Stream — das kommt in VOGELALBUM_SOURCE
http://<pi>:8080/ Vorschauseite im Browser
http://<pi>:8080/status Bildrate, Funkstärke, Temperatur, Laufzeit als JSON
http://<pi>:8080/temp CPU-Temperatur
http://<pi>:8080/gesund 200, sobald Bilder ankommen

Die drei unteren fassen den Stream nicht an — das ist der richtige Weg, eine laufende Kamera zu prüfen. /status liefert dieselben Felder wie die ESP32-Kameraseite (../esp32cam/), ein Werkzeug muss also nicht wissen, was am anderen Ende hängt.

Hängt die Kamera über Kopf: --vflip und --hflip stehen im Skript bereits im Aufruf von rpicam-vid (capture_loop). Beide zusammen ergeben die 180°-Drehung, einzeln spiegeln sie.

Drei Dinge, die man einmal falsch macht

Nur ein Zuschauer gleichzeitig

Ein Pi Zero W liefert über WLAN rund 2 Mbit/s. Ein zweiter Client teilt sich diese Strecke — und der Detector fällt in Timeouts, ohne dass irgendwo ein Fehler auftaucht. Die Bildrate halbiert sich einfach.

Das ist bei der Entwicklung zweimal passiert: einmal durch ein curl auf den Stream, mit dem eine Störung diagnostiziert werden sollte, und einmal, weil die Vorschauseite im Browser offen stand. Gemessen: 0,9 statt 1,9 Bilder pro Sekunde, solange ein zweites Fenster mitlas.

Deshalb weist vogelkamera.py einen zweiten Zuschauer mit 503 ab, statt beide schlecht zu bedienen. Wer selbst zuschauen will, hält kurz den Detector an — oder nimmt /snapshot, das kostet nur ein Bild.

⚠️ Bis zum 09.10.2026 stand das hier und stimmte nicht. Die Fassung, die auf dem Gerät läuft, hat den zweiten Zuschauer nur gezählt und beide bedient; abgewiesen hat ihn nur die ESP32-Kameraseite. Aufgefallen ist es erst beim Nachziehen der Tests, die seit dem 25.09. auf die alte Fassung zeigten. Seitdem tut der Code, was hier steht.

Ein Stream, der 15 Sekunden kein Bild liefert, wird beendet. Das klingt nebensächlich, ist aber die Bedingung dafür, dass die Absage überhaupt funktionieren darf: Ohne neues Bild schreibt der Server nichts und merkt deshalb nicht, dass der Zuschauer schon weg ist — der eine Platz bliebe für immer besetzt und die Kamera wäre durch ihre eigene Schutzsperre dauerhaft stumm. Der Detector verbindet von selbst neu.

Die Temperatur im Blick behalten

Ein Vogelhaus in der Sonne ist ein geschlossener Kasten. Gemessen an einem Septembernachmittag: 72 °C auf einem Pi Zero W. Ab etwa 80 °C drosselt der Pi von selbst, und die Bildrate fällt — ohne Fehlermeldung.

curl http://<pi>:8080/temp

Diese Abfrage kostet den Stream nichts, im Gegensatz zu einem Blick auf das Bild. ping taugt hier nicht als Gesundheitsprüfung: ICMP fällt bei gesättigtem WLAN als Erstes aus, während TCP weiterläuft — eine Entwarnung per ping ist Glück, keine Messung.

Das vollständige Bild gibt /status, ebenfalls ohne den Stream anzufassen:

curl -s http://<pi>:8080/status
{"kamera":"bereit","bilder":1751409,"fps":3.84,"letztes_bild_s":0.2,
 "fehler":"0x0","laufzeit_s":401238,"zuschauer":true,"rssi":-58,
 "temperatur_c":55.1,"speicher_frei":218103808}
Feld Gesund Woran es sonst liegt
fps stabil über Stunden fällt → zweiter Zuschauer, Funkstrecke, Hitze
rssi über −70 dBm darunter näher ran oder Antenne ausrichten
temperatur_c unter 70 °C geschlossener Kasten in der Sonne drosselt
letztes_bild_s unter 1 s steht → Kamera liefert nicht mehr
zuschauer true im Normalbetrieb false, obwohl der Detector läuft → er kommt nicht durch

bilder und fps zählen, was die Kamera liefert, nicht was ein Zuschauer abholt. Ein fps über null bei zuschauer: false heißt also: die Kamera arbeitet, es holt nur niemand ab.

Wer den Pi in einen geschlossenen Kasten baut: Schattenseite wählen, Öffnung für Luft lassen, und die Kamera nicht direkt neben den SoC setzen.

Die Bildrate hängt am Licht, nicht an der CPU

Über WLAN ist die Strecke bandbreitenbegrenzt. Ein helles, detailreiches Bild ergibt größere JPEGs und damit weniger Bilder pro Sekunde. Gemessen über neun Tage am selben Aufbau: nachts 6,2 fps (fast schwarzes Bild), tagsüber 3,7–4,2 fps.

Reicht die Bildrate nicht, ist --qualitaet der Hebel — nicht die Auflösung. Die Auflösung braucht man, damit der Vogel groß genug im Bild ist.

Wie groß muss der Vogel im Bild sein?

Das ist die Frage, an der ein Aufbau scheitert, und sie lässt sich vorher beantworten — ohne auf einen echten Vogel zu warten:

python -m detector karte --source http://<pi>:8080/stream --ziel karte.jpg

Der Detector montiert ein Vogelfoto über ein Raster von Positionen in aktuelle Bilder deiner Kamera und zeigt, wo er anspringt. Details im Kamera-Kapitel.

Die zwei Regeln, die dabei herausgekommen sind:

  • Ein Vogel braucht 5–8 % der Bildfläche. Vor ruhigem Hintergrund reichen 5 %, vor einem Baum im Wind sind es 8 %.
  • Die Landefläche gehört in die untere Bildmitte, nicht an den unteren Bildrand. Dort unten findet der Detektor auch einen großen Vogel nicht zuverlässig. Eine Kamera, die flach über das Futterbrett hinwegschaut, legt jeden Gast genau in diese tote Zone.