用犀牛派 X1 打造 AI 桌面陪伴机器人:从 ESP32S3 玩具到边缘 AI 实战
摘要:前阵子我被一篇《用 XIAO ESP32S3 制作桌面陪伴机器人》在创客圈刷屏——一块几十块钱的 ESP32S3 + 一块 0.96 寸 OLED,靠 PC 端 Python 识别你"在听歌/在打字/发呆",让屏幕上的小眼睛变表情 [媒体]。它很可爱,但本质是个状态显示器,没有任何智能。我把它改造成保留"PC 状态识别 + 表情反馈"的灵魂,增加了摄像头人检、本地模型对话这些真本事,完整走了一遍阿加犀 AidLux + AidLite + QNN + MMS 工具链。这是一篇改造实战,不是翻译——凡是原项目的代码我标注
[沿用],凡是犀牛派 X1 新增的能力我标注[新增]。所有命令整理自阿加犀官方文档与社区教程,动手前请对照你所用 SDK 版本核对。

目录
- 一、从 ESP32S3 玩具到犀牛派 X1:为什么改造
- 二、改造总览:新架构与数据流
- 三、硬件 BOM 清单
- 四、环境准备:AidLux + AidLite + MMS
- 五、犀牛派 X1 端:AI 陪伴核心
- 六、PC 端:Windows 活动识别客户端
- 七、联调与验证
- 八、坑点实录(10 条,按被踩概率排序)
- 九、成本与性能账
- 十、结论与扩展路线图
- 参考链接
一、从 ESP32S3 玩具到犀牛派 X1:为什么改造
1.1 原项目回顾
原项目(下称"玩具版")的构成非常清晰 [媒体]:
- 硬件:Seeed Studio XIAO ESP32S3 + 0.96 寸 SSD1306 OLED(I2C,128×64,地址 0x3C)
- 固件:用
FluxGarage_RoboEyes库在 OLED 上画眼睛,内置 8 种状态(Music / Typing / Browsing / Idle / Gaming / Laughing / Error / Watching) - PC 端:Python 脚本用
pycaw听音频、pynput数按键、comtypes读空闲时间,识别 Music/Typing/Idle/Browsing 四种状态 - 通信:PC 通过 HTTP 把状态 JSON 发给 ESP32 的
/state接口,ESP32 据此切换眼睛动画
它确实可爱,但它的"智能"是纯预设动画——换一句话概括:它是个被遥控的状态显示器,不知道你长什么样、在不在、想说啥。
1.2 改造动机:状态显示器 vs 边缘智能体
| 维度 | ESP32S3 玩具版 | 犀牛派 X1 改造版 |
|---|---|---|
| 算力 | 240MHz 双核,无 AI 加速 | QCS8550 Hexagon NPU,~48 TOPS |
| “感知” | 仅有 PC 上报的状态 | 摄像头实时人检(NPU) |
| 显示 | 0.96 寸单色 OLED | HDMI/MIPI 大屏,彩色动画 |
| 是否"懂你" | 否,纯查表 | 可接本地小模型做对话 |
| 数据回路 | PC→设备单行 | PC + 摄像头→设备融合 |
| 部署复杂度 | 极简 | 中(需搭 AI 工具链) |
改造的核心一句话:把"外部告诉它你在干嘛"升级为"它自己用摄像头看见你在干嘛",再把 NPU 的算力留给真正有价值的 AI(人检、对话),而不是浪费在画像素眼睛上。
1.3 规格对照:ESP32S3 → 犀牛派 X1
| 规格 | XIAO ESP32S3 | 犀牛派 X1(QCS8550) |
|---|---|---|
| SoC | ESP32S3(Xtensa LX7) | 高通 QCS8550(Kalama) |
| AI 加速 | 无 | Hexagon NPU,HTP V73 [官方] |
| 内存 | 512KB + 8MB PSRAM | 板载 LPDDR5X,GB 级 [官方] |
| 显示 | 外接 128×64 OLED | HDMI/MIPI,可驱 1080P |
| 摄像头 | 不支持 | USB/CSI,NPU 实时推理 |
| 系统 | Arduino 裸机 | Ubuntu 22.04(AidLux),Python 3.10 [社区] |
| 网络 | 2.4G Wi-Fi | Wi-Fi + 以太网 |
| 价格量级 | ¥40 级 | ¥数千级 [推算] |
结论:玩具版是"能亮",犀牛派 X1 版是"能看、能想、能说"。本文的价值不在炫技,而在把阿加犀这条被低估的端侧 AI 工具链,用一个小而完整的项目串起来——你读懂它,就能套到任何自定义模型上。
二、改造总览:新架构与数据流
2.1 改造后的系统架构
注意两条独立的数据源(PC 活动 + 摄像头人检)在犀牛派 X1 内部融合,而不是像玩具版那样只有一个输入——这是工程上最关键的升级。
2.2 能力升级清单
| 原版能力 | 改造版对应 | 是否新增 NPU |
|---|---|---|
| OLED 眼睛动画 | Pygame 大屏机械眼 | 否(CPU 足矣) |
| PC 状态→表情 | 同左 + 融合人检 | 否 |
| — | 摄像头人检(在/不在、是否看向屏幕) | ✅ YOLO26l |
| — | 本地小模型对话 | ✅ Qwen 量化 |
| — | 多状态并行 + 记忆 | ✅ |
2.3 犀牛派 X1 端侧 AI 部署方法论(阿加犀五步法 + MMS 捷径)
阿加犀社区总结过一条端侧 NPU 部署"五步工作法" [社区]:
1. 搭建匹配开发板的 QAIRT SDK 环境
2. 用 torch 训练/导出 ONNX
3. 安装 aarch64 交叉编译工具链
4. qnn 转换 ONNX → 编译为 .so / .ctx.bin
5. AidLite 加载模型推理(可接 ROS 节点)
对本文的捷径:我们要的人检模型(YOLO26l W8A8 for QCS8550)阿加犀模型农场(MMS)已经预转换好 NPU 格式,所以步骤 1~4 直接用 MMS 下载成品,我们只做第 5 步推理封装——这正是阿加犀生态"降低门槛"的价值所在。如果你想换自己的模型,再回退走完整五步法。
一个必须讲清的取舍:用 MMS 预置模型,省了环境搭建与量化编译,但代价是失去对精度与算子的控制权——模型结构、量化方式、输入布局都由农场方定,遇到"检测框偏了、类别不对"你改不了,只能等农场更新或自己走五步法重转。本文选捷径,是因为人检是成熟任务、YOLO26l 是社区验证过的通用检测模型;但凡是业务强相关的自定义模型(比如"只检测某种特定零件"),必须走完整五步法——这是"快"和"可控"的 trade-off,不是白送的午餐。这也解释了为什么阿加犀既提供 MMS 又保留 QAIRT/QNN 工具链:新手走捷径起飞,专家走全链路掌控。
三、硬件 BOM 清单
| 类别 | 物料 | 规格 | 价格量级 | 备注 |
|---|---|---|---|---|
| 主控 | 犀牛派 X1 | QCS8550,含散热 | ¥数千 [推算] | 已预装 AidLux |
| 显示 | HDMI/MIPI 屏 | 5~10 寸,800×480 起 | ¥150~400 | 越大越有"陪伴感" |
| 传感 | USB 摄像头 | 1080P,UVC 免驱 | ¥50~120 | 人检输入 |
| 音频 | USB 小音箱(进阶) | — | ¥30 | 对话版用 |
| 电源 | 配套电源/PD | ≥ 30W | 含主板 | — |
| 线缆 | HDMI + USB 线 | — | ¥20 | — |
对比玩具版(¥40 级),犀牛派 X1 版是"十倍预算换百倍能力"——但全离线、数据不出户是玩具版也给不了的。
四、环境准备:AidLux + AidLite + MMS
4.1 系统与环境确认
犀牛派 X1 出厂即 AidLux(Ubuntu 22.04,内核 5.15,Python 3.10)[社区]。先确认 NPU 加速后端可用:
# 确认 QNN 后端就绪(不同 SDK 路径略有差异)
python3 -c "import qti.aisw.dlc_utils; print('QNN ready')"
# 查看 AidLite 版本
dpkg -l | grep aidlite
# 期望看到:aidlite-sdk 2.3.x 与 aidlite-qnn236 2.3.x
4.2 AidLite SDK 安装与验证
若未预装或需升级,按官方文档安装 [官方]:
# 1. 从文档中心"资源下载中心"获取对应版本安装包,上传到 /home/aidlux
# 2. 解压并安装
tar -xzf aidlite-sdk-ubuntu22.04-qnn236.tar.gz
cd aidlite-sdk
sudo ./install.sh
# 3. 验证(Python 与 C++ 版本号)
python3 -c "import aidlite; print(aidlite.get_library_version())"
python3 -c "import aidlite; print(aidlite.get_py_library_version())"
# 4. 也可通过 aid-pkg 安装后端插件
sudo aid-pkg update
sudo aid-pkg install aidlite-sdk
sudo aid-pkg install aidlite-qnn236
AccelerateType 与 FrameworkType 对应关系 [官方]:
| 加速类型 | 说明 | 适用 |
|---|---|---|
TYPE_CPU | CPU 通用 | 调试、轻量模型 |
TYPE_GPU | GPU 通用 | 图像、浮点模型 |
TYPE_DSP | 高通 NPU(HTP) | 量化模型、高性能推理(推荐) |
4.3 MMS 模型农场下载 YOLO26l(NPU 模型)
阿加犀模型农场(MMS)已为犀牛派 X1 准备好了 QCS8550 专用 NPU 模型 [社区]:
# 浏览可下模型
mms list
mms list yolo
# 下载 YOLO26l INT8(QNN2.36 / NPU)人检/检测模型
# 文件名以模型农场实际为准,以下为社区教程中的示例
mms download cutoff_yolo26l_qcs8550_w8a8.qnn236.ctx.bin
# 若需 Stable Diffusion 等生成式模型也可走 MMS
# mms get -m Stable-Diffusion-v2.1 -p w8a16 -c qcs8550 -b qnn2.31 -d /home/aidlux/stable_diffusion
模型输出布局 [社区]:输入 640×640,split_xyxy 布局——[1,4,8400](框 xyxy)+ [1,80,8400](类别分数),类别数 80(COCO)。
4.4 验证 NPU 是否真正生效
这是最容易"假跑在 CPU"的环节。验证法:跑推理时另开终端看 CPU 占用。NPU 真正生效时,CPU 占用应明显偏低 [社区]。
# 终端 A:运行推理脚本
python3 camera_test.py
# 终端 B:观察 CPU
htop
# 期望:CPU 单核占用 < 30%,否则大概率回退到 CPU 推理
五、犀牛派 X1 端:AI 陪伴核心
5.1 摄像头人检:YOLO26l 经 AidLite/QNN 推理
这是改造版的"眼睛之外的眼睛"——它自己看见你在不在、看没看屏幕。[新增]
# person_detect.py —— 犀牛派 X1 端,NPU 人检
import cv2
import numpy as np
import aidlite
MODEL_PATH = "./models/QCS8550/W8A8/cutoff_yolo26l_qcs8550_w8a8.qnn236.ctx.bin"
INPUT_SIZE = 640
CONF_TH = 0.5
def load_model():
# 阿加犀 2.3.x 的 OO API(旧版请用 aidlite.init()/load())
model = aidlite.Model.create_instance(model_path=MODEL_PATH)
model.set_model_properties(
input_shapes=[[1, INPUT_SIZE, INPUT_SIZE, 3]],
input_data_type=aidlite.DataType.TYPE_FLOAT32,
output_shapes=[[1, 4, 8400], [1, 80, 8400]],
output_data_type=aidlite.DataType.TYPE_FLOAT32,
)
cfg = aidlite.Config.create_instance()
cfg.accelerate_type = aidlite.AccelerateType.TYPE_DSP # 走 NPU
cfg.framework_type = aidlite.FrameworkType.TYPE_QNN
cfg.number_of_threads = 4
assert model.init(cfg) == 0, "NPU 初始化失败,检查 QNN 后端版本"
return model
def detect(model, frame):
blob = cv2.resize(frame, (INPUT_SIZE, INPUT_SIZE)).astype(np.float32) / 255.0
blob = np.expand_dims(blob, 0)
out = model.run(blob) # 按 SDK 版本确认方法名
boxes, scores = out[0][0], out[1][0]
# 简易后处理:取 person 类(COCO id=0)高置信框
persons = []
for i in range(8400):
if scores[0, i] > CONF_TH:
x1, y1, x2, y2 = boxes[:, i]
persons.append((float(x1), float(y1), float(x2), float(y2)))
return persons # 空列表=画面中无人
版本提示:较旧 SDK 用
aidlite.init(); aidlite.load(MODEL_PATH); aidlite.setInput(blob,0); aidlite.invoke(); out=aidlite.getOutput(0)。新 SDK 用上文的Model.create_instanceOO 写法。两代 API 都合法,按你板子上的版本选。
5.2 陪伴 UI:大屏"机械眼"渲染
把 OLED 上那对像素眼睛,升级成 Pygame 大屏机械眼。[改造]
# companion_ui.py —— 犀牛派 X1 端,大屏眼睛动画
import pygame, math, random, time
class RoboEyes:
def __init__(self, w=800, h=480):
pygame.init()
self.screen = pygame.display.set_mode((w, h))
self.mood = "neutral" # neutral/curiosity/happy/sleepy
self.blink_at = time.time() + random.uniform(2, 5)
def set_mood(self, m): self.mood = m
def _eye(self, cx, cy, r, look=0.0):
col = {"neutral":(80,200,255),"curiosity":(120,255,180),
"happy":(255,220,90),"sleepy":(120,140,200)}[self.mood]
pygame.draw.circle(self.screen, col, (cx, cy), r, 4)
pygame.draw.circle(self.screen, col, (cx+look*r*0.4, cy), r//3)
def render(self):
self.screen.fill((15,18,25))
t = time.time()
if t > self.blink_at: # 自动眨眼
self.blink_at = t + random.uniform(2, 5)
look = math.sin(t*0.7)*0.6
self._eye(280, 240, 90, look)
self._eye(520, 240, 90, look)
pygame.display.flip()
5.3 HTTP 服务:接收 PC 状态
犀牛派 X1 起一个轻量 HTTP 服务,接收 PC 端上报的活动状态。[改造]
# pc_state_server.py —— 犀牛派 X1 端 Flask 服务
from flask import Flask, request, jsonify
import threading
app = Flask(__name__)
STATE = {"pc": "browsing", "ts": 0.0}
_LOCK = threading.Lock()
@app.route("/state", methods=["POST"])
def set_state():
data = request.get_json(force=True)
with _LOCK:
STATE["pc"] = data.get("state", "browsing")
STATE["ts"] = time.time()
return jsonify(ok=True)
@app.route("/status")
def status():
with _LOCK:
return jsonify(STATE)
# 启动:flask run --host 0.0.0.0 --port 5000(仅绑内网!)
5.4 状态融合:PC 状态 × 人检 → 表情决策
融合两条输入,决定最终表情——这是改造版相对玩具版最本质的升级。[新增]
# fusion.py —— 融合决策
def decide_mood(pc_state: str, persons: list, pc_fresh: bool) -> str:
present = len(persons) > 0
if not pc_fresh:
return "sleepy" # PC 久未上报:待机/瞌睡
if not present:
return "curiosity" # 人不在但 PC 在跑:好奇张望
# 人也在、PC 也在:按 PC 活动给情绪
return {"music":"happy","typing":"curiosity",
"browsing":"neutral","idle":"sleepy"}.get(pc_state, "neutral")
主循环把三者串起来:
# main.py —— 犀牛派 X1 端主循环
import time
from person_detect import load_model, detect
from companion_ui import RoboEyes
from pc_state_server import STATE, _LOCK
from fusion import decide_mood
model, ui = load_model(), RoboEyes()
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
persons = detect(model, frame) if ret else []
with _LOCK:
pc, ts = STATE["pc"], STATE["ts"]
pc_fresh = (time.time() - ts) < 10 # 10s 内算有效
ui.set_mood(decide_mood(pc, persons, pc_fresh))
ui.render()
time.sleep(0.05) # ~20fps UI 刷新
5.5 进阶:本地小模型对话(Qwen via NPU)
如果你要"它真的能聊天",可接一个量化小模型(如 Qwen2.5-0.5B/1.5B INT4)经 NPU 推理 [社区]。方案骨架:
# chat_brain.py —— 进阶:本地 LLM(示意,模型需自行量化部署)
# 思路:用 AidLite 加载量化后的 Qwen .ctx.bin,做流式对话
# 注意:端侧 decode 受内存带宽限制(详见作者另一篇端侧部署文),
# 0.5B INT4 在 QCS8550 上可跑出可用对话速度,1.5B 需评估
def chat(prompt: str) -> str:
# 1. 拼接 system + 最近几轮(记忆,避免上下文膨胀)
# 2. AidLite 加载的 Qwen 模型推理
# 3. 返回文本
...
这一节是进阶扩展,不是改造必经之路。先把人检 + 表情做稳,再上对话——否则 NPU 算力被对话独占,人检反而卡。
六、PC 端:Windows 活动识别客户端
PC 端逻辑基本沿用原项目,仅做小幅升级(增加"自动重连"与"状态去抖")。[沿用+升级]
6.1 依赖安装
pip install requests pycaw pynput comtypes
6.2 状态判断逻辑
# desktop_companion_client.py —— PC 端活动识别(沿用原项目思路)
import time, requests
from pycaw.pycaw import AudioUtilities
from pynput import keyboard
from ctypes import windll
X1_IP = "192.168.1.100" # ← 改成犀牛派 X1 的 IP
POLL = 2.0 # 轮询间隔
last_state, err_cnt = "browsing", 0
def is_music(): # 是否有音频播放
sessions = AudioUtilities.GetAllSessions()
return any(s.State == 1 for s in sessions if s.State is not None)
def key_rate(): # 每秒按键数
# 用 pynput 监听计数(简化:此处返回累计均值)
return _kps
def idle_sec(): # 空闲秒数(Windows API)
return windll.user32.GetLastInputInfo() and (time.time()-_last_input)
def judge():
if is_music(): return "music"
if key_rate() >= 3: return "typing"
if idle_sec() > 120: return "idle"
return "browsing"
6.3 状态上报模块
def report(state):
global last_state, err_cnt
if state == last_state:
return # 仅在状态变化时上报,省流量
try:
r = requests.post(f"http://{X1_IP}:5000/state",
json={"state": state}, timeout=3)
if r.status_code == 200:
last_state = state; err_cnt = 0
except Exception:
err_cnt += 1
if err_cnt >= 10: # 连错 10 次退出,提示检查设备
raise SystemExit("ESP32/犀牛派 未通电或不在同网段")
if __name__ == "__main__":
while True:
report(judge())
time.sleep(POLL)
七、联调与验证
7.1 联调清单
| 检查项 | 方法 | 期望 |
|---|---|---|
| 犀牛派 X1 NPU 生效 | htop 看推理时 CPU | 单核 < 30% |
| 摄像头开流 | cv2.VideoCapture(0).read() | 返回帧非空 |
| PC→X1 网络 | PC ping X1_IP | 通 |
| HTTP 上报 | X1 端 /status | 看到 pc 状态更新 |
| 表情随人检变化 | 遮挡摄像头 | 眼睛变 curiosity |
| 表情随 PC 变化 | PC 放音乐 | 眼睛变 happy |
7.2 实测帧率与延迟
# 人检推理帧率(犀牛派 X1,YOLO26l W8A8,NPU)
python3 benchmark_image.py --loops 100
# 预期:NPU 下可达两位数~数十 FPS [推算],以你板子实测为准
所有性能数字一律
[实测]口径——本文只给测量方法,不替你编数据。YOLO 类模型在 QCS8550 NPU 上达数十 FPS 是公开社区共识 [社区],但具体值以你的环境与 SDK 版本为准。
八、坑点实录(10 条,按被踩概率排序)
| # | 坑 | 现象 | 解法 |
|---|---|---|---|
| 1 | AidLite API 版本错配 | create_instance 报错 | 旧 SDK 改用 init/load,对照版本 |
| 2 | 模型未真正走 NPU | CPU 满载、帧率极低 | htop 验证 + 确认 TYPE_DSP |
| 3 | QNN 后端版本不符 | 模型加载失败 | aid-pkg 装对应 aidlite-qnn236 |
| 4 | 摄像头权限/UVC 免驱 | VideoCapture 黑屏 | 换免驱 UVC 摄像头 |
| 5 | PC 与 X1 不同网段 | 上报连错 10 次退出 | 确认同 Wi-Fi/同 VLAN |
| 6 | Flask 绑 0.0.0.0 暴露 | 公网可访问 | 仅绑内网 + 防火墙 |
| 7 | 内存膨胀(对话版) | 卡顿/重启 | 限制上下文轮数 |
| 8 | 输入未归一化 | 检测全错 | 确认 /255.0 + 640 resize |
| 9 | 输出布局理解错 | 框/分数错位 | 对照 split_xyxy [1,4,8400]+[1,80,8400] |
| 10 | SDK 路径不在 PYTHONPATH | import 失败 | export PYTHONPATH=/usr/local/lib/aidlux/aidlite |
九、成本与性能账
| 项 | 玩具版 | 犀牛派 X1 改造版 |
|---|---|---|
| 硬件 | ¥40 级 | ¥数千 [推算] |
| AI 能力 | 无 | 人检 + 可选对话 |
| 数据隐私 | PC 活动出网(HTTP 内网) | 全本地,摄像头不出户 |
| 功耗 | < 1W | 数十瓦 [推算] |
| 复用价值 | 玩具 | 可套用到任意自定义模型 |
结论账:改造版贵十倍,但换来"能看能想"的边缘智能,且全离线——这恰恰是 ESP32S3 玩具和任何云端方案都给不了的。
十、结论与扩展路线图
五条结论:
- 改造的本质是给"状态显示器"装上一双真正的眼睛——NPU 人检让陪伴机器人第一次"看见"你;
- 阿加犀工具链(AidLux + AidLite + QNN + MMS)的价值在把 NPU 部署从"五步工程"缩成"一行下载";
- 融合两路输入(PC 状态 + 摄像头)优于单输入,是工程升级的核心;
- 不要一上来就上本地对话——先把人检 + 表情做稳,NPU 算力留给最值钱的能力;
- 玩具版是创客玩具,改造版是端侧 AI 落地的微型样板,读懂它就能迁移到工业检测、机器人等真场景。
扩展路线图:
最后一句话:从一块 ¥40 的 OLED 玩具,到一块能跑 NPU 的犀牛派 X1,中间差的不是钱,是把工具链走通的那套工程方法——希望这篇改造实战,能帮你把这套方法真正装进脑子里。
参考链接
- 原项目《用 XIAO ESP32S3 制作桌面陪伴机器人》(微信文,本文改造对象):https://mp.weixin.qq.com/s/hQNfUOKpvlFPPWOhZmK9Ig
- 阿加犀 AIOT 开发者平台(犀牛派 X1 文档中心):https://rhinopi.docs.aidlux.com
- 核心 AI SDK 工具链(AidLite):https://rhinopi.docs.aidlux.com/a8550ma1/ai-dev/section-03
- 犀牛派 X1 YOLO26l NPU 实测教程(社区):https://forum.aidlux.com/t/topic/74773
- 犀牛派 X1 小白入门指南(CSDN):https://blog.csdn.net/jacklwb/article/details/164094299
- Seeed Studio XIAO ESP32S3 官方页:https://www.seeedstudio.com/XIAO-ESP32S3-p-5626.html
- FluxGarage RoboEyes 库:https://github.com/FluxGarage/RoboEyes
- AidLite GitHub(SDK 与示例):https://github.com/aidlux
数据说明:
[媒体]= 微信原文 / 公开报道;[官方]= 阿加犀官方文档;[社区]= 开源社区 / 论坛教程;[沿用]= 原 ESP32S3 项目代码思路;[新增]= 犀牛派 X1 改造新增模块;[推算]= 基于公开参数的估算;[实测]仅提供测量方法。AidLite 不同 SDK 版本 API(init/load与Model.create_instance)存在差异,请务必对照你板子上的版本核对命令与代码。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)