.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_fun_camera:
4.1 Kamera
===================
**Einführung**
Dieses Projekt zeigt, wie Sie mit dem Raspberry Pi Zero ein einfaches Kamerasystem mit Auslösetaste erstellen können. Wenn Sie die Taste drücken, nimmt die Kamera ein Foto auf, und eine LED blinkt zur Bestätigung der Aktion. Dieses Projekt ist eine gute Möglichkeit, praktische Erfahrung mit GPIO-Steuerung und dem Raspberry-Pi-Kameramodul zu sammeln.
----------------------------------------------
**Was Sie benötigen**
Für dieses Projekt werden die folgenden Komponenten benötigt:
.. list-table::
:widths: 30 20
:header-rows: 1
* - KOMPONENTENBESCHREIBUNG
- KAUFLINK
* - :ref:`cpn_breadboard`
- |link_breadboard_buy|
* - :ref:`cpn_wires`
- |link_wires_buy|
* - :ref:`cpn_resistor`
- |link_resistor_buy|
* - :ref:`cpn_led`
- |link_led_buy|
* - :ref:`cpn_button`
- |link_button_buy|
* - :ref:`cpn_camera_module`
- |link_camera_buy|
* - :ref:`cpn_fusion_hat`
- \-
* - Raspberry Pi
- \-
----------------------------------------------
**Schaltplan**
Nachfolgend ist die GPIO-Pinbelegung für dieses Projekt dargestellt:
.. image:: img/fzz/4.1.1_sch.png
:width: 80%
:align: center
----------------------------------------------
**Verdrahtungsdiagramm**
#. Um das Kameramodul bequem zu verwenden, wird :ref:`assemble_fusion_hat_pan_tilt` empfohlen.
.. note::
Das Zusammenbauen der Pan-Tilt-Halterung kann einige Pins verdecken. Daher wird empfohlen, sie nur bei Verwendung der Kamera zu montieren oder sie nach der Montage außen zu platzieren.
.. image:: ../quick_start/img/gimbal_assemble.png
#. Folgen Sie diesem Verdrahtungsdiagramm, um die Schaltung aufzubauen:
.. image:: img/fzz/4.1.1_bb.png
:width: 80%
:align: center
----------------------------------------------
**Beispiel ausführen**
#. Greifen Sie auf den Raspberry-Pi-Desktop zu:
* :ref:`remote_desktop`: Verwenden Sie **VNC** für eine vollständige Desktop-Umgebung.
* |link_rpi_connect|: Verwenden Sie **Raspberry Pi Connect**, um sicher über einen Browser auf Ihren Pi zuzugreifen.
#. Öffnen Sie ein Terminal und wechseln Sie in den Code-Ordner:
.. raw:: html
.. code-block:: shell
cd ~/ai-lab-kit/python
#. Führen Sie das Skript aus, um die Kamera zu starten:
.. raw:: html
.. code-block:: shell
sudo python3 4.1_Camera.py
#. Nach dem Start des Programms beginnt die Kamera zu arbeiten.
- Drücken Sie die Taste, um ein Foto aufzunehmen:
- Die LED leuchtet auf.
- Ein Foto wird aufgenommen und im Ordner ``Pictures`` gespeichert
(zum Beispiel ``photo_001.jpg``, ``photo_002.jpg``).
- Lassen Sie die Taste los:
- Die LED erlischt.
- Das Programm läuft weiter, bis Sie **Ctrl + C** drücken. Danach wird es
ohne Fehlermeldung sauber beendet.
.. note::
Die QT-Vorschau erfordert eine Desktop-Umgebung.
Wenn die Vorschau nicht gestartet werden kann (z. B. bei Zugriff über SSH),
kann die Kamera dennoch Fotos aufnehmen und normal speichern.
----------------------------------------------
**Code**
Hier ist der in diesem Projekt verwendete Python-Code:
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
import os
import time
import threading
from picamera2 import Picamera2, Preview
from fusion_hat.pin import Pin, Mode, Pull
# Resolve the correct user's home directory (works with sudo)
REAL_USER = os.getenv("SUDO_USER") or os.getlogin()
USER_HOME = f"/home/{REAL_USER}"
PICTURES_DIR = os.path.join(USER_HOME, "Pictures")
os.makedirs(PICTURES_DIR, exist_ok=True)
# Initialize camera
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={"size": (800, 600)}))
# Photo counter with thread safety
photo_index = 1
photo_lock = threading.Lock()
# Track whether preview was started successfully
preview_started = False
# Initialize LED and button
led = Pin(17, mode=Mode.OUT)
button = Pin(4, mode=Mode.IN, pull=Pull.DOWN)
def take_photo():
"""Capture one photo and increment the index."""
global photo_index
with photo_lock:
filepath = os.path.join(PICTURES_DIR, f"photo_{photo_index:03d}.jpg")
print(f"\nCapturing: {filepath}")
camera.capture_file(filepath)
print("Saved.")
photo_index += 1
def main():
global preview_started
# Start preview only when a GUI display is available (remote SSH often has no DISPLAY)
preview_started = False
if os.getenv("DISPLAY"):
try:
camera.start_preview(Preview.QT)
preview_started = True
except Exception as e:
preview_started = False
print(f"Preview start failed (continue without preview): {e}")
else:
print("No DISPLAY detected (running headless without preview).")
camera.start()
print("Camera is running.")
print("Press the button to take a photo.")
print(f"Photos will be saved to: {PICTURES_DIR}")
print("Press Ctrl+C to exit.\n")
try:
while True:
if button.value(): # Button pressed (HIGH)
led.on() # LED on
take_photo() # Take photo
time.sleep(0.3) # Simple debounce (avoid multiple shots)
while button.value(): # Wait until button is released
time.sleep(0.01)
led.off() # LED off after release
time.sleep(0.01)
except KeyboardInterrupt:
print("\nExiting...")
finally:
# Turn off LED
try:
led.off()
except Exception:
pass
# Stop the camera first
try:
camera.stop()
except Exception:
pass
# Stop preview only if it was started
if preview_started:
try:
camera.stop_preview()
except Exception:
pass
try:
camera.close()
except Exception:
pass
if __name__ == "__main__":
main()
------------------------------------------
**Code verstehen**
1. **Importe und Zweck**
.. code-block:: python
import os
import time
import threading
from picamera2 import Picamera2, Preview
from fusion_hat.pin import Pin, Mode, Pull
Diese Module sind für folgende Aufgaben zuständig:
- ``os``: zum Umgang mit Umgebungsvariablen und Dateipfaden.
- ``time``: für Zeitverzögerungen und um die Hauptschleife am Laufen zu halten.
- ``threading``: sorgt dafür, dass das Aufnehmen von Fotos thread-sicher erfolgt.
- ``Picamera2`` und ``Preview``: zur Steuerung der Raspberry-Pi-Kamera und der Vorschau-Modi.
- ``Pin``, ``Mode`` und ``Pull``: zur Steuerung der LED und zum Auslesen des Tasters über das Fusion HAT.
2. **Bestimmen des Speicherverzeichnisses (funktioniert mit sudo)**
.. code-block:: python
REAL_USER = os.getenv("SUDO_USER") or os.getlogin()
USER_HOME = f"/home/{REAL_USER}"
PICTURES_DIR = os.path.join(USER_HOME, "Pictures")
os.makedirs(PICTURES_DIR, exist_ok=True)
Dieser Abschnitt stellt sicher, dass Fotos immer im Verzeichnis
``~/Pictures`` des tatsächlichen Benutzers gespeichert werden:
- Wenn das Skript mit ``sudo`` ausgeführt wird, verweist ``SUDO_USER`` auf den ursprünglichen Benutzer.
- Wenn ``sudo`` nicht verwendet wird, wird der aktuell angemeldete Benutzer verwendet.
- Der Ordner ``Pictures`` wird automatisch erstellt, falls er noch nicht existiert.
3. **Initialisierung und Konfiguration der Kamera**
.. code-block:: python
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={"size": (800, 600)}))
Die Kamera wird initialisiert und für Vorschau und Fotoaufnahme konfiguriert:
- Es wird eine Vorschauauflösung von ``800 × 600`` verwendet.
- Diese Konfiguration funktioniert sowohl mit als auch ohne sichtbares Vorschaufenster.
4. **Fotoindex und Thread-Sicherheit**
.. code-block:: python
photo_index = 1
photo_lock = threading.Lock()
- ``photo_index`` speichert die aktuelle Bildnummer.
- ``photo_lock`` verhindert, dass mehrere Tastendrücke gleichzeitig überlappende Fotoaufnahmen auslösen.
5. **Status der Vorschau**
.. code-block:: python
preview_started = False
Dieses Flag speichert, ob ein Vorschaufenster erfolgreich gestartet wurde,
sodass das Programm nur dann versucht, die Vorschau zu beenden, wenn sie tatsächlich existiert.
6. **LED- und Tasterkonfiguration**
.. code-block:: python
led = Pin(17, mode=Mode.OUT)
button = Pin(4, mode=Mode.IN, pull=Pull.DOWN)
- Die LED ist mit GPIO 17 verbunden und als Ausgang konfiguriert.
- Der Taster ist mit GPIO 4 verbunden und als Eingang mit internem Pull-Down-Widerstand konfiguriert.
- Ein HIGH-Signal bedeutet, dass die Taste gedrückt wurde.
7. **Funktion zum Aufnehmen eines Fotos**
.. code-block:: python
def take_photo():
"""Capture one photo and increment the index."""
global photo_index
with photo_lock:
filepath = os.path.join(PICTURES_DIR, f"photo_{photo_index:03d}.jpg")
print(f"\nCapturing: {filepath}")
camera.capture_file(filepath)
print("Saved.")
photo_index += 1
Diese Funktion:
- erstellt einen Dateinamen wie ``photo_001.jpg``.
- nimmt ein Bild mit der Kamera auf.
- erhöht den Fotoindex, sodass das nächste Bild einen neuen Namen erhält.
8. **Bedingte Vorschau für lokale und entfernte Nutzung**
.. code-block:: python
preview_started = False
if os.getenv("DISPLAY"):
try:
camera.start_preview(Preview.QT)
preview_started = True
except Exception as e:
preview_started = False
print(f"Preview start failed (continue without preview): {e}")
else:
print("No DISPLAY detected (running headless without preview).")
- Wenn eine grafische Desktop-Umgebung verfügbar ist, wird eine QT-Vorschau gestartet.
- Wenn das Programm über SSH ohne Anzeige ausgeführt wird, wird die Vorschau übersprungen.
- Die Fotoaufnahme funktioniert in beiden Fällen normal.
9. **Hauptschleife und Tasterlogik**
.. code-block:: python
try:
while True:
if button.value(): # Button pressed (HIGH)
led.on() # LED on
take_photo() # Take photo
time.sleep(0.3) # Simple debounce (avoid multiple shots)
while button.value(): # Wait until button is released
time.sleep(0.01)
led.off() # LED off after release
time.sleep(0.01)
Innerhalb der Hauptschleife:
- Beim Drücken der Taste wird die LED eingeschaltet.
- Sofort wird ein Foto aufgenommen.
- Das Programm wartet, bis die Taste losgelassen wird, um Mehrfachaufnahmen zu vermeiden.
- Nach dem Loslassen der Taste wird die LED ausgeschaltet.
10. **Sauberes Beenden und Freigabe der Ressourcen**
.. code-block:: python
except KeyboardInterrupt:
print("\nExiting...")
finally:
# Turn off LED
try:
led.off()
except Exception:
...
Wenn ``Ctrl+C`` gedrückt wird:
- Die LED wird ausgeschaltet.
- Die Kamerapipeline wird gestoppt.
- Die Vorschau wird nur beendet, wenn sie zuvor gestartet wurde.
- Die Kameraressourcen werden sauber freigegeben.
Dadurch wird sichergestellt, dass das Programm ohne Fehler beendet wird – sowohl bei lokaler Ausführung mit Bildschirm als auch im Headless-Modus.
----------------------------------------------
**Fehlerbehebung**
1. **Foto wird nicht aufgenommen**:
- **Ursache**: Der Taster ist falsch angeschlossen oder die Kamera wurde nicht initialisiert.
- **Lösung**:
- Stellen Sie sicher, dass der Taster mit GPIO-Pin 4 und GND verbunden ist.
- Prüfen Sie, ob die Kamera korrekt angeschlossen und über ``raspi-config`` aktiviert ist.
2. **LED blinkt nicht**:
- **Ursache**: Falsche Verdrahtung der LED oder falsche GPIO-Konfiguration.
- **Lösung**:
- Stellen Sie sicher, dass die LED mit GPIO-Pin 17 über einen passenden Widerstand verbunden ist.
- Testen Sie die LED separat, um sicherzustellen, dass sie funktioniert.
3. **Skript stürzt mit einem Kamerafehler ab**:
- **Ursache**: Das Kameramodul wird nicht erkannt oder wird von einem anderen Prozess verwendet.
- **Lösung**:
- Stellen Sie sicher, dass die Kamera korrekt angeschlossen ist, und starten Sie den Raspberry Pi neu.
- Prüfen Sie auf Konflikte mit ``sudo lsof /dev/video*``.
----------------------------------------------
**Erweiterungsideen**
1. **Mehrere Fotos**: Erlauben Sie das Aufnehmen mehrerer Fotos in einer Sitzung, jeweils mit einem eindeutigen Dateinamen:
.. code-block:: python
counter = 0
camera.capture_file(f'{user_home}/photo_{counter}.jpg')
counter += 1
2. **Videoaufnahme**: Erweitern Sie die Funktion, sodass beim Drücken der Taste ein Video aufgenommen wird:
.. code-block:: python
camera.start_recording(f'{user_home}/my_video.h264')
time.sleep(10)
camera.stop_recording()
3. **LED-Statusanzeige**: Verwenden Sie die LED, um den Status der Kamera anzuzeigen:
- Dauerlicht: Kamera bereit.
- Blinken: Foto wird aufgenommen.
4. **Fotoverwaltung**: Organisieren Sie aufgenommene Fotos in Ordnern nach Datum oder Ereignis.
5. **Zeitrafferfotografie**: Nehmen Sie in regelmäßigen Abständen Fotos auf, um einen Zeitraffer zu erstellen:
.. code-block:: python
for i in range(10):
camera.capture_file(f'{user_home}/timelapse_{i}.jpg')
time.sleep(5)
----------------------------------------------
**Fazit**
Dieses Projekt zeigt eine grundlegende Kameraanwendung mit einem tastergesteuerten Auslösemechanismus. Es kombiniert GPIO-Steuerung mit der Picamera2-Bibliothek und demonstriert, wie interaktive Raspberry-Pi-Projekte erstellt werden können. Experimentieren Sie weiter, um zusätzliche Funktionen zu integrieren und noch vielseitigere Anwendungen zu entwickeln.