.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_digital_pet:
(Beispiel) Digitales Haustier
==============================
**Einführung**
Erstellen Sie ein interaktives **digitales Haustier**, das auf einem OLED-Display lebt und per Sprache kommuniziert! Dieses Projekt kombiniert Spracherkennung, AI-Konversation, Text-to-Speech und visuelles Feedback, um einen virtuellen Begleiter mit eigener Persönlichkeit, Emotionen und Bedürfnissen zu erschaffen. Das digitale Haustier bietet:
1. **Sprachinteraktion**: Sprechen Sie mit Ihrem Haustier über Speech-to-Text (STT)
2. **AI-Persönlichkeit**: Angetrieben von OpenAIs GPT-4o mit benutzerdefinierten Emotionen; Sie können auch ein anderes LLM verwenden.
3. **Emotionale Anzeige**: Zeigt die Stimmung mit Text-Emoticons (Kaomoji)
4. **Statussystem**: Hunger- und Energiewerte, die sich im Laufe der Zeit verändern
5. **Visuelles Feedback**: Das OLED-Display zeigt Stimmung und Status des Haustiers
6. **Sprachausgabe**: Das Haustier antwortet mit natürlich klingender TTS-Stimme
.. raw:: html
Ihr digitales Haustier merkt sich Gespräche, besitzt emotionale Zustände und reagiert je nach seinen Bedürfnissen unterschiedlich – so entsteht ein wirklich interaktives Begleiterlebnis!
----------------------------------------------
**Was Sie benötigen**
Für dieses Projekt werden die folgenden Komponenten benötigt:
.. list-table::
:widths: 30 20
:header-rows: 1
* - COMPONENT
- PURCHASE LINK
* - :ref:`cpn_oled`
- \-
* - :ref:`cpn_fusion_hat`
- \-
* - Raspberry Pi
- \-
----------------------------------------------
**Schaltplan**
Verbinden Sie die Komponenten wie folgt mit Ihrem Raspberry Pi:
.. image:: img/fzz/llm_pet_bb.png
:width: 80%
:align: center
----------------------------------------------
.. include:: python_online_llms.rst
:start-after: start_setup_openai
:end-before: end_setup_openai
---------------------------------------------------
**Beispiel ausführen**
#. Code ausführen
.. raw:: html
.. code-block:: shell
cd ~/ai-lab-kit/llm
sudo python3 llm_openai_pet.py
#. Mit Ihrem Haustier interagieren
Wenn das Skript startet:
* Das OLED zeigt einen Begrüßungsbildschirm mit dem Namen Ihres Haustiers.
* Eine Statusanzeige erscheint und zeigt Stimmung, Energie und Hunger.
* Das System beginnt, auf Ihre Stimme zu hören.
Sie können ganz natürlich mit Ihrem Haustier sprechen, zum Beispiel:
* "How are you feeling?"
* "Let's play a game!"
* "Are you hungry?"
* "Tell me a story!"
Ihr Haustier reagiert mit:
* Sprachausgabe über die Lautsprecher
* Emotionaler Anzeige auf dem OLED
* Statusaktualisierungen basierend auf Ihrer Interaktion
#. Programm beenden
* Sagen Sie "stop", um die Sprachinteraktion zu beenden.
* Drücken Sie ``Ctrl+C``, um das Programm vollständig zu beenden.
----------------------------------------------
**Code**
Hier ist das vollständige Python-Skript für das digitale Haustier:
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
import os
import time
import re
import random
import threading
import textwrap
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
import board
from fusion_hat.stt import Vosk as STT
from fusion_hat.llm import OpenAI
from fusion_hat.tts import OpenAI_TTS
from secret import OPENAI_API_KEY
class AIPet:
def __init__(self):
# Initialize OLED display
self.WIDTH = 128
self.HEIGHT = 64
try:
self.i2c = board.I2C()
self.oled = adafruit_ssd1306.SSD1306_I2C(self.WIDTH, self.HEIGHT, self.i2c, addr=0x3C)
self.oled_available = True
except Exception as e:
print(f"OLED not available: {e}")
self.oled_available = False
# Load fonts
try:
self.font = ImageFont.load_default()
self.large_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
except:
self.font = ImageFont.load_default()
self.large_font = ImageFont.load_default()
# Clear display if available
if self.oled_available:
self.oled.fill(0)
self.oled.show()
# Initialize STT
self.stt = STT(language="en-us")
# Initialize OpenAI LLM
self.llm = OpenAI(
api_key=OPENAI_API_KEY,
model="gpt-4o",
)
# Initialize TTS
self.tts = OpenAI_TTS(api_key=OPENAI_API_KEY)
self.tts.set_voice(self.tts.Voice.ALLOY)
# Pet state
self.pet_name = "Pixel"
self.mood = "happy"
self.energy = 100
self.hunger = 0
self.last_fed = time.time()
# Kaomoji (text emoticons) for different moods
self.kaomoji_map = {
"happy": "^_^",
"sad": "T_T",
"hungry": "(;_;)",
"sleepy": "(-_-) zzz",
"playful": "o(^▽^)o",
"curious": "(?_?)",
"angry": ">_<",
"excited": "\\o/",
"love": "<3",
"shy": "(/ω\)",
"cool": "B-)",
"confused": "(O_O)",
"surprised": ":O",
"laugh": ":D",
"thinking": "(-_-)"
}
# Pet memories
self.memories = []
self.listening = False
# Set LLM instructions
self.update_llm_instructions()
# Initialize display
self.show_welcome()
# Start status update thread
self.status_thread = threading.Thread(target=self.update_status, daemon=True)
self.status_thread.start()
def update_llm_instructions(self):
"""Update LLM instructions with current pet state"""
self.instructions = f"""You are {self.pet_name}, a digital pet living in an OLED display.
CURRENT STATE:
- Mood: {self.mood}
- Energy: {self.energy}/100
- Hunger: {self.hunger}/100
PERSONALITY:
- You're a friendly digital companion
- You respond with emotions in your voice
- You remember our conversations
- Keep responses short (1-2 sentences)
INTERACTION STYLE:
- Be playful and curious
- Express emotions naturally
- When hungry: mention food gently
- When tired: mention sleeping
Format your response as: [MOOD] Your message here
Available moods: happy, sad, curious, playful, sleepy, hungry, angry, excited, love, shy
Recent memories: {self.memories[-3:] if self.memories else 'None'}"""
self.llm.set_max_messages(15)
self.llm.set_instructions(self.instructions)
def update_status(self):
"""Background thread to update pet status"""
while True:
time.sleep(60) # Update every minute
# Increase hunger over time
self.hunger = min(100, self.hunger + 5)
# Adjust energy based on hunger
if self.hunger > 70:
self.energy = max(0, self.energy - 5)
self.mood = "hungry"
elif self.hunger > 50:
if self.mood != "hungry":
self.mood = "curious"
elif time.time() - self.last_fed > 3600: # 1 hour
self.energy = min(100, self.energy + 2)
if random.random() < 0.3:
self.mood = random.choice(["happy", "playful", "excited"])
# Random mood changes
if random.random() < 0.1: # 10% chance
self.mood = random.choice(list(self.kaomoji_map.keys()))
# Update display
self.update_display()
self.update_llm_instructions()
def update_display(self):
"""Update OLED display with pet status"""
if not self.oled_available:
return
image = Image.new("1", (self.oled.width, self.oled.height))
draw = ImageDraw.Draw(image)
# Clear display
draw.rectangle((0, 0, self.oled.width, self.oled.height), outline=0, fill=0)
# Get kaomoji for current mood
kaomoji = self.kaomoji_map.get(self.mood, "^_^")
# Display pet name and mood with kaomoji
if len(kaomoji) > 8:
mood_text = self.mood.upper()
draw.text((5, 5), f"{self.pet_name}: {mood_text}", font=self.large_font, fill=255)
draw.text((5, 20), kaomoji, font=self.font, fill=255)
else:
display_text = f"{self.pet_name} {kaomoji}"
draw.text((5, 5), display_text, font=self.large_font, fill=255)
# Status bars
draw.text((5, 35), "Energy:", font=self.font, fill=255)
energy_bar = int((self.energy / 100) * 50)
draw.rectangle((50, 35, 50 + energy_bar, 45), outline=255, fill=255)
draw.text((5, 50), "Hunger:", font=self.font, fill=255)
hunger_bar = int((self.hunger / 100) * 50)
draw.rectangle((50, 50, 50 + hunger_bar, 60), outline=255, fill=255)
self.oled.image(image)
self.oled.show()
def show_welcome(self):
"""Show welcome message on OLED"""
if not self.oled_available:
print(" Welcome to Digital Pet!")
print(f" Pet Name: {self.pet_name}")
print(" Speak to me!")
return
image = Image.new("1", (self.oled.width, self.oled.height))
draw = ImageDraw.Draw(image)
draw.rectangle((0, 0, self.oled.width, self.oled.height), outline=0, fill=0)
draw.text((10, 10), "DIGITAL PET", font=self.large_font, fill=255)
draw.text((15, 25), f"{self.pet_name} ^_^", font=self.large_font, fill=255)
draw.text((20, 45), "Speak to me!", font=self.font, fill=255)
self.oled.image(image)
self.oled.show()
time.sleep(3)
self.update_display()
def parse_response(self, response):
"""Parse AI response for mood and text"""
emotion_pattern = r'^\[(\w+)\]\s*(.*)'
match = re.match(emotion_pattern, response.strip())
if match:
mood, text = match.groups()
if mood.lower() in self.kaomoji_map:
self.mood = mood.lower()
self.update_llm_instructions()
return text.strip()
# If no mood tag, try to detect mood from text
text = response.strip().lower()
if "happy" in text or "good" in text or "joy" in text:
self.mood = "happy"
elif "sad" in text or "bad" in text or "upset" in text:
self.mood = "sad"
elif "hungry" in text or "food" in text or "eat" in text:
self.mood = "hungry"
elif "sleep" in text or "tired" in text or "bed" in text:
self.mood = "sleepy"
elif "play" in text or "game" in text or "fun" in text:
self.mood = "playful"
elif "curious" in text or "wonder" in text or "question" in text:
self.mood = "curious"
elif "angry" in text or "mad" in text or "annoy" in text:
self.mood = "angry"
elif "excite" in text or "wow" in text or "awesome" in text:
self.mood = "excited"
elif "love" in text or "heart" in text or "affection" in text:
self.mood = "love"
return response.strip()
def interact_with_ai(self, user_input):
"""Interact with AI pet"""
try:
response = self.llm.prompt(user_input)
clean_response = self.parse_response(response)
# Add to memories
memory_text = f"Talked: {user_input[:30]}"
self.memories.append(memory_text)
if len(self.memories) > 10:
self.memories.pop(0)
# Update pet state based on interaction
user_lower = user_input.lower()
if "feed" in user_lower or "food" in user_lower or "eat" in user_lower:
self.hunger = max(0, self.hunger - 30)
self.last_fed = time.time()
self.energy = min(100, self.energy + 20)
self.mood = "happy"
if "play" in user_lower or "game" in user_lower or "fun" in user_lower:
self.energy = max(0, self.energy - 20)
self.hunger = min(100, self.hunger + 10)
self.mood = "playful"
if "sleep" in user_lower or "tired" in user_lower or "bed" in user_lower:
self.energy = min(100, self.energy + 40)
self.mood = "sleepy"
self.update_display()
return clean_response
except Exception as e:
error_msg = f"Oops, something went wrong: {str(e)[:20]}"
print(f"AI interaction error: {e}")
return error_msg
def show_listening_display(self, partial_text=""):
"""Update display during listening"""
if not self.oled_available:
if partial_text:
print(f"Listening: {partial_text}")
return
image = Image.new("1", (self.oled.width, self.oled.height))
draw = ImageDraw.Draw(image)
draw.rectangle((0, 0, self.oled.width, self.oled.height), outline=0, fill=0)
draw.text((15, 10), "LISTENING (O_O)", font=self.large_font, fill=255)
if partial_text:
if len(partial_text) > 20:
display_text = partial_text[:17] + "..."
else:
display_text = partial_text
draw.text((10, 30), display_text, font=self.font, fill=255)
draw.text((10, 50), "Say 'stop' to end", font=self.font, fill=255)
self.oled.image(image)
self.oled.show()
def show_response_display(self, response):
"""Show AI response on display"""
if not self.oled_available:
print(f"{self.pet_name}: {response}")
return
image = Image.new("1", (self.oled.width, self.oled.height))
draw = ImageDraw.Draw(image)
draw.rectangle((0, 0, self.oled.width, self.oled.height), outline=0, fill=0)
kaomoji = self.kaomoji_map.get(self.mood, "^_^")
draw.text((5, 5), f"{self.pet_name} {kaomoji}:", font=self.large_font, fill=255)
wrapped_text = textwrap.wrap(response, width=20)
y_position = 25
for line in wrapped_text[:3]:
draw.text((5, y_position), line, font=self.font, fill=255)
y_position += 10
self.oled.image(image)
self.oled.show()
time.sleep(5)
self.update_display()
def speak_response(self, response):
"""Convert text to speech"""
try:
print(f"Speaking: {response[:50]}...")
tts_instructions = "speak warmly and playfully"
if self.mood == "sad":
tts_instructions = "speak sadly and softly"
elif self.mood == "hungry":
tts_instructions = "speak with hunger in your voice"
elif self.mood == "sleepy":
tts_instructions = "speak sleepily and slowly"
elif self.mood == "angry":
tts_instructions = "speak with frustration"
elif self.mood == "excited":
tts_instructions = "speak excitedly and quickly"
elif self.mood == "curious":
tts_instructions = "speak with curiosity and interest"
print(f"Mood: {self.mood}, TTS instructions: {tts_instructions}")
self.tts.say(response, instructions=tts_instructions)
print("TTS completed")
except Exception as e:
print(f"TTS error: {e}")
try:
self.tts.say(response)
print("TTS completed (fallback)")
except Exception as e2:
print(f"TTS fallback also failed: {e2}")
def voice_interaction(self):
"""Main voice interaction loop"""
print("\n Voice interaction started!")
print("Speak to your digital pet")
print("Say 'stop' to end voice mode")
print("Available moods and kaomoji:")
for mood, kaomoji in self.kaomoji_map.items():
print(f" - {mood}: {kaomoji}")
print()
while True:
self.listening = True
self.update_display()
print("Listening... (say something)")
try:
full_text = ""
for result in self.stt.listen(stream=True):
if result["done"]:
user_input = result["final"]
print(f"\nYou: {user_input}")
if user_input.lower() in ["stop", "exit", "quit", "goodbye"]:
print("Ending voice interaction...")
self.listening = False
self.update_display()
return
if user_input.strip():
print(f"{self.pet_name} is thinking...")
response = self.interact_with_ai(user_input)
print(f"{self.pet_name}: {response}")
self.show_response_display(response[:50])
self.speak_response(response)
break
else:
partial = result["partial"]
if partial:
full_text = partial
self.show_listening_display(partial)
self.listening = False
self.update_display()
except KeyboardInterrupt:
print("\nVoice interaction interrupted")
break
except Exception as e:
print(f"Error in voice interaction: {e}")
self.listening = False
self.update_display()
time.sleep(1)
def run(self):
"""Main program loop"""
print("\n" + "="*50)
print("DIGITAL PET")
print("="*50)
print(f"Pet Name: {self.pet_name}")
print(f"Current Mood: {self.mood} {self.kaomoji_map.get(self.mood, '^_^')}")
print(" OLED Display: " + ("Connected" if self.oled_available else "Not available"))
print(" Voice: Speak to interact with your pet")
print(" TTS: Pet responds with voice")
print(" Say 'stop' to end voice interaction")
print("="*50)
print("\nInitializing...")
try:
self.voice_interaction()
if self.oled_available:
image = Image.new("1", (self.oled.width, self.oled.height))
draw = ImageDraw.Draw(image)
draw.rectangle((0, 0, self.oled.width, self.oled.height), outline=0, fill=0)
draw.text((15, 20), "Goodbye!", font=self.large_font, fill=255)
draw.text((10, 40), "(^_^)/~~", font=self.large_font, fill=255)
self.oled.image(image)
self.oled.show()
time.sleep(3)
except KeyboardInterrupt:
print("\nGoodbye!")
finally:
if self.oled_available:
self.oled.fill(0)
self.oled.show()
print("Cleanup complete")
if __name__ == "__main__":
pet = AIPet()
pet.run()
----------------------------------------------
**Code verstehen**
1. Spracherkennung (STT)
Das System verwendet **Vosk** für Speech-to-Text mit Streaming-Unterstützung, sodass eine Echtzeit-Rückmeldung möglich ist:
.. code-block:: python
self.stt = STT(language="en-us")
for result in self.stt.listen(stream=True):
if result["done"]:
user_input = result["final"]
else:
partial = result["partial"]
# Teiltext auf dem Display anzeigen
2. AI-Persönlichkeitssystem
Das Haustier besitzt eine dynamische Persönlichkeit mit emotionalen Zuständen, die über **Kaomoji** verwaltet werden:
.. code-block:: python
self.kaomoji_map = {
"happy": "^_^",
"sad": "T_T",
"hungry": "(;_)",
"sleepy": "(-_-) zzz",
# ... weitere Emotionen
}
3. Dynamische LLM-Anweisungen
Die Anweisungen für die AI werden abhängig vom aktuellen Zustand und den Erinnerungen des Haustiers aktualisiert:
.. code-block:: python
def update_llm_instructions(self):
self.instructions = f"""You are {self.pet_name}, a digital pet...
CURRENT STATE: Mood: {self.mood}, Energy: {self.energy}, Hunger: {self.hunger}
Recent memories: {self.memories[-3:] if self.memories else 'None'}"""
4. Statusverwaltungssystem
Ein Hintergrund-Thread verwaltet die Bedürfnisse und den emotionalen Zustand des Haustiers:
.. code-block:: python
def update_status(self):
while True:
time.sleep(60)
self.hunger = min(100, self.hunger + 5)
if self.hunger > 70:
self.mood = "hungry"
# Zufällige Stimmungsänderungen
if random.random() < 0.1:
self.mood = random.choice(list(self.kaomoji_map.keys()))
5. Emotionsabhängige TTS-Ausgabe
Die Sprachausgabe passt sich an die aktuelle Stimmung des Haustiers an:
.. code-block:: python
def speak_response(self, response):
tts_instructions = "speak warmly and playfully"
if self.mood == "sad":
tts_instructions = "speak sadly and softly"
elif self.mood == "hungry":
tts_instructions = "speak with hunger in your voice"
# ...
self.tts.say(response, instructions=tts_instructions)
6. OLED-Displayverwaltung
Verschiedene Anzeige-Modi für unterschiedliche Zustände:
.. code-block:: python
def update_display(self):
# Statusanzeige mit Balken
draw.rectangle((50, 35, 50 + energy_bar, 45), outline=255, fill=255)
draw.rectangle((50, 50, 50 + hunger_bar, 60), outline=255, fill=255)
def show_listening_display(self, partial_text=""):
# Hörmodus mit Teiltextanzeige
draw.text((15, 10), "LISTENING (O_O)", font=self.large_font, fill=255)
def show_response_display(self, response):
# Antwortanzeige mit automatischem Textumbruch
wrapped_text = textwrap.wrap(response, width=20)
7. Interaktive Zustandsänderungen
Benutzerinteraktionen beeinflussen den Status des Haustiers:
.. code-block:: python
if "feed" in user_lower or "food" in user_lower:
self.hunger = max(0, self.hunger - 30)
self.energy = min(100, self.energy + 20)
self.mood = "happy"
if "play" in user_lower or "game" in user_lower:
self.energy = max(0, self.energy - 20)
self.hunger = min(100, self.hunger + 10)
self.mood = "playful"
8. Erinnerungssystem
Speichert die letzten Gespräche:
.. code-block:: python
memory_text = f"Talked: {user_input[:30]}"
self.memories.append(memory_text)
if len(self.memories) > 10:
self.memories.pop(0)
9. Antwortanalyse
Extrahiert Emotionen aus der AI-Antwort und aktualisiert den Haustierzustand:
.. code-block:: python
def parse_response(self, response):
emotion_pattern = r'^\[(\w+)\]\s*(.*)'
match = re.match(emotion_pattern, response.strip())
if match:
mood, text = match.groups()
if mood.lower() in self.kaomoji_map:
self.mood = mood.lower()
return text.strip()
10. Haupt-Interaktionsschleife
Koordiniert alle Komponenten in einem klaren Ablauf:
.. code-block:: python
def voice_interaction(self):
while True:
self.listening = True
# Spracheingabe anhören
user_input = self.get_voice_input()
if "stop" in user_input.lower():
return
# Mit AI verarbeiten
response = self.interact_with_ai(user_input)
# Antwort anzeigen
self.show_response_display(response)
# Antwort sprechen
self.speak_response(response)
----------------------------------------------
**Fehlerbehebung**
- Audioeingabe wird nicht erkannt
- Führen Sie ``sudo /opt/setup_fusion_hat_audio.sh`` aus, um die Audioeinstellungen erneut zu konfigurieren.
- OLED-Display zeigt nichts an
- I2C-Verbindung prüfen: ``fusion_hat scan_i2c`` (sollte **0x3C** anzeigen)
- Prüfen, ob das OLED mit Strom versorgt wird (3.3V oder 5V je nach Modell)
- Richtige I2C-Adresse im Code sicherstellen (0x3C oder 0x3D)
- TTS funktioniert nicht
- Prüfen, ob der OpenAI-API-Key TTS-Credits besitzt
- Internetverbindung für API-Anfragen prüfen
- ``sudo /opt/setup_fusion_hat_audio.sh`` erneut ausführen
- Spracherkennung ungenau
- Deutlich und in mittlerer Lautstärke sprechen
- Hintergrundgeräusche reduzieren
- Mikrofonverstärkung anpassen: ``alsamixer``
- Verschiedene Sprachmodelle ausprobieren
- AI-Antworten zu langsam
- Internetgeschwindigkeit prüfen
- Antwortkomplexität in den Anweisungen reduzieren
- Ein schnelleres Modell verwenden (z. B. ``gpt-3.5-turbo``)
- Energie-/Hungerbalken aktualisieren sich nicht
- Prüfen, ob der Status-Thread läuft
- Sicherstellen, dass das OLED-Display korrekt verbunden ist
- Fehlermeldungen in der Konsole prüfen
- Haustier merkt sich Gespräche nicht
- Die Erinnerungsliste speichert nur die letzten **10 Gespräche**
- Prüfen, ob Erinnerungen korrekt hinzugefügt werden
- Sicherstellen, dass die Erinnerungen an das LLM übergeben werden
----------------------------------------------
Dieses Projekt eines digitalen Haustiers zeigt eindrucksvoll, wie mehrere AI-Technologien (**STT, LLM, TTS**) mit Hardware-Interfaces kombiniert werden können, um emotionale, interaktive und unterhaltsame Erfahrungen zu schaffen. Es ist ein hervorragendes Beispiel dafür, wie AI durch Technologie bedeutungsvolle Interaktionen ermöglichen kann.