Files
pizerovoice/README.md
2026-01-18 15:45:12 +00:00

36 KiB

Raspberry Pi Zero 2W mit Seed Studio ReSpeaker v1.2 - Komplette Anleitung

Offline-Sprachsteuerung mit Soundausgabe

Status: Vollständige Standalone-Konfiguration mit systemctl-Services Zielgruppe: Anfänger bis Fortgeschrittene Geschrieben: Januar 2025


TEIL 1: HARDWARE-VORBEREITUNG UND OS-INSTALLATION

1.1 Hardware-Anforderungen

Du benötigst folgende Komponenten:

  • Raspberry Pi Zero 2W (nicht Zero oder Zero W!)
  • Seed Studio ReSpeaker Pi Hat v1.2 mit 2 Mikrofonen
  • microSD-Karte: mindestens 16GB (Class 10 empfohlen)
  • USB-Stromversorgung: 2A Netzteil mit Micro-USB
  • SD-Kartenleser zum Flashen auf deinem PC
  • Computer mit Internetverbindung
  • Optional: HDMI-Monitor, USB-Tastatur/Maus zur Direktverbindung

1.2 Raspberry Pi OS Installation

Schritt 1: Raspberry Pi Imager herunterladen

  1. Öffne https://www.raspberrypi.com/software/
  2. Lade Raspberry Pi Imager für dein Betriebssystem herunter
  3. Installiere die Anwendung

Schritt 2: SD-Karte flashen

  1. Stecke die microSD-Karte in deinen Kartenleser und verbinde ihn mit dem PC
  2. Öffne Raspberry Pi Imager
  3. Klicke auf "Gerät auswählen" und wähle "Raspberry Pi Zero 2"
  4. Klicke auf "Betriebssystem auswählen" und wähle:
    • Raspberry Pi OS (Legacy, 32-Bit) - Bullseye
    • Hinweis: v1.2 des ReSpeaker-Hats ist am besten mit Bullseye kompatibel
  5. Klicke auf "Speicher auswählen" und wähle deine microSD-Karte
  6. Klicke auf das Zahnrad-Symbol (Erweiterte Optionen) und konfiguriere:
    • Hostname: respeaker-pi (oder ein beliebiger Name)
    • SSH aktivieren: Ja (mit Passwort)
    • Benutzername: pi
    • Passwort: Ein sicheres Passwort deiner Wahl (aufschreiben!)
    • WLAN konfigurieren:
      • SSID: Dein WiFi-Netzwerk
      • Passwort: Dein WiFi-Passwort
      • Land: DE (Deutschland)
    • Zeitzone: Europe/Berlin
  7. Klicke "Speichern" und dann "Schreiben"
  8. Warte bis zum Abschluss (5-10 Minuten)
  9. Entnehme die SD-Karte

Schritt 3: Erster Start

  1. Stecke die SD-Karte in den Pi Zero 2W ein
  2. Verbinde den ReSpeaker Hat mit dem Pi:
    • Ausrichten der GPIO-Pins (40-polige Reihe)
    • ReSpeaker sollte fest aufgesetzt sein
  3. Verbinde die USB-Stromversorgung
  4. Warte 1-2 Minuten für den ersten Start

Schritt 4: IP-Adresse ermitteln

Methode A (über Router):

  1. Öffne die Verwaltungsoberfläche deines WLAN-Routers
  2. Suche nach verbundenen Geräten
  3. Notiere die IP-Adresse von respeaker-pi

Methode B (mit arp-scan auf Linux/Mac):

sudo arp-scan -l | grep -i "raspberry\|b8:27"

Methode C (mit Advanced IP Scanner auf Windows):

  1. Lade Advanced IP Scanner herunter
  2. Scanne dein Netzwerk
  3. Suche nach respeaker-pi

Beispiel-IP: 192.168.1.100


TEIL 2: RASPBERRY PI KONFIGURATION

2.1 SSH-Verbindung herstellen

Auf Linux/Mac (Terminal):

ssh pi@192.168.1.100
# Gib dein Passwort ein

Auf Windows: Nutze PuTTY

  1. Lade PuTTY herunter
  2. Öffne PuTTY
  3. Host: 192.168.1.100
  4. Port: 22
  5. Klicke "Open"
  6. Login: pi
  7. Passwort: (dein gesetztes Passwort)

2.2 System aktualisieren

sudo apt update
sudo apt upgrade -y
sudo apt install -y git python3-pip python3-dev libatlas-base-dev

Dieser Prozess dauert 10-15 Minuten. Warte ab.

2.3 Audio- und I2C-Konfiguration

sudo raspi-config

Navigiere zu:

  1. SchnittstellenI2CAktivieren
  2. SchnittstellenSPIAktivieren
  3. LokalisierungsoptionenZeitzoneEuropaBerlin
  4. LokalisierungsoptionenSprachede_DE.UTF-8
  5. Bestätige alle Änderungen und Starten Sie neu.

Nach dem Reboot erneut verbinden:

ssh pi@192.168.1.100

TEIL 3: RESPEAKER HAT INSTALLATION

3.1 seeed-voicecard Treiber installieren

Der ReSpeaker benötigt die speziellen Seeed-Treiber:

cd ~
git clone https://github.com/HinTak/seeed-voicecard.git
cd seeed-voicecard
sudo ./install.sh

Dies dauert 5-10 Minuten. Wenn es fertig ist:

sudo reboot now

Nach dem Reboot wieder verbinden:

ssh pi@192.168.1.100

3.2 ReSpeaker-Installation verifizieren

Überprüfe, ob die Sound-Karte erkannt wurde:

aplay -l

Du solltest folgende Ausgabe sehen:

**** PLAYBACK hardware devices ****
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
card 1: seeed2miccard [seeed-2mic-voicecard], device 0: ...

Überprüfe auch die Aufnahmegeräte:

arecord -l

Du solltest auch hier seeed-2mic-voicecard sehen.

3.3 Ton-Tests

Lautsprecher testen:

speaker-test -l 1 -c 2 -t sine -f 1000 -r 48000

Du solltest einen Ton hören (drücke Ctrl+C zum Beenden).

Mikrofon testen:

arecord -D hw:1,0 -f S16_LE -c 2 -r 48000 test_recording.wav

Sprich kurz ins Mikrofon und stoppe mit Ctrl+C. Dann abspielen:

aplay -D hw:1,0 test_recording.wav

TEIL 4: PYTHON-UMGEBUNG EINRICHTEN

4.1 Notwendige Python-Pakete installieren

# Audio-Verarbeitung
sudo apt install -y portaudio19-dev libatlas-base-dev
pip3 install pyaudio

# Spracherkennung
pip3 install SpeechRecognition
pip3 install pydub

# Offline-Spracherkennung mit Vosk
pip3 install vosk

# Sprachmodell für Deutsch (kleines Modell, ~50MB)
mkdir -p ~/voice_models
cd ~/voice_models
wget https://alphacephei.com/vosk/models/vosk-model-de-0.21.zip
unzip vosk-model-de-0.21.zip
rm vosk-model-de-0.21.zip

4.2 Ordnerstruktur erstellen

mkdir -p ~/voice_assistant
mkdir -p ~/voice_assistant/sounds
mkdir -p ~/voice_assistant/logs
mkdir -p ~/voice_assistant/config
cd ~/voice_assistant

TEIL 5: PYTHON-SKRIPTE ERSTELLEN

5.1 Haupt-Spracherkennungs-Skript

Erstelle die Datei ~/voice_assistant/speech_recognizer.py:

nano ~/voice_assistant/speech_recognizer.py

Kopiere folgenden Code ein:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Offline-Spracherkennungssystem für Raspberry Pi Zero 2W mit ReSpeaker Hat v1.2
Funktioniert vollständig offline mit lokalem Vosk-Modell
"""

import json
import os
import sys
import logging
import queue
import sounddevice as sd
import vosk
import subprocess
import time
from pathlib import Path

# ============================================================================
# KONFIGURATION
# ============================================================================

class Config:
    # Pfade
    BASE_DIR = Path(__file__).parent
    MODEL_PATH = Path.home() / "voice_models" / "vosk-model-de-0.21"
    SOUNDS_DIR = BASE_DIR / "sounds"
    LOGS_DIR = BASE_DIR / "logs"
    
    # Audio-Einstellungen
    SAMPLERATE = 16000
    BLOCKSIZE = 4096
    DEVICE_INDEX = None  # Wird automatisch erkannt
    
    # Spracherkennung
    ACTIVATION_WORD = "computer"  # Aktivierungswort
    CONFIDENCE_THRESHOLD = 0.5
    LISTENING_TIMEOUT = 10  # Sekunden zum Abhören nach Aktivierungswort
    
    # Logging
    LOG_FILE = LOGS_DIR / "voice_assistant.log"
    LOG_LEVEL = logging.INFO
    
    # Kommandos und Aktionen
    COMMANDS = {
        "hello": {"sound": "hello.wav", "action": "greet"},
        "musik": {"sound": "music.wav", "action": "play_music"},
        "stopp": {"sound": "stopped.wav", "action": "stop"},
        "zeit": {"sound": "time.wav", "action": "tell_time"},
        "helligkeit": {"sound": "brightness.wav", "action": "adjust_brightness"},
    }

# ============================================================================
# LOGGING-SETUP
# ============================================================================

def setup_logging():
    """Konfiguriere Logging in Datei und Console"""
    Config.LOGS_DIR.mkdir(exist_ok=True)
    
    logger = logging.getLogger("VoiceAssistant")
    logger.setLevel(Config.LOG_LEVEL)
    
    # File Handler
    fh = logging.FileHandler(Config.LOG_FILE)
    fh.setLevel(Config.LOG_LEVEL)
    
    # Console Handler
    ch = logging.StreamHandler()
    ch.setLevel(Config.LOG_LEVEL)
    
    # Formatter
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    fh.setFormatter(formatter)
    ch.setFormatter(formatter)
    
    logger.addHandler(fh)
    logger.addHandler(ch)
    
    return logger

logger = setup_logging()

# ============================================================================
# AUDIO-GERÄTE MANAGEMENT
# ============================================================================

def find_respeaker_device():
    """
    Suche nach ReSpeaker-Audiogerät und gebe seinen Index zurück
    """
    logger.info("Suche nach ReSpeaker-Gerät...")
    
    try:
        for index, name in enumerate(sd.query_devices()):
            if isinstance(name, dict):
                device_name = name.get('name', '')
            else:
                device_name = str(name)
            
            if 'seeed' in device_name.lower():
                logger.info(f"ReSpeaker gefunden: Index {index}, Name: {device_name}")
                return index
    except Exception as e:
        logger.error(f"Fehler bei der Gerätesuche: {e}")
    
    logger.warning("ReSpeaker nicht gefunden, nutze Standard-Eingang")
    return None

def list_audio_devices():
    """Zeige alle verfügbaren Audio-Geräte an"""
    logger.info("Verfügbare Audio-Geräte:")
    for index, name in enumerate(sd.query_devices()):
        if isinstance(name, dict):
            device_name = name.get('name', 'Unknown')
        else:
            device_name = str(name)
        logger.info(f"  {index}: {device_name}")

# ============================================================================
# SPRACHERKENNUNG
# ============================================================================

class SpeechRecognizer:
    """
    Offline-Spracherkennung mit Vosk
    """
    
    def __init__(self):
        """Initialisiere Spracherkenner"""
        logger.info("Initialisiere Spracherkenner...")
        
        # Überprüfe ob Modell existiert
        if not Config.MODEL_PATH.exists():
            logger.error(f"Modell nicht gefunden: {Config.MODEL_PATH}")
            logger.error("Bitte installiere das Vosk-Modell (siehe Dokumentation)")
            raise FileNotFoundError(f"Vosk-Modell nicht gefunden: {Config.MODEL_PATH}")
        
        # Lade Modell
        try:
            self.model = vosk.Model(str(Config.MODEL_PATH))
            logger.info(f"Vosk-Modell geladen: {Config.MODEL_PATH}")
        except Exception as e:
            logger.error(f"Fehler beim Laden des Modells: {e}")
            raise
        
        # Suche ReSpeaker-Gerät
        Config.DEVICE_INDEX = find_respeaker_device()
        if Config.DEVICE_INDEX is None:
            list_audio_devices()
        
        # Erstelle Audio-Queue
        self.q = queue.Queue()
        self.recognizer = None
        self.stream = None
        self.is_listening = False
    
    def audio_callback(self, indata, frames, time, status):
        """Callback für Audio-Stream"""
        if status:
            logger.warning(f"Audio-Status: {status}")
        self.q.put(bytes(indata))
    
    def start_listening(self):
        """Starte Audio-Stream"""
        logger.info("Starte Audio-Listening...")
        try:
            self.stream = sd.RawInputStream(
                samplerate=Config.SAMPLERATE,
                blocksize=Config.BLOCKSIZE,
                channels=1,
                dtype='int16',
                device=Config.DEVICE_INDEX,
                callback=self.audio_callback
            )
            self.stream.start()
            self.recognizer = vosk.KaldiRecognizer(self.model, Config.SAMPLERATE)
            self.is_listening = True
            logger.info("Audio-Listening aktiv")
        except Exception as e:
            logger.error(f"Fehler beim Starten des Audio-Streams: {e}")
            raise
    
    def stop_listening(self):
        """Stoppe Audio-Stream"""
        logger.info("Stoppe Audio-Listening...")
        if self.stream:
            self.stream.stop()
            self.stream.close()
        self.is_listening = False
    
    def wait_for_activation(self, timeout=None):
        """
        Warte auf das Aktivierungswort
        Rückgabe: True wenn erkannt, False bei Timeout
        """
        logger.info(f"Warte auf Aktivierungswort: '{Config.ACTIVATION_WORD}'")
        
        start_time = time.time()
        while True:
            if timeout and (time.time() - start_time) > timeout:
                logger.info("Aktivierungs-Timeout erreicht")
                return False
            
            try:
                data = self.q.get(timeout=1)
            except queue.Empty:
                continue
            
            if self.recognizer.AcceptWaveform(data):
                result = json.loads(self.recognizer.Result())
                recognized_text = result.get('result', [])
                
                if recognized_text:
                    text = ' '.join(recognized_text)
                    logger.info(f"Erkannt (Aktivierung): {text}")
                    
                    if Config.ACTIVATION_WORD in text.lower():
                        logger.info("Aktivierungswort erkannt!")
                        return True
    
    def listen_for_command(self, timeout=Config.LISTENING_TIMEOUT):
        """
        Höre auf ein Kommando nach Aktivierung
        Rückgabe: erkannter Text oder None
        """
        logger.info(f"Höre auf Kommando ({timeout}s)...")
        
        self.recognizer = vosk.KaldiRecognizer(self.model, Config.SAMPLERATE)
        start_time = time.time()
        
        while True:
            if (time.time() - start_time) > timeout:
                logger.info("Kommando-Timeout")
                return None
            
            try:
                data = self.q.get(timeout=1)
            except queue.Empty:
                continue
            
            if self.recognizer.AcceptWaveform(data):
                result = json.loads(self.recognizer.Result())
                recognized_text = result.get('result', [])
                
                if recognized_text:
                    text = ' '.join(recognized_text)
                    logger.info(f"Kommando erkannt: {text}")
                    return text
                    
            else:
                # Partial result
                try:
                    partial = json.loads(self.recognizer.PartialResult())
                    partial_text = partial.get('partial', '')
                    if partial_text:
                        logger.debug(f"Teilweise: {partial_text}")
                except:
                    pass

# ============================================================================
# SOUND-AUSFÜHRUNG
# ============================================================================

class SoundPlayer:
    """Verwalte Soundausgabe"""
    
    def __init__(self):
        self.sounds_dir = Config.SOUNDS_DIR
        self.sounds_dir.mkdir(exist_ok=True)
    
    def play_sound(self, filename):
        """Spiele Sounddatei ab"""
        sound_path = self.sounds_dir / filename
        
        if not sound_path.exists():
            logger.warning(f"Sound nicht gefunden: {sound_path}")
            return False
        
        try:
            logger.info(f"Spiele Sound ab: {filename}")
            # Nutze aplay zur Ausgabe auf ReSpeaker-Lautsprecher
            subprocess.run(
                ['aplay', '-D', 'hw:1,0', str(sound_path)],
                check=True,
                capture_output=True
            )
            logger.info(f"Sound abgespielt: {filename}")
            return True
        except subprocess.CalledProcessError as e:
            logger.error(f"Fehler beim Abspielen von {filename}: {e}")
            return False
        except FileNotFoundError:
            logger.error("aplay nicht gefunden - installiere alsa-utils")
            return False
    
    def play_beep(self, frequency=440, duration=0.5):
        """Spiele einfachen Beep ab"""
        try:
            logger.info(f"Spiele Beep ab ({frequency}Hz, {duration}s)")
            # Generiere Beep mittels sox
            os.system(f'play -n synth {duration} sine {frequency} 2>/dev/null')
        except Exception as e:
            logger.error(f"Fehler beim Beep: {e}")

# ============================================================================
# AKTION-HANDLER
# ============================================================================

class ActionHandler:
    """Behandle erkannte Kommandos und führe Aktionen aus"""
    
    def __init__(self, sound_player):
        self.sound_player = sound_player
    
    def handle_command(self, recognized_text):
        """
        Verarbeite erkannten Text und führe entsprechende Aktion aus
        """
        if not recognized_text:
            logger.warning("Leerer Text erhalten")
            return False
        
        text_lower = recognized_text.lower().strip()
        logger.info(f"Verarbeite Kommando: {text_lower}")
        
        # Suche nach passendem Kommando
        for command, config in Config.COMMANDS.items():
            if command in text_lower:
                logger.info(f"Kommando erkannt: {command}")
                
                # Spiele Sound ab
                if 'sound' in config:
                    self.sound_player.play_sound(config['sound'])
                
                # Führe Aktion aus
                action = config.get('action')
                if action:
                    self.execute_action(action)
                
                return True
        
        logger.info(f"Kommando nicht in Liste: {text_lower}")
        return False
    
    def execute_action(self, action):
        """Führe spezifische Aktion aus"""
        logger.info(f"Führe Aktion aus: {action}")
        
        if action == "greet":
            logger.info("Aktion: Grüßen")
        elif action == "play_music":
            logger.info("Aktion: Musik abspielen")
        elif action == "stop":
            logger.info("Aktion: Stoppen")
        elif action == "tell_time":
            import datetime
            now = datetime.datetime.now().strftime("%H:%M")
            logger.info(f"Aktuelle Zeit: {now}")
        elif action == "adjust_brightness":
            logger.info("Aktion: Helligkeit anpassen")

# ============================================================================
# HAUPT-PROGRAMM
# ============================================================================

class VoiceAssistant:
    """Haupt-Sprachassistent"""
    
    def __init__(self):
        logger.info("=" * 80)
        logger.info("Starte Voice Assistant für Raspberry Pi Zero 2W")
        logger.info("=" * 80)
        
        try:
            self.recognizer = SpeechRecognizer()
            self.sound_player = SoundPlayer()
            self.action_handler = ActionHandler(self.sound_player)
            self.running = False
        except Exception as e:
            logger.error(f"Fehler bei der Initialisierung: {e}")
            raise
    
    def run(self):
        """Starte Haupt-Schleife"""
        logger.info("Starte Haupt-Schleife...")
        self.running = True
        
        try:
            self.recognizer.start_listening()
            
            while self.running:
                try:
                    # Warte auf Aktivierungswort
                    if self.recognizer.wait_for_activation(timeout=120):
                        # Spiele Bestätigungs-Beep
                        self.sound_player.play_beep(frequency=1000, duration=0.2)
                        
                        # Höre auf Kommando
                        command = self.recognizer.listen_for_command()
                        
                        if command:
                            # Verarbeite Kommando
                            self.action_handler.handle_command(command)
                        else:
                            logger.info("Kein Kommando erkannt")
                    
                except KeyboardInterrupt:
                    logger.info("Unterbrochen durch Benutzer")
                    break
                except Exception as e:
                    logger.error(f"Fehler in Haupt-Schleife: {e}")
                    time.sleep(1)
        
        finally:
            self.recognizer.stop_listening()
            logger.info("Voice Assistant beendet")
    
    def stop(self):
        """Beende Programm"""
        logger.info("Stoppe Voice Assistant...")
        self.running = False

# ============================================================================
# EINSTIEGSPUNKT
# ============================================================================

if __name__ == "__main__":
    try:
        assistant = VoiceAssistant()
        assistant.run()
    except KeyboardInterrupt:
        logger.info("Programm beendet")
        sys.exit(0)
    except Exception as e:
        logger.error(f"Kritischer Fehler: {e}", exc_info=True)
        sys.exit(1)

Speichere die Datei (Ctrl+X, dann Y, dann Enter).

Mache das Skript ausführbar:

chmod +x ~/voice_assistant/speech_recognizer.py

5.2 Test-Sounddateien erstellen

Erstelle einfache Test-Sounds:

# Test-Sound 1: Hello
python3 << 'EOF'
import wave
import math

def generate_tone(frequency, duration, sample_rate=16000):
    """Generiere einfachen Ton"""
    samples = []
    for i in range(int(sample_rate * duration)):
        sample = int(32767 * 0.3 * math.sin(2 * math.pi * frequency * i / sample_rate))
        samples.append(sample)
    return samples

# Erzeuge Ton-Sequenz für "hello"
sounds = []
sounds += generate_tone(440, 0.1)  # A4
sounds += generate_tone(494, 0.1)  # B4
sounds += generate_tone(523, 0.2)  # C5

# Speichere als WAV
with wave.open('/home/pi/voice_assistant/sounds/hello.wav', 'wb') as f:
    f.setnchannels(1)
    f.setsampwidth(2)
    f.setframerate(16000)
    f.writeframes(b''.join(s.to_bytes(2, byteorder='little', signed=True) for s in sounds))

print("hello.wav erstellt")
EOF

# Test-Sound 2: Music start
python3 << 'EOF'
import wave
import math

def generate_tone(frequency, duration, sample_rate=16000):
    samples = []
    for i in range(int(sample_rate * duration)):
        sample = int(32767 * 0.3 * math.sin(2 * math.pi * frequency * i / sample_rate))
        samples.append(sample)
    return samples

sounds = []
sounds += generate_tone(523, 0.1)  # C5
sounds += generate_tone(587, 0.1)  # D5
sounds += generate_tone(659, 0.2)  # E5

with wave.open('/home/pi/voice_assistant/sounds/music.wav', 'wb') as f:
    f.setnchannels(1)
    f.setsampwidth(2)
    f.setframerate(16000)
    f.writeframes(b''.join(s.to_bytes(2, byteorder='little', signed=True) for s in sounds))

print("music.wav erstellt")
EOF

# Weitere Sounds analog...
for sound in stopped time brightness; do
    python3 << EOF
import wave
import math

def generate_tone(frequency, duration, sample_rate=16000):
    samples = []
    for i in range(int(sample_rate * duration)):
        sample = int(32767 * 0.3 * math.sin(2 * math.pi * frequency * i / sample_rate))
        samples.append(sample)
    return samples

sounds = generate_tone(440, 0.3)

with wave.open('/home/pi/voice_assistant/sounds/${sound}.wav', 'wb') as f:
    f.setnchannels(1)
    f.setsampwidth(2)
    f.setframerate(16000)
    f.writeframes(b''.join(s.to_bytes(2, byteorder='little', signed=True) for s in sounds))

print("${sound}.wav erstellt")
EOF
done

# Verifiziere Dateien
ls -la ~/voice_assistant/sounds/

5.3 Teste das Skript manuell

cd ~/voice_assistant
python3 speech_recognizer.py

Du solltest folgende Ausgabe sehen:

2025-01-18 15:30:45 - VoiceAssistant - INFO - ================================================================================
2025-01-18 15:30:45 - VoiceAssistant - INFO - Starte Voice Assistant für Raspberry Pi Zero 2W
2025-01-18 15:30:45 - VoiceAssistant - INFO - ================================================================================
2025-01-18 15:30:46 - VoiceAssistant - INFO - Initialisiere Spracherkenner...
2025-01-18 15:30:46 - VoiceAssistant - INFO - Vosk-Modell geladen: /home/pi/voice_models/vosk-model-de-0.21
2025-01-18 15:30:46 - VoiceAssistant - INFO - Suche nach ReSpeaker-Gerät...
2025-01-18 15:30:46 - VoiceAssistant - INFO - ReSpeaker gefunden: Index 1, Name: seeed-2mic-voicecard
2025-01-18 15:30:46 - VoiceAssistant - INFO - Starte Audio-Listening...
2025-01-18 15:30:46 - VoiceAssistant - INFO - Audio-Listening aktiv
2025-01-18 15:30:46 - VoiceAssistant - INFO - Haupt-Schleife gestartet
2025-01-18 15:30:46 - VoiceAssistant - INFO - Warte auf Aktivierungswort: 'computer'

Teste jetzt:

  1. Sprich: "Computer"
  2. Nach dem Beep sprich: "Musik"
  3. Der Pi sollte music.wav abspielen

Beende mit Ctrl+C.


TEIL 6: SYSTEMCTL SERVICE ERSTELLEN

6.1 Service-Datei erstellen

Erstelle eine systemctl-Service-Datei, damit der Voice Assistant beim Booten automatisch startet:

sudo nano /etc/systemd/system/voice-assistant.service

Füge folgenden Inhalt ein:

[Unit]
Description=Voice Assistant Service for ReSpeaker
After=network.target sound.target
Wants=network-online.target

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/voice_assistant
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/home/pi/.local/bin"
ExecStart=/usr/bin/python3 /home/pi/voice_assistant/speech_recognizer.py
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

# Ressourcen-Limits für Pi Zero
MemoryMax=256M
CPUQuota=50%

[Install]
WantedBy=multi-user.target

Speichere die Datei (Ctrl+X, Y, Enter).

6.2 Service aktivieren und starten

# Lade systemd-Daemon neu
sudo systemctl daemon-reload

# Aktiviere Service für Auto-Start
sudo systemctl enable voice-assistant.service

# Starte Service jetzt
sudo systemctl start voice-assistant.service

# Überprüfe Status
sudo systemctl status voice-assistant.service

6.3 Service-Logs anzeigen

# Zeige aktuelle Logs
sudo journalctl -u voice-assistant.service -f

# Zeige letzte 50 Zeilen
sudo journalctl -u voice-assistant.service -n 50

# Zeige Logs mit Zeitstempel
sudo journalctl -u voice-assistant.service --since "1 hour ago" -a

6.4 Service steuern

# Starte Service
sudo systemctl start voice-assistant.service

# Stoppe Service
sudo systemctl stop voice-assistant.service

# Starte Service neu
sudo systemctl restart voice-assistant.service

# Deaktiviere Auto-Start
sudo systemctl disable voice-assistant.service

TEIL 7: MONITORING UND LOGGING

7.1 Log-Rotation einrichten

Erstelle Logrotate-Konfiguration um Log-Dateien zu verwalten:

sudo nano /etc/logrotate.d/voice-assistant

Füge ein:

/home/pi/voice_assistant/logs/*.log {
    daily
    rotate 7
    compress
    delaycompress
    notifempty
    create 0640 pi pi
    sharedscripts
}

7.2 Monitor-Skript erstellen

Erstelle ~/voice_assistant/monitor.py zur Überwachung:

nano ~/voice_assistant/monitor.py
#!/usr/bin/env python3
"""
Monitor-Skript zur Überwachung des Voice Assistant Services
"""

import subprocess
import sys
import time
from datetime import datetime

def check_service_status():
    """Überprüfe Service-Status"""
    try:
        result = subprocess.run(
            ['systemctl', 'status', 'voice-assistant.service'],
            capture_output=True,
            text=True
        )
        return result.returncode == 0
    except:
        return False

def get_recent_logs(lines=10):
    """Hole letzte Log-Einträge"""
    try:
        result = subprocess.run(
            ['sudo', 'journalctl', '-u', 'voice-assistant.service', '-n', str(lines), '-a'],
            capture_output=True,
            text=True
        )
        return result.stdout
    except:
        return "Logs nicht verfügbar"

def restart_service():
    """Starte Service neu"""
    try:
        subprocess.run(['sudo', 'systemctl', 'restart', 'voice-assistant.service'], check=True)
        return True
    except:
        return False

def main():
    """Hauptüberwachung"""
    print("Voice Assistant Monitor")
    print("=" * 60)
    
    while True:
        try:
            is_running = check_service_status()
            status = "✓ LÄUFT" if is_running else "✗ GESTOPPT"
            print(f"[{datetime.now().strftime('%H:%M:%S')}] Status: {status}")
            
            if not is_running:
                print("Service nicht aktiv, starte neu...")
                if restart_service():
                    print("Service neu gestartet")
                    time.sleep(5)
            
            # Zeige letzte Logs
            print("\nLetzte Log-Einträge:")
            print("-" * 60)
            logs = get_recent_logs(5)
            print(logs)
            print("-" * 60)
            
            time.sleep(30)  # Überprüfe alle 30 Sekunden
            
        except KeyboardInterrupt:
            print("\nMonitor beendet")
            sys.exit(0)
        except Exception as e:
            print(f"Fehler: {e}")
            time.sleep(5)

if __name__ == "__main__":
    main()

Speichere und starte:

chmod +x ~/voice_assistant/monitor.py
python3 ~/voice_assistant/monitor.py

TEIL 8: ADVANCED - EIGENE KOMMANDOS HINZUFÜGEN

8.1 Neue Kommandos definieren

Bearbeite die COMMANDS Sektion in speech_recognizer.py:

COMMANDS = {
    "hello": {"sound": "hello.wav", "action": "greet"},
    "musik": {"sound": "music.wav", "action": "play_music"},
    "stopp": {"sound": "stopped.wav", "action": "stop"},
    "zeit": {"sound": "time.wav", "action": "tell_time"},
    "helligkeit": {"sound": "brightness.wav", "action": "adjust_brightness"},
    # NEU: Eigene Kommandos hinzufügen
    "licht": {"sound": "light.wav", "action": "toggle_light"},
    "temperatur": {"sound": "temp.wav", "action": "read_temp"},
}

8.2 Eigene Aktionen implementieren

Bearbeite die execute_action() Methode in der ActionHandler Klasse:

def execute_action(self, action):
    """Führe spezifische Aktion aus"""
    logger.info(f"Führe Aktion aus: {action}")
    
    if action == "greet":
        logger.info("Aktion: Grüßen")
    elif action == "toggle_light":
        logger.info("Aktion: Licht umschalten")
        # Beispiel: GPIO-Pin steuern
        import RPi.GPIO as GPIO
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(17, GPIO.OUT)
        GPIO.output(17, not GPIO.input(17))  # Toggle
    elif action == "read_temp":
        logger.info("Aktion: Temperatur auslesen")
        # Beispiel mit Sensor
        try:
            with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
                temp = float(f.read()) / 1000
                logger.info(f"Aktuelle Temperatur: {temp:.1f}°C")
        except:
            logger.error("Temperatur-Sensor nicht verfügbar")

8.3 GPIO-Steuerung erweitern

Für komplexere GPIO-Anwendungen:

pip3 install gpiozero

Beispiel in ActionHandler:

from gpiozero import LED, Motor, Buzzer
from gpiozero.pins.rpigpio import RPiGPIOFactory

class ActionHandler:
    def __init__(self, sound_player):
        self.sound_player = sound_player
        
        # Definiere GPIO-Geräte
        # Pin 17: LED
        # Pin 27: Buzzer
        # Pin 22, 23: Motor (Richtung)
        # Pin 24: PWM für Motor-Geschwindigkeit
        try:
            GPIO.setmode(GPIO.BCM)
            self.led = LED(17)
            self.buzzer = Buzzer(27)
        except:
            logger.warning("GPIO-Geräte nicht verfügbar")

TEIL 9: TROUBLESHOOTING

9.1 Häufige Probleme und Lösungen

Problem: "ReSpeaker-Gerät nicht gefunden"

# Überprüfe Geräte
aplay -l
arecord -l

# Überprüfe I2C-Verbindung
i2cdetect -y 1

# Reboot
sudo reboot now

Problem: "Vosk-Modell nicht gefunden"

# Überprüfe Modell-Pfad
ls -la ~/voice_models/

# Download neu
mkdir -p ~/voice_models
cd ~/voice_models
wget https://alphacephei.com/vosk/models/vosk-model-de-0.21.zip
unzip vosk-model-de-0.21.zip

Problem: "Audio sehr leise oder verrauscht"

# Überprüfe Lautstärke
alsamixer

# Nutze Pfeiltasten zur Anpassung
# Mit F6 ReSpeaker-Gerät auswählen
# ESC zum Beenden

# Microphone-Gain erhöhen (rechte Pfeiltaste)
# Playback-Lautstärke erhöhen (linke Pfeiltaste)

Problem: "Service startet nicht beim Boot"

# Überprüfe Service
sudo systemctl status voice-assistant.service

# Zeige Fehler
sudo journalctl -u voice-assistant.service -n 50

# Überprüfe Pfade in Service-Datei
cat /etc/systemd/system/voice-assistant.service

Problem: "SSH-Verbindung möglich, aber Service antwortet nicht"

# Überprüfe Netzwerk
ping 8.8.8.8

# Überprüfe SSH-Key
ssh -vvv pi@192.168.1.100

# Starte Pi neu
sudo reboot now

9.2 Debug-Modus aktivieren

Ändere in speech_recognizer.py:

# KONFIGURATION
class Config:
    # ...
    LOG_LEVEL = logging.DEBUG  # Vorher: logging.INFO

Starte das Skript mit ausführlichem Logging:

python3 ~/voice_assistant/speech_recognizer.py 2>&1 | tee ~/voice_assistant/logs/debug.log

TEIL 10: OPTIMIERUNGEN FÜR PI ZERO 2W

10.1 Speicher-Optimierung

Der Pi Zero 2W hat nur 512MB RAM. Optimierungen:

# Deaktiviere unnötige Services
sudo systemctl disable bluetooth.service
sudo systemctl disable hciuart.service
sudo systemctl disable avahi-daemon.service
sudo systemctl disable cups.service

# Reduziere GPU-Memory
sudo nano /boot/config.txt

Finde oder erstelle diese Zeile:

gpu_mem=64

Dies reduziert GPU auf 64MB, gibt dem CPU mehr RAM.

10.2 CPU-Auslastung überwachen

# Echtzeit-Überwachung
top

# Prozessüberwachung
ps aux | grep python3

# Speichernutzung
free -h

# Disk-Space
df -h

10.3 Performance-Tuning

Erweitere Config in speech_recognizer.py:

class Config:
    # ...
    # Optimiert für Pi Zero 2W
    BLOCKSIZE = 8192  # Größere Blöcke für weniger Overhead
    CONFIDENCE_THRESHOLD = 0.4  # Etwas niedriger für schnellere Reaktion
    
    # Nutze nur 1 Kern für Vosk
    VOSK_THREADS = 1

CHECKLISTE FÜR ERFOLGREICHE INSTALLATION

  • Pi Zero 2W mit Raspberry Pi OS (Bullseye 32-bit) installiert
  • ReSpeaker Hat v1.2 korrekt angebracht und verbunden
  • seeed-voicecard Treiber installiert
  • I2C und SPI aktiviert
  • Vosk-Modell (Deutsch) heruntergeladen
  • Alle Python-Pakete installiert
  • speech_recognizer.py manuell getestet
  • Sound-Dateien erstellt
  • systemctl-Service erstellt und aktiviert
  • Service-Logs überprüft
  • Aktivierungswort reagiert und Sounds abspielen

NÄCHSTE SCHRITTE

  1. Web-Interface: Erstelle ein Web-Dashboard zur Verwaltung
  2. Datenbank: Speichere Kommandos und Logs in SQLite
  3. Integration: Verbinde mit MQTT für Smart Home
  4. Machine Learning: Trainiere eigene Sprachmodelle
  5. Mehrsprachigkeit: Unterstütze zusätzliche Sprachen

RESSOURCEN UND REFERENZEN


HÄUFIG GESTELLTE FRAGEN

F: Kann ich das System auch mit größeren Raspberry Pi Modellen nutzen? A: Ja, das System funktioniert auch mit Pi 3, 4 und 5. Allerdings benötigt der Pi 4/5 mehr Kühlungsvorkehrungen.

F: Wird eine Internetverbindung benötigt? A: Nach der initialen Einrichtung nicht! Das System funktioniert vollständig offline mit Vosk.

F: Kann ich weitere Sprachmodelle nutzen? A: Ja, Vosk bietet Modelle in vielen Sprachen. Lade sie herunter und passe den Modell-Pfad an.

F: Wie erkenne ich, ob der Service läuft? A: Mit sudo systemctl status voice-assistant.service oder überprüfe die LEDs auf dem ReSpeaker.


SUPPORT UND KONTAKT

Bei Problemen:

  1. Überprüfe die Logs: sudo journalctl -u voice-assistant.service
  2. Konsultiere das Troubleshooting-Kapitel
  3. Öffne ein Issue im GitHub-Repository
  4. Kontaktiere Seeed Studio Support

Fertig! Dein Pi Zero 2W mit ReSpeaker sollte nun vollständig funktionsfähig sein! 🎉