Add fully local conversational AI pipeline for Reachy Mini

Local STT (Qwen3-ASR), VLM (Gemma 4 26B-A4B), and TTS (Spark-TTS) running
on Apple Silicon via MLX, with bracket-tag action system for nod, shake,
wiggle, dance, photo, and pre-recorded emotions.
This commit is contained in:
Norbert Schmidt
2026-05-12 09:24:02 +02:00
parent 3a8a8e3145
commit 5a04a7133a
12 changed files with 4074 additions and 0 deletions

48
record_voice.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
# Record a voice reference via the Reachy Mini's microphone.
# Usage: ./record_voice.sh [seconds] [output.wav]
set -e
DURATION="${1:-12}"
OUTPUT="${2:-voice_ref.wav}"
echo "Recording ${DURATION}s via Reachy Mini's microphone to ${OUTPUT}"
echo "Stand/sit near the robot. Starting in 3 seconds..."
sshpass -p 'root' ssh -o StrictHostKeyChecking=no pollen@reachy-mini.local "/venvs/mini_daemon/bin/python -c \"
import time, wave, numpy as np
from reachy_mini import ReachyMini
with ReachyMini() as mini:
mini.media.start_recording()
time.sleep(1.5) # warm-up + countdown buffer
print('GO — speak now', flush=True)
sr = mini.media.get_input_audio_samplerate()
chunks = []
start = time.time()
while time.time() - start < ${DURATION}:
sample = mini.media.get_audio_sample()
if sample is not None:
mono = sample.mean(axis=1) if sample.ndim == 2 else sample
chunks.append(mono)
else:
time.sleep(0.01)
mini.media.stop_recording()
audio = np.concatenate(chunks).astype(np.float32)
peak = float(np.abs(audio).max())
print(f'Captured {len(audio)/sr:.1f}s @ {sr}Hz, peak={peak:.3f}', flush=True)
# Normalize + convert to int16
if peak > 0:
audio = audio / peak * 0.9
pcm = (audio * 32767).astype(np.int16)
with wave.open('/tmp/voice_ref.wav', 'wb') as f:
f.setnchannels(1); f.setsampwidth(2); f.setframerate(sr)
f.writeframes(pcm.tobytes())
\""
sshpass -p 'root' scp pollen@reachy-mini.local:/tmp/voice_ref.wav "${OUTPUT}"
echo "Saved ${OUTPUT}"