起因

前面写过一篇《瑞芯微RK3588应用程序开发全流程记录》,把 SDK、设备树、烧录、自启动这条 Linux 工程链走了一遍——那是"把平台跑成产品"的一半。另一半是应用本身:怎么把 RK3588 那 6 TOPS 的 NPU 真正用起来,做成一套流水线机械臂分拣的视频识别方案。这篇就补这一半:从模型训练、量化部署,到手眼标定、皮带跟踪、跟机械臂握手,把整条链路的设计决策过一遍。

需求定义

先立一个典型的假想需求,后面所有设计围绕它展开:

指标 目标值
工件类别 8 类金属件 + OK/NG 缺陷判定
视野 600×450mm(单工位俯拍)
皮带速度 0.3~0.5m/s
节拍 ≥40 PPM(每分钟件数)
定位精度 ±2mm(交给机械臂的抓取坐标)
来料姿态 平铺单层,允许少量堆叠/紧挨

选型:为什么是 RK3588

端侧跑检测模型的可选项摆一排:

方案 算力 优势 劣势
RK3588 6 TOPS (INT8) 三核 NPU 灵活分配、8 核 A76+A55、多路摄像头、双网口、板卡 ¥600~900、中文资料最全 算力只够轻量模型,训练生态要自己搭
Jetson Orin Nano ~40 TOPS CUDA 生态零迁移 价格 ×3、功耗更高、供应链看外资脸色
IPC SoC(海思/国科微类) 无~弱 NPU 视频管线成熟 跑 YOLO 类模型基本没戏
工控机 + 入门 GPU 看卡 生态最自由 体积/功耗/成本全面劣势

算一笔账看 6 TOPS 够不够:YOLOv8s@640 INT8 在 RK3588 上单帧推理 2035ms 量级(三核并行,具体以 rknn_model_zoo 实测为准),折合 3050 FPS,单帧可检出视野内全部工件——瓶颈在机械臂动作(300~500ms/件),不在 NPU。这个结论决定了整个方案:视觉做"一次看全、并发流水",不追求极限帧率。

系统架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  工业相机(全局快门,GigE/USB3)      到位光电(硬触发)
          │帧数据                        │触发脉冲
          ▼                             ▼
┌─────────────────── RK3588 ───────────────────┐
│ 采集线程 → RGA预处理(resize/CSC/letterbox)     │
│        → RKNN推理(NPU×3) → 后处理(NMS+位姿)   │
│        → 手眼变换(像素→皮带坐标)               │
│        → 皮带跟踪补偿(编码器计数)              │
└──────┬───────────────────────────┬───────────┘
       │TCP/Modbus                  │编码器(正交脉冲)
       ▼                            ▼
  机械臂控制器                    皮带编码器

设计要点:

  • 预处理交给 RGA:resize、色彩空间转换、letterbox 全部走 RK3588 的 2D 加速器 RGA,不占 CPU;条件允许时整条链路用 DMA-BUF 零拷贝(采集→RGA→NPU 不memcpy)
  • 三核 NPU 的分配策略:单相机场景绑满三核(RKNN_NPU_CORE_0_1_2)跑一个大模型;多工位扩展时每路相机绑单核(CORE_0/1/2)各跑各的模型,RK3588 的 MIPI 接入能力足够多相机
  • 双网口分网段:相机网段与机械臂/PLC 控制网段物理隔离,收流风暴打不到控制链路

NPU 开发链路

这是整条方案的核心,三段式:PC 训练 → 转换量化 → 板端部署

PC 侧:训练与导出

PyTorch 照常训(YOLOv8 或同量级检测模型),数据全部来自产线实拍——500~2000 张起步,重点覆盖反光、堆叠、油污、不同光照这些困难样本。训完导出 ONNX。

转换:rknn-toolkit2

在 PC 上用 rknn-toolkit2 把 ONNX 转成 .rknn,INT8 量化是重头戏:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from rknn.api import RKNN

rknn = RKNN(verbose=True)
rknn.config(
    mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]],
    target_platform="rk3588",
    quantized_dtype="w8a8",              # INT8 权重+激活
    quantized_algorithm="normal",        # 精度掉了换 KL/percentile
    quantized_method="channel",
    optimization_level=3,
)
rknn.load_onnx("yolov8s_sort.onnx")
rknn.build(do_quantization=True, dataset="calib.txt")  # 200张代表性校准图
rknn.export_rknn("yolov8s_sort.rknn")

校准集决定量化质量:必须用产线实拍图,覆盖实际光照和工位视角,拿公开数据集的图校准,上产线精度必崩。量化掉点严重时上混合量化(检测头附近层保 fp16)。

板端:rknn C API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
rknn_context ctx;
rknn_init(&ctx, model_data, model_size, 0, NULL);

// 多相机场景给每路绑定独立NPU核,避免争抢
rknn_core_mask core = RKNN_NPU_CORE_0;   // CORE_1 / CORE_2 / CORE_0_1_2
rknn_set_core_mask(ctx, core);

rknn_input  inputs[1];                   // RGA输出的NV12 buffer
rknn_inputs_set(ctx, 1, inputs);
rknn_run(ctx, NULL);
rknn_outputs_get(ctx, 1, outputs, NULL); // 后处理: 解码+NMS+位姿

运行时版本匹配是高频坑:板上的 librknnrt.so 必须与 PC 端 rknn-toolkit2 大版本对应cat /sys/kernel/debug/rknpu/version 先查 NPU 驱动固件版本再定工具链版本,不匹配就是一堆玄学报错。

性能量级参考(640 输入、INT8,量级仅供预算,落地以 rknn_model_zoo 实测为准):

模型 单核 三核
YOLOv8n 20+ FPS 60+ FPS
YOLOv8s 10~15 FPS 30~50 FPS

视觉算法:从检测框到抓取位姿

分拣和普通检测的本质差别:机械臂要的不只是"是什么",还有"精确在哪、朝向多少"。bbox 只能做粗定位,抓取坐标要再进一步,两条路线:

  • 关键点回归:模型输出中心点 + 朝向点,一次推理同时拿类别、位置、角度——金属件这类形状固定的工件首选,推理开销几乎不增
  • 实例分割 + minAreaRect:掩码最小外接矩形给角度,精度高但慢,留给形状复杂的件

OK/NG 缺陷判定看情况:缺陷特征明显就并进检测类别(8+2 类一把出);缺陷细微(划痕、压伤)再加一个裁剪后二次分类的小模型,两段式,小模型单核跑。

数据是这类项目真正的成本大头:标注、补拍困难样本、迭代三轮以上是常态,预算时留够。

手眼标定与皮带跟踪

手眼标定(eye-to-hand)

相机固定在工位上方俯拍(eye-to-hand),皮带平铺场景不需要 3D 相机,单目 2D + 标定就够:

  1. 机械臂末端装标定针,示教走 9 个均匀分布的点(覆盖全视野)
  2. 每个点同时记录像素坐标(相机读)和皮带坐标(控制器读)
  3. 最小二乘解像素→皮带的单应/仿射变换
  4. 验收:针尖触碰视野四角与中心的标定点,残差 <1mm 才算过关

标定会漂移:相机支架振动、镜头松动、更换相机都要重标。开机自检流程固化进程序——每次启动抓一次标准件比对坐标,超阈值报警停线。

皮带跟踪与延迟补偿

工件在皮带上走,从拍照到机械臂到位有时差,坐标会"过时"。静态分段方案(到位光电触发→臂在固定位等)简单但节拍低;动态跟踪方案是正解:

  • 编码器正交脉冲接入(GPIO 中断计数或专用编码器输入),每个被检工件绑定一个随脉冲实时更新的皮带坐标
  • 延迟补偿:Δx = v × (t曝光传输 + t推理 + t后处理 + t通信)。例:0.4m/s 皮带、总延迟 50ms → 20mm 固定提前量,机械臂按提前量预约抓取窗口

端到端延迟预算表:

环节 典型值
曝光+传输 10~20ms
RGA+推理 25~35ms
后处理+通信 10ms
机械臂响应+到位 300~500ms

视觉全链路 <100ms,与臂动作流水化并发(臂抓 A 件时 NPU 已经在算 B 件),40 PPM 节拍从容。

与机械臂的通信

两种主流方式,按现场生态选:

TCP 自定义帧(自建方案、机械臂控制器支持 socket 时首选):

字段 字节 说明
帧头 2 0xAA55
长度 2 帧总长
命令 1 0x01 拍照请求 / 0x02 坐标下发 / 0x03 抓取完成
数据 N 工件ID、x、y、角度、类别(int16/int32 定点)
CRC16 2 校验

Modbus TCP(现场有 PLC、要求"标准协议"时):坐标写保持寄存器,触发/完成走线圈,兼容性最好但吞吐低。

不管哪种,握手时序必须严谨:视觉侧发的是"皮带坐标系+工件ID",抓没抓到由臂侧回报闭环,丢件、重抓、超时全部要有状态机分支。EtherCAT 一般不自己做,要实时总线上从站芯片/协议栈,中小项目用不上。

踩坑记录与注意事项

  1. 量化掉点:先查校准集(是不是产线实拍、覆盖够不够),再换量化算法,最后才上混合精度——按这个顺序排查,别一上来就改模型结构
  2. librknnrt 与 rknn-toolkit2 版本不匹配:上板报错先核版本,NPU 驱动固件、运行库、PC 工具三者对齐
  3. 多进程抢 NPU:每个进程各自 rknn_init 各绑核,没规划 core_mask 就互相踩;建议单进程多线程统一调度
  4. 运动模糊:曝光时间按皮带位移算——0.5m/s 下位移 <1mm 要求曝光 <2ms,全局快门相机 + 高亮频闪光源,卷帘快门直接出局
  5. 硬触发优于软触发:光电信号直连相机 IO 触发曝光,比"收到网络命令再拍"少一截抖动
  6. GigE 收流吃 CPU:中断亲和绑核、多 buffer 环形队列收流,250MB/s 级带宽 RK3588 没压力,处理不当丢帧有压力
  7. RGA 对齐限制:宽高 4/16 对齐这类约束查 RGA 文档,信手写个非对齐分辨率会静默出错或性能腰斩
  8. 散热:NPU 三核持续满载 + 产线环境温度,被动散热片撑不住,壳体内风扇或整机风道提前设计
  9. 模型 OTA:模型文件独立路径 + 版本号 + 校验 + 失败回滚,产线升级变砖是大事故
  10. 验收口径:视觉定位精度用"标准件重复抓取偏散布"验收,别只看标定残差——后者好看前者翻车的案例太多

成本核算

部件 量级
RK3588 板卡(工业封装版) ¥600~1200
工业相机(全局快门 GigE)+ 镜头 ¥1500~3000
光源(频闪控制器 + 条光/穹顶) ¥500~1500
光电/编码器/线缆辅料 ¥500
视觉单机合计 约 ¥3000~6000

对比工控机 + 入门 GPU 方案,体积、功耗、成本全线下降,且产线多工位复制就是"加一块板卡"的事。机械臂本体另计(四轴 SCARA/六轴按节拍选型)。

参考链接