人体检测与姿态估计:让系统"看见"老人
视觉是跌倒监护的第一入口:YOLOv8(RKNN,640×640,仅 person 类)先框出人,BlazePose 33 点(整图 256×256)或 RTMPose 17 点(top-down 192×256,工厂按 pose_type 切换)再摆好骨架,后面的躯干角、宽高比判定才有依据。本实验走通 letterbox → RGB 4D → NPU 推理 → DFL 解码 → classwise NMS → unletterbox 逆映射 全链路,理解补边/缩放对坐标的影响,按"逐层单跑 → 集成联调 → 参数调优"完成 T8-1~T8-6。
01问题引入:检测框总会"缩水"的真相
第一次跑 detect_demo 的同学常惊呼:"框明明画在人的身上,怎么总往左上偏一点、像没对准?" 真相是:模型看到的图不是你拍的那张图 —— 送进 NPU 前原图被等比缩放并居中补了黑边(letterbox)。解码出的框在"补边后的空间"里,必须用 scale 和 pad 两个参数把坐标搬回原图,少一个都会整体偏移。
02原理通俗讲解:检测与姿态的"两步舞"
anchor-free 单阶段:网格直接预测,每条边回归 16-bin 分布(DFL)softmax 加权成亚像素偏移;NMS(IoU 0.45)去重后只留 person(class_id=0)。nano 版约 320 万参数,INT8 量化后 30–50ms/帧,正好塞进 6TOPS NPU。
同一人可能被多个网格同时报框:按分数从高到低保留,凡与保留框 IoU > 0.45 的全部压掉。阈值太高(0.9)同人多框、ID 乱跳;太低(0.2)挨得近的两个人被误删一个 —— 漏检比误检更危险。
256×256 整图一次推理,33 点含面部/手部细节;vis < 0.5 的点不画。优势是不依赖检测框,输输出 195=39×5 交错布局。
单人裁剪 192×256 竖版输入,SimCC 双头输出 simcc_x(1,17,384) + simcc_y(1,17,512),argmax ÷ 2 得像素坐标;vis = 双头峰值均值(FP32 实测有人 0.75~1.2)。躺姿裁剪稳定,是默认后端。
03关联知识点:六个知识块对号入座
| 编号 | 知识点 | 关键机制 / 位置 |
|---|---|---|
| 4.1.1 | YOLOv8 行人检测(COCO person) | 640×640 RGB;3 层 × 3 张量输出;vision/yolo/detector.py L13-L41 |
| 4.1.2 | letterbox 预处理与 NMS 解码 | scale=min(640/h,640/w)、pad=0 黑边;DFL 16-bin;classwise_nms IoU 0.45;preprocess.py L18-L53 |
| 4.1.3 | RKNN NPU 推理与 core_mask | rknnlite2.3.2;core_mask 0=AUTO(None 会报不支持);双模型共 NPU 排队;backend.py L34-L65 |
| 4.2.1 | BlazePose 33 关键点 | 整图 256×256;195=39×5 交错;VIS_THRESHOLD=0.5;vision/pose/pose.py L42-L80 |
| 4.2.2 | RTMPose 17 点 SimCC 双头 | 384=192×2、512=256×2、SPLIT_RATIO=2.0;vis=双头峰值均值;vision/pose_rtm/simcc.py L17-L63 |
| 4.2.3 | 双后端切换与 MJPEG 推流 | model.pose_type;工厂 build_pose_engine(fall/monitor.py L41-L61);MjpegServer :8088/fall.mjpg |
04实验目标
- 理解检测六段链路 letterbox → RGB 4D → RKNN → DFL → NMS → unletterbox,能说出每段的数据形状变化
- 掌握BlazePose 与 RTMPose 的四维差异(整图 vs top-down、33pt vs 17pt、vis 计算、耗时),能独立切后端对比
- 理解letterbox 补边对关键点逆映射的影响(scale+pad 双参数)与 NMS IoU 阈值的漏检/误检权衡
- 独立完成detect_demo / pose_demo / fall_video_demo 三层验证,能定位漏检、姿态不跟帧、推流卡顿
- 掌握MJPEG 推流与 --text 文本叠加观察,理解"视觉层可独立于语音层验证"的设计意图
- 迁移把"逐层单跑→集成联调→参数调优"方法论用到其他检测场景
05实验环境与准备
python3 common/env_check.py --config config/fall_config.yaml 确认 EXIT 006动画讲解:letterbox → NPU → NMS → 逆映射
左侧一张竖图被缩放补边送进模型、再"拆框去重"搬回原图;右侧 SimCC 双头解码的 argmax 扫描:
07互动讲解:置信度阈值实验台
拖动等价于 CLI 的 --score-thr / yaml 的 iou_thr 预设,观察检出数与风险的跷跷板:
08实操步骤:单图 → 双后端 → 视频集成 → 调参
# ① 单图行人检测(预期: 76ms / 13.1fps / score 0.837) cd ~/ros2_ws/src/xflz_eai_car1/app/elderly_companion python3 apps/detect_demo.py --input testdata/stand_up.jpg --save # OK: 1 detections in 76 ms → cls=0(person) box=(31.2,7.8,183.2,393.5) # 标注图存 runs/out/detect_image.jpg;无人图返回 0 框不崩溃 # ② 单图姿态(默认 model.pose_type: rtmpose → 17 keypoints) python3 apps/pose_demo.py --input testdata/fall_1.png --save grep -A2 "pose_type" config/fall_config.yaml # 确认当前后端 # ③ 切 blazepose 对比 33 点,完成后必须改回 rtmpose sed -i 's/pose_type: rtmpose/pose_type: blazepose/' config/fall_config.yaml python3 apps/pose_demo.py --input testdata/fall_1.png --save sed -i 's/pose_type: blazepose/pose_type: rtmpose/' config/fall_config.yaml # ④ 视频集成 + 文本叠加 + 可选 MJPEG 预览(浏览器 :8088/fall.mjpg) python3 apps/fall_video_demo.py --video testdata/fall_slow_x264.mp4 --text --mjpeg # 逐帧: [frame N] person_id=1 box=... pose: 17 keypoints → IDLE→FALLING→LYING # ⑤ 阈值调参(CLI 覆盖不落盘,记录三档检出数) python3 apps/detect_demo.py --input testdata/stand_up.jpg --save --score-thr 0.3 python3 apps/detect_demo.py --input testdata/stand_up.jpg --save --score-thr 0.7 # 图片模式等价性: python3 apps/fall_app.py --mode image --input testdata/fall_1.png --save
09重点与易错点
- 六段链路每段的形状与坐标空间:原图 → 640² letterbox → 张量 → letterbox 空间框 → 原图框
- 双后端切换是纯配置行为:工厂 build_pose_engine 按 pose_type 创建,接口同为 process() → PoseResult
- NPU 是共享资源:YOLO+姿态共 2 核,双模型串行排队是帧率下降主因
- 验证方法论:单点 → 集成 → 调参;单点挂是模块问题,单点过集成挂是接口/参数问题
| ⚠️ 常见坑 | 现象 → 解法 |
|---|---|
| RKNN 加载失败 | load_rknn failed → 核对 detect.model_path;跑 env_check 校验模型文件 |
| need 4dims input | 板端 static_shape 要求 4D NHWC → 确认 to_rgb 已补批维 |
| 检测框整体偏移 | unletterbox 漏传 pad → 检查 letterbox 返回的 (r,(dw,dh)) 链路 |
| 报"未检测到人体关键点" | 遮挡/模糊/vis 阈值过高 → 换图或调低 model.rtmpose.vis_threshold(默认 0.3) |
| RTMPose score 异常漂移 | INT8 量级漂移属已知现象 → 调 vis_threshold,别急着怀疑解码 |
| 切完 blazepose 忘恢复 | 污染 EXP-06 判定基线 → 实验结束固定改回 rtmpose |
| MJPEG 404 | 8088 被占 → lsof -i :8088 找占用进程;start 前有端口预检 |
| 帧率低/推理堆积 | 双模型抢 NPU → pkill llm_server 等释放资源,关非必要服务 |
| core None 报错 | "The core mode None is not supported" → core_mask 用 0(AUTO),不是 None |
10总结与思考
检测结果、姿态结果、双后端对比表、--text 运行日志、T8 用例与调参记录。
scale+pad 贯穿预处理与后处理;任何一步空间不一致,画面上就是"整体偏移"。
detect/pose/fall_video 逐层单跑,再用 CLI 临时覆盖扫阈值区间。
报错先确认 tensor 维度(4D?195?384/512?),形状对了再查坐标与阈值。