尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

端侧AI语音机器人Microduck复刻指南:从硬件选型到模型微调

端侧AI语音机器人Microduck复刻指南:从硬件选型到模型微调 Microduck 这个项目圈内玩嵌入式 AI 和端侧大模型的朋友应该不陌生。它本质上是 OpenDuckMini 开源项目的一个衍生规格——把一只玩具鸭改装成能听懂人话、能对话、能联网查资料的桌面机器人而 Microduck 更强调轻量化和低成本的复刻路径。最近看到不少人在问训练教程和源码地址我把自己从硬件选型到模型微调、从踩坑到跑通的全过程整理出来希望能帮到正准备动手的人。先说结论这套东西的核心价值不在于“鸭”而在于它把语音唤醒、大模型推理、语音合成、联网搜索这一整套端侧 AI 链路压缩到了千元级硬件上。对想入门边缘计算和端侧大模型部署的人来说是性价比极高的实验平台比单纯在电脑上跑 demo 有价值得多。1. 项目拆解Microduck 到底是什么能做什么1.1 一台会说话的鸭子背后是完整的端侧 AI 链路很多人第一次看到 Microduck 的视频第一反应是“这不就是个蓝牙音箱套了个鸭壳子吗”实际上完全不是一回事。它是一台具备完整语音交互能力的边缘计算设备你叫它的名字它能唤醒你说一句话它能理解并给出回答你问天气、问新闻它能自己上网搜然后念给你听。整个过程全部在设备本地完成推理不需要依赖云端的商业 API。硬件组成上Microduck 主要由这么几个部分构成主控板通常是 Jetson Orin Nano 或 Orin Nano Super 开发者套件负责跑大模型推理。这是整个系统的算力核心。麦克风阵列用于拾音和声源定位让鸭子知道声音从哪个方向来。扬声器语音回复的输出设备。舵机控制鸭头转动实现“看向说话人”的互动效果。玩具鸭外壳市面上常见的儿童玩具鸭内部掏空用来容纳硬件。软件层面则是典型的端侧语音 Agent 架构语音识别ASR模块负责把声音转成文字大语言模型负责理解并生成回复语音合成TTS模块把文字转回声音。中间还有一个会话管理模块用来维护多轮对话上下文以及工具调用模块用来触发联网搜索等外部动作。1.2 它和 OpenDuckMini 的渊源Microduck 这个名字经常和 OpenDuckMini 一起出现两者的关系可以理解为OpenDuckMini 是上游项目Microduck 是在它基础上做轻量化调整的复刻版本。原版 OpenDuckMini 由社区爱好者发起设计目标是让更多人能以较低门槛复现一个“能对话的机器鸭”Microduck 则在元器件选型、模型量化和部署流程上做了更适合个人玩家操作的优化。我这边的复刻以 Jetson Orin Nano 为主控8GB 显存版本的开发套件在二级市场已经降到 1500 元以下加上麦克风、舵机、外壳和其他配件整套成本控制在 2000 元左右。如果手头有闲置的 Orin Nano 或者愿意用性能稍弱的树莓派 5 来跑小尺寸模型成本还能进一步压缩。1.3 适合谁来复刻能学到什么我自己的体会是Microduck 的受众主要分三类第一类是刚入门端侧 AI 的开发者。通过复刻过程能完整理解大模型从云端走向边缘设备时面临的算力约束、量化精度权衡和推理延迟优化这是看多少文档都换不来的实践经验。第二类是嵌入式系统工程师。Microduck 涉及 Jetson 平台的外设驱动、音频采集、舵机控制、串口通信以及 TensorRT、ONNX Runtime 等多种推理引擎的部署。对嵌入式背景的人这是把 AI 能力融入传统硬件控制的好案例。第三类纯粹是爱好者和创客。喜欢折腾硬件、喜欢做点有意思东西的人Microduck 的互动性和可扩展性都很强。改个提示词换个人格、加个传感器、接个智能家居控制玩法很多。如果你只想在电脑上跑一个大模型聊天这个项目帮不上什么忙但如果你想亲手搭一个“看得见、摸得着”的 AI 硬件产品Microduck 是目前少有的适合个人复刻的完整方案。2. 核心组件拆解与选型思路2.1 主控平台为什么推荐 Jetson Orin Nano主控是整个系统的算力核心选型直接决定了你能跑多大参数的模型、推理速度有多快。Microduck 社区比较常用的方案是 Jetson Orin Nano 8GB原因有三个首先是显存容量。端侧跑大模型最缺的就是显存。Orin Nano 8GB 在 INT4 量化下可以流畅运行 7B 到 8B 参数规模的模型配合流式输出对话延迟能控制在 2 秒以内体验已经比较接近云端 API。4GB 显存版本只能跑 3B 到 4B 的模型对话质量差距明显。其次是功耗和散热。Orin Nano 的默认功耗模式为 7W 到 15W在性能模式下最高 25W配合一个 5V 风扇和铝合金散热片就能稳定运行。相比动辄几百瓦的桌面 GPU它可以完全靠 USB PD 移动电源供电这让鸭子可以脱离插座走动展示。最后是生态成熟度。NVIDIA 为 Jetson 平台提供了完整的 JetPack SDK把 CUDA、cuDNN、TensorRT 全部预装好。部署 ONNX 或 PyTorch 模型时可以直接调用 TensorRT 加速大幅降低推理优化的工作量。个人开发阶段用 PyTorch 直接推理也能满足需求社区里现成的部署脚本也基本都是基于这套生态的。2.2 语音交互模块麦克风阵列、扬声器和语音库选型语音交互链路对硬件的要求集中在前端的拾音质量和后端的播放清晰度上。Microduck 常见的麦克风配置是 ReSpeaker 2-Mic 或 4-Mic 阵列通过 USB 连接到 Jetson也直接兼容 ROS 音频驱动。如果你希望鸭子能根据声源位置转头就选 4-Mic 阵列配合算法可以实现声源定位如果只做简单唤醒和对话2-Mic 就够用成本更低。扬声器方面我试过 JBL 的小型蓝牙音箱和普通 3W 全频喇叭前者的声音层次感明显更好后者胜在体积小、安装方便。如果鸭壳内部空间充足优先选蓝牙音箱外放效果对整体体验的影响比很多人想象中大。语音能力这块社区常用的方案是 Sherpa-ONNX 做本地语音识别模型用 Zipformer 或 Paraformer 的中文模型TTS 则用 Piper 或 Sherpa-ONNX 自带的 VITS 模型。两个模块都是 ONNX 运行时在 Jetson 上跑 CPU 推理也能接受基本不会构成性能瓶颈。2.3 模型部署方式TensorRT 和 llama.cpp 的选择大语言模型的推理优化我在 Microduck 上实测过两种主流方案一种是 TensorRT-LLM。优势是与 Jetson 平台高度契合推理速度最快但环境配置相对繁琐且对模型结构有要求。社区提供的量化脚本可以自动完成从 HuggingFace 模型到 TensorRT Engine 的转换不过对新手来说单是trtllm-build的编译时间就可能等上半小时。另一种是 llama.cpp。以 GGUF 格式运行量化模型部署简单、跨平台兼容性好在 Jetson 上开启 CUDA 加速后性能损失相对可控。我在 Orin Nano 上跑 Qwen2.5-7B-Instruct 的 Q4_K_M 量化版出字速度大约 15 到 20 token/s配合流式输出生成的等待感不像完整计算延迟那样明显。我的建议是第一版先跑通 llama.cpp因为流程最短、出错概率最低。等系统全链路稳定之后再考虑迁移到 TensorRT-LLM 追求极限速度。3. 完整复刻流程从硬件组装到系统跑通3.1 硬件准备与外壳改造硬件清单我整理成了一张表方便对照采购部件推荐规格预估价格备注主控板Jetson Orin Nano 8GB 开发者套件1400-1800 元二手性价比更高麦克风阵列ReSpeaker 2-Mic / 4-Mic100-200 元USB 接口免驱扬声器JBL GO 系列或 3W 全频喇叭80-150 元优先选蓝牙音箱舵机SG90 或 MG90S10-20 元控制鸭头左右转动电池支持 15V/3A 输出的 USB PD 移动电源100-200 元容量建议 10000mAh 以上玩具鸭中大型塑料鸭高度30cm以上30-60 元内部空间至少能放下开发板外壳改造是整个过程中最“手工”的环节也是很多人卡住的地方。我踩过的教训是不要试图把开发板完整塞进鸭子身体里要把鸭身从腹部切开底部用亚克力板做一个托盘把主板、麦克风、电池固定在托盘上再把鸭壳罩上去。这样既方便散热也方便以后维护。麦克风的位置也有讲究不要直接塞在鸭子肚子里否则拾音会被塑料壳严重衰减。我是在鸭嘴位置开了一个小孔把麦克风阵列伸出来实测唤醒率从不足六成提升到了九成以上。3.2 系统烧录与基础环境配置Jetson 的系统烧录相对简单用 NVIDIA SDK Manager 在 PC 上安装 JetPack 6.0 及以上版本选择 Jetson Orin Nano 开发者套件即可。系统烧录完成后需要做几件基础配置开启 64GB 或更大容量的 NVMe SSD 作为系统盘否则 16GB 的 eMMC 根本不够用。把系统电源模式切换到 25W 性能模式sudo nvpmodel -m 0。安装常用依赖Python 3.10、pip、git、CMake 和构建工具。配置虚拟内存 swap建议 8GB 以上防止编译和加载大模型时内存不足。3.3 语音链路部署ASR、LLM、TTS 全打通语音链路部署是核心中的核心我按顺序拆解一遍。先做 ASR。用 Sherpa-ONNX 的 Python API 写一个简单的识别函数import sherpa_onnx recognizer sherpa_onnx.OnlineRecognizer.from_transducer( encodersherpa-onnx-streaming-zipformer-zh-14M-2023-02-23/encoder.onnx, decodersherpa-onnx-streaming-zipformer-zh-14M-2023-02-23/decoder.onnx, joinersherpa-onnx-streaming-zipformer-zh-14M-2023-02-23/joiner.onnx, tokenssherpa-onnx-streaming-zipformer-zh-14M-2023-02-23/tokens.txt, num_threads4, sample_rate16000, feature_dim80, enable_endpoint_detectionTrue, )这里enable_endpoint_detectionTrue是关键它能让识别器在检测到说话结束后自动返回完整句子省去自己在音频流上做 VAD 的麻烦。实际录制时注意采样率要统一为 16000Hz声道数为单声道用sounddevice或pyaudio采集都可以。接着是 LLM 推理。用 llama.cpp 的 Python 绑定写一个流式对话函数import llama_cpp llm llama_cpp.Llama( model_pathqwen2.5-7b-instruct-q4_k_m.gguf, n_gpu_layers-1, # 全部层加载到 GPU n_ctx4096, # 上下文窗口 n_threads8, verboseFalse, ) def chat(prompt, history[]): messages history [{role: user, content: prompt}] stream llm.create_chat_completion( messagesmessages, streamTrue, temperature0.7, max_tokens512, ) response for chunk in stream: delta chunk[choices][0][delta] if content in delta: response delta[content] return response, messages [{role: assistant, content: response}]n_gpu_layers-1表示把所有层都放到 GPU 上8GB 显存跑 7B Q4 量化模型是够的。上下文窗口 4096 够日常对话用调太大会明显增加首 token 延迟。最后是 TTS。用 Sherpa-ONNX 的离线 TTS 接口生成语音import sherpa_onnx import soundfile as sf import numpy as np tts sherpa_onnx.OfflineTts( modelsherpa-onnx-vits-zh-ll, vocoder, # VITS 不需要单独声码器 providercpu, ) text 你好我是 Microduck。 audio tts.generate(text, sid0, speed1.0) sf.write(reply.wav, audio.samples, samplerateaudio.sample_rate)VITS 模型不需要额外的声码器一次性生成完整音频在 Orin Nano 上生成一句 5 秒钟的话大约需要 1 秒体验上没有明显等待感。3.4 会话管理、工具调用与联网搜索到这一步鸭子已经能听、能说、能对话了但还缺一个关键能力——联网搜索。要让它能回答天气、新闻这类时效性问题需要接入搜索 API 并将结果喂给大模型。我的实现思路是用一个简单的函数调用协议用户问题先让大模型判断是否需要搜索需要的话返回结构化 JSON 指令主程序执行搜索并把结果拼接为上下文再次送入模型。示例代码如下tools_prompt 你是一个工具调度助手判断用户问题是否需要联网查询。 如果需要查询请输出 JSON{need_search: true, query: 搜索关键词} 如果不需要请输出 JSON{need_search: false} def should_search(user_input: str) - dict: response, _ chat(tools_prompt \n用户问题 user_input) try: return json.loads(response.strip().strip(json).strip()) except: return {need_search: False} def run_search(query: str) - str: url https://api.duckduckgo.com/ params {q: query, format: json, no_html: 1} resp requests.get(url, paramsparams, timeout10) data resp.json() results data.get(RelatedTopics, [])[:5] return \n.join([r.get(Text, ) for r in results if Text in r])实际使用中搜索结果的截断和拼接待优化——通常只需要把前 3 到 5 条摘要拼进上下文比如在系统提示词里追加“搜索结果xxx”让模型基于这些信息组织回答。这个方案不需要额外 API key免费可用个人玩家首选。会话管理方面我维护一个简单的双端队列只保留最近 6 轮对话超出就丢弃最旧的一条。如果想实现长期记忆可以在每轮结束后用嵌入模型把对话总结写入本地 SQLite下次唤醒时检索相关片段拼入上下文。这是社区里比较成熟的做法也能避免上下文窗口被长对话占满。4. 模型训练与个性化定制让鸭子听懂你的话、拥有自己的声音很多人问 Microduck 能不能训练其实是问两个层面一是让它更懂你、回答风格更贴合你的需求二是让它的声音更像某个特定的人。这两件事对应的是大模型的微调和 TTS 的语音克隆。4.1 大模型微调用 LLaMA-Factory 把 7B 模型“调教”成你的风格默认的 Qwen2.5-7B-Instruct 是一个面向通用场景的模型回答风格偏保险、偏官方。如果你想让它像一个朋友那样说话或者成为一个懂某个特定领域的问答助手需要做轻量微调。我在 Microduck 上的做法是用 LLaMA-Factory 做 LoRA 微调只训练少量参数就能达到不错的效果。训练数据格式是 Alpaca 风格的三段式instruction指令、input输入、output期望回答。举个例子[ { instruction: 你是 Microduck一只友善可爱的机器鸭助手。, input: 介绍一下你自己, output: 你好呀我是 Microduck一只会用嘴巴思考的机器鸭。我的家在 Jetson 主板上虽然身体是塑料的但我可是有一颗真实的 AI 大脑哦 } ]数据量方面我准备了大约 500 条这样的对话样本覆盖自我介绍、问候、闲聊、问答等日常场景。在单张消费级显卡上训练 2 到 3 个 epoch整套流程大约一小时。LoRA 的r值设为 16alpha设为 32学习率 2e-4这些是社区常用的稳定参数。微调完成之后需要把 LoRA 权重合并回原模型导出为 GGUF 格式。LLaMA-Factory 本身支持导出 HuggingFace 格式再通过 llama.cpp 的转换脚本转成 GGUF。这一步有个常见坑转换时要确保tokenizer_config.json完整否则输出可能会出现乱码或非法字符。坦白说对于大部分只是想“让鸭子更可爱一点”的用户微调不是必选项。直接在系统提示词里写下人格设定零训练成本也能达到 80% 的效果。我自己是在跑通了全流程之后才做的微调用 LoRA 打磨回答风格做实验同时也为了验证模型本地迭代的路径是走得通的。4.2 语音克隆用 GPT-SoVITS 让鸭子说你的声音如果说微调是“性格定制”语音克隆就是“声线定制”。Microduck 的 TTS 模块用的 VITS 模型是通用音色听起来机械感较强。要让鸭子的声音更像自己或者某个特定人我用的是 GPT-SoVITS 做语音克隆再把克隆模型导出为 ONNX 格式供 Sherpa-ONNX 调用。操作流程是先用手机录 30 到 60 秒的干净人声注意安静环境避免混响然后用 GPT-SoVITS 的训练脚本做微调。在消费级显卡上大约训练 100 到 200 步即可得到一个效果不错的音色模型训练时间通常在十几分钟到半小时之间。训练完成后导出为 ONNX 格式替换掉 Sherpa-ONNX 的默认 VITS 模型路径接口调用方式完全不变。语音克隆的效果和我自己的原声非常接近听感上大约有八分相似在玩具鸭这种窄带扬声器上播放已经能骗过熟人了。有一点需要提醒语音克隆有被滥用的风险如果打算做成公开产品或者发视频请确保你克隆的声音是你自己的并且使用时明确标注为 AI 合成避免引发不必要的争议。4.3 从云端到本地的完整部署流程训练和推理都在本地完成不依赖任何云服务这个特性对注重数据隐私的人很有吸引力。整个流程可以总结为买一套 Jetson Orin Nano烧录 JetPack 系统。用 LLaMA-Factory 在自己的电脑或其他算力环境微调 Qwen 模型导出 GGUF。把 GGUF 文件拷贝到 Jetson用 llama.cpp 直接加载推理。用 GPT-SoVITS 克隆音色导出 ONNX替换 TTS 模型。组装硬件外壳运行主控脚本完成语音交互全流程。这套流程的好处是全部离线可用。出门在外没有网络时鸭子依然能正常对话关闭联网搜索功能后所有推理都在本地完成不用担心录音数据被上传。5. 常见问题与排查技巧实录复刻过程中我踩了不少坑这里整理几个出现频率最高、也最容易劝退新人的问题按排查难度从低到高排。5.1 麦克风拾音效果差、唤醒率低问题现象喊鸭子名字十次有六七次不回应或者对话过程中语音识别老是听错。排查步骤确认录音采样率是 16000Hz 单声道检查麦克风是否被外壳遮挡用arecord -l确认系统识别到 USB 麦克风用speech-recognition自带的 VAD 检测看看唤醒前后的音量曲线确认唤醒词之后确实有实质语音输入。我的经验是大部分唤醒率低的问题都出在物理层麦克风的位置影响远大于算法参数。把麦克风从鸭壳内部移到鸭嘴附近开孔朝外唤醒率能提升 30% 以上。5.2 推理速度慢每句话要等很久问题现象提完问题后要等 5 秒以上才开始回答体验很差。排查步骤用tegrastats确认 GPU 频率是否跑满如果只有 300MHz 左右说明没开性能模式用sudo nvpmodel -m 0切到最高功耗档。其次检查n_gpu_layers是否设为 -1如果只加载了部分层到 GPU推理速度会大打折扣。最后看看模型是否被 swap 到了内存在htop里如果VIRT远大于物理内存说明上下文开太大导致内存不足。根据我的实测7B Q4 模型在 Orin Nano 8GB 上首 token 延迟大约 1 到 2 秒如果超过 4 秒基本是配置出了问题。5.3 系统内存不足、编译常被杀问题现象安装依赖或转换模型时进程被系统 OOM Killer 杀掉。这是 Jetson 上非常典型的问题。处理方法是先检查可用内存和 swapfree -h。建议把 swap 文件设为 8GB 以上。在 25W 模式下8GB 内存加 8GB swap 跑 llama.cpp 推理是够用的但编译大项目时最好临时关掉其他占内存的服务。5.4 蓝牙音箱爆音或延迟卡顿问题现象扬声器播放语音时经常爆音或者声音忽快忽慢。蓝牙音箱的延迟和丢包在语音回放上确实会有影响。我的解决方案是调试阶段用 3.5mm 音频线直连一个 3W 小喇叭只保留成品展示时才切换蓝牙音箱。这能大幅减少变量方便定位问题。6. 进阶方向把 Microduck 拓展成你的私人 AI 助手Microduck 跑通之后它的可玩性才刚刚开始。根据我自己的探索和社区里的案例分享几个性价比比较高的拓展方向。6.1 接入 Home Assistant让鸭子控制智能家居通过 MQTT 协议把 Microduck 接入 Home Assistant就可以用语音控制窗帘、灯光、空调做一个“语音桌面机器人”的智能家居入口。实现上只需要让主程序识别“打开客厅灯”这类意图然后向 MQTT 主题发布控制指令。这个扩展对代码能力要求不高但会让鸭子从“玩具”变成“工具”。6.2 把 Facebook 聊天记录“喂”给鸭子有个社区玩家开源了一个项目 QZoneArchive原理是先通过平台的个人数据导出拿到聊天记录清洗成 JSON 格式后用嵌入模型检索相关片段再注入系统提示词。效果是鸭子能聊你的往事比如“你还记得去年你生日那天我去了哪里吗”它能基于真实历史数据给出回答。这个扩展本质上是“检索增强生成”的个人化应用贴近实际使用场景。6.3 加入视觉能力如果你愿意再花一点钱买一个 USB 摄像头Microduck 就能获得视觉能力。用 Jetson 平台上的 YOLO 或 GroundingDINO 做实时目标检测再用视觉语言模型做场景理解可以让鸭子“看到”谁在跟它说话甚至描述你桌上的物件。这个方向性能消耗更大8GB 显存需要在视觉模型和语言模型之间做权衡但对喜欢折腾的玩家来说非常有吸引力。6.4 打造多鸭协作系统社区里已经有人把两三只 Microduck 放在同一个局域网里通过 MQTT 共享对话状态实现“一群鸭子开会”的效果。这个方向对理解分布式状态同步和消息队列很有帮助也是比较有趣的展示项目。我在实际使用中的体会是Microduck 复刻最有价值的反而不是最终跑通的那一刻而是在每一层链路调试时被迫去搞清楚的那些底层原理为什么 ONNX 换成 TensorRT 之后速度翻倍为什么量化位宽从 8bit 降到 4bit 之后显存占用少一半但困惑度只涨了一点点为什么唤醒率在麦克风挪了个位置之后从六成升到九成。这些东西在任何一篇文档里都学不到只有亲手做一遍才理解得最透彻。最后再分享一个小技巧如果你也想做语音人格定制别一上来就追求大模型微调。先用系统提示词把人格设定清楚跑够一周的日常对话把实际交互中表现不好的句子录下来整理成训练数据再做微调。这样既省时间数据质量也远高于凭空想的样本。先让鸭子“像”你要的样子再让它“是”你要的样子这个顺序很重要。
返回列表