资讯动态

RK3588上RTMPose人体关键点检测部署与优化实践

发布时间:2026/9/27 1:41:07 来源:尧图企业网站定制
前一阵子接了一个边缘视觉项目要在 RK3588 上跑实时人体关键点检测。模型选型阶段对比了 HRNet、MSPN 和 RTMPose 几套方案最后锁定了 RTMPose。从 PyTorch 导出 ONNX到转成 RKNN再到板端跑通、压帧率、抠 CPU 占用整个过程踩了不少坑也沉淀了一些可以复用的经验。这篇文章就把完整实践整理出来重点放在部署链路、量化调优和工程化加速上给同样在 RK3588 上做 rtmpose 部署的工程师一些参考。这篇文章的核心内容对三种人最有用一是刚拿到 RK3588 开发板准备跑姿态估计模型但还没摸清 RKNN 工具链的二是已经把模型跑通了但帧率不达标想知道该优化哪里的三是在做边缘盒子量产需要处理多路视频流、模型版本管理等工程问题的。1. 选型逻辑为什么是 RTMPose为什么是 RK35881.1 RTMPose 的核心价值SimCC 与轻量结构RTMPose 是 OpenMMLab 推出的实时多人姿态估计模型。它的核心思路是用 SimCC坐标分类替代传统的高分辨率热图回归。HRNet、MSPN 这一代模型需要生成空间分辨率较高的热图再对热图做 argmax 和偏移校正才能得到关键点坐标。这个流程在 GPU 上没问题但在边缘 NPU 上有两个痛点反卷积上采样层在 NPU 上跑不快热图后处理在 CPU 上又是一笔不小的开销。RTMPose 把坐标预测拆成一维的 x、y 概率分布向量相当于把回归问题变成了分类问题。它用轻量 Transformer 做特征交互配合 CSPNeXt 作为骨干网络整个模型的参数量和计算量都控制得很好。以 RTMPose-s 为例参数量大约 5.5M在 256x192 输入分辨率下计算量不到 1 GFLOPs这对 RK3588 的 NPU 来说是一个比较舒服的负载。需要注意一点RTMPose 本身是 Top-down 方案只管关键点回归不管人检测。完整链路是“检测器先框人RTMPose 再对每个框回归关键点”。部署时一定要把这一级设计进去是分两个模型跑还是固定场景整图推理会直接影响后续整个方案的复杂度和性能。1.2 RK3588 的算力底座与 NPU 算子约束RK3588 是瑞芯微旗舰 SoC8nm 工艺CPU 是 4×Cortex-A76 4×Cortex-A55GPU 是 Mali-G610最关键的是集成了第三代自研 NPU算力标称 6 TOPSINT8。这个 NPU 支持 INT4/INT8/INT16 混合量化也支持部分 FP16 算子。跟 Jetson Orin Nano 这类平台相比RK3588 的绝对算力不算高但它在价格、功耗和供货稳定性上有明显优势特别适合做边缘计算盒子、智能摄像头这类产品。但 RK3588 的 NPU 不是一个“什么算子都能高效跑”的通用加速器。它擅长卷积、pooling、全连接、GELU 这些常见算子但遇到不规则 transpose、动态 shape、长尾算子时要么不支持要么会退到 CPU 上执行拖慢整体速度。所以模型选型阶段就要考虑算子兼容性而不是等转 RKNN 时再回头改网络结构。1.3 检测器与关键点模型的两级方案必须提前规划部署 RTMPose 时最容易被忽略的是检测器这一层。RTMPose 输入的是“已经裁剪好的人体框”如果你没有把检测器一并部署好RTMPose 根本跑不起来。我见过两种常见做法方案 ARTMDet 检测 RTMPose 关键点两个模型都转成 RKNN。适合多人场景框数量多时需要用 NMS 清理板端后处理逻辑会复杂一些。方案 B固定相机角度单人或者主要目标居中的场景直接跳过检测器整图送入 RTMPose。省掉检测耗时但只适用于受控场景。检测器也可以用 YOLOv8 替代 RTMDetYOLOv8 在 RK3588 上的部署资料更丰富踩坑时更容易搜到解决方案。但要注意检测器输出的框要经过坐标映射和裁剪裁剪后的图像要 resize 到 RTMPose 的输入尺寸这部分前处理能不能用 RGA 加速要在设计最初就考虑进去。2. 全链路部署路径从 PyTorch 到 RKNN 的四个环节2.1 环境准备工具链与 runtime 的版本匹配RK3588 的部署工具链核心是 rknn-toolkit2它运行在 x86 PC 上负责把 ONNX 转成 RKNN 格式。板端运行时用 rknn-toolkit-lite2或者直接调 C API 的 librknnrt.so。这里最大的坑就是版本匹配。rknn-toolkit2 的版本必须和板端 librknnrt.so 版本严格对应否则模型加载时会报“RKNN Run Failed”而且报错信息经常很模糊不会告诉你具体是哪里不匹配。我的建议是在开始部署前先把官方 demo比如 resnet18 分类示例在板端完整跑通一遍。这样能确认工具链、runtime、系统镜像三者是配套的再继续推进 RTMPose否则后面排查问题时变量太多会非常痛苦。板端系统我用的 Ubuntu拿到板子后可以用下面的命令确认 librknnrt.so 的版本strings /usr/lib/librknnrt.so | grep -i version如果想手动替换 runtime先通过 ADB 连上板子再 push 对应版本的库文件adb connect 板卡IP adb push librknnrt.so /usr/lib/ adb shell sync adb rebootADB 连接偶尔会遇到掉线问题先执行adb kill-server再重新adb connect大概率能恢复。2.2 ONNX 导出与简化固定 shape、控制 opset、清洗算子RTMPose 在 MMPose 框架里有官方实现也提供了往 ONNX 导出的工具。但直接导出的 ONNX 会带上一堆后处理节点比如 SimCC 解码、DARK 等。这些在训练时有意义部署时反而是转换障碍。更稳妥的做法是用 MMPose 的 config 构建模型只保留 backbone 和 head 的 forward 路径导出时关闭所有后处理逻辑。也就是只导出“输入图像输出原始 SimCC 概率向量”的最小模型。导出后用 onnx-simplifier 清洗一遍消除掉冗余的 reshape、transpose 和常量节点python -m onnxsim rtmpose_s_raw.onnx rtmpose_s_sim.onnx \ --overwrite-input-shapeinputs:1,3,256,192导出时重点注意两点输入 shape 固定。虽然 RKNN 支持动态 shape但动态 shape 意味着 NPU 每次推理都要重新做内存规划性能损失很大。边缘部署基本上都用固定分辨率RTMPose 常用的有 256x192、192x192。opset 版本。太新的 opset 里某些节点 RKNN 工具链可能还没适配我一般用 opset 11 或 12兼容性最稳。导出完成后在 PC 上先用 ONNX Runtime 跑一遍跟 PyTorch 模型的输出做对比。随机生成一个 batch 的输入计算两个输出张量的余弦相似度大于 0.999 基本说明导出没问题。2.3 RKNN 转换与量化配置拿到干净 ONNX 后用 rknn-toolkit2 转 RKNN。核心代码如下from rknn.api import RKNN rknn RKNN() rknn.config( mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588, quantized_dtypeasymmetric_quantized-uint8, quantized_algorithmnormal, optimization_level3 ) rknn.load_onnx(modelrtmpose_s_sim.onnx, input_size_list[[1, 3, 256, 192]]) rknn.build(do_quantizationTrue, datasetcalib.txt) rknn.export_rknn(rtmpose_s_rk3588_1.6.0_int8.rknn)config 里的 mean_values 和 std_values 就是做归一化换算规则是(像素值 - mean) / std。我习惯把归一化直接融进 RKNN 模型里这样板端预处理只做 resize 和通道转换不用再跑一遍浮点归一化的除法省下不少 CPU 开销。quantized_dtype 用 asymmetric_quantized-uint8这是 RK3588 NPU 执行效率最高的量化格式。quantized_algorithm 默认 normal 就行如果精度掉点明显可以试 mmse 算法。校准数据集 calib.txt 里存的是图片文件路径列表每行一张。这些图片最好来自真实部署场景而不是训练集否则校准统计出来的激活分布和实际推理分布会有偏差量化精度会掉得莫名其妙。我一般准备 200 张左右。2.4 转换后的输出一致性验证RKNN 转完后别急着上板先在同一台 PC 上用 rknn-toolkit2 提供的模拟推理功能跑一遍跟 ONNX Runtime 的输出做对比。重点看各层输出的均方误差如果某个 head 输出张量误差特别大记录下来量化调优时专门盯这个张量。这一步能帮你把“模型转换问题”和“板端运行问题”隔离开。模拟推理过了上板还出错那就是板端 runtime、内存排布或者输入数据的问题模拟推理就挂了那就是转换或者量化配置的问题。3. 精度调优量化掉点可能出现在哪些环节3.1 姿态模型量化敏感度分析RTMPose 这种关键点回归任务对量化的敏感度比目标检测高。检测模型掉几个点的 box AP视觉上可能看不出来但关键点坐标最终要精确到像素级别SimCC 概率分布一旦在量化里被削平或者偏置关键点就会漂移几像素甚至十几像素视频里看起来就是关节抖动。我的实测规律是feature map 通道数越少、激活数值范围越大的层量化误差越大。RTMPose 的 head 部分输出的 SimCC 向量数值范围天然比较大是量化风险最高的区域。遇到量化掉点优先级最高的手段不是立刻上混合量化而是先增加校准数据确保校准图片来自真实场景。然后逐个尝试不同的量化算法。最后再用混合量化把误差最大的几个层设成 FP16其余保持 INT8。3.2 校准数据集的选择与量化算法对比校准数据集的选择直接影响量化质量。我踩过一个典型问题用训练集的 200 张图片做校准转出来的模型在实验室测试一切正常拿到现场跑就出现大量钥匙点漂移。原因是现场摄像机角度和光线分布跟训练集差异太大校准统计的激活分布已经完全失配。后来我改成从现场录制视频中抽样抽帧选 200 张覆盖不同光照、不同人物姿态的图片做校准量化精度明显改善。这个细节经常被忽略但影响很大。量化算法精度表现转换耗时适用场景normal中规中矩多数场景可用快默认首选mmse通常比 normal 好 0.1~0.3 个点中等精度掉点明显时尝试kl_divergence对长尾分布友好中等激活分布不均匀时尝试3.3 混合量化的触发条件与实施步骤混合量化不是默认方案。INT8 已经能覆盖大多数场景但如果你做了校准集优化、量化算法切换之后关键点精度仍然不达标就考虑混合量化。实施步骤是先用 rknn-toolkit2 的模拟推理输出各层的量化误差统计找出误差最大的 5~10 个层。然后重新调用 rknn.config用 custom_quantize_layers 指定这些层保持 FP16rknn.config( mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588, quantized_dtypeasymmetric_quantized-uint8, custom_quantize_layers[layer_name_1, layer_name_2] )混合量化会牺牲一部分推理速度所以只把真正敏感的那几个层切到 FP16不要一刀切全量混合。4. 板端推理的工程化加速CPU 侧的每一毫秒都要抠4.1 输入预处理用 RGA 替代 OpenCV 的 resize 和色彩转换很多人在板端跑起来发现帧率上不去以为 NPU 推理慢结果 perf 一看CPU 预处理占了三分之一的时间。OpenCV 的 resize 和 cvtColor 在 ARM CPU 上并不快。1080P 图像 resize 到 256x192一次 cv::resize 就要 3~5ms加上 BGR2RGB 和 memcpy一轮预处理超过 8ms 很常见。RK3588 的 RGA 硬件单元就是干这个的。RGA2/RGA3 支持缩放、格式转换、旋转、翻转可以把 NV12 格式的摄像头数据直接 resize 并转成 RGB。通过 librga 调用一次 rga_convert整个过程 CPU 只做一次内存同步开销极小。实测下来1080P 图像到 256x192 的预处理耗时从 8ms 左右降到 1ms 以内效果非常显著。4.2 SimCC 后处理优化softmax 近似与窗口截断RTMPose 的 head 输出是两个方向的 SimCC 分类向量。输入 256x192 时按输出宽度扩两倍算x 方向长度约 512y 方向约 384。每个关键点都要对两个方向的得分向量做 softmax然后加权求和得到亚像素坐标。假设检测到 N 个人、17 个关键点CPU 端要做 17×N 次 softmax 和加权求和。我初版实现用完整 softmax单人单帧的后处理大约 3ms多人场景立刻变成性能瓶颈。我做了三个优化用查表法近似 exp。精度损失小于 0.1 像素速度快了约 30%。softmax 前先减去最大值保持数值稳定然后用 NEON 指令向量化一次处理 4 个浮点。利用概率分布的峰值特性峰值以外的区域概率接近 0直接截断到峰值附近的局部窗口做加权求和运算量大幅下降。这几个优化叠加后单人单帧后处理降到 0.3ms 以内多人场景可以通过多线程分片并行处理。4.3 内存对齐与缓存一致性边缘部署的基础课RKNN 的输入输出 buffer 要求 64 字节对齐用普通 malloc 申请的堆内存很容易踩坑。建议用 RKNN 提供的rknn_create_mem接口申请内存或者用 C11 的aligned_alloc(64, size)。另外如果用 dma-buf 做零拷贝记得在 NPU 推理前加一次 dma_sync 操作否则会出现 Cache 一致性问题表现为输出结果时对时错特别难排查。5. 多线程流水线和多路接入吃掉 NPU 的空闲时间5.1 双缓冲流水线预处理器、NPU、后处理器三级并行RKNN 的 C API 里rknn_run是异步提交rknn_wait会阻塞等待结果。基于这个特性可以设计一个三级并行的流水线线程 A 负责取帧和预处理写完输入 buffer 后提交 NPU 推理不等结果立刻开始下一帧预处理线程 B 负责等待上一帧的 NPU 输出做后处理和结果上报。本质上就是 producer-consumer 模型让 CPU 预处理、NPU 推理、CPU 后处理三个环节在不同帧上并行。单看 NPU 推理时延没有变但整个系统的吞吐能提升 40%~60%。伪代码// Frame thread while (running) { cv::Mat frame capture(); preprocess(frame, input_buf[pending_slot]); rknn_run(ctx, input_buf[pending_slot]); pending_slot 1 - pending_slot; notify_postprocess_thread(); } // Postprocess thread while (running) { wait_for_npu_completion(); int done_slot get_completed_slot(); postprocess(output_buf[done_slot], result); publish_result(result); }实际的并发模型里要加条件变量做同步避免读写同一块 buffer。我通常给每个 buffer 加一个状态标记空闲、已填充、推理中、已完成。线程根据状态跳转状态不对就等待。5.2 多路视频共享 context 与带宽预算如果做 4 路甚至 8 路视频流同时姿态识别NPU 单个 context 加载多个实例会很耗内存。更合理的做法是不同路共享同一个模型 context通过 input_index 区分不同路的输入。RKNN 支持多 context也支持单 context 多线程提交但要注意 NPU 执行单元是串行的过多并发提交反而可能增加切换开销。多路接入真正被挑战的不是 NPU 算力而是 DDR 带宽。RK3588 的 LPDDR4X 带宽不低但多路同时做 RGA 缩放、RGB 转换、NPU 输入输出带宽会非常紧张。建议从摄像头 sensor 端直接输出低分辨率流别拿到 1080P 再统一缩放。6. 实测性能数据与避坑清单6.1 我自己环境下的帧率与耗时分布我用的是 RTMPose-s输入 256x192INT8 量化在 RK3588 上跑单路视频流。注意这个数据受 rknn-toolkit2 版本、Ubuntu 镜像、散热条件影响不同环境会有波动但量级可以做预算参考。环节耗时说明图像采集 RGA 预处理约 1ms1080P 到 256x192NPU 推理INT8约 22msrknn_run rknn_waitSimCC 后处理单人约 0.3ms优化后整链路帧率30fps 左右未开多线程流水线时约 22~25fps单看 NPU 推理时延RTMPose-s 在 RK3588 上表现不错。如果换 RTMPose-t推理时延会进一步降到 12~15ms帧率可以更高。RTMPose-m 则建议谨慎评估因为计算量翻倍NPU 压力会大不少。6.2 高频问题排查对照表部署过程中遇到最多的几个问题我一个一个排查过整理出来供你们对照现象可能原因排查方向模型加载报错且 error code 为 12librknnrt.so 版本与 RKNN 模型版本不匹配替换配套版本的 librknnrt.so首次推理特别慢后续正常NPU 做权重加载和内存分配不算 bug计时前先跑 30 帧预热转换时报 Op resolve failedONNX opset 太高或节点参数不兼容用 onnxsim 清洗降低 opset量化后关键点抖动校准集不匹配或敏感层被量化换真实场景校准图找误差大的层做混合量化视频里偶发错点dma-buf 未做 cache sync检查 dma_sync 操作帧率不稳定波动大CPU 预处理占用过高改用 RGA 做 resize 和色彩转换7. 给后续项目的版本管理与扩展建议7.1 模型命名、工具链版本与校准集管理跑通一台设备只是开始。如果要量产边缘盒子模型版本管理一定不能随意。我早期吃过亏没有把每版 RKNN 模型的工具链版本记录下来后面想回溯某个精度正常的模型翻半天也找不到对应配置。现在我的命名规则是rtmpose_s_rk3588_1.6.0_int8_v3.rknn含义是模型结构_平台_工具链版本_量化类型_迭代版本。同时维护一个部署记录文档记录每个版本对应的校准数据集来源、量化算法、混合量化层列表、板端 runtime 版本以及实测精度和耗时。7.2 与检测器协同的两种部署姿势如果场景需要完整的多人体姿态识别检测器和关键点模型都要上板。此时有两种部署姿势一是检测器和 RTMPose 串行执行。先跑 RTMDet 或 YOLOv8 检测拿到人体框再对每个框做裁剪和 resize送入 RTMPose。这种方式逻辑简单但单帧耗时是检测器推理和所有人关键点推理的总和。二是固定相机角度的场景可以提前配置感兴趣区域检测器只在特定区域内找人减少误检也能降低关键点模型的推理负载。7.3 我的几点体会最后说几点我在实际项目中的体会。RK3588 的推理时延数据一定要在真机上测不要用模拟推理的数据去估算帧率。rknn-toolkit2 的模拟推理精度用来做层间误差对比没问题但执行速度和真机差异很大预估性能必须以上板实测为准。另外散热对 RK3588 的 NPU 性能影响比想象中大。长时间满载跑 6 TOPS 的 NPU如果散热片没压好CPU 和 NPU 都会降频。看到帧率越来越低先检查板卡温度别急着优化代码。这个项目做完之后我最大的感受是边缘部署不是“把模型 file 转个格式”那么简单它是一个横跨模型训练、算力理解、系统编程的交叉工程。每个环节都有坑但每个坑都有迹可循。希望这篇文章能帮你少走一些弯路如果你在部署 RTMPose 到 RK3588 时遇到了我文章里没写到的问题欢迎一起交流。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑