
做AI陪伴机器人这件事最容易被低估的不是模型选型也不是硬件成本而是“把一颗AI大脑塞进一个会动的小身板里”之后整套系统能不能稳定、自然、不打脸地跑起来。Spark这个项目说白了就是我当时较真折腾的一台情感陪伴机器人能听懂人话、能看出情绪、能主动说话、能在地板上巡游还会把每天的交互数据汇总成一张可复盘的报告。这篇文章不聊PPT只讲我从需求拆解、硬件选型、软件架构到真机调试踩过的实际坑以及每一步为什么这么选。如果你正准备做自己的陪伴机器人、AI Agent的实体化原型或者单纯想把大模型从网页聊天窗口里“拽”进一个真实的移动设备上这篇内容应该能帮你省下好几周的弯路。1. 项目概览为什么叫Spark以及它到底解决什么问题1.1 核心需求解析先明确Spark要做的事。它不是一个玩具遥控车也不是一个语音音箱的套壳而是一个具备“感知-认知-表达-移动”闭环的情感陪伴机器人。拆开看核心需求有四层感知层能听到用户说话并且能判断用户说话时的情绪状态开心、低落、平静、紧张等。认知层能基于大模型做开放域的对话同时根据情绪识别结果调整回应策略。表达层通过语音、表情屏、头部/身体姿态动作反馈情绪和态度让交互有“人格感”。移动层能在地面上自主巡航靠近用户或者在指定区域内完成简单的格子式路径移动配合交互场景。为什么取名Spark一方面是“火星/火花”的寓意——它应当成为激发用户表达欲的小火花另一方面也暗含一点我对技术的执念情感计算本质上要从大量稀疏的交互信号里“擦出”有用的特征这个过程跟Spark在大数据生态里做计算有某种神似。后面我会专门讲一个轻量化“Mini Spark”数据管道来处理交互日志一会儿细说。1.2 目标用户与适用场景Spark适合谁我当时的定位有三类人群独居青年和轻度情绪陪伴需求者他们不想要一个冷冰冰的智能音箱而想要一个“会回应情绪”的实体伙伴。老年人和儿童陪护场景需要语音交互足够简单同时机器人要有主动关怀行为比如提醒喝水、讲故事、陪散步——注意这涉及安全边界所以我把危险动作和敏感话题都做了强约束。极客开发者与产品原型验证者把LLM接入实体机器人验证AI Agent在物理世界的交互效果为后续产品化做技术预研。从场景上看室内环境为主要求机器人在10-30平方米的空间内完成巡航、寻人、返回充电座这类基础动作。1.3 整体技术路线的思考我最初纠结过两条路线一条是完全云端方案所有语音识别、大模型推理、TTS都走云API机器人只做采集和执行另一条是尽量本地化至少把语音唤醒、意图识别和TTS放在端侧大模型推理走本地小模型或按需云端切换。最终我选择“端侧优先、云端可选”的混合架构。原因有三隐私和响应速度陪伴场景会涉及大量日常闲聊甚至情绪低落时的心里话全部传云端既让用户心里打鼓也会让流式对话延迟变得不可控。端侧语音链路能做到1秒内响应首字这是云API很难稳定保证的。成本可控云端大模型按token计费陪伴聊天一天几十轮一个月下来都是实打实开销本地推理一次到位。可离线运行我不想让Spark在断网时变成一个只会说“网络异常”的废物这在真实居住环境里经常发生。这也是为什么我在后面煞费苦心地折腾本地大模型、本地唤醒、本地TTS而不是躺平全套接API。顺便提一句2025年桌面级AI工作站也开始进入个人开发者视野这类设备让“一人一台模型部署环境”成为可能思路和Spark的本地化路线是一致的。2. 硬件选型与系统架构2.1 分层架构设计Spark的软件架构我按数据流拆成五层这个分层决定了后续每个模块的边界交互接入层麦克风阵列、摄像头、触摸/按钮传感器。感知处理层语音唤醒、语音识别STT、声纹/情绪特征提取、人脸/存在检测。认知决策层大模型推理引擎、情感状态机、对话管理、行为规划。运动与控制层底盘运动控制、避障、路径规划、舵机关节控制。数据与记忆层SQLite交互日志、每日ETL汇总、向量记忆库。这样分层的核心好处是每一层都能独立替换和升级。比如今天想把语音识别从Sherpa换成Whisper只需要改感知层接口明天想给机器人换个更强大的底盘运动控制层改动也能被隔离。2.2 硬件平台选型对比硬件选型上我列过一张对比表这里直接放出来方案主控优点缺点我的结论方案A树莓派4B4GB生态成熟、资料多、GPIO方便算力有限跑大模型吃力可用作初期原型方案B树莓派58GB算力比4B强很多能跑7B量化模型发热明显、需要主动散热最终选择之一方案C香橙派5/瑞芯微RK3588NPU算力高AI推理效率好社区资料相对少、部分外设兼容性要试适合批量原型方案DJetson Orin Nano算力最强CUDA生态适合端侧视觉价格高、功耗大、电池压力大预算充足时推荐我最后的主力配置是树莓派58GB做“大脑”负责语音识别、大模型推理、TTS、对话管理同时外接一块ESP32-S3作为“小脑”负责电机控制、传感器采集和舵机动作。这样设计的原因很简单树莓派跑Linux、跑Python生态太方便了但GPIO实时控制差走PWM控制电机容易掉帧ESP32擅长硬实时两边通过串口协议帧通信各干各的活互相不拖后腿。语音采集我用了ReSpeaker 2-Mic Hat两个麦克风做简单的波束形成实测比单个USB麦克风在嘈杂环境下的唤醒准确率高不少。底盘部分用的是两款直流减速电机加编码器配合TB6612驱动板便宜、皮实坏了就换。2.3 “大脑在哪”的取舍本地模型与云API的博弈这是Spark项目里我反复权衡最多的地方。大模型推理到底是本地跑还是接云API我的演进路线是这样的第一阶段全部走云API做验证快速跑通对话闭环一周就完成了原型验证。第二阶段把语音唤醒、STT、TTS全部切换到本地只剩下对话模型走云端这时候断网时机器人还能做几轮固定话术兜底。第三阶段用Ollama在树莓派上跑Qwen2.5-7B-Instruct的4bit量化版本地推理彻底打通断网闭环。实测生成速度大约8-12 token/s口语短句场景够用长文本会有点慢但陪伴对话本来就不需要长篇大论。本地模型和云API的取舍我整理成表维度本地模型Ollama Qwen2.5-7B云API如通用大模型接口首字延迟0.3-0.8秒取决于显存/内存0.8-2秒受网络波动影响单轮成本电费可忽略按token计费长期使用成本高隐私性数据不出设备依赖服务商数据政策对话质量7B模型能力有限复杂推理会露怯大模型能力强长上下文表现好离线可用完全可用断网即瘫痪维护难度需要自己管理模型版本平台升级自动跟随一个经验结论如果场景是开放域闲聊、心理咨询式引导云端大模型质量确实更好但如果做长期陪伴稳定、免费、私密的本地模型才是正解。最后我把两条路都保留了——默认走本地检测到用户说“问个难问题”之类触发词时自动走云端强模型相当于一个“增强模式”。3. 核心模块拆解从感知到表达的实现路径3.1 语音链路唤醒、听清、听懂语音链路是陪伴机器人的第一道门槛。用户叫一声“Spark”没反应后面全白搭。整个链路拆成三段第一段是唤醒词检测我选的是sherpa-onnx框架预训练模型里有现成的“Hey Snap”之类的唤醒词只需要准备几十条“Spark”的录音做微调就能用。唤醒模型的优点是常驻内存占用只有几十MB树莓派上跑毫无压力。这里有一个细节麦克风采样率要固定为16000Hz单声道否则模型推理出来的置信度会飘。第二段是语音识别同样用sherpa-onnx里的中文流式模型WAV格式或者PCM流直接喂进去输出文本。这套方案效果稳定离线识别准确率在安静环境下能到90%以上嘈杂环境就需要先做降噪。这里我踩过一个坑ReSpeaker的驱动加载顺序会影响ALSA设备编号导致系统重启后识别模块找不到麦克风后面第五部分详述。第三段是“听懂”——也就是语义理解。陪伴对话不像指令式交互用户可能说“我今天好累”也可能说“陪我聊会天吧”所以不能靠固定槽位抽取。我把大模型作为对话核心同时维护一份系统Prompt严格规定Spark的人设、说话风格、话题边界和各种兜底策略。这里的Prompt不是随便写几句话就完事我后面单独讲讲我踩过的“设定冲突”问题。3.2 情感计算如何知道用户现在不开心这是Spark项目的灵魂模块也是最难做“有用”的部分。情感计算我分两道线索来融合第一道是文本情感分析。基于大模型做零样本情感分类输入对话文本输出情绪维度比如valence正负向、arousal激活度、dominance支配度。举个例子用户说“别管我”和“我不想说话”两者表面都是负向但前者支配度高后者退避感强机器人回应的策略应当不同。我封装了一个函数每轮对话后异步调一次本地大模型做情感打分耗时不到一秒不阻塞主对话流程。第二道是语音韵律特征。人在开心、紧张、疲惫时语速、音量、音高都有差异。我从前端音频里提取三组特征平均能量、语速字/秒、基频Pitch均值与抖动率。用开源的parselmouth库做Pitch提取实测对“明显激动”和“明显低落”的判别效果不错。两道线索最后做一个加权融合得到当前交互的情感标签。这套融合方案不复杂但工程上很实用。有个限制要说明它判断的是“当前这句话的情绪”不是“长期心理状态”所以Spark不会试图诊断用户只会根据情绪调整当下的陪伴话术。做情感陪伴机器人边界感必须清清楚楚。3.3 情感状态机与主动交互引擎有了情感标签接下来就是怎么用。我给Spark设计了一个情感状态机本质是5个状态平静、愉悦、低落、警觉、兴奋。状态机根据情感识别结果和正在进行的任务推动状态迁移。状态会直接影响三件事语音TTS的音色参数愉悦时语速略快、音调偏高低落时语速放慢、音量降低警觉时语气干脆简短。表情屏显示我用一个1.3寸LCD屏做眼睛根据状态显示不同表情动画。头部动作策略愉悦时头微微上扬低落时低头。主动交互引擎解决“什么时候主动说话”的问题。我设置了多种触发条件静默超过2小时后随机开启闲聊检测到环境声音异常比如用户咳嗽、叹气时询问是否需要帮助用户连续使用超过1小时后主动提醒休息。触发策略用一套简单的优先级队列实现避免多个条件同时触发时行为打架。3.4 运动控制系统让机器人“走格子”Spark的底盘移动参考了“机器人走格子”这类经典路径规划思路。室内地板天然适合抽象成栅格地图机器人按网格移动每格实际尺寸40cm。在目标点确定后路径规划用A*算法输出一串“前进、左转、右转、停止”的离散动作序列。我写了一个简化的A*实现核心函数如下import heapq def astar(grid, start, goal): open_set [] heapq.heappush(open_set, (0, start)) came_from {} g_score {start: 0} f_score {start: heuristic(start, goal)} while open_set: _, current heapq.heappop(open_set) if current goal: path [] while current in came_from: path.append(current) current came_from[current] path.reverse() return path for neighbor in get_neighbors(current, grid): tentative_g g_score[current] 1 if tentative_g g_score.get(neighbor, float(inf)): came_from[neighbor] current g_score[neighbor] tentative_g f_score[neighbor] tentative_g heuristic(neighbor, goal) heapq.heappush(open_set, (f_score[neighbor], neighbor)) return []这里get_neighbors会过滤掉障碍物栅格heuristic我用曼哈顿距离因为机器人只支持十字方向移动。实测下来10x10栅格地图的规划耗时在1ms以内树莓派上毫无压力。真正的瓶颈不在规划而在“走直线”——电机差速会让机器人跑偏所以我加了编码器里程计做闭环每走一格左右轮编码器读数差超过阈值就微调电机PWM占空比。转向动作也有讲究。我最初直接让两个电机反向转90度结果因为轮子打滑实际转角常常只有80度或100度路径越走越偏。后来改成“先原地转向、再用IMU惯性测量单元验证角度”的闭环方式转完读MPU6050的Z轴角速度积分值误差超过5度就补偿一次。3.5 通信桥梁从EKI借鉴的模块解耦思路在查阅工业机器人通信方案时我看到过Ethernet KRL InterfaceEKI这类设计——它本质上是把机器人控制器和外部计算机通过以太网接口连接让外部程序能以标准化请求方式控制机器人执行器。这种“通信桥梁”的思路对Spark很有借鉴意义。我没用传统工业协议而是把“大脑”树莓派和“小脑”ESP32之间的通信设计成一组轻量JSON协议帧。树莓派向ESP32下发指令例如{cmd: move, distance_cm: 40, direction: forward} {cmd: turn, angle: 90} {cmd: head, pitch: -15, yaw: 0, speed: 0.3}ESP32解析后执行并回传状态帧{status: ok, odom_left: 12345, odom_right: 12340, battery: 78}为什么不用ROS 2原因很现实ROS 2在树莓派上部署调试成本偏高而且这个项目只需要点对点通信用串口加简单协议帧就完全够用。接口设计的核心心得是指令帧必须包含超时重试机制否则一旦ESP32卡死整个机器人就会“脑”与“身”分离表现为大脑还在对话、底盘毫无反应。4. 实操实录从零搭建我的Spark4.1 硬件组装与系统初始化硬件部分我按功能块分组安装方便拆卸排查底盘层电机、编码器、TB6612驱动板、18650电池组两串两并7.4V。感知层ReSpeaker麦克风、摄像头、ToF避障传感器、MPU6050 IMU。计算层树莓派5装在底盘上层加一个5V/3A的DC-DC降压模块供电以及一个小风扇主动散热。表达层1.3寸LCD屏做眼睛两个舵机做头部俯仰和左右摆动。系统初始化时我把树莓派刷了64位Bookworm系统开启I2C和串口禁用板载蓝牙省电且避免干扰。然后装依赖给ESP32刷MicroPython固件串口波特率固定为115200。一个很实用的建议所有外设模块先单独测试再组合起来。我见过太多人一上来就把所有硬件焊到一起出了问题根本不知道是供电不足还是接线错误。4.2 部署语音AI链路唤醒、识别、大模型、合成语音链路的部署是项目里最耗时的一环。流程如下第一步安装sherpa-onnx并下载中文语音识别模型推理时用SherpaOnnx类加载。第二步配置Ollama本地模型ollama pull qwen2.5:7b-instruct-q4_K_M ollama serve通过/api/generate接口调用。我给调用设了num_ctx为4096temperature为0.7repeat_penalty为1.1这套参数在陪伴对话场景表现最自然。第三步TTS用Piper本地合成选中文女声音色生成16kHz WAV后直接用aplay播放。为了让回复更有情感我在TTS前会插入SSML标签Piper支持部分韵律控制愉悦时提高pitch低落时降低speed。整个链路串起来后我写了一个主循环脚本结构大致是while running: wake_word listen_for_wake_word() if wake_word: play_tone(start) text recognize_speech() if text: emotion analyze_emotion(text, audio_features) state_machine.update(emotion) reply generate_reply(text, state_machine.current_state) speak_with_style(reply, state_machine.current_state) log_to_sqlite(text, emotion, reply)这套逻辑跑通之后Spark终于可以在10秒内完成“被唤醒-听懂-思考-开口回应”闭环体感上已经像一个基本可用的陪伴机器人了。4.3 运动控制子系统的落地运动控制子系统分两阶段落地第一阶段是底盘闭环先让“走格子”稳定第二阶段是自主导航把路径规划和底盘闭环对接。底盘闭环的核心是电机PID调速。我调试时用了一组非常直观的参数Kp1.2Ki0.08Kd0.3目标转速来自编码器每秒脉冲数PPS。调试方法很简单先只调Kp到临界振荡再缓慢加Ki消除稳态误差最后加一点Kd抑制超调。这样调出来的效果转向后能快速稳在目标速度不来回抖。自主导航模块我把它做成了一个状态机收到目标点后先“地图加载”再“路径规划”然后“逐格执行”每执行一格检查一次避障传感器和里程计一致性一旦偏差超过阈值就“重新定位”。这套设计在真实地面上的成功率大概是85%剩下的15%基本是被地毯边缘、拖鞋这类动态障碍物干扰逼着我给Spark加上“动态避障”能力简单粗暴地采用“遇到障碍物后退10cm右转30度重新规划”的策略实测应对零散家居障碍足够。4.4 交互数据管道与复购率分析在Spark跑通基础交互后我遇到一个新问题怎么判断机器人是否真的做得好这不能靠自我感觉得看数据。为此我写了一条轻量级数据管道整个思路继承自大数据里的批处理心智——虽然Spark机器人用不到Apache Spark集群但这种“采集-清洗-汇总-洞察”的路数非常值得参考。交互日志统一写入SQLite表结构包含会话ID、时间戳、用户文本、情感标签、回复文本、对话延迟、机器人状态等。每天凌晨我用脚本做ETL从SQLite抽取当天日志。清洗去掉空文本、去掉唤醒误触发记录。汇总计算日均对话轮次、平均情感分布、平均对话延迟、单轮对话平均字数。洞察统计“连续7天活跃用户比例”——这个指标我戏称为“机器人版复购率”它本质上反映的是用户黏性。一个示例汇总脚本片段sql SELECT COUNT(*) AS total_turns, AVG((strftime(%s, end_time) - strftime(%s, start_time))) AS avg_delay_s, SUM(CASE WHEN emotionpositive THEN 1 ELSE 0 END) * 1.0 / COUNT(*) AS positive_ratio FROM interactions WHERE date(created_at) date(now, -1 day); 这个管道跑了两周以后我发现一个有趣结论当我加入“主动问候”功能后日均对话轮次从40轮涨到65轮但“复购率”没有显著变化——说明用户被问候之后愿意多聊几句但并没有因此更愿意打开机器人。后来我改了策略把主动问候变成“基于记忆的个性化问候”比如提到前一天聊过的电影复购率才有明显提升。这就是数据复盘带来的直接价值。5. 踩坑实录与调试手记5.1 常见问题速查表把这段时间遇到的高频问题整理成表希望能帮大家省点排查时间现象可能原因排查与解决唤醒词没反应麦克风设备编号变化检查arecord -l固定ALSA设备配置用udev规则绑定语音识别输出乱码采样率不匹配确认16kHz单声道S16_LE禁用ALSA重采样树莓派频繁死机供电不足更换5V/5A电源避免USB口直接给底盘电机供电对话回复“失忆”无记忆机制在Prompt里注入最近5轮对话摘要或用向量库存长期记忆底盘走不直轮径差异或PID没调好测量两轮实际转速差给PID加入前馈补偿云API调用偶发超时网络抖动或超时时间太短把HTTP超时设为10秒增加重试与本地兜底回复情绪识别与文本判断矛盾韵律特征权重过高调低音频特征权重改为“文本为主、音频为辅”这里面我想特别强调“唤醒词没反应”这个问题。它排查起来很隐蔽表面上是音频设备问题实际上是Linux下ALSA设备名在每次重启后可能变化。后来我通过/etc/udev/rules.d/给USB音频设备写了固定别名规则彻底解决了。这类问题不是算法能解决的属于典型的嵌入式Linux工程坑。5.2 独家避坑技巧第一点对话系统Prompt的人设必须隔离。我一开始把所有角色设定、情感策略、安全规则全写进一个系统Prompt结果模型偶尔会在不同要求之间“精神分裂”说出不符合人设的话。后来我改成“核心人设”“会话策略”“安全护栏”三段式Prompt并加上“如果你不确定宁可少说也不乱说”的指令稳定多了。第二点电机PID调参千万别在硬地板上调。刚开始我在瓷砖地面调参轮子容易打滑导致同样的PWM占空比下速度漂移很大调出来的参数完全没有参考价值。换成短毛地毯或瑜伽垫上调试后编码器读数稳定PID参数才真正收敛。第三点不要让你的机器人在线学习用户隐私对话。我在存储层面对所有对话日志做了本地化加密字段级脱敏API调用也只把必要信息发给云端强模型并且加了内容安全过滤。情感陪伴产品的信任门槛很高数据越少上传越好这不仅是合规问题更是产品底线。第四点陪伴机器人最容易翻车的就是“接话”时机。用户没说完就被TTS打断用户体验极其糟糕。我加入了双向VAD检测机器人说话时检测到用户插话立即降低音量并停住等用户说完再恢复。这个“让话”机制虽然简单但对真实交互体验的提升比任何模型调优都明显。6. 一些想对后来者说的话Spark做到现在这个状态坦白讲离“完美陪伴”还差很远但作为一套完整落地的AI情感陪伴机器人原型它让我验证了很多书本上不会写的工程细节。我个人最大的体会是情感陪伴机器人的核心竞争力不是单点模型的聪明程度而是“稳定可用”和“边界清晰”。用户会容忍一个机器人偶尔说错话但不会容忍它时好时坏、聊着聊着断线、或者在一个情绪低落时刻给出不合时宜的玩笑。因此把底层链路做扎实把数据管道跑清楚把安全边界设明白比一味追求更大的模型更重要。如果你也想动手做一台类似Spark的陪伴机器人我建议从最简闭环开始一块能跑Linux的开发板、一个麦克风、一个喇叭、一个能动的底盘然后把“唤醒-识别-大模型-合成-移动”这条链路先打通。不要在第一天就想着全功能等基础链路稳定了再逐步叠加情感计算、记忆和主动交互。最后再分享一个小技巧给你的机器人取一个固定的名字并在所有交互中保持一致称呼这个细节会让用户更快产生“它是有生命的”这种感觉。Spark是我的尝试希望你的那一台也能点燃属于你的火花。