XGO-mini2SWWiki 首页
Wiki 首页/XGO-mini2SW/Luwu-OS 用户手册 / 开发篇
XGO-mini2SW 知识库Luwu-OS 用户手册 / 开发篇

06 AI 模型替换

适用版本新版 Luwu-OS
版本说明
系列共用文档切换机型,继续阅读本篇
共用内容按本机型的执行器、外设和版本使用。

Luwu-OS 的视觉、语音、情绪识别都靠 ONNX 模型。本章先给出现有模型清单与“哪个应用用哪个模型”的对照表,再讲通用推理接口与替换步骤,最后附排查表——让你能安全地换上自己的模型并确认它生效。

本章包含:

小节

内容

难度

6.1 ONNX 模型列表

现有模型清单 + 模型→应用对照表(含 6.1.1)

★☆☆

6.2 通用推理接口

onnxruntime / FaceDetectorYN / MediaPipe 包装类调用

★★★

6.3 替换人脸检测模型

换模型四步 + 重启

★★☆

6.4 替换手势识别模型

换模型 + 适配包装类张量签名

★★★

6.5 替换后常见问题

张量不匹配、检不出、帧率下降等排查表

★★☆

6.6 常用查询命令

打印模型输入/输出签名、查看占用

★☆☆

🧭 阅读路径:只想“换个现成模型”,看 6.3/6.4 即可;要在模型层面做二次开发,先看 6.1.1 对照表和 6.2 推理接口。建议任何替换前先跑一遍 6.6 的命令保存原始签名。

前置条件:

  • 完成第一章,已能 SSH 登录机器狗;

  • 有一个待替换的 ONNX 模型文件(保持输入尺寸/预处理与现有模型接近,最省事);

  • 知道要替换的是哪个应用的哪个模型(见 6.1.1)。

6.1 ONNX 模型列表

/opt/luwu-os/model/ 目录中的模型文件(不同固件版本可能增减,文件名为常见/公开命名,实际以机器狗上该目录为准):

模型文件

用途

来源

face_detection_yunet_2023mar.onnx

人脸检测

YuNet (OpenCV)

palm_detection_mediapipe_2023feb.onnx

手掌检测

MediaPipe

handpose_estimation_mediapipe_2023feb.onnx

手部 21 关键点

MediaPipe

person_detection_mediapipe_2023mar.onnx

人体检测

MediaPipe

pose_estimation_mediapipe_2023mar.onnx

人体姿态估计(33 关键点)

MediaPipe

emotion.onnx

教育 / 视觉库中的情绪识别(libs/atom_vision.py、xgoedu-luwuos)

自定义训练

gender_age.onnx

性别 / 年龄估计

自定义训练

yolo_coco.onnx

通用目标检测(COCO 80 类)

YOLO

embedding_model.onnx / melspectrogram.onnx

语音 / 音频嵌入与频谱特征(命名因版本而异)

自定义训练

唤醒词模型(hi_luka.onnx / hi_luka_qwen.onnx / hi_luwu.onnx / xiaolu_classmate.onnx / xiaolu_tongxue.onnx 等)

唤醒词检测(英文“Hi Luka” / 中文“小陆同学”等,按系统语言选定)

自定义训练

msyh.ttc

中文字体(UI 渲染,非模型)

微软雅黑

mp_handpose.py / mp_palmdet.py / mp_persondet.py / mp_pose.py

MediaPipe 模型的 Python 包装类(非模型,含 MPPalmDet/MPHandPose/MPPersonDet/MPPose)

项目自带

⚠️ 上表中自定义训练的模型(情绪/性别年龄/语音嵌入等)文件名会随版本变化;ls /opt/luwu-os/model/ 才是权威清单。替换逻辑不变,只是文件名要按实际目录写。

6.1.1 模型 → 应用对应关系

各个 ONNX 模型并非集中加载,而是由具体的应用在启动时按需加载。替换模型前,先确认它被哪个应用引用(表中的变量名与路径以实际源码为准,不同版本可能改名):

模型

使用它的应用

加载位置

face_detection_yunet_2023mar.onnx

人脸跟随

apps/face_follow/main.py(FACE_MODEL_PATH)

palm_detection_* / handpose_estimation_*

手势命令

apps/gesture/main.py(PALM_MODEL / HAND_MODEL)

person_detection_*

人体跟随

apps/person_follow/main.py

pose_estimation_*

姿态识别

相关演示应用

emotion.onnx

教育 / 视觉库中的情绪识别(并非 AI 对话的表情引擎,后者由服务器返回的 emotion 名驱动)

libs/atom_vision.py / libs/xgoedu-luwuos/edulib/_vision.py

yolo_coco.onnx

通用目标识别 / 示例程序

相关应用

语音 / 音频嵌入与频谱模型

唤醒词 / 自定义 ASR 相关

apps/ai/wakeword_manager.py(MEL_ONNX / EMBEDDING_ONNX),新版 AI Chat Pro 复用它

唤醒词模型

唤醒词激活(“Hi Luka” / “小陆同学”)

apps/ai/wakeword_manager.py(EN_MODEL_PATH=hi_luka_qwen.onnx、MODEL_PATH=xiaolu_classmate.onnx),新版 AI Chat Pro 复用同一管理器

gender_age.onnx

特征展示

相关演示应用

💡 所有 ONNX 模型统一通过 onnxruntime 推理;MediaPipe 系列模型(手掌/手部/人体/姿态)额外被 mp_*.py 包装类封装,内部做输入预处理与输出后处理。包装类通过 sys.path.insert(0, model目录) 后 from mp_palmdet import MPPalmDet 导入(见 apps/gesture/main.py)。

怎么快速定位某模型被哪个应用引用:不用一个个翻源码,直接在 /opt/luwu-os 下 grep 文件名(限制只搜 .py):

grep -rl "face_detection_yunet" /opt/luwu-os/apps --include="*.py"
grep -rl "palm_detection" /opt/luwu-os/apps --include="*.py"

💡 结果列出含该文件名 / 变量的应用路径,即它的加载位置;多数应用还会定义模型路径变量(如 FACE_MODEL_PATH),再 grep 那个变量名就能定位到具体赋值的行。

6.2 通用推理接口(onnxruntime 封装)

所有应用加载 ONNX 模型的写法基本一致:先用 onnxruntime.InferenceSession 建会话,再把图像帧预处理成模型输入,最后取输出。以人脸检测为例(真实代码中的 apps/face_follow/main.py 使用 OpenCV 的 FaceDetectorYN):

import cv2

# 加载 YuNet 人脸检测器
face_detector = cv2.FaceDetectorYN.create(
    model_path.replace("model/", "/opt/luwu-os/model/"),  # 绝对路径
    "",                        # config(YuNet 无配置文件)
    (320, 240),                # 输入尺寸 (width, height)
    scoreThreshold=0.7,        # 置信度阈值
    nmsThreshold=0.3,          # NMS 阈值
    topK=5000,                 # 最多返回人脸数
)

# 每帧推理:先把 BGR 帧转为 RGB(YuNet 需要 RGB)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# setInputSize 允许逐帧指定输入尺寸;输入尺寸会自动等比缩放
face_detector.setInputSize((frame.shape[1], frame.shape[0]))
ok, faces = face_detector.detect(rgb)
# faces: Nx15 数组;前 4 列为框,其后 10 列为右眼/左眼/鼻尖/右嘴角/左嘴角 5 个关键点的坐标(每点 2 列),最后 1 列为置信度

而 MediaPipe 软件件走包装类,内部同样用 onnxruntime 推理(见 apps/gesture/main.py):

import sys
sys.path.insert(0, "/opt/luwu-os/model")  # 包装类在 model/ 目录下

from mp_palmdet import MPPalmDet
from mp_handpose import MPHandPose

palm = MPPalmDet()   # 内部读取 model/palm_detection_mediapipe_2023feb.onnx
hand = MPHandPose()  # 内部读取 model/handpose_estimation_mediapipe_2023feb.onnx

# 每帧推理:先预处理(缩放/归一化),再推理,最后后处理(解码关键点/框)
palm_out = palm.detect(frame)
hand_pts = hand.estimate(frame, palm_out)   # 输出 21 个关键点

💡 上面 palm.detect(...) / hand.estimate(...) 是示意调用,包装类的方法名、参数与签名以实际 mp_*.py 定义为准;不确定时先 cat /opt/luwu-os/model/mp_handpose.py 查看真实接口。

手工调用 onnxruntime 的一般流程(替换非 OpenCV/非 MediaPipe 包装的自定义模型时参考): ```python import onnxruntime as ort, numpy as np sess = ort.InferenceSession("/opt/luwu-os/model/your_model.onnx") inp = sess.get_inputs()[0] # 取输入签名,名字/形状/类型

图像预处理:resize + 归一化 + CHW → NCHW + 转 float32

x = preprocess(frame) # shape=(1, C, H, W) out = sess.run(None, {inp.name: x})[0] # 取第一个输出 out = postprocess(out) # 按模型输出定义解码 ```

6.3 替换人脸检测模型

  1. 放置模型:将新 ONNX 文件放到 /opt/luwu-os/model/ 目录

  2. 改路径:修改 apps/face_follow/main.py 中的 FACE_MODEL_PATH 变量,指向新文件(目前指向 model/face_detection_yunet_2023mar.onnx)

  3. 适配参数:除非新模型输出与 YuNet 完全一致,否则要同步调整 cv2.FaceDetectorYN.create() 的参数——输入尺寸 (320, 240)、scoreThreshold、nmsThreshold、topK 都要按新模型重新调。若换了非 YuNet 结构(如另一个检测网络),需改用 6.2 节的 onnxruntime 通用推理方式

  4. 重启应用:人脸跟随是常驻进程,改完需重启才会重新加载模型

# 重启人脸跟随应用(由 launcher 完整启动)
echo "apps/face_follow/main.py" > /tmp/luwu_run.fifo

⚠️ 验证时可临时改为日志/终端打印检测结果的数量,确认能正常检出人脸,再逐步调阈值。

6.4 替换手势识别模型

  1. 替换文件:替换 /opt/luwu-os/model/ 下的 palm_detection_*.onnx 和 handpose_estimation_*.onnx

  2. 改路径:更新 apps/gesture/main.py 中的 PALM_MODEL / HAND_MODEL 路径变量

  3. 适配包装类:MPPalmDet / MPHandPose 会按固定的输入/输出张量签名做预处理与后处理。若新模型的输入形状、归一化方式或输出张量名不一致,需同步修改包装类(mp_palmdet.py / mp_handpose.py),否则会报张量名不存在或结果解码错乱

  4. 重启应用:同样用 echo "apps/gesture/main.py" > /tmp/luwu_run.fifo 重启

6.5 替换后常见问题

替换模型后若检测异常,按下面顺序排查(多为"张量签名不匹配"):

现象

可能原因

处理

启动即报 Invalid graph / No op named ...

模型输入/输出张量名与包装类硬编码不一致

打开包装类,用 sess.get_inputs()/get_outputs() 打印实际签名,改成一致的

能加载但一直检不出目标

输入预处理不对(尺寸 / 归一化 / BGR↔RGB / 通道顺序)

对照原模型的预处理逻辑逐个核对

检测到但框位置偏

输出后处理解码公式与模型不匹配

按新模型输出定义重写解码(缩放回原尺寸)

帧率明显下降

模型过大 / 输入尺寸过大 / 未走 setInputSize 缩放

降低输入分辨率、换小模型、或预热 session

只对特定目标有效

换成了不同类别的模型

确认应用逻辑与模型输出类别一致(如 yolo_coco 为 COCO 80 类)

性能提示:机器狗主控为 Raspberry Pi 5(CM5),建议用量化版 ONNX(如 int8)或较小的模型;推理次数应控制在每帧 1~2 次以内,否则会拖慢主界面帧率。也可把重复加载的 session 缓存为全局变量,避免每帧重建。

6.6 常用查询命令

# 查看某 ONNX 模型的输入/输出签名(执行前把路径换成实际的模型路径)
python3 - <<'EOS'
import onnxruntime as ort, glob, os
for p in glob.glob('/opt/luwu-os/model/*.onnx'):
    s = ort.InferenceSession(p)
    ins = [(i.name, i.shape, i.type) for i in s.get_inputs()]
    outs = [(o.name, o.shape) for o in s.get_outputs()]
    print(os.path.basename(p), ins, outs)
EOS

# 查看 model 目录占用与文件
du -sh /opt/luwu-os/model && ls -la /opt/luwu-os/model

6.7 替换前后检查清单

换模型前先跑一遍 6.6 存好基线,换的过程对照下面逐项打勾:

阶段

检查项

替换前

① 记下原模型输入/输出签名(6.6);② 备份原文件(cp);③ 明确新模型预处理(尺寸 / 归一化 / BGR-RGB / 通道)

替换后

① 应用能启动不报错;② 能稳定检出目标;③ 帧率可接受;④ 结果定位正确

出问题时

依次看 6.5 排查表;先怀疑张量签名 → 预处理 → 解码 → 性能

📌 若只是想体验自定义模型而暂不进源码,可先在 model/ 放一个同输入输出签名的 ONNX,只改路径不写代码,先用最小改动验证整条链路。

资料来源XGO 官方 Wiki
返回 XGO-mini2SW 知识库目录