
RTMPose这个词这两年做边缘计算的人肯定不陌生。作为OpenMMLab在姿态估计方向放出的一个重要模型它的卖点就是轻量化和高精度能同时做尤其适合RK3588这种端侧NPU平台。RK3588这颗芯片瑞芯微旗舰级的边缘SoC8核ARM 6TOPS算力NPU做视觉AI落地工程的应该人手一块开发板了。把RTMPose部署上去等于在端侧拿到了实时人体关键点检测能力健身姿态纠正、安防行为分析、人机交互这类项目都能直接接上。这篇内容就围绕“RK3588部署rtmpose模型”这件事展开从模型选型、ONNX导出、RKNN量化转换到板端推理和前后处理把完整流程和过程中的坑都过一遍。适合手里有RK3588开发板、想把姿态估计模型真正跑起来的人看也适合准备做端侧AI落地但还没摸清工具链的朋友。我不讲虚的全是实际跑过之后总结出来的东西。1. 先想清楚为什么是RTMPose RK35881.1 模型选型边缘端姿态估计的几种选项姿态估计模型不算少但能塞进端侧NPU、跑得动实时推理的数来数去就那几个。我在选型的时候对比过OpenPose、LiteHRNet、MoveNet和RTMPose最后选了RTMPose核心原因是它在精度和推理速度之间的平衡点非常合适端侧场景。OpenPose是老牌模型精度高但结构复杂计算量太大即便裁剪版本在RK3588上也很难实时更适合服务器。LiteHRNet在移动端口碑不错但它在ONNX导出后算子偏杂转换到RKNN时经常遇到算子不支持的问题调试成本高。MoveNet是TensorFlow生态的如果项目纯走PyTorch流程还得ffmpeg导入导出绕一圈麻烦。RTMPose是2023年OpenMMLab团队放出的基于Transformer的轻量级姿态估计模型。它最核心的设计思路是用simCCsimulated coordinate classification替代传统heatmap做关键点定位。传统heatmap方案需要输出高分辨率的特征图来保证定位精度计算量和内存开销都大。simCC把坐标预测拆成两个一维分类问题x方向和y方向各做一次分类输出维度大幅压缩定位精度还能保持甚至更好。这就是RTMPose能在极低计算量下拿到高精度的根本原因。具体到型号选择RTMPose-t参数量只有3.3M左右计算量在1 GFLOPs上下对RK3588的NPU来说属于轻松负载RTMPose-m大概是18M参数、8 GFLOPs左右精度更高但算力消耗大不少。实测下来如果只是单人姿态估计或者低并发场景RTMPose-t完全够用如果要做多人、高精度场景可以看RTMPose-m但要做好帧率下降的心理准备。1.2 平台与工具链RK3588 NPU的玩法RK3588的NPU标称6 TOPS INT8算力这在端侧芯片里属于中上水平。但芯片规格只是纸面数据真正决定落地速度的其实是工具链的成熟度。RKNN-Toolkit2是瑞芯微官方推出的模型转换与推理工具链支持从PyTorch、ONNX、TensorFlow等框架导入模型转换成RKNN格式后在板端运行。它的整体流程是这样的PyTorch训练/导出的模型先转成ONNX再用RKNN-Toolkit2在PC上完成量化转换生成.rknn文件最后拷贝到RK3588板子上用RKNN Runtime加载推理。这个链路中ONNX是承上启下的中间格式RKNN-Toolkit2负责格式转换和量化压缩。选择RK3588还有一个优势是它同时配了4个A76大核和4个A55小核GPU是Mali G610。这意味着即使某些算子没有落到NPU上执行CPU也能扛一部分不会出现整个推理动弹不得的局面。实际项目中前处理图像缩放、归一化这些操作如果在CPU上做A76大核的性能完全可以支撑瓶颈反而不大。1.3 整体部署路线的技术架构拆解把整个部署过程拆开看大致是四段模型准备、模型转换、板端推理、业务集成。模型准备阶段的目标是拿到干净、标准、可复现的ONNX文件转换阶段的目标是得到精度损失可控的INT8量化模型推理阶段要处理好输入输出数据结构把模型能力真正用起来集成阶段才是把姿态数据变成业务价值的部分。我最初犯过的错误是急于进入转换阶段拿到一个ONNX就直接扔给RKNN工具链结果后面所有问题都堆在推理阶段爆发。后来学乖了每个阶段固定节点做验证ONNX先用onnxruntime跑一遍确认输出坐标合理再做RKNN转换。这个习惯帮我筛掉了很多无用功。2. 正式开工前的模型导出与预处理2.1 拿到RTMPose的ONNX模型RTMPose在OpenMMLab生态里分布在MMPose仓库MMPose 1.0以上版本对它支持完整。如果从零开始最稳妥的方法是先安装mmcv、mmdet、mmpose这几个包然后从MMPose官方模型库下载rtmpose-t或rtmpose-m的预训练权重。如果是纯部署项目不想引入整套MMPose依赖直接从官方仓库release页面下载onnx文件也是可以的。但我个人更建议自己走一遍pytorch2onnx的导出流程因为这一步能让你看清楚模型输入的尺寸要求、归一化参数、输出结构后面写代码时心里有底。模型导出脚本大概长这样这是MMPose 1.0时代的常见做法from mmpose.apis import init_model import torch model init_model(rtmpose-t.py, rtmpose-t.pth, devicecpu) model.eval() dummy_input torch.randn(1, 3, 192, 192) torch.onnx.export( model, dummy_input, rtmpose-t.onnx, opset_version11, input_names[input], output_names[simcc_x, simcc_y], dynamic_axesNone )注意这里输入尺寸设成了192×192dummy_input随机生成的形状必须固定。RKNN工具链对动态shape支持不友好转换时建议固定输入尺寸。如果你要换256×192或者224×224后面的归一化和坐标映射都要跟着调整建议一开始就定好不要中途频繁换。2.2 输入尺寸、归一化与RGB顺序三个最容易被反噬的细节RK3588部署rtmpose模型时这三个细节是最容易被忽略又最容易出问题的。先说输入尺寸。RTMPose训练时用的是192×192或256×192两种常见分辨率直接决定了推理速度和精度。192×192在NPU上的计算量不到256×192的一半如果需要高并发或多路视频分析192是更合理的选择如果精度要求高、单路处理256×192更好。然后是归一化参数。RTMPose在训练时的图像归一化统计量mean是[123.675, 116.28, 103.53]std是[58.395, 57.12, 57.375]这个参数不是随便写的它来自ImageNet数据集的统计模型训练时也就地固化了这个配置。板端推理时必须用完全相同的mean/std做归一化否则输入数据分布和训练时不一致关键点位置会整个偏移。我记得第一次部署时偷懒没做归一化输出热力图全部塌掉排查了半天才发现是这个原因。最后是RGB顺序。OpenMMLab生态默认图像通道顺序是BGR但很多ONNX模型转换后期望的是RGB。这个坑特别隐蔽因为如果顺序反了不会直接报错只是精度明显下降。我的做法是在转换前后各跑一遍同一张图像的推理对比关键点输出如果位置偏差超过5个像素大概率就是通道顺序问题。用Netron打开ONNX模型查看输入节点的定义如果写了BGR那板端预处理就必须按BGR顺序喂数据。3. RKNN转换与量化从ONNX到rknn3.1 RKNN-Toolkit2环境准备RKNN-Toolkit2提供PC端的Python工具包以及板端的推理运行时RKNN Runtime。转换这个动作必须在PC端完成因为量化过程需要跑浮点模型来收集激活值分布板端算力不适合干这个活。安装RKNN-Toolkit2最简单的方式是pip install但要注意它依赖的numpy、onnx版本不能太高装完后再用官方提供的rknpu2交叉编译包做板端运行时。实操中我建议把Python环境单独用conda或venv隔离出来因为RKNN-Toolkit2对部分依赖包版本敏感和其他项目混在一起容易出现libstdc或者onnx版本冲突。这一点是我的亲身教训一开始我在一个既有PyTorch训练环境又有TensorRT环境的机器上装RKNN-Toolkit2结果两个依赖链互相打架光是处理依赖冲突就花了两天。后来单独建了一个环境五分钟装完。做嵌入式AI工具链部署环境隔离是底线。3.2 转换脚本与量化参数核心转换代码不长如下import os import numpy as np from rknn.api import RKNN rknn RKNN() rknn.config( mean_values[[123.675, 116.28, 103.53]], std_values[[58.395, 57.12, 57.375]], target_platformrk3588 ) ret rknn.load_onnx(modelrtmpose-t.onnx) if ret ! 0: print(load onnx failed) exit(-1) ret rknn.build( do_quantizationTrue, datasetdataset.txt ) if ret ! 0: print(build rknn failed) exit(-1) rknn.export_rknn(rtmpose-t.rknn)这里有几个关键点。mean_values和std_values如果已经在config里写了那板端推理时就不需要再做一遍归一化RKNN Runtime会自动处理输入这个设计很贴心但也意味着如果两边都做了归一化数据就双重归一化错了。我建议把归一化交给RKNN Runtime处理板端代码只做resize和通道顺序转换逻辑简单很多。dataset.txt里每行写一张用于量化校准的图像路径建议至少写30张以上真实场景图像。量化过程实际上就是用这些图像在模型里跑一遍浮点推理统计激活值分布然后按照INT8映射关系做参数压缩。如果你的应用场景是室内健身校准图就应该用室内健身的人体图像如果是户外安防就准备户外视频帧。校准数据分布和实际推理数据分布越接近量化带来的精度损失越小。build阶段do_quantizationTrue开启INT8量化。RTMPose模型量化为INT8后体积会从几十MB压缩到几MBNPU推理速度提升明显。实测RTMPose-t在192×192输入下FP32推理一帧需要20-30msINT8量化后能压到8ms以内这个收益非常大。代价是精度可能掉1%-3%的AP在姿态估计任务里表现为关键点抖动稍明显一点大多数人可接受。3.3 量化精度不够时怎么办有时候INT8量化掉点会比较厉害尤其是小模型RTMPose-t本身就轻量敏感层被量化后误差可能被放大。遇到这种情况不要急着换大模型先尝试混合量化方案。RKNN-Toolkit2支持设置部分层不参与INT8量化保留为FP16或FP32精度。具体做法是在rknn.build之前先做一次不量化的build用rknn的模型分析工具找出对精度影响较大的层再针对这些层单独设置混合精度。更简单粗暴但有效的方法是如果精度实在保不住就退一步将输出层附近的若干层设为FP16其余保持INT8。这个方法实测可以把AP掉点降到1%以内推理速度损失并不大。这里还要强调一个测试指标问题。部署完模型后不要只看单张图像的主观效果就下结论要用一个带标注的测试集跑一遍mAP或者PCK量化前后对比。用我之前的项目数据做参考RTMPose-t原模型在COCO val上AP约68%INT8量化后大概65%左右混合精度能回到66%以上这个下降幅度对大多数业务场景都是够用的。4. 板端推理落地与性能调优4.1 RKNN Runtime的两种用法RKNN Runtime在板端有两个主流用法Python API和C API。Python API适合快速验证原型代码写起来快适合把流程跑通生产环境建议用C API内存占用更低线程控制更灵活性能也更好。我个人的开发路径是先Python原型验证再转C API做正式交付。Python侧核心逻辑比较简单from rknnlite.api import RKNNLite rknn_lite RKNNLite() ret rknn_lite.load_rknn(rtmpose-t.rknn) rknn_lite.init_runtime() outputs rknn_lite.inference(inputs[img_192x192_bgr])注意这里用的是rknnlite不是PC端那个rknn库板端SDK里带的是RKNNLite简化版。init_runtime执行后模型就加载到NPU里了inference输入是一个四维numpy数组形状是(1, 3, 192, 192)数据类型是uint8或者float32具体取决于config里normalize的方式。C API那边代码量会大不少但核心就三步rknn_init加载模型、rknn_inputs_set输入数据、rknn_run rknn_outputs_get拿输出。如果你没有C开发经验先用Python做完整项目也没问题RK3588跑Python部署速度仍然可接受。4.2 前后处理的正确姿势RTMPose的输出结构需要特别说明。由于模型用的是simCC分类方式输出通常是两个分支x方向的分类得分和y方向的分类得分每个分支的形状是[1, K, D]K是关键点数量COCO是17D是分类长度一般等于输入尺寸除以4或者固定值。后处理时需要对每个关键点的x分支和y分支分别算softmax然后用期望值weighted average算出浮点坐标再乘上缩放因子映射回原图。这个处理逻辑比heatmap要简单但也是容易出错的地方。有些版本的ONNX导出会在模型内部包含softmax输出已经是归一化的概率分布有些则没有需要你在外部做。建议先用Netron查看输出节点定义再写对应的后处理代码。把坐标映射回原图是整个后处理中最关键的环节。RTMPose训练和推理都使用一个“框内姿态估计”的范式——先检测人体框然后把框内的图像resize到192×192输入模型。模型输出的关键点坐标是相对于192×192图像坐标系的要映射回原始帧需要记录裁剪和缩放参数然后逆变换回去。如果跳过这一步直接把输出坐标当成原图坐标使用关键点位置会完全错乱。如果不接检测器直接把整帧图像resize成192×192输入模型那映射公式就简单一些只需要乘上原图尺寸和输入尺寸的比值但这种方式在多人场景下效果会很差因为模型训练时是以单人体为中心的。4.3 多线程流水线与实测性能姿态估计的完整推理链路包含解码、resize、推理、后处理四步。如果串行执行每一步之间都在等系统的吞吐量会很低。我用的是经典的四段流水线线程1读视频帧线程2做预处理线程3做NPU推理线程4做后处理和可视化。四组线程之间用队列衔接队列满了就丢帧保证整个系统不会因为某一帧处理慢而整体卡死。线程同步这块要注意RKNNLite推理接口本身是阻塞的同一个实例不能同时被多个线程调用。如果要多线程并发推理需要创建多个RKNNLite实例分别加载同一个模型每个线程用各自的实例。实测NPU对多实例并发的支持还不错但过多实例也有上限一般两个到四个并发就足够覆盖大部分场景。实测性能数据供参考。在RK3588上RTMPose-t192×192输入INT8量化NPU单次推理大约7-9ms加上前后处理整个pipeline能跑到45-60fps单路1080p输入抠出人体框后做推理RTMPose-m在同样的输入尺寸下NPU单次推理约15-20ms全流程约25-30fps。这个性能对实时互动类应用完全够用。另一个影响性能的因素是输入图像缩放。Python下直接调cv2.resize192×192的resize一次只需零点几毫秒但如果先裁剪出多个人体框再逐个resize会累积不少开销。建议用缓存池复用图像缓存避免频繁申请内存。5. 我踩过的坑常见问题与排查速查表5.1 转换阶段的坑转换阶段最容易遇到的一个问题是ONNX格式不兼容。RTMPose如果从MMPose导出的ONNX版本过新RKNN-Toolkit2可能报算子不支持的错误。解决办法是设置导出时的opset_version我推荐先用opset_version11试不行再降到10再不行就检查猫腻算子。Transformer结构里常见的GELU激活、LayerNorm在某些rknn版本上支持不够好可以先用onnxsimplifier把图优化一遍再转换。量化校准阶段的灾难现场是dataset.txt路径写错或者图像打不开工具直接报错退出。用绝对路径是最稳妥的而且一定要在日志里确认每张校准图都被正常加载。我遇到过一种诡异情况校准图集用了一组全是深色背景的图像导致量化后模型在亮色环境下明显掉点所以校准集覆盖范围要广光照、服装颜色、姿态分布都要覆盖。5.2 运行阶段的坑板端运行最常见的错误是报libstdc.so.6版本问题原因是板子系统库版本和RKNN Runtime要求的版本不匹配。解决办法是升级系统库或者编译一个静态链接的推理程序我更推荐后者。另一个常见问题是模型加载后init_runtime报错提示找不到目标设备多半是NPU驱动没加载在板子上挂载并启用rknpu驱动就行。推理结果异常这块我总结过一张问题速查表现象可能原因解决参考关键点整体偏到图像角落归一化参数配错或通道顺序反了检查mean/std和BGR/RGB设置关键点位置对但抖动厉害量化精度不够或输入分辨率偏低混合量化保留FP16敏感层提高分辨率输出全为0或NaNONNX输出节点没找对用Netron确认输出分支名称首帧推理特别慢模型在板端做了设备侧编译预热一次推理或转换时缓存设备模型多线程调用报段错误同一个RKNNLite实例被并发调用每个线程独立加载模型实例掉点严重且只在特定场景量化校准集分布与实际场景不匹配换用真实场景图像重新校准后处理用softmax时如果输入数值过大softmax容易出现溢出NaN。稳妥的处理方式是先减最大值再softmax这在数值上等价但稳定性高很多。这是一行代码的事但能省一晚上的排查时间。5.3 精度与性能调优的经验如果量化后关键点精度不够先确认是不是校准集问题校准图像最好在20到100张之间太少分布覆盖不够太多转换时间太长收益反而有限。然后是混合量化把输出层和分类头相关的层设置为FP16这是性价比最高的一招。再不行就提高输入分辨率但性能会下降属于最后手段。性能调优方面我建议用RKNN工具包自带的perf工具做profile它能显示每一层NPU算子的耗时。如果发现某一层耗时特别高优先考虑是不是该算子没有完全走NPU而是回退到了CPU。另一个简单但有效的优化是调整NPU工作频率RK3588的NPU支持不同频率档位可以手动设置到最高档来换更低的单帧延迟代价是功耗上升。如果是电池供电的设备要谨慎如果是插电边缘盒子直接拉满。内存分配也值得注意。RKNN Runtime在运行时会为输入输出分配连续内存如果在Python侧反复创建新的numpy数组内存碎片会越来越多。建议在循环外预先分配好输入缓冲区和输出缓冲区推理时直接复用既能减少gc压力也能让整体运行时间更稳定。结尾最后分享一个我自己在实际项目里的做法。我一般不会把RTMPose单模型当全线用而是配一个轻量检测器先做人脸或人体框检测再把框送给RTMPose做姿态分析。RK3588 NPU跑一个RTMDet-s再加RTMPose-t整体还能稳住实时效果比只输入整帧要好很多。另外如果业务对关键点稳定性要求高建议在后处理端加一个轻量的坐标平滑算法比如卡尔曼滤波或者滑动窗口平滑能让姿态看起来更自然。在RK3588上部署RTMPose这件事真正决定项目成败的不是模型本身而是你在输入预处理、量化校准、坐标映射这些容易被忽略的细节上花了多少功夫。把每一步都抠干净部署基本就成功一大半了。