没有清闲座椅怎么办?在犀牛派 X1 上做一个工位 AI 教练:姿态识别、久坐监测与体感交互
设备:犀牛派 X1(高通 QCS8550,AidLux / Ubuntu 22.04)|相机:Logitech C920,/dev/video2,1280×720 MJPG|姿态:AidLite TFLite GPU,float32,上半身 31 点|底栏:镜像自带 YOLOv5s W8A8,QNN236 DSP|日期:2026-09-15
摄像头对着工位上半身。人在不在、是不是在静坐、坐姿好不好,都从同一组关键点来。静坐满 25 分钟,或者坐姿把 HP 扣光,进入一组 2 分钟的上半身动作。做完回到监测,这段久坐清零。
浏览器打开 http://<板子IP>:8090/。接线、刷机、摄像头为什么不能用 VideoCapture(0)、NPU 体检的 4.7 ms,都在上一篇。代码在 https://github.com/peterpanstechland/rhinopi-x1-aidlux 的 examples/06_shadow_puppet。
C920 架在屏幕上沿,镜头对着人的上半身。犀牛派 X1 就放在手边的垫子上。
这帧底栏是 FPS 7.0、POSE 93 ms、NPU 15.3 ms,右侧坐姿分 84。
1. 两套模型,只有 YOLO 是量化的
没有从模型广场另下权重,用的都是融合镜像里的官方文件。
| 用途 | 模型 | 精度和后端 |
|---|---|---|
| 人在不在、坐姿、动作 | /opt/aidlux/app/aid-examples/pose_detect_track/models/ 里的 BlazePose |
TFLite,输入 float32,默认 GPU |
| 弹琴那一屏的手指 | 同目录旁边 hand_track/models/ 的手掌检测 + 21 点 |
同样是 TFLite float32,只在这一屏加载 |
| 底栏脉冲 | .../cutoff_yolov5s_640_sigmoid_w8a8.qnn236.ctx.bin |
W8A8,TYPE_QNN236 + TYPE_DSP |
姿态不走量化包。板上访问不了 pypi.org,镜像里也没有 MediaPipe。官方示例已经带了检测器和 landmark,AidLite 能加载 TFLite。检测框解码、ROI 对齐和关键点平滑在 pose_tracker.py 里。
先看叠点可以跑预览,游戏本身不依赖这个页面:
cd ~/rhinopi-lab/examples/04_mediapipe
python3 pose_preview.py
浏览器打开 http://<板子IP>:8091/。默认 /dev/video2,画面做了镜像。
PoseTracker 默认 accel="GPU",工位用 full_body=False。
| 阶段 | 模型 | 输入 | 输出 |
|---|---|---|---|
| 人体检测 | pose_detection.tflite |
128×128 | 896 个框 |
| 上半身关键点 | pose_landmark_upper_body.tflite |
256×256 | 31 点 |
配置是 TYPE_TFLITE、TYPE_FAST、TYPE_GPU,输入输出都声明为 TYPE_FLOAT32。检测阈值 0.42。检出人之后把 ROI 仿射到 256,再把点映回原图。镜像画面下左肩如果落在画面右侧,会按自拍关系纠正。关键点平滑系数 0.35。
全身模型是 39 点的 pose_landmark_full_body.tflite。C920 的工位视野包不住整个人,试过,不作为主线。
--accel DSP 能选。报出来的 70–110 ms 是 GPU。没有把这份 float32 BlazePose 当成 DSP 主路径去报帧率。
底栏的 W8A8 YOLO 每 18 帧跑一次,回答「NSP 还通不通」。坐姿判断不看它。坐姿错了改阈值和基线,不要去改这个脉冲。
2. 监测什么,一局怎么走
| 阶段 | 画面 | 推理 |
|---|---|---|
| 监测、提醒、人离开 | 摄像头加像素叠层,右侧坐姿分 | 每帧上半身姿态 |
| 挑战和总结 | 上半像素舞台,下半实时小窗、倒计时、坐姿 | 姿态;弹琴再加手部 |
| 底栏 | FPS、POSE ms、NPU ms、NSP/CPU 温度 |
YOLOv5s,不参与玩法 |
数字人用关键点驱动四肢角度和头的朝向。坐姿不看动画,直接看点。四项是低头前伸、整个人往下塌、肩膀歪、离屏幕太近,另有一项水平偏移看人是不是坐在画面正中。分数 0–100,掉到 62 以下按差坐姿扣 HP。一回合结束用当时的坐姿重新标定基线。
这帧右侧是「肩膀歪了」,坐姿分 67。底栏 FPS 5.6、POSE 92 ms、NPU 12.4 ms、CPU 48°C。
默认 --interval-min 25。人在画面里并且动作很小时累加静坐时间。满 25 分钟,或 HP 被扣到 0,弹出提醒。展翅或举手可以提前开始。
一回合 120 秒,默认抽 6 个动作。每个动作一屏,完成才算过关,然后自动下一屏。时间用完,剩下的记未完成。
| 动作 | 过关条件 | 用到的点 |
|---|---|---|
| 展翅 | 双臂撑满并保持 5 秒 | 上半身 |
| 老鹰 | 扇动 8 次 | 上半身 |
| 挥手 | 左右挥手送走 10 个人 | 上半身 |
| 滑雪 | 左右侧身碰到 6 面旗 | 上半身 |
| 弹琴 | 按中 8 个黄键 | 手部 21 点;姿态降到每 3 帧一次 |
| 跳跃 | 坐着向上颠,跳过 5 个仙人掌 | 上半身 |
| 转头 | 左右各 3 次 | 上半身 |
| 点头 | 6 次 | 上半身 |
这三帧是同一回合里连着的滑雪、点头、展翅。人在左下小窗里。底栏的 POSE 是 GPU 上的姿态耗时:滑雪 102 ms、点头 77 ms、展翅 84 ms。NPU 大约 18–19 ms,CPU 48–51°C。FPS 在 5.7 到 7.0。
弹琴时手部每帧都跑,姿态降频,把帧率留在指尖上。手部是 palm_detection.tflite(128)加 hand_landmark.tflite(224)。这一屏没有单独记 fps。
人离开画面大约 40 秒算离席,离开超过 5 分钟会把当前这段静坐清掉。进度按天写在 save.json。
3. 在板上怎么跑
cd ~/rhinopi-lab/examples/06_shadow_puppet
python3 game.py
python3 game.py --demo
python3 game.py --no-npu
--demo 把间隔收成 45 秒,用来走完一局,不要拿这个间隔当正式监测。--accel 默认 GPU。
状态是 http://<板子IP>:8090/status。不用坐在镜头前,也可以把每一屏渲染出来核对排版:
python3 render_probe.py --out /tmp/ui
4. 帧率记在哪一段
2026-09-15。相机和姿态不是同一个数,量化 YOLO 的两次计时也不是同一个数。
| 读数 | 大约多少 | 含义 |
|---|---|---|
| 相机 MJPG | 30.46 fps(平均 32.82 ms) | C920,1280×720,USB 2.0 |
| 相机 YUYV | 10 fps | 同一分辨率,带宽不够。游戏不用这条 |
| 久坐页 | 约 7–13 fps | 采集 + 姿态 + 叠层 + JPEG。上面这帧是 7.0 |
| POSE,检出人 | 70–110 ms | TFLite GPU,检测 + landmark |
| POSE,没检出 | 约 33 ms | 只跑检测器 |
| NPU 底栏 | 9–20 ms | 每 18 帧一次,含 letterbox |
| NPU 体检 | 4.772 ms;9 月 28 日复查 4.734 ms | 上一篇。不含 letterbox,不含后处理 |
画面卡在姿态那 70–110 ms,再加上叠层,久坐页大约 7–13 fps。相机本身仍是大约 30 fps。4.7 ms 是 W8A8 的纯推理节拍,大约 210 次/秒,不是游戏帧率。肩膀歪了那帧 POSE 92 ms、FPS 5.6。回合里滑雪 / 点头 / 展翅三帧 POSE 是 102 / 77 / 84 ms,FPS 5.7–7.0,NPU 大约 18–19 ms,CPU 48–51°C。
5. 工位上会碰到的情况
| 现象 | 处理 |
|---|---|
| 点在跳,坐姿分乱飘 | 人只有半个肩膀在画面里;镜头对准上半身 |
| 弹琴时帧率掉下去 | 手部是临时加载的第二套模型;其他屏不要开 |
| 底栏 NPU 是 off | 确认 aidlite-qnn236 和那个 ctx 文件还在;玩法不依赖它,可以 --no-npu |
| 25 分钟太长,想看完一局 | --demo,看完再回到默认间隔 |
| 黑屏或 720p 只有十几帧 | 采集节点和 MJPG 见上一篇 |







