第二章:系统架构总览:端侧与云侧架构、模块划分、数据流与信号流设计

好,咱们正式开始搭建语音交互系统。这一章,我带你从高空俯瞰整个系统长什么样。

很多人一上来就扎进代码细节,结果越写越乱。我个人的习惯是——先画一张架构图。哪怕画在餐巾纸上都行。你得知道你的系统分几块,数据怎么流,信号怎么传。

说白了,车载语音交互系统就两大块:端侧(车机本地)和云侧(云端服务器)。这两者配合,才能实现流畅的语音体验。

2.1 端侧架构:车机本地的大脑

端侧,就是车机上的那块芯片和跑在上面的软件。为什么要有端侧?你想想看,如果每次说“打开空调”都要等网络往返,那体验得多糟糕。我在项目中遇到过,网络信号不好的地下车库,语音直接瘫痪。所以,端侧必须能处理一部分任务。

端侧架构通常分为以下几个模块:

  • 音频采集模块:负责从麦克风阵列获取原始音频。嗯,这里要注意,不是简单录音,还要做回声消除、降噪、波束成形。我见过不少项目,麦克风选型没做好,导致远场唤醒率极低。
  • 唤醒词引擎:持续监听音频流,检测预设的唤醒词(比如“你好,小驰”)。这个模块必须低功耗、低延迟。我曾经踩过一个坑——唤醒词模型太大,导致车机待机时CPU占用过高,电瓶亏电。
  • 语音识别(ASR)前端:将唤醒后的语音片段进行特征提取,准备发送给云端做完整识别。端侧也会做一部分轻量级识别,比如本地命令词。
  • 本地技能引擎:处理一些不需要联网的技能,比如“打开车窗”、“调高音量”。这些指令必须秒级响应。
  • 音频播放模块:负责合成语音(TTS)的播放,以及系统提示音的管理。

核心原则:端侧负责“快”和“稳”,云侧负责“强”和“全”。

2.2 云侧架构:云端的大脑

云侧,说白了就是后端服务集群。它处理端侧搞不定的复杂任务。比如,你想问“附近有什么好吃的川菜馆”,这需要联网搜索,端侧做不了。

云侧架构的模块划分,我习惯这样分:

  • 接入层:负责接收端侧发来的音频流或文本。通常用WebSocket或HTTP/2协议。这里要做好负载均衡和鉴权。我记得有一次,某个车型的并发量突然暴增,接入层没做限流,直接把后端打挂了。
  • 语音识别(ASR)引擎:将音频流转换成文本。这是云侧的核心能力之一。它支持多语种、多方言、自定义热词。比如,你车机里有个“导航到望京SOHO”,这个“望京SOHO”就得加到热词表里。
  • 自然语言理解(NLU)引擎:理解文本的意图。比如,“我有点冷” -> 意图是“调节空调温度”,槽位是“温度调高”。NLU的准确率直接决定了用户体验。
  • 对话管理(DM)引擎:管理多轮对话的状态。比如,你说“导航到天安门”,系统问“走高速还是普通路”,你回答“高速”。DM要记住刚才的上下文。
  • 技能执行层:调用具体的服务。比如,导航技能调用地图API,音乐技能调用音乐库API。
  • 语音合成(TTS)引擎:将文本转换成自然流畅的语音。现在流行用神经网络TTS,声音更自然。

个人经验:云侧架构一定要设计成无状态的。这样方便水平扩展。我见过一个项目,把用户会话状态存在本地内存里,结果一扩容,用户全掉线了。

2.3 模块划分:一张表说清楚

为了方便你理解,我把端侧和云侧的核心模块整理成了一张表。你设计系统时,可以直接参考这个划分。

层级 模块名称 核心职责 部署位置
端侧 音频采集 麦克风阵列管理、AEC、降噪 车机DSP/CPU
端侧 唤醒引擎 关键词检测、低功耗监听 车机NPU/CPU
端侧 本地ASR 本地命令词识别 车机CPU
端侧 本地技能 车辆控制、本地设置 车机MCU/CPU
云侧 接入层 连接管理、鉴权、限流 云端服务器
云侧 ASR引擎 大词汇量连续语音识别 云端GPU/CPU
云侧 NLU引擎 意图识别、槽位填充 云端CPU
云侧 DM引擎 多轮对话、状态跟踪 云端CPU
云侧 TTS引擎 文本转语音 云端GPU/CPU

2.4 数据流设计:语音是怎么变成动作的

数据流,就是语音信号从麦克风到最终执行动作的完整路径。我把它分成几个关键步骤:

  1. 音频采集:麦克风采集到模拟信号,经过ADC转换成数字信号。采样率通常是16kHz,16bit。
  2. 前端处理:做回声消除、降噪、波束成形。这一步很关键,直接影响识别率。我见过一个项目,没做回声消除,车机自己播放音乐时,语音识别全是音乐声。
  3. 唤醒检测:端侧唤醒引擎持续分析音频流。一旦检测到唤醒词,就启动后续流程。
  4. 音频上传:端侧将唤醒后的音频片段(通常是VAD检测到的语音段)压缩后上传到云端。常用编码格式有Opus、Speex。
  5. 云端识别:云端ASR引擎将音频转成文本。这里会用到语言模型和声学模型。
  6. 语义理解:NLU引擎分析文本,提取意图和槽位。比如,“导航到人民广场” -> 意图:导航,槽位:目的地=人民广场。
  7. 技能执行:DM引擎根据意图调用对应的技能。比如,导航技能调用地图SDK,计算路线。
  8. 结果返回:云端将执行结果(比如导航路线)和TTS文本返回给端侧。
  9. 端侧执行:端侧播放TTS语音,并执行具体操作(比如显示导航界面)。

避坑指南:我曾经遇到过一个性能问题——端侧上传音频时,没有做静音检测(VAD),结果把一大段静音也上传了。这不仅浪费带宽,还导致云端ASR识别出大量空文本。所以,一定要在端侧做好VAD。

2.5 信号流设计:谁在跟谁说话

信号流,关注的是模块之间的通信协议和接口。说白了,就是各个模块之间怎么“打招呼”。

我把它分为两类:

  • 端侧内部信号流:通常通过进程间通信(IPC)或共享内存。比如,音频采集模块把处理后的音频数据通过共享内存传给唤醒引擎。这样做延迟最低。
  • 端侧与云侧信号流:通常通过WebSocket或HTTP/2。WebSocket适合双向实时通信,比如音频流上传和TTS流式返回。HTTP/2适合请求-响应模式,比如查询天气。

这里我给出一个典型的信号流设计示例:

// 端侧 -> 云侧:音频流上传
// 使用WebSocket,二进制帧格式
{
  "type": "audio_data",
  "session_id": "abc123",
  "sequence": 1,
  "payload": "<base64编码的音频数据>"
}

// 云侧 -> 端侧:识别结果
{
  "type": "asr_result",
  "session_id": "abc123",
  "text": "导航到人民广场",
  "is_final": true
}

// 云侧 -> 端侧:技能执行结果
{
  "type": "skill_result",
  "session_id": "abc123",
  "skill": "navigation",
  "data": {
    "route": "<路线数据>",
    "tts_text": "已为您规划路线,全程10公里"
  }
}

嗯,这里要注意一点:信号流设计一定要考虑超时和重试。网络是不稳定的。我建议端侧设置一个3秒的超时,如果云端没响应,就提示用户“网络开小差了”。

2.6 架构设计的几个关键原则

最后,我总结几条我在项目中反复验证过的原则:

  • 端侧优先:能本地处理的,绝不走云端。这能保证基本体验。
  • 异步解耦:端侧和云侧之间用消息队列解耦。比如,端侧上传音频后,不用等云端处理完,可以继续做其他事。
  • 降级设计:云端挂了,端侧要能独立工作。至少本地命令词要能用。我见过一个项目,云端一挂,整个语音系统都瘫痪了,连“打开空调”都做不了。
  • 可观测性:每个模块都要有日志和监控。出了问题,能快速定位。我曾经靠一条日志定位到一个NLU模型加载失败的问题。

好了,这一章的内容就到这里。你理解了端侧和云侧的划分,掌握了数据流和信号流的设计思路,接下来就可以深入到每个模块的细节了。下一章,我们聊聊音频采集和前处理——这可是语音系统的“耳朵”,搞不好,后面全白搭。