智能语音送餐机器人交互系统(侧轻量化版)
面向树莓派的端侧轻量化语音交互系统:KWS 唤醒与命令识别、Silero VAD、ERES2Net 声纹验证、Matcha-TTS 应答,多进程 + ZeroMQ 单机自闭环
语音送餐机器人交互系统 技术文档(树莓派端侧轻量化版)
版本定位:面向树莓派(Raspberry Pi)等轻量嵌入式设备的”端侧一体化、脱离服务器”版本。 唤醒、命令识别、声纹、语音合成等所有语音能力都在单台设备本地推理闭环完成, 不依赖任何云端/服务器语音算力,也不为语音处理联网;MQTT 仅用于送餐订单业务的对外上报。 本文档基于当前工作区代码编写,描述系统采用的实现方法与逻辑,不含具体代码。
1. 系统概述
本项目是一套面向送餐机器人的语音交互与说话人识别系统,实现了”唤醒 → 听令 → 声纹验证 → 执行送餐任务 → 语音应答”的完整闭环。
本版本为端侧轻量化版:整套系统以单台树莓派(或 Jetson 类设备)为唯一计算载体独立运行,所有语音链路(KWS 唤醒与命令识别、Silero VAD、ERES2Net 声纹、Matcha-TTS 合成)均为本地 ONNX 模型的 CPU 推理,系统整体脱离服务器/云端运行。与”依赖远程服务器做大词表 ASR、云端声纹、在线 TTS”的重型方案不同,本版本刻意采用 KWS 关键词检索 + 规则解析 + 端侧小模型 + 预合成语音 的低算力组合,把语音交互的时延与功耗收敛在设备本地。
主要能力包括:
- 关键词唤醒:持续监听并识别唤醒词”小七小七”
- 语音命令识别:通过流式关键词识别(KWS)检出”楼层 + 房间 + 动作”类短语命令(本方案不使用整句大词表 ASR,而是用关键词检索 + 规则解析实现命令意图)
- 声纹(说话人)系统:声纹注册、声纹识别(识别是谁在说话)、声纹验证(命令执行前确认说话人身份)
- 语音合成应答:预合成语音优先、实时 Matcha-TTS 兜底
- 任务执行:送餐导航订单累积与提交、开门/关门、充电、取消订单等指令,并将订单通过 MQTT 上报局域网业务端(该上报为可选项,语音主流程不依赖)
- 交互式状态机:空闲(IDLE)/ 唤醒(AWAKE)两种工作状态,超时自动退出唤醒
1.1 整体架构(多进程 + ZeroMQ 消息总线)
系统采用多进程解耦架构,但全部进程运行在同一台树莓派等设备上(单机部署,无服务器节点):由 launch.py 一键启动 4 个常驻进程,进程间走 ZeroMQ 本机回环(tcp://localhost)。进程拆分的目的仅是”模块隔离、崩溃互不影响、便于单独重启”,并不是分布式部署,设备独立即可提供完整语音服务。
┌─────────────────────────────────────────────┐
│ launch.py(进程管理器) │
└───────┬──────────┬──────────┬────────┬──────┘
▼ ▼ ▼ ▼
audio_capture controller recognition agv.py
(音频采集/KWS) (主控状态机) (识别/声纹) (任务执行)
| 进程 | 文件 | 职责 |
|---|---|---|
| 音频采集 | audio_capture.py | 麦克风采集(16kHz/0.1s 帧)、自动增益(AGC)、流式 KWS 检测、音频分发;运行 TCP 切换服务(8889),可在”正常识别 / 声纹注册”两种模式间切换,声纹注册时以子进程方式拉起 register_speaker.py |
| 主控 | controller.py | 全局状态机(IDLE/AWAKE);接收唤醒事件、调度识别开关、播放 TTS、管理唤醒超时、调节音量 |
| 识别 | recognition.py | 语音端点检测与分段、KWS 结果归属、声纹识别/验证、命令解析、向 AGV 分发命令 |
| 执行 | agv.py | 订单累积、导航/门控/充电/下单/取消等动作处理、请求 TTS 播报、写订单日志、上报执行完成 |
| 注册服务 | register_speaker.py | 声纹注册 TCP 服务(8888),由 audio_capture 按需启动为子进程 |
| 远端采集客户端 | client_mci_new.py | (可选变体,树莓派单机形态默认不启用)运行在远端麦克风工控机上做采集与本地唤醒;本机版本由 audio_capture 用本地声卡直接采集 |
说明:系统所有常驻模块在树莓派单机上即可完整运行;
client_mci_new.py/client/只是面向”麦克风不在机器人本体上”的特殊部署场景提供的可选项,不是本版本的必要组件。
1.2 进程间通信(ZeroMQ 端口拓扑)
进程间全部使用 ZeroMQ 通信(PUSH/PULL 单向流水、REQ/REP 控制应答、PUB/SUB 下行推送),端口定义集中在 config.py:
| 端口 | 模式 | 发送方 → 接收方 | 用途 |
|---|---|---|---|
| 5556 (REP_CTRL) | REQ/REP | controller → audio_capture | 控制音频采集(开关识别/KWS/清队列) |
| 5557 (PORT_WAKE_PUSH) | PUSH/PULL | audio_capture → controller | 唤醒事件上报 |
| 5558 (PUSH_REC) | PUSH/PULL | audio_capture → recognition | 实时音频帧流(含帧序号、时间戳) |
| 5562 (PORT_REC_CTRL) | REQ/REP | controller → recognition | 控制识别(start/stop/重载声纹库/查状态) |
| 5572 (PORT_AGV_PUSH_CMD) | PUSH/PULL | recognition → agv | 已通过声纹验证的命令下发 |
| 5573 (PORT_AGV_COMPLETE) | PUSH/PULL | agv → controller | 命令完成通知 / TTS 播报请求 / 音量调节请求 |
| 5574 (PORT_AGV_CTRL) | REQ/REP | controller → agv | AGV 控制(stop/状态查询) |
| 5575 (PORT_REC_ACTIVITY) | PUSH/PULL | recognition → controller | 语音活动心跳(防 AWAKE 超时) |
| 5576 (PORT_KWS_DETECTION) | PUSH/PULL | audio_capture → recognition | 流式 KWS 检测结果(非唤醒关键词) |
| 5553 (PORT_CLIENT_PUB) | PUB/SUB | controller → 客户端 | TTS 音频/清队列指令下行推送 |
| 8888 | TCP | 外部 → register_speaker | 声纹注册指令(enroll/list/delete) |
| 8889 | TCP | 外部 → audio_capture | 识别/注册模式切换指令 |
上表中 5553~5576 均为本机进程间端口(仅监听/连接 localhost),它们只是模块间通道,并非对外网络服务;对外仅保留 8888/8889(注册与模式切换,供本机或局域网内调试工具调用)与 MQTT 1883(订单上报)。所有语音处理全程不出设备。
音频帧协议:时间戳 + 头部(帧序号 seq + 元数据)+ float32 PCM 字节。recognition 依 seq 做丢帧检测。
2. 用到的模型与关键技术
2.1 模型清单
运行形态(端侧):所有模型均以 ONNX 文件随包存放在设备本地,推理引擎为 sherpa-onnx / onnxruntime,执行端一律
provider=cpu,运行期不产生任何云/服务器调用。每个模型选型都遵循端侧原则:能小则小(KWS 用轻量 Zipformer 流式解码并 int8 化)、能用小模型就不用大模型(声纹取 base 级 ERES2Net)、能离线预生成就不实时算(TTS 应答用预合成 wav)。
| 用途 | 模型/文件 | 运行引擎 | 说明 |
|---|---|---|---|
| 关键词识别(唤醒/命令) | sherpa-onnx KWS(sherpa-onnx-kws/):Zipformer transducer 流式关键词模型 encoder-...-chunk-16-left-64.int8.onnx + decoder-... + joiner-....int8.onnx + tokens.txt + keywords.txt | sherpa-onnx KeywordSpotter | int8 量化、chunk-16 流式编码、left-64 左上下文;通过 beam 解码在 38 个词条中检索 |
| 语音活动检测(VAD) | Silero VAD model/silero_vad.onnx | sherpa-onnx VoiceActivityDetector | 辅助/备用端点检测;min_silence_duration=0.7s、min_speech_duration=0.4s |
| 声纹特征提取 | 3D-Speaker ERES2Net model/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx(另有 large / int8 量化版) | sherpa-onnx SpeakerEmbeddingExtractor | 输入 16kHz 语音,输出说话人 embedding 向量并做 L2 归一化;相似度用余弦(内积)度量 |
| 语音合成(TTS) | Matcha-TTS 声学模型 tts_models/matcha-icefall-zh-baker/model-steps-3.onnx + Vocos 神经声码器 vocos-22khz-univ.onnx + lexicon.txt / tokens.txt;规则 FST phone.fst/date.fst/number.fst | sherpa-onnx OfflineTts(Matcha 配置) | 中文烘焙语料模型;FST 规则负责数字/日期等的文本规范化 |
关键词库
sherpa-onnx-kws/keywords.txt使用”拼音音素序列 + @词条”格式,覆盖:唤醒词”小七小七”、6 个房间短词(积厚厅/韶华厅/云程厅/行远厅/观潮厅等)、“第X层+房间”复合词、动作词(提高音量/降低音量/开门/关门/去送餐/取消送餐/取消订单)。设计上已删除短楼层词(如”第一层”)只保留房间短词与复合词,以避免短词先触发导致复合词漏检。
2.2 关键技术方案
- 命令识别 = KWS 关键词检索 + 规则解析:不是通用 ASR。audio_capture 逐帧喂 KWS,命中唤醒词发唤醒事件;唤醒态命中其他关键词则转发 recognition;recognition 用
kws_command_detector.py的文本规则(parse_keyword/parse_action)从关键词文本中解析出命令参数。 - 说话人识别 = embedding 余弦相似度:语音段 → ERES2Net embedding → 与库中说话人特征做余弦相似度 → 取最高分与阈值(0.4)比较判定身份。
- 多特征注册与融合:同一说话人可多次注册叠加特征,支持多种合并策略(见 §4.3)。
- 异步声纹推理:声纹在独立工作线程中推理,通过”命令结果队列 + 声纹结果队列 + segment_id”配对,避免阻塞音频主循环。
3. 唤醒与主控状态机(controller.py)
3.1 交互流程
IDLE(空闲,仅跑 KWS)
│ KWS 检测到"小七小七"
▼
AWAKE(唤醒态,暂停 KWS 非唤醒词、开启识别)
├─ 播放 TTS"你好?" → 提前 0.12s 开识别(TTS 播完即听)
├─ 用户说命令 → KWS 检出关键词 → recognition 分段/声纹/解析 → agv 执行 → TTS 应答
├─ 有语音活动心跳 / 命令完成 → 重置 8s 超时定时器
└─ 8s 无有效交互 → 随机播"等待超时。"等 → 回到 IDLE
3.2 状态管理细节
- 唤醒去重:同一唤醒事件 1.8s 内去重,防止重复播报。
- 唤醒响应:收到唤醒事件后先停识别/KWS、清空音频队列,播放”你好?“,然后按”音频时长 + 配置余量”规划恢复识别的时机,尽量贴近播报结束,避免漏听用户紧接的话。
- 超时管理(AWAKE 超时回落):8 秒定时器 +
generation代际计数防止旧定时器误触发;AWAKE 期间 recognition 通过 5575 上报的语音活动心跳可重置超时;10s 内有有效命令也会重置;超时则播报超时文案并回到 IDLE(IDLE 下仅保留 KWS 检测唤醒词)。 - 对识别进程的编排:所有识别开/关都由 controller 统一通过 5556/5562 下发,保证音频采集端与识别端状态一致;TTS 播放前停止识别、播放后恢复识别。
- 音量控制:controller 持有 TTS 播放增益系数(默认 5.0,范围 0.5~5.0,步长 1)。识别到”提高/降低音量”命令后由 agv 上报 controller 调增益并播报反馈。
4. 声纹系统(speaker_identification.py 等)
核心类:StreamSpeakerRecognition、VoiceDatabase、AudioQualityAnalyzer。
4.1 声纹注册(register_speaker.py + 注册数据流)
注册指令通过 TCP 8888 JSON 协议下发(enroll / list / delete)。执行流程(enroll):
- 录音:
sounddevice采集 8 秒单声道 16kHz float32。 - 音频质量分析:
AudioQualityAnalyzer基于频谱法估计信噪比(语音带 1004000Hz vs 噪声带),输出质量分数(0100)与等级(excellent/good/fair/poor/bad/silent);分数 < 30 直接拒绝注册。 - 特征提取:ERES2Net embedding 模型单次推理(
_extract_directly),得到 L2 归一化声纹向量。 - 保存备份音频:录音写入
gallery/app/voice_data/audio_recordings/下 WAV。 - 写入特征库:
VoiceDatabase.add_speaker—— 新用户建条目;同名再次注册则追加为新特征(支持一人多音色/多次录音),并记录每次特征的features_info(质量分、时间、时长);按hybrid合并策略更新融合特征后落盘。
注册过程会记录各阶段耗时日志,便于性能分析。注册完成后(audio_capture 收到子进程 [REGISTER_READY] 标记 → 通知 UI “arrange”),切换回正常识别模式时,audio_capture 会主动向 recognition 下发 reload_speakers,使识别进程热重载声纹库(无需重启)。
4.2 数据存储(VoiceDatabase)
| 文件 | 格式 | 内容 |
|---|---|---|
gallery/app/voice_data/voice_database.pkl | pickle,版本 3.0 | speakers(姓名 → embedding 特征列表)、merged_embeddings(融合特征缓存)、audio_files、speakers_info |
gallery/app/voice_data/speakers_info.json | JSON | 每位说话人的注册时间、质量分、特征数等元信息 |
audio_recordings/*.wav | WAV | 注册时的原始录音(删除用户时联动删除) |
旧版本库(v2.0/旧字典格式)在 load() 时自动做版本兼容迁移。
4.3 声纹匹配与验证逻辑
匹配流程(recognition.py 对每个待验证语音段):
- 语音段送入 ERES2Net 提取归一化 embedding;
VoiceDatabase.find_similar_speaker计算与库内各说话人的余弦相似度,取最高分;- 最高分 ≥ 阈值(0.4)→ 判定为该说话人,否则判定”未知/不匹配”。
两种匹配模式(match_mode):
merged:与每个说话人的融合特征向量比较(默认,先合并再比,速度快、抗单次噪声干扰);all_features:与该说话人注册的每一个特征逐一比较,取最高分(不丢细节)。
识别时特征合并策略(recognition_merge_strategy)——多特征说话人实时计算融合向量时可选:
| 策略 | 方法 | 特点 |
|---|---|---|
average | 算术平均后归一化 | 最简单 |
quality_weighted | 按质量分加权(低分特征 40 分以下减半、20 分以下乘 0.2) | 抑制噪声录音 |
recency_weighted | 按时间指数衰减 exp(-天数/30) 加权 | 强调近期音色 |
diversity_weighted | 与本说话人其它特征差异越大的特征权重越高 | 覆盖音色多样性 |
hybrid | 0.4×质量 + 0.3×时间 + 0.3×多样性 加权(默认) | 综合最优 |
声纹验证的业务规则(recognition.py):
- 只有真正要执行的业务命令才做声纹验证(
_action_requires_speaker):navigate/door/submit_order/cancel_order/charge需要验证;volume、mode_switch这类低风险操作不需要验证; - 声纹库为空时不做验证,直接放行(避免空库时每条命令白跑一次推理);
- 验证失败 → 生成
reject(reason=speaker_mismatch)消息发给 agv,由 agv 播报”声纹验证未通过”;验证通过 → 命令附上user_id与置信度发给 agv。
并发/时序设计:语音段完成后,如确认是业务命令才投递声纹任务(SPEAKER_VERIFY_ONLY_COMMANDS 默认开启)到有界任务队列(上限 20),由声纹工作线程异步推理,结果以 segment_id 与命令结果配对后统一出口,保证低延迟与不丢配对。
4.4 量化工具(quantize_sv.py)
使用 onnxruntime 对 ERES2Net fp32 模型做动态 int8 量化(权重 QUInt8,排除输出层 embedding 以保精度),生成 3dspeaker_eres2net_base_int8.onnx,用于低算力设备降载(model/ 目录内已有 int8/静态量化变体)。
5. 语音识别进程(recognition.py)
5.1 端点检测与语音分段
系统对每 0.1s 音频帧提取 RMS / 峰值 / 过零率(ZCR) 三指标,实现自适应双门限能量分段(主方案),另有 Silero VAD 模型作辅助/备用:
- 开始判定:RMS 超过”绝对阈值 与 噪声底噪×倍数”、峰值达标、连续 N 帧满足(防止杂音误触发);对突发高能量脉冲有专门通道;
- 噪声底噪自适应:指数平滑跟踪环境噪声(快降慢升,带上下限);
- 结束/切段判定:静音持续 ≥ 1s,且当前 RMS/峰值需同时低于”本段峰值×相对比例”与绝对阈值(避免尾音衰减慢导致拖长);语音最长 5s 强制封段;不足 0.4s 的过短段丢弃;
- 每段分配唯一
segment_id,可拼接入语音前的历史音频作为前缀(当前默认关闭前缀以避免污染声纹/KWS)。
5.2 KWS 检测结果归属(延迟归属)
由于 KWS 模型有约 500ms 内部解码延迟,检测结果发生时当前语音段可能已结束。方案:
- 语音段结束后不立即处理,先存入
pending_segments(最多保留 10 段、10s); - 主循环
check_results()定期轮询:对每条 KWS 检测,用检测时间戳 − 0.5s回推语音实际发生时刻,归属到时间窗匹配的暂存段; - 匹配成功后把该段的音频投递声纹任务、解析命令并进入配对出口;超过 10s 未匹配的段清理。
5.3 命令解析(kws_command_detector.py)
parse_keyword/parse_action 对 KWS 文本做规则解析(不是 NER/LLM):
- 文本归一化:去空白与标点、同音纠错(如”小气/小琪”→“小七”);
- 楼层提取:正则匹配”(第)X 层”,支持中文数字与阿拉伯数字,映射到 4 层索引;
- 房间匹配:命中配置的房间热词表(积厚厅、韶华厅、云程厅、行远厅、观潮厅、员工间);
- 解析出
navigate(含房间 + 楼层位向量 dish[4],标记每层各 1 份餐); - 按顺序匹配其它动作关键词:取消优先于送餐(避免”取消送餐”误判为”去送餐”)→ 开门/关门 → 提交订单/送餐 → 充电;另有独立解析的模式切换(进送餐模式)与音量调节关键词。
- 唤醒词本身被过滤(不作为命令)。
5.4 命令出口
验证通过后消息带上 action、user_id、confidence、时间戳、语音时长等字段推给 agv(5572)。
6. 任务执行进程(agv.py)
AGVHandler 采用响应模板 + 订单累积器模型:
- 导航(navigate):校验楼层与房间齐全(缺楼层播”请告诉我几层。“,缺房间播”请告诉我房间名称。”),把
(room_id, dish[4])累积进订单池,并 TTS 播”好的,X层XX厅。”; - 提交订单(submit_order):将订单池封装为统一协议 JSON(含
protocol_version、robot_id、task_info、时间戳),追加写入orders_log.json后清空订单池,播”送餐准备就绪。”; - 取消(cancel_order):清空订单池;
- 充电(charge)、门控(door)、reject(声纹不通过)、tts_prompt(模式切换提示) 均按模板播报;
- 执行完成通知:每个动作处理后向 controller 发送
command_complete,controller 据此重置超时并确保识别已恢复(支持连续多轮对话下单)。
6.1 订单上报(MQTT,独立于语音主流程)
MQTT 只承担”把订单协议交给局域网内的送餐调度端”,与语音交互完全解耦:即使 broker 不可达,设备本地对话、下单累积不受任何影响(订单先落盘
orders_log.json,上报进程轮询补发)。
orders_log.json由mqtt_publisher.py轮询监控(1s),检测到新订单后通过 paho-mqtt 发布到主题device/server/command(局域网 broker,QoS 与 keepalive 配置完备);- 发布前可自动切换 WiFi(
nmcli连接 ROS2_AGV),断线重连有重试策略; mqtt_subcribe.py为同协议的订阅测试工具,用于验证链路。
7. 语音合成应答(tts_utils.py)
TTSManager 采用两级合成策略:
- 预合成文件优先:应答文本在
pre_synth/目录按<文本>.wav查找(由pre_synthesize_all.py离线批量生成固定应答与”好的,X层XX厅。“模板),命中即读取播放,避免运行时加载 TTS 模型(大幅降低内存与延迟); - 实时合成兜底:未命中时延迟加载 Matcha-TTS + Vocos 模型实时合成,并带 32 条 LRU 结果缓存;模型加载失败不阻塞主流程。
其它细节:
- 自动加标点:无句末标点时按语气词猜测 ?/! 或默认加”。“(提升语音自然度,预合成文件与实时合成使用同一文本规范,保证命中文案);
- 音量后处理:峰值归一化 + 乘法增益系数(受语音调音量命令控制),可选择是否允许削波;
- 播放通道:本地
sounddevice播放为主,播放期间暂停识别,按 TTS 时长规划恢复识别,保证用户说完话后系统已就绪;(可选)经 PUB(5553) 同步推送给调试客户端。
8. 音频采集进程(audio_capture.py)
- 本地麦克风:
sounddevice.InputStream采集 16kHz/单声道,按 0.1s 分块(可对接远端工控机网络推流); - 自动增益(AGC):以目标 RMS(默认 0.07)计算放大增益,限制最大增益(12)与峰值(0.95),弱信号不放大、强信号不削波;
- 流式 KWS:每一块音频直接喂给
sherpa-onnx KeywordSpotter(单套模型同时负责唤醒词与命令词,避免两套 KWS 的重复算力——见修复记录 #15);命中唤醒词进入”唤醒态”并上报 controller;唤醒态内命中其它关键词转发 recognition;检测带 0.6s 冷却防抖; - 音频分发:识别态(AWAKE)才把音频帧推给 recognition,其余时间不转发以省 CPU;队列满时丢最旧帧并计数;
- 模式切换:8889 TCP 服务接收
open(切注册模式:关麦克风 → 拉起 register_speaker 子进程 → 等待[REGISTER_READY])与close(杀子进程 → 开麦克风 → 通知识别进程重载声纹库)。
9. 目录结构与辅助文件说明
| 路径 | 说明 |
|---|---|
launch.py | 单机进程启动器:清理残留进程、一次拉起树莓派上的全部模块进程、统一信号退出(重启即恢复整套语音服务) |
config.py | 全局配置:采样率、所有 ZMQ 端口、模型路径、关键词/房间/音量参数、TTS 模型与参数 |
recognition.py / controller.py / audio_capture.py / agv.py / register_speaker.py / speaker_identification.py | 系统主体 |
kws_command_detector.py | 命令文本归一化与规则解析库(被 recognition 调用) |
tts_utils.py | TTS 管理器(预合成优先 + 实时合成) |
pre_synthesize_all.py | 批量预合成所有固定应答文案到 pre_synth/ |
quantize_sv.py | 声纹模型 int8 量化工具 |
mqtt_publisher.py / mqtt_subcribe.py | 订单 MQTT 上报发布器 / 测试订阅器 |
sherpa-onnx-kws/ | KWS 模型(encoder/decoder/joiner、tokens、keywords) |
model/ | 声纹 ERES2Net(fp32/int8/大模型)与 Silero VAD 模型 |
tts_models/ | Matcha-TTS 中文模型 + Vocos 声码器 + 规则 FST |
gallery/app/voice_data/ | 声纹库与注册录音实际存储位置 |
orders_log.json | 订单协议日志(MQTT 上报的数据源) |
docs/issues_and_fixes.md | 开发中的问题定位与修复记录(KWS 流式化、双 KWS 合并、声纹归属、PulseAudio 冲突等) |
1/、备份2/ | 历史版本代码 |
10. 典型业务时序
场景:注册声纹(8888/8889 为本机或局域网内的可选调试接口,不注册语音功能也能完整自运行)
调试工具 ──TCP 8889 open──▶ audio_capture:关麦克风 → 启动 register_speaker 子进程
register_speaker 加载模型后打印 [REGISTER_READY] → audio_capture 返回 arrange
调试工具 ──TCP 8888 enroll──▶ register_speaker:录音8s → SNR质量分析 → ERES2Net特征
→ 存WAV → add_speaker(追加特征+hybrid融合) → 返回成功
调试工具 ──TCP 8889 close──▶ audio_capture:杀子进程 → 开麦克风 → 通知 recognition reload_speakers
场景:唤醒 → 命令 → 声纹验证 → 执行
audio_capture 持续 KWS
└─ "小七小七" → controller(IDLE→AWAKE) → TTS"你好?" → 开启识别
用户:"第一层积厚厅"
├─ audio_capture:KWS 检出复合词 → 音频帧推 recognition
├─ recognition:能量分段 → pending_segments;KWS归属到段 → 命令解析 navigate(积厚厅,第1层)
│ → 声纹任务(异步 ERES2Net) → 余弦相似度≥0.4 → 命中 user → 推 agv
├─ agv:累积订单 → 请 controller 播"好的,一层积厚厅。" → command_complete
└─ controller:重置超时、保持识别,等待下一句(如"提交订单")
11. 端侧轻量化与稳定性设计要点(面向树莓派)
11.1 “脱离服务器”的落地方式
- 单机自闭环:唤醒、命令识别、声纹、TTS 全部由树莓派本机进程完成,不向任何服务器/云发送语音与推理请求;掉电或进程崩溃后由
launch.py一键拉起全部进程,设备即恢复服务; - 断网可用:语音交互不依赖外网;网络仅用于可选的 MQTT 订单上报(断网只影响上报,不影响本地对话与执行)。
11.2 面向树莓派算力的轻量化手段
- KWS 合并成单实例:唤醒词与命令词由同一套 int8 Zipformer KWS 承担(早期版本是双 KWS 实例,后合并),省掉一整份模型内存与推理开销;
- 推理只在必要处花:KWS 用 chunk-16 流式解码省实时算力;声纹默认
base级 ERES2Net(fp32 还可经quantize_sv.py动态量化为 int8);仅”业务命令且声纹库非空”才触发声纹推理,音量/模式切换等直接放行; - 空闲即省资源:IDLE 态只跑 KWS,AWAKE 态才推流给识别进程做分段与声纹;各队列 HWM 受限、队满丢旧帧并计数,杜绝内存膨胀;
- 内存友好:固定应答全部预合成为
pre_synth/*.wav(pre_synthesize_all.py一次性生成),日常运行无需加载 TTS 模型;实时合成模型延迟加载 + LRU 缓存,合成带锁排队防止抢占 CPU; - 存储友好(保护 SD 卡):语音段默认不落盘,仅注册录音保留 wav;声纹库为小体积 pickle,注册后热更新无需重启;
- 分层异步:音频接收 → 能量分段 → 命令解析与声纹推理分层解耦;声纹推理独享线程 + 有界队列(上限 20),CPU 峰值受控;
- TTS 不阻塞关键路径:预合成优先、模型延迟加载、播放与识别时序精确衔接,避免”答话”堵住”听话”;
- 可观测性:各进程输出带耗时/统计日志(丢帧数、识别数、语音段数、声纹耗时、TTS 时长等),便于在树莓派上现场调参(采样率、阈值、HWM 等集中在
config.py)。