资讯详情

用sherpa-onnx在树莓派上构建离线语音助手实战

发布时间:2026/9/27 5:49:15

500+
企业客户服务经验
120+
行业领域内容覆盖
3000+
原创页面设计沉淀
98%
客户满意度

用sherpa-onnx在树莓派上构建离线语音助手实战

树莓派吃灰率最高的项目我猜是智能音箱。硬件有、Linux 也有可每次想让它干活都要先连上云端、等响应、再等 TTS 回话网络一抖整个流程就瘫了。今天分享一个我用 sherpa-onnx 在树莓派 4B 上搭的离线语音助手从唤醒、识别到语音回复全部在本地跑不需要网络不依赖任何云平台整套代码放在文里照着抄就能用。先说实话这个东西不是 Siri也不是小爱同学。它是一个“规则明确的本地语音开关”适合做定时提醒、控制 GPIO 外设、查询本机状态这类轻量任务。但它的核心价值非常清楚完全离线、隐私不出门、延迟可控。树莓派 4B 这种性能水平的板子跑 sherpa-onnx 的唤醒词检测、离线识别和本地 TTS 完全够用。文章末尾我会把 5 分钟快速跑通的时间线和完整代码都放出来项目适合树莓派玩家、嵌入式开发者也适合毕设想找“软硬结合”题目的同学。1. 项目定位与整体方案拆解1.1 sherpa-onnx 是什么为什么值得选sherpa-onnx 是 k2 生态出来的一个跨平台推理库专门跑语音相关模型支持关键词唤醒KWS、语音识别ASR、语音合成TTS、VAD 等。它的模型大多被转成 ONNX 格式所以部署起来不挑框架Python、C、Android、iOS 都有绑定。对于树莓派这种 ARM 平台来说直接用 Python API 是最省事的路子。选 sherpa-onnx 而不是其他语音方案主要是三个原因离线推理是原生设计模型文件在本地不需要联网鉴权。模型体积控制得比较好KWS 模型可以做到几 MB 到几十 MBTTS 和 ASR 模型也有一批适合边缘设备的中小型选择。社区活跃模型仓库里面中文 ASR、中文 TTS 都有现成的不用自己训练。对比一下如果走云端 API每次交互都有网络延迟而且断网就废如果自己训练模型数据、算力、时间成本都太高。sherpa-onnx 正好卡在“能离线跑”和“开箱即用”之间对个人项目和毕设都非常友好。1.2 树莓派方案的取舍与功能边界树莓派在这个项目里的角色是“边缘主机”。它内存和算力有限但足够跑推理而且带 GPIO、USB、音频口可以往外接设备。我不建议用树莓派 Pico 或 ESP32 这类单片机来做因为语音模型以 ONNX 形式加载后需要几十 MB 到几百 MB 内存单片机根本塞不下。功能边界先说清楚免得后面踩坑才发现方向不对。这套离线语音助手能做的事本地唤醒词触发比如“你好小智”。唤醒后录制一段语音离线识别成文字。通过简单规则匹配意图执行本地命令。用本地 TTS 合成中文语音并播报结果。通过 GPIO 控制 LED、继电器等外设。它不能做的事也很明显不能理解复杂对话不能做知识问答方言适配要看模型本身。不过这些不影响它的核心价值——它给了一个可靠的、离线的“语音控制入口”你可以在它上面叠更多自己的自动化逻辑。2. 环境准备与硬件搭配2.1 硬件清单与系统镜像选择先列一份我实际用到的硬件清单照这个买不会错部件推荐规格说明树莓派4B2GB 起步4GB 更稳2GB 能跑但编译和缓存空间紧张TF 卡32GB 以上A1 或 A2 速度等级模型文件加起来不到 1GB但系统更新需要空间USB 麦克风带降噪的会议麦或桌面麦别买那种几块钱的免驱声卡底噪太大音箱3.5mm 小音箱或 USB 音箱用板载 3.5mm 口音质一般能听清就行电源官方 5V 3A 电源供电不足会导致 USB 麦克风随机掉线系统镜像我用的是 Raspberry Pi OS Lite64 位也就是无桌面版。很多人喜欢装完整版桌面但在树莓派上跑服务型项目无桌面版省内存、少干扰开机自启也更干净。如果你手里是 Ubuntu 22.04 或 24.04也可以跑通只是音频设备命名和 ALSA 配置会和官方系统略有差异。第一次开机后先做常规配置设置时区、开 SSH、更新系统。这里提醒一句不要一上来就装桌面后面所有操作都用 SSH 连到板子上调试效率会高很多。2.2 音频采集设备与调试语音助手的输入端是麦克风输出端是音箱这两个设备在树莓派上经常出问题。别急着写 Python 代码先把音频链路调通。插上 USB 麦克风后用两条命令确认设备是否被识别arecord -l aplay -l如果都能看到设备说明驱动没问题。接下来要做的是让树莓派默认使用 USB 麦克风采集、默认音箱播放。可以编辑~/.asoundrc文件内容类似这样pcm.!default { type asym capture.pcm hw:1,0 playback.pcm hw:0,0 }注意hw:1,0和hw:0,0要按你arecord -l和aplay -l看到的实际 card 编号来改不要照抄。写完后用下面两条命令做一轮录音回放测试arecord -d 5 -f S16_LE -r 16000 -c 1 test.wav aplay test.wav能听到自己的声音说明音频链路没问题。这时候可以用alsamixer把麦克风输入音量调高一些通常建议 70% 到 90%太高容易破音太低唤醒词检测不到。这一步很多人忽略结果后面 KWS 一直不触发排查半天发现是麦克风增益不够。2.3 安装 sherpa-onnx 运行时与依赖树莓派上的 Python 环境建议用虚拟环境管理避免系统级的 Python 包冲突。下面的命令创建虚拟环境并安装核心依赖python3 -m venv ~/vaenv source ~/vaenv/bin/activate pip install --upgrade pip pip install sherpa-onnx sounddevice numpysherpa-onnx是核心推理库sounddevice负责实时采集麦克风音频numpy用来处理音频数据。如果pip install sherpa-onnx比较慢可以先换国内镜像源再装。安装完成后在 Python 里执行import sherpa_onnx不报错就说明环境正常。提示如果你的树莓派网络环境不太好也可以从 sherpa-onnx 项目的 Release 页面下载预先编译好的 wheel 包再用pip install 下载的文件.whl安装。安装过程中如果提示缺libsndfile、libatlas这类依赖用apt install补上即可。3. 完整代码实现与部署流程3.1 整体工作流与代码结构整个语音助手的逻辑可以用一句话概括音频块持续送入唤醒词检测检测到唤醒后录音 N 秒再把这段录音送进离线识别器得到文字经过规则匹配生成回复文本最后用本地 TTS 合成语音并播放。这个流程里有三个独立的模型组件KWS 唤醒词模型、ASR 语音识别模型、TTS 语音合成模型。它们各自独立加载互不影响。代码结构如下offline-voice-assistant/ ├── main.py ├── setup.sh └── va.servicemain.py是主程序setup.sh是一键部署脚本va.service是 systemd 服务文件用来开机自启。下面我把main.py拆开讲。3.2 唤醒词检测的接入与调参唤醒词检测用的是 sherpa-onnx 的KeywordSpotter。它做的事情是对输入音频流持续打分当某个预置关键词的置信度达到阈值时就返回触发结果。模型文件建议选中文友好或者多语种支持的 KWS 模型下载时看模型说明别选纯英文数据的模型那样对中文唤醒词不敏感。核心代码段import wave import time import numpy as np import sounddevice as sd import sherpa_onnx SAMPLE_RATE 16000 BLOCK_SIZE 2048 RECORD_SECONDS 4 MODEL_DIR /home/pi/sherpa-onnx-models KWS_DIR f{MODEL_DIR}/kws ASR_DIR f{MODEL_DIR}/asr TTS_DIR f{MODEL_DIR}/tts def init_kws(): cfg sherpa_onnx.KeywordSpotterConfig( modelsherpa_onnx.KeywordSpotterModelConfig( tokensf{KWS_DIR}/tokens.txt, keywordsf{KWS_DIR}/keywords.txt, encoderf{KWS_DIR}/encoder.onnx, decoderf{KWS_DIR}/decoder.onnx, joinerf{KWS_DIR}/joiner.onnx, num_threads2, ), keywords_filef{KWS_DIR}/keywords.txt, max_active_paths4, ) return sherpa_onnx.KeywordSpotter(cfg)keywords.txt文件每行一个唤醒词比如你好小智 hello xiaozhiKWS 模型的keywords.txt有的版本还支持自定义阈值比如你好小智:1.5阈值越高越不容易误触发但也会更不灵敏。这个值需要根据实际环境调我的建议是家里比较安静就默认环境嘈杂就把阈值往上提一点。唤醒监听的循环逻辑def wait_for_wake_word(spotter): stream spotter.create_stream() with sd.InputStream(samplerateSAMPLE_RATE, channels1, dtypefloat32, blocksizeBLOCK_SIZE) as s: while True: block, _ s.read(BLOCK_SIZE) stream.accept_waveform(block[:, 0], SAMPLE_RATE) while spotter.is_ready(stream): spotter.decode(stream) if spotter.is_detected(stream): result spotter.get_result(stream) print(f[唤醒] {result.keyword}) return这里的关键是音频块要持续送入同一个 stream不能每块重新建流。KWS 模型的上下文信息保留在 stream 内部如果频繁重建前面的音频信息就丢了唤醒词后半句很容易漏检测。3.3 语音识别与意图处理的实现唤醒后进入正式指令采集。我设置的录音时长是 4 秒录完直接送进离线识别器。如果你习惯说长句可以改成 5 秒但录音时间越长等待识别的延迟也越高。识别模型我推荐sherpa-onnx-paraformer-zh系列中文识别效果在边缘设备里表现很好模型体积也不大。def init_asr(): return sherpa_onnx.OfflineRecognizer.from_paraformer( modelf{ASR_DIR}/model.onnx, tokensf{ASR_DIR}/tokens.txt, num_threads2, ) def record_audio(duration): frames [] with sd.InputStream(samplerateSAMPLE_RATE, channels1, dtypefloat32, blocksizeBLOCK_SIZE) as s: for _ in range(int(duration * SAMPLE_RATE / BLOCK_SIZE)): block, _ s.read(BLOCK_SIZE) frames.append(block[:, 0].copy()) return np.concatenate(frames) def recognize(recognizer, audio): stream recognizer.create_stream() stream.accept_waveform(audio, SAMPLE_RATE) recognizer.decode_stream(stream) return stream.result.text.strip()识别得到文字后进入意图处理环节。这里不搞复杂 NLP就用关键词匹配简单直接。def build_reply(text): if any(k in text for k in [关灯, 关闭灯, 把灯关了]): return 好的关灯。 if any(k in text for k in [开灯, 打开灯, 把灯打开]): return 好的开灯。 if 时间 in text: now time.localtime() return f现在是 {now.tm_hour} 点 {now.tm_min} 分 if any(k in text for k in [退出, 再见, 休息]): return 好的有需要再叫我。 return 我在不过这个指令我还没有学会。如果你要接 GPIO可以在build_reply返回回复文本的同时把控制动作作为副作用执行。比如用gpiozero控制 LED代码可以写成try: from gpiozero import LED led LED(17) except ImportError: led None def execute_action(text): if led is None: return if 开灯 in text: led.on() elif 关灯 in text: led.off()这里先不把 GPIO 的逻辑混进回复生成的函数里保持两个函数各司其职后续扩展其他设备时结构更清晰。3.4 本地语音合成与播报语音合成用的是 sherpa-onnx 的OfflineTts。中文模型里sherpa-onnx-vits-zh-hf-fanchen是比较省事的选择只需要 model 和 tokens 两个文件不需要额外的词典和 FST 规则。def init_tts(): cfg sherpa_onnx.OfflineTtsConfig( modelsherpa_onnx.OfflineTtsModelConfig( vitssherpa_onnx.OfflineTtsVitsModelConfig( modelf{TTS_DIR}/model.onnx, tokensf{TTS_DIR}/tokens.txt, ), num_threads2, ), ) return sherpa_onnx.OfflineTts(cfg) def speak(tts, text): result tts.generate(text, sid0, speed1.0) sd.play(result.samples, result.sample_rate) sd.wait()sid0表示使用模型里的第一个说话人。如果模型支持多说话人可以改这个下标。speed1.0是正常语速树莓派 4B 上生成一句话大约需要几秒这时候不用太追求速度稳定更重要。主循环把这些串起来def main(): print(加载模型...) kws init_kws() asr init_asr() tts init_tts() print(语音助手已就绪说唤醒词试试。) while True: wait_for_wake_word(kws) audio record_audio(RECORD_SECONDS) text recognize(asr, audio) print(f[识别] {text}) if not text: speak(tts, 没有听清请再说一次。) continue reply build_reply(text) print(f[回复] {reply}) speak(tts, reply) if __name__ __main__: main()整个循环的逻辑是监听唤醒词唤醒后录音识别处理完再回到监听状态。这是最自然的语音交互方式比一直开着识别省电也能避免误触发。3.5 5分钟跑通与开机自启标题说 5 分钟搞定这里我把时间线说明白。假设板子系统已经装好代码已经拷到树莓派上那么流程是建虚拟环境装依赖约 1 分钟下载模型文件约 2 到 5 分钟看网络速度运行 Python 脚本进入监听状态约 10 秒。整个过程在 5 到 8 分钟之间。为了减少手工操作我写了一个setup.sh#!/bin/bash set -e sudo apt update sudo apt install -y python3-pip python3-venv libsndfile1 libatlas3-base python3 -m venv ~/vaenv source ~/vaenv/bin/activate pip install --upgrade pip pip install sherpa-onnx sounddevice numpy mkdir -p ~/sherpa-onnx-models/{kws,asr,tts} echo 请把训练好的 KWS、ASR、TTS 模型文件放到对应目录 echo 然后运行: python main.py模型文件需要你从 sherpa-onnx 的模型列表中手动下载。下载哪个模型我在前面的章节已经说过KWS 挑中文友好的ASR 用 paraformer-zhTTS 用 vits-zh-hf-fanchen。放到对应目录后保持文件路径和 main.py 里的一致。开机自启用 systemd 服务新建va.service[Unit] DescriptionOffline Voice Assistant Afternetwork-online.target sound.target [Service] ExecStart/home/pi/vaenv/bin/python /home/pi/offline-voice-assistant/main.py WorkingDirectory/home/pi/offline-voice-assistant Restartalways Userpi [Install] WantedBymulti-user.target然后把服务文件放到/etc/systemd/system/va.service执行sudo systemctl daemon-reload sudo systemctl enable va.service sudo systemctl start va.service这样树莓派上电后会自动启动语音助手。调试时先用journalctl -u va.service -f看日志不要盲改代码。4. 常见问题与排查技巧实录4.1 高频问题速查表我实际调试过程中踩过的坑整理成一张表按现象排后面再展开讲几个重要的问题现象常见原因解决办法arecord -l没有设备USB 麦克风供电不足或没插好换 USB 口用带供电的 HUB检查lsusb唤醒词一直不触发麦克风音量太低或模型不支持中文alsamixer调高输入增益换成中文 KWS 模型识别结果乱码模型和 tokens 文件不匹配检查 ASR 模型目录tokens 必须和模型配套TTS 没有声音默认输出设备不对或音量是 0用aplay -l查设备改.asoundrc调alsamixerCPU 占用接近 100%线程数设置过高或模型过大降低num_threads换更小的 ASR 模型systemd 启动后进程退出模型路径不对或虚拟环境路径错误journalctl -u va.service看日志确认路径4.2 准确率优化与模型替换如果你发现识别准确率不够优先查三件事。第一麦克风摆放位置和增益麦克风离嘴越近识别越好这个比换模型效果更明显。第二录音时长4 秒可能截断长句可以改成 5 秒但延迟会增加。第三是否做了增益归一化sounddevice返回的 float32 数据理论上已经在 [-1, 1] 区间不需要额外归一化但如果音频是从 wav 文件读的 int16要除以 32767 才能送进模型。模型替换方面ASR 从 paraformer 换成 zipformer 系列时代码要改成sherpa_onnx.OfflineRecognizer.from_zipformer(...)同时传入 encoder、decoder、joiner 三个文件。TTS 换成其他 VITS 模型时注意有些模型还需要 lexicon 和 dict 目录路径少了会直接报错。提示下载模型后不要重命名文件保持模型目录里的原始文件名。因为很多 ONNX 模型内部的输入输出张量名和文件名无关但是 sherpa-onnx 的一些工具脚本会依赖默认文件名查找文件乱改名容易给自己挖坑。4.3 性能调优与后续扩展树莓派 4B 跑这套方案我个人实际观察到的情况是KWS 空转时 CPU 占用大约 15% 到 25%ASR 识别 4 秒音频大约需要 1 到 2 秒TTS 合成一句 10 个字的回复大约需要 1 秒。整体交互延迟在 3 到 4 秒左右完全可以接受。如果觉得慢可以先加载模型做一次空推理再进入主循环让 CPU 频率稳定在高位。线程数不建议盲目调高。树莓派 4B 是四核KWS、ASR、TTS 各分配 2 个线程系统负载已经比较均衡。把线程数调成 4 反而可能因为 CPU 争抢导致延迟上升。扩展方向上这套语音助手很适合作为智能家居的本地语音入口。你可以把意图匹配从关键词改成更灵活的规则引擎比如接入 MQTT 控制家里其他设备也可以在 GPIO 上接继电器控制风扇、窗帘甚至可以把 TTS 换成其他音色模型让回复更有辨识度。如果你手里是树莓派 5内存和算力都更好直接加大模型、降低线程数体验会再上一个台阶。最后再分享一点个人经验这个项目我做完之后最深的感受是语音助手的难点不在模型推理而在音频链路和交互细节。模型用 sherpa-onnx 几乎是开箱即用但麦克风增益、默认声卡配置、唤醒词阈值这些看起来不起眼的地方才是真正消耗时间的地方。所以我强烈建议你先单独跑通录音回放测试再逐步接入模型不要一上来就全链路调试否则出了问题你根本不知道是设备问题还是代码问题。另外一个很有用的小技巧调试阶段不要开着 systemd 服务跑直接在前台运行 Python 脚本每个阶段加print打印关键信息。等所有环节都确认正常了再放到服务里跑。日志是最好的朋友比反复测试麦克风靠谱得多。等你把这一套流程跑顺再回头看看整个项目会发现一个几十块钱的 USB 麦克风加一块树莓派就能拥有一个永远在线、不依赖外网、随叫随到的本地语音助手这种掌控感是云端方案给不了的。
热门专题

继续阅读更多专题内容

围绕企业服务、数字化转型与官网运营的常青话题,持续输出深度内容

企业官网建设指南 企业托管服务模式 财税政策与解读 企业数字化转型 官网SEO与获客 网站安全与运维
配套服务

读完这篇文章,了解更多服务

从整站搭建到SEO布局,17项核心服务助您打造高转化的企业官网

01

企业托管整站搭建

从信息架构到栏目预留,搭建可生长的企业站点骨架,每个页面独立原创设计。...

了解详情
02

规整可信网页设计

雪地靴温暖风原创设计,金属铜线条贯穿全页,拒绝通用模板与AI流水线。...

了解详情
03

企业服务SEO布局

关键词体系与语义化结构,从建站源头为搜索排名而生。...

了解详情
04

业务预约咨询表单

多场景表单与线索收集体系,把访问流量转化为可追踪的销售线索。...

了解详情
05

企业服务站点运维

安全巡检、数据备份与内容更新支持,全年守护网站稳定运行。...

了解详情
06

全终端商务适配

电脑、平板、手机一致呈现,移动端体验与转化同样出色。...

了解详情
需要专业建议?

让专业顾问为您解读行业趋势

关于企业官网建设、SEO获客与数字化转型的任何疑问,欢迎一对一咨询我们的专业顾问。