代码剥离 - 助手本体、TTS 服务、P0 基准脚本、ESP32 固件移到 ~/workspace/ai-companion (独立 git 仓库),本目录只留方案、P0 报告与硬件记录 - 因此删除 esp32_smile_test.ino / esp32_touch_eyes.ino - 同时移除已失效的 .gitignore(原本只为挡住模型体积,代码已搬走) 方案 v2.4 重大变更:架构从"单机"改为"服务器即助手、本地为客户端" - 起因:实测 N5095 无 AVX2,跑不动可用的语音模型 - N5095 降为客户端/外设桥,只做 USB 接入、DoA、WebRTC 客户端 - 实施阶段表加进度列(P0/P1/P3/P4a 部分完成),新增三条验收要求 - 采购清单按实际到货更新,标出屏幕形态待确认(2.8寸 vs 原定1.28寸圆屏) 技术栈(实测更新) - LLM:DeepSeek deepseek-flash(云端,必须关思考);本机 27B 已停用 - TTS:Qwen3-TTS 1.7B(服务器 3090);vits-zh-ll 因音色不满意被替换 - STT:Zipformer int8 未变,中文识别率仍是未闭合项 新增两份 P0 报告 - P0-Qwen3TTS报告.md:TTS 换型过程与实测 - P0-DeepSeek验证报告.md:四条路径验证 两份旧报告加了状态说明(结论仍有效但选型已变), 并修正了随代码移走而失效的引用与复现命令。
5.7 KiB
P0 报告 · Qwen3-TTS 选型与部署
日期:2026-09-15。起因:用户试听后反馈 vits-zh-ll 音色不满意, 要求换"体量更大效果更好的模型",并提出 LLM 可以用线上 API、 把 GPU 让给语音。
服务:~/tts-qwen/(服务器)· 模型:~/tts-service/models/Qwen3-TTS-1.7B-CustomVoice
一、为什么选 Qwen3-TTS
查证后的结论(来源见文末):
- 实时对话场景的首选是 Qwen3-TTS 或 CosyVoice 2/3; IndexTTS2 和 F5-TTS 虽然中文指标也强,但流式不是它们的目标,不做实时助手首选
- Qwen3-TTS 官方标称流式首包 97ms,RTX 3090 上实测约 130ms
- 官方推荐硬件就是 RTX 3090/4090 24GB,与现有硬件吻合
- Apache-2.0,10 种语言 + 中文方言
- 9 个预置音色 + 自然语言风格指令(这是 vits 那一代没有的能力)
二、部署路上撞到的两个障碍
障碍 1:vLLM-Omni 装不起来能用的服务
原本的打算是走 vLLM-Omni(它提供 OpenAI 兼容的流式 /v1/audio/speech
分块接口,是延迟最优的路径)。两个问题叠加:
--task-type参数没被注册。vllm-omni 把 CLI 扩展注册在vllm.general_plugins,但 vllm 0.28 的 serve 解析器没加载它,vllm-omni serve --help里根本看不到这个参数。- 绕过后仍崩溃。模型架构能正确识别
(
Resolved architecture: Qwen3TTSForConditionalGeneration), 但 vLLM 因为模型 config 里的 mrope 参数 (transformers 5.14 报Unrecognized keys in rope_parameters: {'interleaved', 'mrope_section'})把它当多模态模型走 mm 路径, 崩在assert isinstance(model, SupportsMRoPE)。
根因是 transformers / vllm / vllm-omni 三者的版本组合问题, 不是配置错误。没有继续深挖。
障碍 2:qwen-tts 与 vllm-omni 依赖互斥
改用厂商自己的 qwen-tts 包后,发现它会把 transformers 从
5.14.1 降到 4.57.3、huggingface_hub 从 1.31.0 降到 0.36.2,
直接破坏 vllm-omni(报 StrictDataclassFieldValidationError)。
结论:两者不能共存于一个 venv。 最终分两个 venv, TTS 跑独立进程,助手通过 HTTP 调用。
三、实测数据
硬件:RTX 3090 24GB,1.7B 模型,非流式,Serena 音色。
| 文本 | 音频时长 | 合成耗时 | RTF |
|---|---|---|---|
| 好的,我记住了。(8字) | 1.52s | 1429ms | 0.94 |
| 今天北京晴转多云…(19字) | 3.36s | 1881ms | 0.56 |
| 我查了一下,明天上午…(26字) | 4.64s | 2527ms | 0.55 |
关键发现:短句 RTF 反而最差。 每次调用有约 1 秒的固定开销,与文本长度无关 (talker 模型自回归解码的启动成本 + 声码器解码)。
这条推翻了"让模型先说个短句来降低首音延迟"的直觉—— 短句单位时间成本更高,反而不划算。
输出:24kHz 单声道(vits-zh-ll 是 16kHz,接播放链路时注意重采样)。
四、用户选定的配置
试听样本见 out/tts_qwen/。用户选定:
音色:Serena —— 温暖柔和的年轻女声
指令:用轻快活泼的语气说
(instruct_7849.wav,1.44s。通过时长比对确认对应关系:
默认 2.56s、轻快活泼 1.44s、温柔关切 1.72s。)
9 个预置音色中 5 个是中文: Vivian(明亮微冷)、Serena(温暖柔和)、Uncle_Fu(低沉男声)、 Dylan(京腔男声)、Eric(成都话男声)。
五、代价:首音延迟涨了 8.6 倍
| 配置 | 首音延迟 |
|---|---|
| 本地 Qwen3.8-27B + vits-zh-ll | 179 ms |
| DeepSeek + Qwen3-TTS | 1545 ms |
拆解这 1545ms:DeepSeek 网络往返约 600ms + Qwen3-TTS 固定开销约 900ms。
加上 VAD 判定(200–500ms),总首音落在 2.0–2.2 秒, 方案 P1 目标是 P50≤2 秒——刚好卡线,没有余量。
换来的是音质从"用户不满意"到"用户试听选定"。
六、速度还能救,但都受阻
| 手段 | 预估收益 | 障碍 |
|---|---|---|
| flash-attn | 1.5–2× | 预编译 wheel 最高支持 torch 2.9,本机 2.14;源码编译要从 GitHub 拉依赖而该机下 GitHub release 断连 |
| 真流式(接管 codes→音频 解码循环) | 可绕开约 900ms 固定开销 | qwen-tts 封装把解码放在全部 codes 生成完之后,拿不到增量;要自己实现 |
| 降 torch 版本装 flash-attn | 1.5–2× | 会牵动 vLLM 0.28 的依赖 |
真流式的收益最大——那 900ms 固定开销正是流式能覆盖掉的部分。 但工作量也最大。
七、备份路线(未采用)
若 Qwen3-TTS 的速度最终不可接受,可考虑的替代:
- CosyVoice 2/3:同为实时对话第一梯队,Apache-2.0,工程成熟度最高, 18+ 中文方言。但搜索结果里首包延迟数据有冲突(150ms vs 1.5s),需实测。
- 云端 TTS API:最快出效果,但助手要说的话会出网, 与方案"不自动发送音频到第三方"的口径有冲突(发的是文本不是音频, 但内容一样出网),且按量计费。
未测:CosyVoice 的本地部署与本机延迟。
八、来源
- 2026 开源 TTS 模型横向对比
- Best Open-Source TTS Models for Production in 2026
- Qwen3-TTS 与 vLLM 集成部署
- Speech Generation on vLLM-Omni:性能优化
- vllm-omni PR #3932:Code2Wav CUDA Graph 调优
- 模型 README:
~/tts-service/models/Qwen3-TTS-1.7B-CustomVoice/README.md