山东大学软件学院项目实训-基于云端智能 Agent 的情绪互动机器人系统-个人博客(三)
本次工作概述
本项目实现了一个基于 Milk-V Duo S 开发板的智能语音陪伴机器人系统。系统采用云端协同架构,PC 端作为代理服务器处理核心逻辑,Duo 板负责音频采集和播放,通过 WebSocket 实现实时双向通信。
一、项目架构
1.1 系统组成
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Milk-V Duo │ │ PC 端代理 │ │ 云服务 │
│ (边端设备) │◄────────►│ (FastAPI) │◄────────►│ - 阿里云 ASR │
│ │ WebSocket│ │ HTTP/WS │ - DashScope LLM │
│ - 音频采集 │ │ - 协议转发 │ │ - Edge TTS │
│ - 音频播放 │ │ - TTS 生成 │ │ │
│ - 动作控制 │ │ - 情感分析 │ │ │
└─────────────┘ └──────────────┘ └─────────────────┘
1.2 技术栈
PC 端(Windows):
- Python 3.12 + FastAPI
- DashScope API(阿里云通义千问)
- Edge TTS(微软免费在线语音合成)
- websockets(WebSocket 通信)
- pydub(音频格式转换)
边端(Milk-V Duo S):
- Python 3 + websocket-client
- ALSA 音频系统(arecord/aplay)
- MCP01 USB 音频模块
二、主要工作内容
2.1 核心功能实现
语音交互流程
- 语音采集:Duo 板通过
arecord采集 16kHz 单声道 PCM 音频 - 语音识别:音频流经 WebSocket 发送至 PC 端,转发至阿里云 Realtime API 进行 ASR
- 智能对话:PC 端调用 DashScope LLM(qwen3.5-flash)生成回复文本和情感分析
- 语音合成:PC 端使用 Edge TTS 将回复文本转换为 24kHz PCM 音频
- 音频播放:PCM 音频分块传输至 Duo 板,通过
aplay实时播放
情感与动作控制
- LLM 返回结构化 JSON,包含
emotion(happy/sad/neutral)和action(wave/idle/sad 等) - Duo 端接收指令后可扩展控制舵机、LED、表情屏等外设
打断机制
- 支持用户插话打断 AI 发言
- 检测到新语音输入时自动停止当前播放
自动重连
- Duo 端实现断线自动重连机制(最多 10 次尝试)
- PC 端优化 WebSocket 心跳配置,避免连接超时断开
三、遇到的问题及解决方案
问题 1:TTS 音频无法播放(只有噪音)
现象:
Duo 端收到音频数据,但扬声器发出"滋滋啦啦"的噪音,无清晰语音。
原因分析:
Edge TTS 输出的是 MP3 格式音频,而 Duo 端的 aplay 期望接收 PCM S16_LE 格式。直接将 MP3 数据写入 aplay 导致解码错误。
解决方案:
在 PC 端使用 pydub 库将 Edge TTS 生成的 MP3 转换为 24kHz 单声道 16bit PCM 格式:
from pydub import AudioSegment
import io
audio_segment = AudioSegment.from_mp3(io.BytesIO(mp3_data))
audio_segment = audio_segment.set_frame_rate(24000).set_channels(1).set_sample_width(2)
pcm_data = audio_segment.raw_data
依赖安装:
pip install pydub
# 并安装 ffmpeg(音频转换工具)
问题 2:WebSocket 连接频繁断开
现象:
对话 1-2 次后连接断开,报错 keepalive ping timeout 或 AssertionError。
原因分析:
- LLM 响应时间较长(10-25 秒),超过 WebSocket 默认心跳超时
- websockets 库在某些版本存在心跳断言 bug
解决方案:
- 禁用 websockets 自动心跳:
ping_interval=None, ping_timeout=None - Duo 端增加重连机制,断线后自动重新连接
- 优化异常处理,使用
return_exceptions=True防止单个协程异常导致整体崩溃
问题 3:会话初始化超时
现象:
Duo 端显示"❌ 会话初始化超时",无法进入录音状态。
原因分析:
PC 端在连接阿里云后阻塞等待 session.updated 消息,但阿里云需要收到 Duo 端的配置后才返回该消息,形成死锁。
解决方案:
移除阻塞的初始化等待,让两个协程(接收板子消息、接收阿里云消息)并行运行,阿里云会在收到配置后自动返回 session.updated。
问题 4:LLM 响应超时
现象:
LLM 调用超过 15 秒超时,返回 fallback 错误消息。
原因分析:
默认的 qwen3.5-plus 模型响应较慢,且超时时间设置过短。
解决方案:
- 切换到更快的模型:
.env中设置DASHSCOPE_MODEL=qwen3.5-flash - 调整超时时间为 20 秒:
LLM_TIMEOUT=20.0 - 简化 Prompt,减少 token 数量
问题 5:reply_text 与 tts_text 不一致
现象:
屏幕显示的回复内容与扬声器播放的内容不同。
原因分析:
LLM 生成的 JSON 中 reply_text 和 tts_text 字段内容不一致。
解决方案:
- 优化 Prompt,明确要求两个字段必须完全相同
- 在 PC 端代码中添加兜底逻辑:如果
tts_text为空,则使用reply_text
if not tts_text or len(tts_text.strip()) == 0:
tts_text = reply_text
问题 6:Milk-V Duo 环境限制
现象:
尝试使用 apt-get install espeak-ng 失败,提示命令不存在。
原因分析:
Milk-V Duo 运行的是 Buildroot 系统,不是标准的 Debian/Ubuntu,不支持 apt-get。
解决方案:
改用纯 Python 方案(Edge TTS),通过 pip 安装依赖,避免系统级包管理。
问题 7:打断后无声音
现象:
按回车打断后,后续对话能收到消息但无声音播放。
原因分析:
打断时发送了 response.cancel 给阿里云,取消了正在生成的 TTS 音频,但 PC 端逻辑未正确处理这种情况。
解决方案:
修改 Duo 端打断逻辑,只停止本地播放,不发送 cancel 给服务器,让 TTS 继续生成完成。
四、关键代码片段
4.1 PC 端 TTS 生成与转换
async def generate_tts_audio(text: str) -> bytes:
"""使用 Edge TTS 生成音频并转换为 PCM"""
communicate = edge_tts.Communicate(text, "zh-CN-XiaoxiaoNeural")
mp3_data = b""
async for chunk in communicate.stream():
if chunk["type"] == "audio":
mp3_data += chunk["data"]
from pydub import AudioSegment
audio_segment = AudioSegment.from_mp3(io.BytesIO(mp3_data))
audio_segment = audio_segment.set_frame_rate(24000).set_channels(1).set_sample_width(2)
return audio_segment.raw_data
4.2 Duo 端音频播放
class AlsaPlayer:
def start_process(self):
self.proc = subprocess.Popen(
["aplay", "-D", self.device, "-f", "S16_LE", "-r", "24000", "-c", "1"],
stdin=subprocess.PIPE,
stderr=subprocess.DEVNULL
)
def write(self, data):
self.proc.stdin.write(data)
self.proc.stdin.flush()
4.3 机器人控制接口
# 在 Duo 端 on_message 中接收 agent.action
if msg_type == "agent.action":
action = data.get("action", "") # wave/idle/sad/happy
emotion = data.get("emotion", "") # happy/sad/neutral
# 示例:控制舵机
if action == "wave":
control_servo("hand", angle=90)
# 示例:控制 LED
if emotion == "happy":
set_led_color(0, 255, 0) # 绿色
五、性能优化
| 优化项 | 优化前 | 优化后 | 说明 |
|---|---|---|---|
| LLM 模型 | qwen3.5-plus | qwen3.5-flash | 响应时间从 25s 降至 7-10s |
| 超时设置 | 30s | 20s | 平衡响应速度与稳定性 |
| 音频分块 | 无 | 4800 字节/块 | 实现流式播放,降低延迟 |
| WebSocket 心跳 | 默认 | 禁用 | 避免 long-polling 导致的断连 |
| TTS 方案 | 阿里云 TTS | Edge TTS | 音质更好,无需额外 API 调用 |
六、待优化方向
- 流式 TTS:当前需等待完整音频生成后再播放,可改为边生成边传输
- 本地缓存:缓存常用回复的音频,减少重复 TTS 调用
- 多语言支持:扩展 Edge TTS 支持其他语言
- 离线模式:研究本地 TTS 引擎(如 Coqui TTS)以实现完全离线
- 动作同步:实现语音播放与机器人动作的精确同步
七、总结
成功实现了基于 Milk-V Duo 的智能语音机器人系统,解决了以下核心技术难点:
- 跨平台音频处理:MP3 到 PCM 的格式转换
- 实时流式通信:WebSocket 双向音频流传输
- 云端协同架构:合理分配计算任务,平衡性能与成本
- 鲁棒性设计:自动重连、异常处理、超时控制
系统目前已能稳定运行,支持自然的多轮对话、情感识别和语音交互,后续会扩展机器人动作控制。
更多推荐



所有评论(0)