☰
树莓派本地AI毛绒玩具:离线语音对话系统拆解
2026/10/9 1:22:26 网站建设 项目流程

如果你在树莓派上折腾过几年,大概会有同样的感觉:大多数人的 DIY AI 玩具,默认是跑到云端去对话的。我做这个叫 The Philosopher Plush 的项目,却反着来——把一只毛绒小熊的内胆掏了,塞进树莓派和麦克风,让它在完全不联网的状态下陪人聊天,而且聊的不是冷笑话,是一些你半夜会认真想的问题:什么是时间,为什么要睡觉,人会不会消失。

这玩意翻译成中文就是“哲学家毛绒玩具”。它跑的全部 AI 都在本地:语音识别在本地,对话模型在本地,语音合成也在本地。没有任何网络请求,也不需要每个月付订阅费。对家长来说,这意味着孩子和玩偶说的每句话都不会被传出去;对喜欢动手的朋友来说,这可能是把一个完整的语音AI系统塞进最小资源里最过瘾的训练场。这篇就来拆解我怎么做出来的,包括硬件选型、模型搭配、管道串联,以及我踩过的那些坑。

1. 缘起:这个“哲学家毛绒玩具”到底是个什么项目

1.1 100% 本地的价值,在动手之后才体会真切

先说说为什么非要本地。我最早也试过直接用大模型 API 把对话功能接进玩具,效果确实好,上下文理解又强又稳定,但接完就不想给人用了:一来每个问题都在花钱,你可能觉得一次几分钱不算什么,让一个小孩追着问一下午,账单立刻就不客气了;二来是隐私问题,毛绒玩具旁边的麦克风,其实等于一直挂在家里的一支录音笔,你录下来的对话全进了别人的服务器,这个画面想想就不舒服。

所以这个项目从一开始就给自己定了一条硬规矩:所有信号处理都必须在玩具壳子内完成。断网之后还能玩,换一个没有网络的房间还能玩,这是基本要求。这样的副产品也很明显,比如响应速度不再受网络波动影响,不会被“服务暂不可用”打断,也不存在账号失效、接口升级导致玩具变砖的问题。整个系统是可保存的,过几年拿出来,照样能跑。

1.2 为什么“哲学”是一个很好的测试场景

名字里的 Philosopher 不是噱头,它是这个项目的定位测试。一个普通 AI 玩具通常聊的是知识问答,答错了也无所谓;但“哲学化”的对话会逼着你把模型提示词、回复长度、上下文策略都做到位。孩子问“人为什么会做梦”的时候,你不可能回一段一千字的百科,毛绒玩具必须用一句干净、有想象力又不敷衍的话接住问题。

而且哲学问题特别暴露 LLM 的毛病。它对抽象问题容易一本正经地胡说,也容易把回答弄得很长。如果不做本地小模型的推理长度限制,一只毛绒熊会突然开始给你讲存在主义流派的演变,那就完全不像玩具了。所以这个项目其实是在同时解决两个问题:一个是“离线端侧 AI 玩具能不能跑通”,另一个是“怎么让一个小模型形成稳定、有性格的对话风格”。

1.3 这个项目适合谁,需要哪些基础

如果你打算复刻,先对一下底子。硬件方面我会在下一章详细说,但大致也就是树莓派、USB 或 I2S 麦克风、功放和喇叭,再加一只二手毛绒玩具。软件方面最好会一点 Python、知道怎么用命令行装系统,以及理解“音频采样率”“进程阻塞”这类基本概念。完全零基础会辛苦一点,但不是做不了,我把过程拆成步骤后,照抄也能完成大部分。

2. 硬件堆叠:把树莓派塞进毛绒壳里的具体方案

2.1 主控平台怎么选

这只玩具的“大脑”我最终选的是树莓派 5,8GB 版本。选它的理由很直接:本地跑 1.5B 参数的 LLM,量化之后大体需要 1.2GB 内存,8GB 版本还能再跑一个 3B 的模型,余地更大。树莓派 4 的 4GB 版本也能做,但推理速度会明显慢,回答一段话等上五六秒很正常。

如果你手头没有树莓派,也可以看看类似的单板电脑,关键是内存不低于 4GB,并且 GPIO 引出要方便。GPU 倒不是硬性要求,CPU 推理小模型也能接受,因为模型本来就不大。真正让我坚持用树莓派的原因是生态:音频 HAT、功放模块、各类驱动资料多,踩坑方案在网上几乎都能查到。

平台内存可跑模型建议综合评价
树莓派 44GB0.5B~1.5B 量化模型便宜,响应偏慢,适合入门
树莓派 58GB1.5B~3B 量化模型综合最优,发热需处理
Jetson 系列8GB+可以上更大模型价格高,杀鸡用牛刀

用树莓派5的时候还要注意:它满载功耗能到 8W 到 12W,玩具内部散热条件差,后面必须专门处理,这一个问题别轻视。

2.2 麦克风与扬声器的选择

声音输入我推荐用 ReSpeaker 2-Mic HAT,因为它可以直接叠在树莓派 GPIO 上,免去 USB 线在玩具肚子里绕来绕去的麻烦。这个 HAT 自带两个麦克风,配合算法可以做简单降噪,程序里还能拿到更整齐的音频流。如果你不想加 HAT,USB 麦克风也能用,比如那种会议用的全向麦,但体积和走线会比较头疼。

扬声器部分用的是 I2S 数字功放模块 MAX98357A,接一只 3W 或 5W 的 4 欧小喇叭。为什么不用普通 3.5mm 耳机放大器?因为 I2S 输出是数字的,直接由树莓派给音频数据,不需要额外声卡,抗干扰也更好。最终整个发声系统只有两组线:数据线和电源线,塞进布绒玩具里非常干净。

2.3 电源、结构与散热

供电是这个项目最容易翻车的地方。树莓派 5 建议用官方 5V/5A 电源,但在毛绒玩具里不能拖着一根充电器线,否则它就不是玩具而是台灯了。我用的是 18650 电池盒加 DC-DC 升压模块,输出 5V/3A 左右,实测正常对话场景够用。注意不要在电池输出端直接并联一堆模块,语音合成时电流尖峰容易把电压拉低,主板直接就重启了。正确做法是加一个大电容组,至少 470μF,并且在软件上避免同时做“模型推理+高音量播放”这组高耗电操作,至少插入几十毫秒的错峰。

外壳结构是纯手工活:把毛绒玩具背后沿缝线拆开,取出填充棉,在肚子位置留出树莓派和电池的容纳空间。我建议在树莓派和棉花之间隔一层硬纸板或亚克力板,防止静电,也方便拆装。最后在玩具背部装一条隐形拉链,后面调试就不用每次都做“开膛手术”了。

散热方面,树莓派 5 就算只是跑对话推理,芯片温度也会迅速升到 70℃ 以上,直接堵死在棉花里更危险。我在散热片上加了一个 5V 小风扇,对着外壳侧面的透气孔抽风,再把温控脚本跑起来:温度超过 60℃ 就开风扇,低于 50℃ 停转。这样安静时段完全没噪声,负载上来也不会闷烧。

3.1 唤醒与识别:用 Vosk 还是 Whisper

先解决“听见”的问题。我的管道里有两段语音识别:一个是唤醒词检测,一个是正式句子识别。一开始我试过用 faster-whisper 的 tiny 模型跑中文识别,准确率尚可,但在树莓派上每一句要等接近一秒到两秒,连续对话时非常拖沓。

后来我换成 Vosk 的中文小模型,体积只有几十 MB,CPU 上识别速度远快于 Whisper,虽然复杂句子的准确度略低,但对玩具场景完全够用。关键点是 Vosk 支持流式识别,我不需要等说完一整句再识别,可以边录边出中间结果,达到“你刚停下来,文字已经出来了”的效果。

唤醒词没有单独训练,我用了一个更省事的方案:让 Vosk 常驻监听,识别结果里匹配“你好小熊”这类的固定词。为什么这么做?独立唤醒词模型通常要自己录音训练,还要处理“唤醒后马上进入识别”的状态切换,逻辑复杂;直接在文本流里做关键词匹配,少一个进程,对树莓派这种资源紧张的环境更友好。

from vosk import Model, KaldiRecognizer model = Model("models/vosk-model-small-cn-0.22") rec = KaldiRecognizer(model, 16000) # 每帧回调中 if rec.AcceptWaveform(audio_bytes): text = json.loads(rec.Result()).get("text", "") if "你好小熊" in text: state_machine.trigger("wake")

3.2 对话模型:小模型的取舍与量化

本地对话模型我首选 Qwen2.5 系列的 1.5B 版本,量化成 GGUF 格式后大约 1GB 左右。这套组合在树莓派 5 上跑得动,回答质量也明显高于 0.5B 版本。如果你的内存只有 4GB,建议降到 0.5B,但要有心理准备:它很爱重复短语,理解稍微绕一点的问题就会显得很傻。

也有人让我试试 DeepSeek 蒸馏出来的 1.5B 版本,确实能给出更多推理痕迹,但那种“推理”在玩具上反而容易坏事:模型会在内心拆解问题,经常带出<think>标记,或者回答“好的,让我想想”之类的话,这不像一个毛绒生物,更像一个工作群里的客服。所以我更推荐指令跟随更直接、风格更容易压住的 Qwen2.5 系列。

模型加载上,我选择通过 llama.cpp 的 C 接口跑,因为它在树莓派上很容易编译,内存占用也比较干净。想省事的也可以直接装 Ollama,然后把 Python 请求库指向本地服务,两种方式都能跑。有一点要提醒:模型一次只留一个进程,不要一会儿加载这个,一会儿加载那个,玩具的可用内存会被碎片占满。

3.3 语音合成:离线 TTS 的选择

语音合成我用的是 Piper,这是一个能在树莓派上运行的离线 TTS 引擎,支持中文音色。跑下来的效果接近“朗读腔”,带一点机械感,但胜在响应速度快——一句话的音频几十毫秒内就能生成,不像某些云端 TTS 需要甩个连接回来再下载。

因为玩具角色的定位是“温和的讲故事者”,我没有刻意追求甜美的少女音色,反而选了一个语速偏慢、音量偏低的中文音色,这样半夜孩子听到也不会觉得吵闹。Piper 生成的是 WAV,我会先做静音裁剪,去掉前后多余空白,再交给播放进程,这样对话节奏不会拖沓。

echo "我觉得,时间不是一条直线,它更像你窗台上那盆花的影子。" | \ piper --model zh_CN-huayan-medium.onnx --output_file reply.wav

4. 软件管道:从“你好小熊”到完整回答的实现路径

4.1 状态机:不要让四个 AI 模块互相打架

整个系统最核心的软件设计,不是模型本身,而是一个简单的状态机。因为 STT、LLM、TTS 三个模块都是“吃资源大户”,如果它们同时工作,树莓派立刻就会卡死,音频输入还会捕捉到自己发出的声音,产生无限回声。

状态流转是这样的:初始状态是idle,此时 Vosk 在低功耗监听;识别到唤醒词后进入listening,录音会连续进行,直到检测到语音停顿;拿到完整句子后进入thinking,请求 LLM 生成回复;等回复文本回来后进入speaking,Piper 合成为音频并播放,播放结束后回到idle。

if state == "idle": if wakeword_detected: state = "listening" audio_buffer.clear() if state == "listening": if silence_timeout: transcript = stt_engine.final_result() state = "thinking" reply_text = llm.generate(transcript) state = "speaking" tts_speak(reply_text) state = "idle"

这个状态机看起来简单,但解决了几个容易崩的问题:只要处于speaking,输入回调就会暂停写入文本,避免玩具说完话自己又触发自己;只要处于listening,就不会有合成音频播放,避免模型被自己的声音分心。

4.2 各模块的音频流与消息格式

音频数据在这里是 16kHz、单声道、16bit PCM。ReSpeaker 采集到的原始数据可能经过不同采样率,进入处理线程前我会统一重采样。这一步不能省——Vosk 模型宣称是 16kHz,喂 48kHz 的数据进去,识别结果会非常离谱。

各模块之间我用 Python 的queue传递消息,而不是直接调用彼此的接口。比如 STT 识别完成的整句文本放进asr_queue,主控线程从队列取出来再给 LLM。这样做的好处是每个模块可以单独崩掉重启,不会拖垮整条链路。

LLM 的输入输出格式也要约束,不要直接裸调 openai 兼容接口。我给模型写了一个明确的 system prompt:

你是“哲学小熊”,一只温柔、耐心的毛绒哲学家。 你说话不超过两句,不使用书面报告式的语言。 如果问题太沉重,你可以说:“这个问题我也不完全知道,但我会一直陪着你。” 回答时不要提“我是一个AI模型”,不要生成列表。

这段提示词要非常具体,否则小模型特别容易跑偏。我甚至试过它突然开始输出“作为一个人工智能助手……”,毛绒玩具的身份崩得毫无挽回余地。

4.3 Prompt 组装与上下文管理

除了 system prompt,每次回答前还会把最近的对话历史拼进 prompt。玩具不该完全失忆,你刚才明明问过它“死后会去哪里”,再问一句“那你会记得吗”,它最好能接上。管理上下文我用一个固定长度的 FIFO:只保留最近两轮对话,每次拼进 prompt,超过长度就丢弃最早的内容。对 1.5B 模型来说,上下文太长既慢容易重复,两轮是一个平衡点。

推理参数上,temperature我调得比较低,0.4 左右,保证回答稳定;max_tokens控制在 60 以内,因为一个毛绒玩具一秒说五六个字的话,60 个 token 已经够撑起一分多钟。如果不限制,它会在某个夜里突然进入长篇大论模式。repeat_penalty也要开起来,小模型重复内容的毛病比大模型严重得多。

5. 实测数据与实战避坑:延迟、回声、散热与幻觉

5.1 延迟体感:从 4 秒到 7 秒

我拿树莓派 5 + Qwen2.5-1.5B + Vosk + Piper 的组合做了完整测试。从“你说完话的最后一个字”到“玩具开始出声”,短问题平均需要 2.5 秒到 3.8 秒。这个数字没有听起来那么慢,因为真实对话里玩具不会像语音助手那样瞬间抢答,而是带一点“沉思感”,反而符合哲学家小熊的人设。

树莓派 4 上延迟会涨到 6 秒左右,原因是 LLM 推理的那几秒明显拉长。如果你对延迟敏感,有两个优化方向:一是换更小模型,二是让玩具在thinking状态播放一个“嗯”的音效。这个小技巧能把用户的等待焦虑压住大半,算是一个低成本体验提升方案。

5.2 回声与自唤醒:听到自己的声音

第一次整机测试时,玩具每次说完话都会自己又进入唤醒状态,对话变成独角戏复读机。根因就是扬声器声音被麦克风重新采集,播放的最后一个词“小熊”正好触发了唤醒词。

我的处理思路分三层。第一层是物理隔离,麦克风阵列和扬声器不要面对面,尽量成 90 度角摆放;第二层是软件策略,播放期间禁止识别线程把结果送入状态机,只保留在缓冲区里;第三层是音量策略,TTS 播放音量限制在 80% 以下,避免喇叭声压过大直接“打”到麦克风振膜上。

物理隔离和软件策略组合后,实测基本消除了自唤醒。如果你的外壳更小、声学条件更差,还可以考虑摘下 ReSpeaker 换成远场麦克风阵列,但成本会上升。

5.3 散热:毛绒肚子里的“铁板烧”

树莓派 5 在没有风扇的毛绒肚子里,高负载跑 10 分钟后外壳已经有些发烫,CPU 温度稳定在 85℃ 左右,随时可能触发热降频。热降频的典型表现是:前两句回答很快,第三句开始变慢,但过一会儿又正常,像极了“犯困”。

我的方案前面提到过:散热片 + 5V 风扇 + 温控脚本。实测温度能控制在 65℃ 上下,风扇启动时噪音很小,但侧面的透气孔一定要留出来,不要用拉链全部封死。如果坚持做完全密封的外壳,那么散热片加风扇也没用,你得考虑用金属外壳做辅助散热,但那就不太毛绒了。

5.4 幻觉与小模型说错话怎么办

本地小模型的幻觉比大模型更常见,尤其面对“哲学”这种开放式问题,它会把一个听起来像那么回事的概念胡乱排列,造出一段话。我的处理方法是:不追求全部答对,而是让回答更谨慎。Prompt 里明确加上一句“如果你不确定,就直接说不知道”,并且把回答格式限制为短句,这样幻觉即使出现,也只是句子级别的偏移,不会变成一本正经的胡说八道。

另外要处理的就是安全边界。孩子在夜里问“人死了会怎样”这类问题时,玩具的回答必须温和且不制造恐惧。我在系统提示词里要求:不描述死亡过程,不渲染恐怖感,不给出宗教性结论,只做陪伴式回应。做好这层约束后测试下来,玩具能稳住基本情绪价值。

6. 后续还能怎么玩:记忆、角色扮演与全家多玩具联动

6.1 给毛绒玩具做长期记忆

目前玩具只记住最近两轮对话,过一会儿就忘了。想要长期记忆,可以用 SQLite 文件存对话标签,把“孩子提到过小狗叫毛毛”这样的信息抽出来存好。每次对话前,把相关记忆拼进 prompt,玩具就能在第二天问一句“毛毛现在还好吗”。本地存储没有成本压力,也不涉及云端同步,反而很适合长期积累。

记忆的抽法不需要做复杂 NLP,简单规则就够了:抓取句子里的人名和宠物词汇,再结合当前对话总结成一条短记录。模型已经能做到这一步,只是我还没给它写这个后续逻辑。

6.2 多玩具与角色分配

以后如果家里来了第二只毛绒动物,可以在同一台主机上挂载两个音频管线,分别跑不同角色的 prompt。比如小熊是哲学家,小狐狸是故事王。这不需要每个玩具都配一块树莓派,可以在主机上跑一个简单的调度器,用局域网把音频流转发过来。玩具本体变成“哑终端”,AI 大脑统一放在一个小服务器上,扩展起来特别轻松。

6.3 接口与扩展的想象

树莓派 GPIO 上还有不少空闲引脚,后续可以接一块小 OLED 当“眼睛”,也可以接一个手势传感器,让小熊在你伸手摸它时做状态切换。我现在的计划是加一个轻量级嵌入式向量数据库,把家里人的口头禅和话题偏好存进去,让玩具越聊越像家人。

最后给一个实操建议:外壳拉链一定要装。看起来只是一个小细节,但当你第无数次把电池接线重新焊一遍的时候,就会感谢自己当初没有把填充棉缝死。做这种项目,方便调试比什么都重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询