Gemini Live + Reachy Mini:构建会说话的桌面机器人
演示用实时语音大模型控制物理机器人的技术栈。教程质量可,但应用场景局限于机器人爱好者。
演示用实时语音大模型控制物理机器人的技术栈。教程质量可,但应用场景局限于机器人爱好者。
想象一个微小的桌面机器人,它能听你说话、实时回答、按命令跳舞、追踪你的脸,并偶尔讲个老爸笑话——所有这些都由 Gemini Live API 驱动。
Reachy Mini 对话应用正是这样做的。它是一个开源 Python 应用,将 Pollen Robotics 的 Reachy Mini 与实时语音 LLM 相连,使机器人能够进行全双工音频对话,同时通过头部运动、触须摆动、舞蹈和情感来表达自己。
在本教程中,你将学到:
该应用分为四个相互配合的层:
┌─────────────┐
│ Your voice │ Microphone audio (16-bit PCM, 16 kHz)
└──────┬──────┘
▼
┌─────────────────────────────────────┐
│ fastrtc (low-latency WebRTC I/O) │
│ ─ streams audio to/from the LLM │
│ ─ resamples between sample rates │
└──────┬──────────────────┬───────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Gemini Live │ │ OpenAI Realtime │ (pick one via MODEL_NAME)
│ Handler │ │ Handler │
└──────┬───────┘ └──────┬───────────┘
│ │
▼ ▼
┌─────────────────────────────────────┐
│ Tool dispatch layer │
│ ─ dance, play_emotion, camera, │
│ move_head, head_tracking, ... │
└──────┬──────────────────────────────┘
▼
┌─────────────────────────────────────┐
│ MovementManager (60 Hz loop) │
│ ─ sequential primary moves │
│ ─ additive secondary offsets │
│ (speech wobble + face tracking) │
│ ─ idle breathing │
└──────┬──────────────────────────────┘
▼
┌─────────────┐
│ Reachy Mini │ Robot hardware / simulator
└─────────────┘
应用的核心是 AsyncStreamHandler(来自 fastrtc 库)。默认后端是 Gemini Live(gemini_live.py 中的 GeminiLiveHandler),它使用 Google GenAI SDK 通过 session.send_realtime_input() 进行双向音频流传输。
还可以使用替代的 OpenAI Realtime 后端(openai_realtime.py 中的 OpenaiRealtimeHandler),如果你更喜欢通过 OpenAI API 的 WebSocket 流传输。通过设置 MODEL_NAME 环境变量在它们之间切换——应用的其余部分不知道或不关心哪个后端是活跃的。
以下是 Gemini 处理器内部的简化流程:
# 1. Microphone → Gemini
async def receive(self, frame):
pcm_bytes = audio_to_int16(frame).tobytes()
await self.session.send_realtime_input(
audio=types.Blob(data=pcm_bytes, mime_type="audio/pcm;rate=16000")
)
# 2. Gemini → Speaker
async def _run_live_session(self):
async with client.aio.live.connect(model=..., config=...) as session:
async for response in session.receive():
if response.server_content and response.server_content.model_turn:
for part in response.server_content.model_turn.parts:
audio_array = np.frombuffer(part.inline_data.data, dtype=np.int16)
await self.output_queue.put((24000, audio_array))
if response.tool_call:
await self._handle_tool_call(response)
音频在 16 kHz 输入,24 kHz 输出,转录和工具调用通过相同的会话流动。
当 LLM 决定机器人应该做什么——跳舞、环顾四周、展示情感——时,它会发出函数调用。应用在 OpenAI 和 Gemini 格式之间自动转换这些,然后通过 BackgroundToolManager 分发它们,这样音频流永远不会被阻止:
LLM says: "dance(name='macarena')"
→ BackgroundToolManager starts a task
→ Task calls MovementManager.queue_move(MacarenaMove)
→ Result sent back to the LLM so it can narrate what happened
内置工具包括:
MovementManager 在专用线程中运行 60 Hz 控制循环。它混合两种类型的运动:
主要运动(舞蹈、情感、goto 姿态)从队列中顺序运行。一次只能播放一个。
次要偏移(语音反应晃动、面部追踪)是叠加的——它们层叠在任何播放的主要运动之上。
当没有任何事情发生时,机器人自动开始温和的呼吸动画——一个微妙的上下摇晃,带有触须运动——使其看起来总是活着的。
当连接相机时,Gemini 处理器运行 1 FPS 视频循环,持续向模型发送 JPEG 帧:
async def _video_sender_loop(self):
while not self._stop_event.is_set():
frame = self.deps.camera_worker.get_latest_frame()
_, buffer = cv2.imencode(".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, 70])
await self.session.send_realtime_input(
video=types.Blob(data=buffer.tobytes(), mime_type="image/jpeg")
)
await asyncio.sleep(1.0)
这给了机器人被动的视觉背景——它可以评论它看到的东西,而不需要你要求它看。
在你开始之前,确保你有:
没有机器人?你仍然可以探索代码并在模拟模式下运行——SDK 包括一个 MuJoCo 模拟器和一个桌面模拟。
该项目使用 uv 进行快速依赖管理(pip 也可以)。
# Clone the repo
git clone https://github.com/pollen-robotics/reachy_mini_conversation_app.git
cd reachy_mini_conversation_app
# Create a virtual environment (macOS example)
uv venv --python python3.12 .venv
source .venv/bin/activate
# Install dependencies
uv sync
想要面部追踪、本地视觉或 YOLO?安装匹配的额外选项:
uv sync --extra mediapipe_vision # Lightweight head tracking
uv sync --extra yolo_vision # YOLO-based face detection
uv sync --extra local_vision # On-device VLM (SmolVLM2, GPU recommended)
uv sync --extra all_vision # Everything
cp .env.example .env
打开 .env 并填充:
# Your Gemini API key — that's all you need to get started
GEMINI_API_KEY=your-gemini-api-key-here
这是最小要求——应用默认为 Gemini Live。完整的选项列表:
对话应用通过 Reachy Mini SDK 守护程序与机器人通信。守护程序作为 Reachy Mini SDK 设置的一部分安装——不在对话应用的 .venv 内。
打开一个单独的终端并激活 SDK 的虚拟环境:
# Navigate to wherever you cloned/installed the Reachy Mini SDK
cd path/to/reachy_mini
source reachy_mini_env/bin/activate
然后启动守护程序(保持此终端运行):
# Physical robot — auto-detects USB connection
reachy-mini-daemon
# Or simulation mode
reachy-mini-daemon --simulation
重要:守护程序必须在整个会话期间在自己的终端中保持运行。切换回你的对话应用终端(激活了 .venv)进行下一步。
如果在启动对话应用时看到 TimeoutError,说明守护程序没有运行。
在你的第 1 步终端(激活了对话应用的虚拟环境)中,运行:
reachy-mini-conversation-app
就是这样!机器人将开始温和地呼吸,你可以开始说话。它默认在控制台模式下运行——你的终端成为界面。
想要一个具有实时转录和聊天机器人面板的可视界面?添加 --gradio:
reachy-mini-conversation-app --gradio
这在 http://127.0.0.1:7860 启动一个 Gradio 应用,你可以在其中看到对话、切换个性和查看相机帧。
# With MediaPipe head tracking
reachy-mini-conversation-app --head-tracker mediapipe
# Audio-only (no camera)
reachy-mini-conversation-app --no-camera
# Verbose logging
reachy-mini-conversation-app --debug
# Connect to a specific robot on the network
reachy-mini-conversation-app --robot-name my-reachy
这是有趣的地方。该应用使用配置文件系统——纯文本文件控制机器人认为自己是谁。
profiles/
├── default/
│ ├── instructions.txt # System prompt
│ └── tools.txt # Which tools are enabled
├── mars_rover/
│ ├── instructions.txt
│ └── tools.txt
├── noir_detective/
│ ├── instructions.txt
│ └── tools.txt
└── ...
在 profiles/ 下创建一个文件夹:
mkdir profiles/pirate_captain
编写一个 instructions.txt:
## IDENTITY
You are Captain Byte, a swashbuckling robot pirate who speaks in nautical
metaphors and ends every sentence with "Arrr" or a pirate-themed quip.
## RESPONSE RULES
Keep responses to 1-2 sentences. Be helpful first, pirate second.
Always refer to the user as "matey" or "landlubber".
创建一个 tools.txt,列出机器人可以使用的工具:
dance
play_emotion
move_head
camera
head_tracking
# In your .env file
REACHY_MINI_CUSTOM_PROFILE="pirate_captain"
或从 Gradio UI 的"个性"面板实时切换——无需重新启动。
配置文件系统支持可组合的提示。与其复制文本,不如引用共享片段:
# instructions.txt
[identities/witty_identity]
[passion_for_lobster_jokes]
You love to dance and will look for any excuse to bust a move.
每个 [placeholder] 从 src/reachy_mini_conversation_app/prompts/ 中提取。这保持配置文件 DRY,让你混合和匹配个性特征。
你甚至可以通过在配置文件文件夹中放置 Python 文件来添加特定于配置的工具。例如,内置示例配置包含一个 sweep_look.py 工具,使机器人缓慢扫描房间:
# profiles/example/sweep_look.py
from reachy_mini_conversation_app.tools.core_tools import Tool
class SweepLookTool(Tool):
name = "sweep_look"
description = "Slowly look around the room in a sweeping motion."
async def run(self, args, deps):
# Queue a sequence of head movements...
return {"status": "done", "description": "Finished looking around"}
在 tools.txt 中启用它:
dance
play_emotion
sweep_look # Your custom tool
让我们追踪一个完整的对话轮次,看看所有的部分是如何配合的。
当应用启动时,它构建一个 LiveConnectConfig,包括:
live_config = types.LiveConnectConfig(
response_modalities=[types.Modality.AUDIO],
system_instruction=types.Content(parts=[types.Part(text=instructions)]),
speech_config=types.SpeechConfig(
voice_config=types.VoiceConfig(
prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name="Kore"),
),
),
tools=[{"function_declarations": declarations}],
input_audio_transcription=types.AudioTranscriptionConfig(),
output_audio_transcription=types.AudioTranscriptionConfig(),
)
你的麦克风音频通过 fastrtc → receive() → 重新采样到 16 kHz → 作为原始 PCM 字节发送到 Gemini。
响应流在单个轮次中可以包含多种类型的数据:
工具调用在后台任务中运行,因此音频流不被阻止。当工具完成时,其结果作为 FunctionResponse 发送回 Gemini,模型可以讲述发生了什么:
"I just did a little happy dance for you! 💃"
如果没有人说话 15+ 秒,并且机器人是空闲的,处理器发送一个推动:
"You've been idle for a while. Feel free to get creative — dance,
show an emotion, look around, do nothing, or just be yourself!"
这触发机器人自主选择一个动作——也许是跳舞,也许是好奇的头部倾斜——使交互即使在暂停期间也保持活跃。
只需按上面所示运行 reachy-mini-conversation-app。该应用连接到本地网络上的机器人守护程序。
该应用也可以部署到 Google Cloud Run,并与 Twilio 集成进行基于电话的对话。这是一个更高级的设置——查看 repo 的部署文档了解详情:
该 repo 随附 15 个现成的配置供你开始:
尝试它们!每一个都完全改变机器人的行为和响应方式。
Reachy Mini 对话应用展示了当你将实时语音 AI 与表达机器人结合时可能的效果。使其工作的关键设计决策:
整个项目在 Apache 2.0 许可证下开源。Fork 它,给你的机器人一个个性,让我们知道你构建了什么!
💃 Dances Library (Hugging Face)
😊 Emotions Library (Hugging Face)
一些评论可能仅对登录访问者可见。登录以查看所有评论。
关于进一步的行动,你可能会考虑屏蔽此人和/或举报滥用。