11 KiB
11 KiB
云端无人机语音服务 (Cloud Voice Service)
基于 FastAPI + WebSocket 的云端语音交互服务,为无人机提供 LLM 意图识别 和 TTS 文字转语音 能力。
📋 特性
- ✅ 协议规范: 完整实现
Cloud Voice Protocol v1.0 (text_uplink) - ✅ LLM 意图识别: 阿里云百炼 Qwen 模型,区分飞控指令与闲聊
- ✅ TTS 本地推理: Piper-TTS 高效本地合成语音 (24kHz PCM)
- ✅ 流式输出: LLM 结果 + TTS 音频块流式下发
- ✅ 并发支持: 最多 4 路无人机并发会话
- ✅ 模块化架构: 易于扩展新的 LLM/TTS 提供者
📁 项目结构
voicellmcloud/
├── app/ # 主应用
│ ├── main.py # FastAPI 入口
│ ├── config.py # 配置管理
│ ├── protocols/ # 协议层
│ │ ├── models.py # 消息数据模型
│ │ └── validators.py # 协议验证
│ ├── websocket/ # WebSocket 管理
│ │ ├── session.py # 会话管理
│ │ └── handler.py # 消息处理
│ ├── services/ # 业务服务接口
│ │ ├── llm_service.py # LLM 接口
│ │ ├── tts_service.py # TTS 接口
│ │ └── intent_service.py # 意图识别
│ ├── providers/ # 第三方服务实现
│ │ ├── dashscope_llm.py # 阿里云 LLM
│ │ └── piper_tts.py # Piper TTS
│ └── utils/ # 工具
│ ├── audio.py # 音频处理
│ └── logger.py # 日志
├── models/ # TTS 模型文件目录
├── requirements.txt # Python 依赖
├── .env # 环境配置
├── .env.example # 配置示例
├── start.sh / start.bat # 启动脚本
└── README.md
🚀 快速开始
1. 环境准备
# Python 3.10+
python --version
# 安装依赖
pip install -r requirements.txt
2. 下载 Piper TTS 模型
# 下载中文模型到 models/ 目录
python -m piper.download_voice zh_CN-huayan-medium
# 或手动下载
# https://huggingface.co/rhasspy/piper-voices/tree/v1.0.0/zh_CN/huayan/medium
# 将 zh_CN-huayan-medium.onnx 和 .json 放到 models/
3. 配置环境变量
# 复制配置示例
cp .env.example .env
# 编辑 .env,修改以下配置:
# - DASHSCOPE_API_KEY: 阿里云百炼 API Key (已预填)
# - TTS_MODEL_DIR: Piper 模型目录 (默认 models)
# - BEARER_TOKEN: 鉴权 Token (客户端需一致)
4. 启动服务
Linux/macOS:
chmod +x start.sh
./start.sh
Windows:
start.bat
或直接运行:
python -m uvicorn app.main:app --host 0.0.0.0 --port 8765 --reload
5. 验证服务
# 健康检查
curl http://localhost:8765/health
# 应返回:
# {"status":"ok","active_sessions":0,"llm_provider":"dashscope","tts_provider":"piper"}
🔧 配置说明
所有配置通过 .env 文件或环境变量设置:
| 配置项 | 默认值 | 说明 |
|---|---|---|
WS_HOST |
0.0.0.0 |
WebSocket 监听地址 |
WS_PORT |
8765 |
WebSocket 端口 |
BEARER_TOKEN |
drone-voice-cloud-token-2024 |
鉴权 Token |
DASHSCOPE_API_KEY |
- | 阿里云百炼 API Key |
LLM_MODEL |
qwen-plus |
LLM 模型 (qwen-turbo/plus/max) |
LLM_CONTEXT_TURNS |
4 |
保留历史对话轮数 |
TTS_PROVIDER |
piper |
TTS 提供者 |
TTS_VOICE_NAME |
zh_CN-huayan-medium |
Piper 语音名称 |
MAX_CONCURRENT_SESSIONS |
4 |
最大并发会话数 |
LOG_LEVEL |
INFO |
日志级别 |
📡 WebSocket 协议
完整协议见 CLOUD_VOICE_PROTOCOL_v1_text_uplink.md
连接地址
ws://<server-ip>:8765/v1/voice/session
基本时序
客户端 服务端
| |
|------ session.start -------------->|
| |
|<----- session.ready ---------------|
| |
|------ turn.text ------------------>|
| |
|<----- dialog_result --------------|
|<----- tts_audio_chunk (text) -----|
|<----- tts_audio_chunk (binary) ---|
|<----- turn.complete --------------|
| |
|------ session.end --------------->|
示例消息
session.start:
{
"type": "session.start",
"proto_version": "1.0",
"transport_profile": "text_uplink",
"session_id": "uuid-v4",
"auth_token": "your-token",
"client": {
"device_id": "drone-001",
"locale": "zh-CN",
"capabilities": {
"playback_sample_rate_hz": 24000,
"prefer_tts_codec": "pcm_s16le"
}
}
}
turn.text:
{
"type": "turn.text",
"proto_version": "1.0",
"transport_profile": "text_uplink",
"turn_id": "uuid-v4",
"text": "起飞然后在前方十米悬停",
"is_final": true,
"source": "device_stt"
}
🧪 测试
测试用例
-
闲聊: "今天天气怎么样"
- 预期:
routing=chitchat, TTS 播报闲聊回复
- 预期:
-
飞控指令: "起飞然后在前方十米悬停"
- 预期:
routing=flight_intent,actions=[takeoff, goto], TTS 播报 summary
- 预期:
-
返航: "返航"
- 预期:
routing=flight_intent,actions=[return_home]
- 预期:
-
非法音频消息: 发送
turn.audio_chunk- 预期:
error code=INVALID_MESSAGE
- 预期:
-
鉴权失败: 使用错误 token
- 预期:
error code=UNAUTHORIZED
- 预期:
Python 测试客户端
import asyncio
import json
import websockets
async def test_client():
uri = "ws://localhost:8765/v1/voice/session"
async with websockets.connect(uri) as ws:
# 1. 发送 session.start
await ws.send(json.dumps({
"type": "session.start",
"proto_version": "1.0",
"transport_profile": "text_uplink",
"session_id": "test-session-001",
"auth_token": "drone-voice-cloud-token-2024",
"client": {
"device_id": "test-drone",
"locale": "zh-CN",
"capabilities": {
"playback_sample_rate_hz": 24000,
"prefer_tts_codec": "pcm_s16le"
}
}
}))
# 接收 session.ready
ready = await ws.recv()
print(f"← {ready}")
# 2. 发送 turn.text
await ws.send(json.dumps({
"type": "turn.text",
"proto_version": "1.0",
"transport_profile": "text_uplink",
"turn_id": "test-turn-001",
"text": "你好,今天天气怎么样?",
"is_final": True,
"source": "device_stt"
}))
# 3. 接收响应
while True:
msg = await ws.recv()
if isinstance(msg, bytes):
print(f"← 音频数据 ({len(msg)} bytes)")
else:
data = json.loads(msg)
print(f"← {data['type']}: {json.dumps(data, ensure_ascii=False)}")
if data.get('type') == 'turn.complete':
break
asyncio.run(test_client())
🏗 架构设计
模块化层次
┌─────────────────────────────────────────┐
│ FastAPI Application │
│ (app/main.py) │
├─────────────────────────────────────────┤
│ WebSocket Handler │
│ (app/websocket/handler.py) │
├──────────┬──────────┬───────────────────┤
│ LLM │ TTS │ Intent Service │
│ Service │ Service │ (意图识别) │
├──────────┼──────────┼───────────────────┤
│DashScope │ Piper │ 协议模型/验证 │
│ (阿里云) │ (本地) │ │
└──────────┴──────────┴───────────────────┘
扩展新的 LLM/TTS 提供者
只需实现对应接口并注册:
# 1. 实现接口
class MyLLMService(LLMServiceInterface):
async def chat(...): ...
async def initialize(...): ...
async def shutdown(...): ...
# 2. 在 app/main.py 中添加
if settings.LLM_PROVIDER == "my_llm":
llm_service = MyLLMService()
📊 性能指标
| 指标 | 预期值 |
|---|---|
| LLM 推理延迟 | 1-3s (阿里云 qwen-plus) |
| TTS 首字节延迟 | <200ms (Piper 本地) |
| 音频采样率 | 24000 Hz |
| 音频格式 | PCM S16LE (mono) |
| 最大并发 | 4 sessions |
🔮 后续规划
- 支持本地 H200 部署 LLM (vLLM/TGI)
- 多语言 TTS 支持
- WebSocket TLS (WSS) 支持
- Prometheus 指标监控
- 会话持久化与断线重连
- Docker 容器化部署
📝 开发说明
添加新模块
# 创建模块目录
mkdir app/new_module
touch app/new_module/__init__.py
touch app/new_module/module.py
日志级别
# 修改 .env
LOG_LEVEL=DEBUG # 查看详细日志
LOG_LEVEL=INFO # 生产环境
调试技巧
# 在 handler.py 中添加断点
import pdb; pdb.set_trace()
❓ 常见问题
Q: Piper TTS 初始化失败?
# 检查模型文件是否存在
ls -lh models/zh_CN-huayan-medium.onnx
# 重新下载
python -m piper.download_voice zh_CN-huayan-medium
Q: LLM 调用超时?
# 检查 API Key
echo $DASHSCOPE_API_KEY
# 增加超时时间
LLM_TIMEOUT=60
Q: 客户端连接被拒绝?
# 检查 BEARER_TOKEN 是否一致
# 服务器 .env 中的 BEARER_TOKEN 必须与客户端 auth_token 一致
扩展阅读
- 项目总结与部署手册:整体能力、架构闭环、生产部署与排障
- 飞控意图 Schema v1、实施计划
- dialog_result v1 + confirm:签字基准(
protocol、confirm、user_input字符串) - 飞控门控历史备选:
flight_intent_pending、turn.confirmation
📄 许可证
内部项目 - 无人机云端语音交互服务
🤝 贡献
提交 Issue 或 Pull Request 以改进本项目。
版本: v1.0.0
更新日期: 2024-04-07
协议版本: Cloud Voice Protocol v1.0 (text_uplink)