
我最初在树莓派上折腾Snowboy是想给一个老旧的USB麦克风找个正经用途。树莓派装Snowboy说到底就是在本地跑一个“离线唤醒词检测引擎”让树莓派像智能音箱一样听到特定词才响应而不是连续录音上传到云端。这个项目对语音助手DIY、智能家居控制、甚至给学生做课程设计都很有参考价值。我们一步步把环境、依赖、编译、模型、调试全部走通顺带聊聊那些网上教程不会明说的坑。这个文章适合有一定Linux基础、想在树莓派上做语音交互的同学。不需要你懂深度学习的细节按照步骤走就能把“唤醒”这件事真正跑起来后面再看代码才不会觉得虚。1. 为什么都2024年了我还要在树莓派上折腾Snowboy1.1 Snowboy到底是什么Snowboy是由Kitt.AI团队推出的唤醒词引擎当年被很多智能硬件项目采用后来团队被百度收购后仓库就停留在1.3.0版本不再更新。它做的事情非常聚焦在本地不断监听麦克风用模型判断当前声音里有没有出现你指定好的唤醒词比如“snowboy”“hey jarvis”“smart mirror”。注意“本地”和“离线”这两个词是关键。它不像现在很多智能音箱那样把音频传到云端做语音识别而是所有声学特征提取和分类推理都在树莓派上实时完成。这意味着哪怕你的树莓派断网了唤醒功能照样能用而且不存在隐私泄露的问题。对做智能家居的人来说“隐私”往往是选择本地方案最直接的动机。Snowboy的内部原理可以简单理解为先用音频预处理从原始波形里提取出类似MFCC的声学特征然后把这些特征送给一个训练好的深度卷积神经网络做二分类判断输出的是“这个声音片段是唤醒词”的概率。整个模型很小计算量也不大所以树莓派这类性能并不强的板子也能实时跑占用CPU一般只有百分之十几比单纯跑一个本地STT要轻得多。1.2 为什么选树莓派而不是PC开发板树莓派跑Snowboy有三个明显的优势第一树莓派生态是语音交互项目的“标准实验室”GPIO可以方便地接LED灯、继电器、红外发射头唤醒之后马上联动硬件扩展性极强第二树莓派系统带完整的ALSA音频栈配合便宜的USB麦克风就能干活入门成本低第三树莓派4B以及更新的5代性能已经足够在跑Snowboy的同时还能很轻松地接一个TTS引擎和简单的意图识别逻辑。如果你用的是树莓派5需要注意一个问题。Snowboy官方提供的预编译库只覆盖了armv7和armv6的32位环境树莓派5默认跑的是64位系统直接拿官方编译产物基本跑不起来。我的建议是如果手上是树莓派5要么刷一个32位的Raspberry Pi OS要么老老实实走源码编译路线。树莓派4B到是不用纠结32位系统下装起来最顺手。有人可能会问Snowboy已经停止维护了为什么不用新的唤醒词引擎我承认新方案有很多Porcupine、openWakeWord都不错但Snowboy在大量开源项目里留下了完整示例和成熟的接入模式尤其是围绕小智语音这类本地对话助手的讨论中依然能看到Snowboy的身影。对学习唤醒词原理的人来说它代码量小、逻辑清晰拆开来看比看一个大框架舒服得多。2. 装之前先把家底盘清楚系统、依赖和音频设备2.1 镜像和Python版本选择安装Snowboy第一步不是敲pip命令而是先把系统环境定下来。我从实际测试得到的结论是Raspberry Pi OS 2023年之前基于Bullseye的32位版本配Python 3.7/3.9安装Snowboy的阻力最小。新版Raspberry Pi OS基于Bookworm默认Python 3.11SDK里SWIG生成的旧式Python绑定编译时经常报“unknown type name”这类错误所以新手我不想让你一上来就和编译器搏斗。我建议你先确认系统位数uname -a输出如果是aarch64说明你跑的是64位内核如果是armv7l说明是32位。如果你用的是树莓派4B且不是必须用64位建议直接刷32位系统后面编译Snowboy会省很多事情。树莓派5想稳定使用可以试64位系统加源码编译但一定要做好移植代码的准备。接着检查Python版本python3 --version如果版本高于3.9还编译不过可以尝试把SWIG生成的代码做小量修改或者使用我后面提到的替代方案。不要被版本劝退先按这个基线走。2.2 安装基础依赖和编译工具Snowboy的Python绑定依赖SWIG来生成C和Python之间的桥接代码另外还需要Atlas数学库用来跑矩阵运算。把这些一起装上sudo apt update sudo apt install -y git swig sox libatlas-base-dev python3-dev portaudio19-dev python3-pyaudio这里我唠叨两句每个包的作用swig把C接口翻译成Python模块的工具Snowboy正式支持Python2后来社区做了Python3适配但依然需要SWIG重新生成。libatlas-base-devBLAS数学库Snowboy推理阶段大量使用矩阵运算依赖这个库。portaudio19-dev和python3-pyaudio给Snowboy的示例代码提供录音输入。Snowboy核心本身可以直接从ALSA读取音频但官方示例decoder模块用的是PyAudio所以少不了它。装完后顺手测试一下麦克风。很多人跳过这一步结果后面跑起来才发现根本没有音频输入这个坑非常典型。先用arecord -l查看系统识别到的录音设备。如果你用的是USB麦克风应该能看到一个card编号比如card 1。然后用下面的命令录一段5秒的音频arecord -D plughw:1,0 -f S16_LE -r 16000 -c 1 test.wav这里的数字要和上面看到的card编号一致。录音结束后播放确认有声音aplay test.wav能听到自己的声音说明音频链路是通的。Snowboy默认采样率是16000Hz单声道16bit所以录音参数直接用这个规格最稳。2.3 别忽略默认声卡配置树莓派默认的声卡不一定是USB麦克风很多系统会把HDMI音频或者板载3.5mm接口排在前面导致Snowboy打开设备失败。你可以在用户目录创建一个 .asoundrc 文件把USB麦克风设为默认输入设备。先用arecord -l找到USB麦克风的card编号我这里是card 1然后nano ~/.asoundrc写入pcm.!default { type hw card 1 device 0 } ctl.!default { type hw card 1 device 0 }保存之后再用arecord -D default -f S16_LE -r 16000 -c 1 test2.wav如果这样能录到音说明默认设备已经生效了。这个配置对后面的Snowboy极其重要因为它默认会打开名为“default”的录音设备如果不对应到实际麦克风就会一直报ALSA错误。3. 两条主流安装路线pip快速装与源码编译3.1 pip安装为什么容易翻车在树莓派上安装Snowboy最简单的方式当然是pip install snowboy如果你的树莓派系统是旧的32位Raspberry Pi OS、Python版本在3.7以下这条命令也许能直接成功。因为PyPI上有编译好的armv7包。但实际环境很少这么顺Python版本稍微新一点pip就会尝试从源码编译然后被SWIG生成代码里的兼容问题卡住。我自己踩过的典型报错是error: command arm-linux-gnueabihf-gcc failed with exit status 1后面跟着一大堆C语法错误。这种情况基本是源码包和Python3.9以上的头文件不兼容导致的。所以我不建议你把这作为首选路线除非你只是想快速验证并且已经准备好了回退方案。3.2 源码编译的正确打开方式源码编译虽然听起来复杂但其实是可控的。先从GitHub拉取Snowboy仓库git clone https://github.com/Kitt-AI/snowboy.git cd snowboy仓库里有现成的Python3 SWIG绑定目录我们只要进入这个目录做编译cd swig/Python3 make如果系统里的Python版本比较老这一步通常能顺利跑完。编译完成后目录里会生成一个snowboydetect.so文件这就是Python可以直接import的扩展模块。不过仅仅有.so还不够还需要同目录下的snowboydetect.py和snowboydecoder.py两个文件前者是C接口的封装后者是官方提供的调用脚手架里面已经写好了麦克风采集和回调逻辑。为了让项目目录干净一点我通常是建一个自己的项目文件夹把这三个文件复制过去再把模型文件也统一放在那里。比如mkdir ~/wake-word cd ~/wake-word cp ~/snowboy/swig/Python3/snowboydetect.py . cp ~/snowboy/swig/Python3/snowboydetect.so . cp ~/snowboy/swig/Python3/snowboydecoder.py . cp -r ~/snowboy/resources .3.3 编译过不去的排查思路这里的源码编译最需要留神的就是Makefile里的Python版本路径。默认的Makefile一般是针对特定Python版本写的如果你的Python是3.9但你机器上是3.7需要打开Makefile看一下这几行PYTHON : /usr/bin/python3 PYTHON_CONFIG : /usr/bin/python3-config确认python3-config这个命令存在并且输出路径和你的Python版本匹配which python3-config ls -l $(which python3-config)如果系统没装python3-dev那么python3-config可能不会返回正确结果编译器会因为找不到Python.h而失败。这也是为什么我在前面一定要你先把python3-dev装好。编译完成后导入验证是最重要的python3 -c import snowboydetect如果没有任何输出说明模块已经能正常导入了。碰到报错缺少某个共享库一般用ldd snowboydetect.so看一下哪个依赖找不到再通过 apt 安装对应的库比如libatlas-base-dev就是很常见的缺失项。4. 让唤醒词真正跑起来模型选择、调用与调试4.1 模型文件哪里找Snowboy的模型文件后缀是.pmdl或者.umdl。官方GitHub仓库的resources文件夹里其实已经带了一批例如snowboy.umdl官方通用唤醒词唤醒词就是“snowboy”jarvis.pmdl唤醒词“jarvis”smart_mirror.pmdl唤醒词“smart mirror”你不需要联网在仓库里直接就能找到这些文件。把这些模型文件放到项目目录后就可以开始写调用脚本了。如果想让唤醒词变成自己的名字或者自定义的英文单词比较标准的方式是去Kitt.AI官网训练模型。但Snowboy项目已经停止维护官网的模型训练服务有时候能访问、有时候很慢甚至可能提示不可用。如果你刚好需要自定义中文唤醒词比如“小智”“小树”Snowboy的官方训练方式对中文支持也非常有限。中文唤醒词建议还是不要死磕Snowboy后面我提到的新方案会更合适。4.2 最小可运行的唤醒脚本现在我们写一个最简脚本让树莓派在听到唤醒词后打印一句话并退出。在项目目录下新建awake.pyimport snowboydecoder def on_detected(): print(唤醒成功检测到唤醒词) # 这里可以扩展后续操作比如开灯、放提示音、开始录音 exit(0) detector snowboydecoder.HotwordDetector( resources/snowboy.umdl, sensitivity0.5, audio_gain1.0 ) print(开始监听请说唤醒词 snowboy……) detector.start(detected_callbackon_detected, sleep_time0.03)运行python3 awake.py对着麦克风说出“snowboy”如果一切正常终端会立刻输出“唤醒成功”。这里有几个参数说明sensitivity灵敏度范围0到1数值越低越难误触但也会变迟钝数值越高越敏感但可能把环境噪声当成唤醒词。我一开始调到0.7发现风扇声音都能触发后来降到0.45才好很多。audio_gain输入音频增益如果你的麦克风离人远或者录音声音轻可以适当提高1.0一般够用。sleep_time检测循环的睡眠间隔越小CPU占用越高但响应越及时0.03在树莓派4B上体验很流畅。detector.start()是一个阻塞函数会一直循环监听想要程序在后台跑可以让它和主逻辑放在同一进程也可以单独开线程。4.3 解决ALSA设备抢不到的问题跑脚本时最常见的报错是ALSA lib pcm.c:8526:(snd_pcm_open) Unknown PCM cards.pcm.rear ALSA lib pcm.c:8526:(snd_pcm_open) Unknown PCM cards.pcm.center_lfe ALSA lib pcm.c:8526:(snd_pcm_open) Unknown PCM cards.pcm.side Cannot open microphone不用慌这些ALSA库的报错信息看着吓人其实是它在枚举周边设备时遇到不存在的“rear”“side”等声卡打印的警告不一定致命。真正致命的是最后一行Cannot open microphone。绝大多数时候这个错误就是默认录音设备不对。按我前面说的写好~/.asoundrc并且用arecord -D default测一遍基本能解决。还有一种情况是你另外的程序先占用了麦克风比如已经在运行某个录音脚本。ALSA设备默认不支持多进程同时打开必须先关掉占用源。排查方法fuser -v /dev/snd/* sudo kill -9 PID这个命令可以看到具体是哪个进程占用了声卡文件杀干净再跑Snowboy。4.4 灵敏度调的三个小技巧调灵敏度不是玄学是可以靠测试量化的。你可以在脚本里加一个计数器连续触发多少次算一次有效命中用不同sensitivity跑几轮统计误触发率和漏报率。我的经验是环境噪声大比如树莓派旁边有风扇sensitivity往0.3到0.4靠麦克风离嘴很近低于50厘米sensitivity可以顶到0.6以上USB麦克风质量一般时audio_gain加太高会引入更多底噪还不如调灵敏度如果你发现“说三遍唤醒词只成功两次”大概率不是灵敏度问题而是模型本身对男女声、语速、口音的泛化差异。官方自带模型对英文唤醒词覆盖还不错但对中文或者带口音的英文确实不友好。5. 唤醒之后怎么办对接语音助手与TTS5.1 搭建一个完整的语音交互流程唤醒词检测从来不是终点它只是一个“门卫”。在真实项目里门卫值班的目的是让后面的录音识别和智能问答逻辑在“被需要”时才运行既省电又省心。一个典型的树莓派本地语音助手流程是这样的唤醒词检测 → 发出“叮”提示音 → 录音3到5秒 → 语音识别STT → 意图解析/调用回复 → 语音合成TTS播放 → 回到唤醒监听这个流程在我自己搭的小智风格语音助手项目里跑得很顺。Snowboy在这个链条里只占第一阶段但它的稳定程度决定了后面所有环节的质量。如果唤醒经常失灵用户会对着机器喊很久体验非常糟糕。5.2 用一个回调函数串起整条链路我这里给出一段比较完整的示例代码它把唤醒后的动作做得更丰满一点包括播放提示音、自动录音、然后保存音频文件方便你后续接STT服务import snowboydecoder import subprocess import time def play_tone(): subprocess.Popen([aplay, ding.wav]) def record_audio(): # 录制3秒音频命名带时间戳 filename command_ str(int(time.time())) .wav subprocess.run([ arecord, -D, default, -f, S16_LE, -r, 16000, -c, 1, -d, 3, filename ]) print(录音已保存:, filename) # 这里可以接入faster-whisper或其他STT引擎 def on_detected(): print(唤醒成功开始提示音并录音……) play_tone() record_audio() def main(): detector snowboydecoder.HotwordDetector( resources/smart_mirror.pmdl, sensitivity0.4, audio_gain1.0 ) detector.start(detected_callbackon_detected, sleep_time0.03) if __name__ __main__: main()注意一个细节我在on_detected里直接调用record_audio而record_audio是同步阻塞的。这会阻塞Snowboy的检测循环但对你录音是合理的。如果你需要在执行任务的同时继续监听唤醒词就必须把任务扔到线程里处理否则第二次唤醒词将不会被识别。就树莓派4B来说CPU跑Snowboy大概占10%到20%录音时占得更多。如果你还要同时跑TTS和STT建议对CPU做一个大致预算。我遇到过同时开唤醒检测、TTS和浏览器树莓派直接卡死的情况后来用htop排查发现唤醒检测占CPU并不高倒是那些“一次性任务”累加起来拖垮了系统。5.3 和TTS怎么配合比较自然语音助手没有语音回复就没有温度。树莓派上最简单的TTS是用espeaksudo apt install espeak -y espeak -v en-us hello中文效果很机械化但胜在离线、快、不占资源。如果你能接受联网TTS比如调用一些在线API声音质量会好很多但要注意网络延迟可能让对话变得卡顿。我在实际项目中测试过唤醒后TTS在2秒内返回语音的体验才算合格离线方案在树莓派本地是更有优势的。SnoWBoy本身不关心你用什么TTS它只要在回调函数里完成触发就行。所以你可以自由组合不用担心绑定关系。我的建议是先把提示音和录音链路调通再慢慢去换更好的TTS这样问题定位起来方便。6 常见问题与排查技巧实录6.1 高频问题速查表为了让后面的人少走弯路我把常见问题和排查方法整理成一张速查表你在实际操作中遇到类似情况可以直接对照现象可能原因解决办法运行时报Cannot open microphone默认录音设备不是USB麦克风配置 ~/.asoundrc用 arecord -D default 验证编译时找不到Python.h缺少python3-devsudo apt install python3-dev导入snowboydetect报错缺少.so依赖libatlas缺失sudo apt install libatlas-base-dev唤醒词不响应麦克风增益太低调大audio_gain到1.5到2.0误触发频繁灵敏度太高或环境噪声降低sensitivity到0.3到0.4运行一段时间后设备被占用其他进程占用了声卡fuser -v /dev/snd/* 找到进程并结束Python3.10以上编译失败SWIG旧代码不兼容新Python换Python3.7/3.9系统或迁移到新唤醒引擎这个表格是个起点不是终点。你最终还是要结合日志判断因为同样的“Cannot open microphone”可能由三种完全不同的原因导致。6.2 我差点被“No module named snowboy”坑到底我最初在树莓派上装Snowboy最崩溃的报错是ModuleNotFoundError: No module named snowboy这是因为pip包名和import名并不完全对应而且在不同平台上有不同的分发方式。用pip安装之后snowboy包里的模块名是snowboydetect、snowboydecoder不是snowboy。所以正确写法是import snowboydecoder而不是import snowboy很多人照着旧教程第一行就写import snowboy教程没问题但Snowboy的目录结构和模块名在不同版本里确实有改动。建议你在导入前先看看到底有没有snowboydecoder.py和snowboydetect.so再决定import什么。6.3 设备占用的一个隐蔽细节还有一个容易被忽略的坑是树莓派系统自带的“蓝牙音频”和“HDMI音频”服务会在后台偷偷打开声卡导致你的程序监听时抢不到设备。如果你把~/.asoundrc配置好依然报Cannot open microphone可以试试临时停掉蓝牙音频服务sudo systemctl stop bluetooth在树莓派上蓝牙服务并不是每次都会干扰但如果你同时接USB麦克风和蓝牙设备冲突概率很大。我后来干脆给模板里加了一条启动脚本手动决定哪些音频服务开机不启动彻底解决设备占用问题。7 Snowboy维护停滞之后我为什么还会用它7.1 老代码的价值比想象中高Snowboy确实是一个“停止维护”的项目这是事实。但我依然喜欢在树莓派上给它配环境倒不是因为它技术前沿而是因为它的代码量小边界清楚适合作为理解“唤醒词引擎”的入门样本。打开它的源码你能直接看到音频预处理、特征提取、模型推理的整体流程不像一些大而全的语音助手框架藏着太多抽象层。如果你只是想让产品跑起来不值得和旧依赖较劲。但如果你和我一样愿意花一个下午把SWIG、ALSA、共享库这些底层东西全走一遍收获是实打实的。树莓派本来就是拿来玩和折腾的有过一次把老项目救活的经验以后再碰其他停止维护的开源项目会淡定了很多。7.2 新项目我会这样选如果是新项目尤其是要上产品、要长期维护我更推荐用还在更新的唤醒词方案。Porcupine支持树莓派和多种语言有免费档位配置更友好openWakeWord也更现代。但它们的模型训练平台、许可证和路径都不同需要额外学习成本。我的建议是两条腿走路学习原理和做课程设计用Snowboy没问题相关资料多、坑也都被前人踩过了如果想做个真正能稳定24小时在线的家居语音助手优先考虑仍在维护、对树莓派有官方支持的引擎否则未来系统一升级维护成本会一直找上你。7.3 关于树莓派5的一点个人判断很多朋友问树莓派5能不能顺利安装Snowboy说实话树莓派5在性能上完全够用但它是64位ARM架构官方编译好的armv7库基本用不了从源码编译又要面对Python 3.11的兼容性问题。如果你非得在树莓派5上跑Snowboy我建议先降级到32位系统再试但这样又浪费了树莓派5的性能优势。我自己折腾下来比较务实的选择是树莓派4B跑Snowboy做唤醒树莓派5跑现代的openWakeWord或Porcupine做同一件事。这样既保留了老方案的学习价值又能用上新硬件的能力。等你有精力做二次开发还会慢慢体会到在不同架构上适配语音开源项目的乐趣。说实话树莓派安装Snowboy这件事技术上并不算复杂难点全在环境和细节。你只要把音频设备配置好、依赖装齐、源码编译通过后面的调用逻辑基本就是一马平川。我在实际调试中最大的体会是不要在“准备工作”上偷懒麦克风测试、asoundrc配置、Python版本确认每一步都做扎实比编译时反复报错再回头找原因要快得多。最后再分享一个小技巧把编译产生的.so文件和模型文件单独备份下次换一张SD卡或者迁移到另一块树莓派上可以省掉重新编译的半小时直接拷贝复用这一点在折腾树莓派的老朋友之间很实用希望你能用上。