端侧商品图像识别方案:搭建、部署与实测数据
识别结果不是法定计量,不能作为计价或法定计数的依据。
这套方案是干什么的
一台摄像头对着收银台传送带或货架正面。画面里的每件商品被框出、裁下、变成 512 维向量,再到已注册 SKU 的商品库里检索。收银台侧输出一份清单(SKU→数量,按 track id 聚合,同一件商品经过相机只计一次);货架侧输出货位状态(ok、empty、wrong_sku、unknown,按 planogram 位置聚合)。每帧一条 MQTT 消息带出这些结果。
门店上新品不用重训:给新 SKU 拍三到八张照片,发到管理端,商品库生成一个新的不可变版本。检测器是单类的,只回答「这里有一件商品」,不认识具体商品;嵌入器也不更新。
- 选型与部署:可部署方案页
- 开源地址:暂无。本包没有
intro.links.github,打包时代码在内部仓库。
新增 SKU 拍 3–8 张照片,不用重训
注册生成一个新的不可变商品库版本。注册图从 1 张加到 8 张,top-1 提高 28 个百分点(见性能与实测数据)。
检测在 NPU 或 GPU 上运行
RK3588 上 RKNN fp16 与 CPU 参考的框一致率 99.85%、p50 56.7 ms;Hailo-8 的 INT8 HEF p50 9.04 ms、一致率 94.77%;reComputer J40 系列(J4012)上 TensorRT fp16 p50 5.18 ms、一致率 99.91%。
自有摄像头与门店系统直接接入
任意 RTSP / USB 摄像头;收银机从 MQTT 读 SKU→数量,补货系统读缺货与错放货位,HTTP API 提供事件查询与商品库管理。
数据不出现场
摄像头、识别主机、管理端主机都在本地,broker 也跑在管理端容器里。随包 broker 是匿名明文的,上线前加账号与 TLS。
管理界面
上游包里有四张管理界面截图:事件列表、单事件逐框详情、商品库、收银台与货架看板。四张都由上游 web_demo 工具用合成夹具生成,里面的 SKU、相似度与事件是测试数据,只展示界面布局。
货架看板上的三种状态
货架判定在 reComputer RK3588 系列与 reComputer R2000 系列 + Hailo-8 上实测过。看板对每个货位给出三种状态之一:摆放正确、缺货、错放。



三种状态连续切换的过程:

收银台链路输出逐件商品的轨迹:

以上画面来自 edge-retail-recognition/evaluation/runs/2026-09-07-runtime-* 两轮真机运行,商品为项目自购实物,不来自任何数据集。
需要哪些硬件
每个站点四样:摄像头、识别主机、管理端主机,以及一台 x86_64 转换机(每个模型用一次)。
① 摄像头 — 任意一台装在收银台上方或正对货架的 RTSP / USB 摄像头,上面不跑任何程序。输入尺寸编译进产物:收银台 640²,货架 1280²。
② 识别主机 — 三个套餐的区别在这里:
| 主机 | 这套硬件上测过什么 | 什么时候选它 | |
|---|---|---|---|
![]() | reComputer Industrial R2035-12(Hailo-8,26 TOPS) 检测上 NPU,嵌入跑在四个 A76 核上 | 两段都测了:检测 9.04 ms,嵌入每裁剪 91.95 ms | 检测与嵌入都要有实测数据 |
![]() | reComputer RK3588-30 检测上 Rockchip NPU,嵌入在 CPU 或 NPU | 检测框一致率 99.85%,56.7 ms;嵌入上 NPU 后,20 SKU 货架回放从检出到结果发出 p50 924 ms | 已经在用 Rockchip 板卡,或相机对着货架。RK3576 工具链相同 |
![]() | reComputer Super J4012(Orin NX 16GB) 或 reComputer Super J3011(Orin Nano 8GB) TensorRT 路线,检测与嵌入都上 GPU | J4012:检测 5.18 ms,嵌入每裁剪 4.23 ms;2956 帧收银回放 0 丢帧 | 已经在用 Jetson,或要整条链路跑在一台机器上 |
数字都是该行硬件上的实测。
③ 管理端主机 — 你自己的 amd64 或 arm64 Linux 机器,装 Docker,不需要 GPU,每台识别设备都能访问到。它跑三个容器:注册/查询服务、管理界面、MQTT broker。两个容器镜像都在这台主机上从上游仓库构建,不从 registry 拉取。
④ 一台 x86_64 转换机 — rknn-toolkit2 与 Hailo Dataflow Compiler 都不在目标板上运行。每个模型转换一次,部署时不用重复。
选型排他边界
- 商品 SKU 规模与检索延迟:单机嵌入向量库若超过 200 个 SKU,端侧线性比对时延会增加。超过 500 SKU 的大型便利店建议在本地部署近似近邻检索(ANN)或上云比对。
- 无 NPU 运行商品嵌入:请勿在纯 CPU 模式下运行商品特征提取嵌入模型(单裁剪耗时超 900ms),务必确保编译上板至 NPU 或 TensorRT。
现场怎么部署
先定产物与机位,再装软件,软件部分每个套餐四步。
一、装硬件:先定产物,再定机位
输入尺寸编译进产物,运行时不能改。用 640² 的产物看货架会漏掉远端商品;换到 1280² 后 SKU-110K test 上的小目标 mAP50-95 从 17.49 升到 26.88。先确定是收银台还是货架,再把机位装到商品在画面里占比合适的位置。
Hailo-8 套餐的嵌入是每个框一次 CPU 推理。按 reComputer R2000 系列上实测的每裁剪 91.95 ms 算,五件商品的篮子约半秒;货架一帧按实测密度 157.6 个框算约 14 秒。这个套餐做货架部署要抽帧或按货位采样。 reComputer J40 系列(J4012)上嵌入在 GPU 上,每裁剪 4.23 ms。
二、装软件:四步
逐设备的操作步骤在可部署方案页,这里只列流程。三个套餐都是同样四步:
- 拉起管理端 — 注册服务、界面与 broker 跑在管理端主机上,部署时写入角色 token 表。没有默认 token,不允许匿名读,表为空时服务拒绝启动。
- 放好嵌入模型 — 把 DINOv2 ONNX 放到管理端挂载的位置,并把
RETAIL_EMBEDDER从占位实现切走。上游默认是fake,它把图片字节哈希成向量;GET /api/health不报告这一点,唯一的症状是注册正常但每次检索都返回错误的 SKU。 - 注册 SKU — 每个 3 到 8 张照片,至少正面、背面、侧面,两种光照。少于三张会被拒绝;同一个 sku_id 返回 409,除非显式
replace=true。 - 在板上转换并核对检测器 — 在 x86_64 主机上转换,把产物拷到板上,再与 CPU 参考做一次一致性核对。
每个套餐四步预留约 90 分钟,另加管理端主机上的容器构建时间。
确认嵌入模型已接上。 一次识别会在事件流里留下一条完整记录:货位、匹配到的 SKU、相似度与逐框坐标。fake 实现下相似度分布会明显异常。

有哪些接口
出口全在管理端主机上:1883 的 MQTT,以及 8089 与 8080 两个 HTTP 接口,都在本地网络内,除 broker 外都走同一个 token 校验。
- 收银机 / POS 订阅
retail/v1/events,读summary.items:这一帧的 SKU→数量映射,不需要解析逐框数组。 - 补货或货架稽核系统 从同一条消息里读
summary.empty_slots与summary.wrong_slots。 - 需要追溯某次判断的系统 读
detections[]:每个框带track_id、归一化bbox、sku_id、similarity、top2_margin、ocr块与fallback标志。
完整的主题与 payload
| 接口 / 端口 | 内容 | 说明 |
|---|---|---|
MQTT retail/v1/events,1883 | event_id、scene、timestamp、gallery.{version,sha256}、detections[]、summary.{items,empty_slots,wrong_slots}、models.{detector_sha256,embedder_sha256} | 一帧一条,不逐框发 |
HTTP /v1/gallery/skus,8089 | POST 提交 sku_id、显示名、别名、许可字段与 3–8 张图 → 生成一个新的不可变版本 | 同 sku_id 返回 409,除非 replace=true |
HTTP /v1/gallery、/v1/gallery/{version}、/v1/gallery/current/download、/v1/gallery/rollback/{version},8089 | 版本列表、单版本 manifest、带 SHA256SUMS 的 tar.gz,以及切换前重校目标 SHA 的回滚 | 回滚不产生新版本 |
HTTP /api/events、/api/events/{id}、/api/summary,8080 | 按场景/SKU/设备/时间过滤的事件列表、逐框详情、看板汇总 | 只读 API,同一个 token 校验 |
similarity 要连同同一条消息里的 gallery.version、gallery.sha256 与模型哈希一起看。 两个不同嵌入器产生的向量不可比,混用时表现为什么都识别不出来;每条消息都带上这四项。
鉴权是 Bearer <token>,角色 viewer < operator < admin。broker 随包配置为匿名明文,任何能访问 1883 端口的人都能写入伪造的识别事件。
性能与实测数据
检测,设备实测
| 设备 | 产物 | p50 / p95 | 与 CPU 参考的框一致率 |
|---|---|---|---|
| reComputer R2000 系列 + Hailo-8 | INT8 HEF,640² | 9.04 / 9.10 ms | 94.77%(200 张),94.68%(300 张) |
| reComputer RK3588 系列 | RKNN fp16,640² | 56.7 / 89.5 ms | 99.85% |
| reComputer RK3588 系列 | RKNN INT8,640² | 26.0 / 33.2 ms | 98.35% |
| reComputer RK3576 系列 | RKNN fp16,640²,双核 | 51.05 / 54.18 ms | 99.91% |
| reComputer J40 系列(J4012) | TensorRT fp16,640² | 5.18 / 5.28 ms | 99.91% |
| reComputer J30 系列(J3011) | TensorRT fp16,640² | 5.88 / 8.89 ms | 99.27%(收银台帧) |
条件:一致率按 IoU ≥ 0.5 与 CPU 参考比对;RK3588、RK3576 与 J4012 用同一批 50 张 SKU-110K 图,J3011 用 50 张收银台帧。Hailo 单流吞吐 110.4 fps(hailortcli benchmark 110.64 fps,纯硬件时间 8.21 ms,多出的 0.8 ms 是 Python vstream 往返)。reComputer R2000 系列上端到端(含 letterbox、输出拼接、解码与 NMS)p50 18.74 ms / p95 24.25 ms:对约 160 个框做纯 numpy 逐类 NMS,耗时超过推理本身。
复现:evaluation/runs/2026-09-06-det-hef/、evaluation/runs/2026-09-06-det-rk3588-radxa/、evaluation/runs/2026-09-07-embed-rknn-rk3576/、evaluation/runs/2026-09-07-jetson-trt/、evaluation/runs/2026-09-08-orin-nano-acceptance/
嵌入,设备实测
| 设备 | 变体 | 单裁剪 p50 / p95 | 相对 fp32 的检索代价 |
|---|---|---|---|
| reComputer R2000 系列(CPU,4 线程) | DINOv2-small,动态量化 INT8 | 91.95 / 105.98 ms | 全部 7 个实测档位上均在 0.65pp 以内 |
| reComputer R2000 系列(CPU,4 线程) | DINOv2-small,fp32 | 180.75 / 233.41 ms | 基线 |
| reComputer RK3588 系列(NPU) | DINOv2-small,RKNN fp16 | 48.68 / 49.86 ms | 21 个指标最大差 0.85pp |
| reComputer RK3576 系列(NPU,双核) | DINOv2-small,RKNN fp16 | 56.38 / 62.17 ms | 21 个指标最大差 0.36pp |
| reComputer J40 系列(J4012) | DINOv2-small,TensorRT fp16 | 4.23 / 4.69 ms | 21 个指标最大差 0.24pp |
| reComputer J30 系列(J3011) | DINOv2-small,TensorRT fp16 | 5.06 / 7.64 ms | 21 个指标最大差 0.21pp |
条件:21 个指标是 Grocery Store Dataset 4 个注册档位与 Products-10K 3 个注册档位上的 top-1、top-5 与开集 AUROC。CPU 上只量化权重几乎不损失检索精度,激活一起量化则损失明显。
复现:evaluation/runs/2026-09-06-embed-small/ §8、evaluation/runs/2026-09-07-embed-rknn/、evaluation/runs/2026-09-07-embed-rknn-rk3576/、evaluation/runs/2026-09-07-jetson-trt/、evaluation/runs/2026-09-08-orin-nano-acceptance/
整链路回放与货架裁剪,设备实测
| 设备 | 场景 | 结果 | 条件 |
|---|---|---|---|
| reComputer J40 系列(J4012) | 收银台回放 | 检出到结果发出 p50 22.3 ms / p95 42.8 ms;2956 帧 0 丢帧 | 录制视频回放,30 SKU 商品库,检测、嵌入、检索与 MQTT 上报在同一台设备上 |
| reComputer J30 系列(J3011) | 收银台回放 | 6726 帧(2.27 轮)0 丢帧,84 个事件全部发布 | 同一段录制视频与商品库 |
| reComputer RK3588 系列 | 货架回放 | 检出到结果发出 p50 924 ms / p95 1153 ms;704 张货位裁剪 top-1 541/704,与 CPU fp32 参考相同 | 20 SKU,检测与嵌入都用 RKNN fp16 上 NPU |
| reComputer RK3576 系列 | 货架裁剪 | 704 张 top-1 76.28%(CPU fp32 76.99%),同 SKU 一致率 99.29%;嵌入单裁剪 p50 61.0 ms | 同一批 704 张裁剪,检测与嵌入都用 RKNN fp16 |
复现:evaluation/runs/2026-09-07-jetson-trt/、evaluation/runs/2026-09-08-orin-nano-acceptance/、evaluation/runs/2026-09-08-rk3588-console-acceptance-020/、evaluation/runs/2026-09-08-rk3576-acceptance/
检索与检测准确率(非设备端)
| 配置 | 结果 | 数据集 |
|---|---|---|
| DINOv2-base,每 SKU 8 张注册图 | top-1 84.67% / top-5 96.66% | Grocery Store Dataset,81 类,fp32 |
| DINOv2-small,每 SKU 8 张注册图 | top-1 79.11% | 同上 |
| DINOv2-small,每 SKU 1 张注册图 | top-1 51.11% | 同上 |
| DINOv2-base,每 SKU 8 张,留出 SKU | top-1 78.92% | Products-10K 留出集,类别多得多 |
| 检测器,640² | mAP50-95 52.84,mAP50 88.26 | SKU-110K test |
| 检测器,1280² | mAP50-95 56.32 | SKU-110K test |
SKU-110K 的公开成绩是 mAP50-95 58.0(DenseDet,Cascade R-CNN + ResNeXt-101)到 58.7(arXiv 2007.11946)。640² 的 mAP50 为 88.26 而 mAP50-95 为 52.84:框能找到,定位精度不足。注册视角数对检索影响最大:同一模型、同一数据集上,每 SKU 1 张 51.11%,8 张 79.11%。
复现:evaluation/runs/(嵌入评测 evaluation/eval_embedder.py)
运行时与关键参数
| 设备 | 检测器 | 嵌入器 | 模型怎么到设备上 |
|---|---|---|---|
| reComputer RK3588 / RK3576 系列 | NPU 上的 .rknn,fp16 或 INT8 | CPU 上的 onnxruntime,或 NPU 上的 RKNN fp16 | 在 x86_64 主机上用 rknn-toolkit2 2.3.2 转换;toolkit 版本必须与板上 librknnrt.so 匹配 |
| reComputer R2000 系列 + Hailo-8 | NPU 上的 INT8 .hef | CPU 上动态量化 INT8 的 DINOv2-small | 在 x86_64 主机上用 Hailo Dataflow Compiler 编译后拷到板上 |
| reComputer J40 / J30 系列(Jetson Orin) | TensorRT fp16(GPU) | TensorRT fp16(GPU) | 部署时在设备上从 ONNX 构建 engine,J3011 上检测 427 s、嵌入 132 s |
检测器是单类 YOLOX-Tiny(640² 或 1280²);嵌入器是在电商商品图上用 ArcFace 微调的 DINOv2,base(348 MB fp32)或 small(23.5 MB INT8),每个裁剪输出一个 512 维向量;SKU 身份只在版本化商品库的 FAISS 余弦检索里。
- 输入尺寸:收银台 640²,货架 1280²,编译进产物,运行时不能改
- 注册图:每个 SKU 3–8 张,少于三张拒绝
RETAIL_EMBEDDER:上游默认fake(把图片字节哈希成向量),部署时必须切到 DINOv2 ONNX
已知退化
- Hailo-8 套餐上嵌入是每个框一次 CPU 推理:按每裁剪 91.95 ms,五件商品的篮子约半秒,货架一帧(157.6 个框)约 14 秒,货架部署要抽帧或按货位采样。
- DINOv2 嵌入器在 Hailo-8 上的两次量化都没有得到可用结果(其中一次所有图映射到同一个向量),所以 Hailo-8 套餐的嵌入留在 CPU。
- 检测与嵌入共用一块 GPU 时,reComputer J40 系列(J4012)进程内检测 p50 从单独运行的 5.18 ms 升到 8.76 ms,嵌入从 4.23 ms 升到 5.37 ms。
- 静态 QDQ INT8(激活也量化)检索掉 3.78–9.96 个百分点,不可用。
- rknn-toolkit2 与
librknnrt.so版本不匹配时可能照常加载并给出错误数值;每次转换后与 CPU 参考做一致性核对。 - 用 640² 产物看货架会漏掉远端商品;SKU-110K 小目标 mAP50-95 在 640² 为 17.49,1280² 为 26.88。
- 两个不同嵌入器的向量不可比,混用时什么都识别不出来。
- 设备端拉取商品库版本、校验、原子切换的运行时未实现,新注册的 SKU 不会自动下发到设备。
- OCR 重排与 VLM 兜底未实现:消息结构里已有
top2_margin、ocr、fallback块,代码未实现。
下一步
- 测收银台计数准确率与货架货位准确率,作为验收指标。
- 实现设备端拉取商品库版本、校验、原子切换的运行时,让新注册的 SKU 自动下发到设备。
数据与素材出处
本包不含任何模型权重与数据集图片。 下面的限制会被基于这些数据训练出来的模型继承,商用部署需要用可商用数据重训检测器与嵌入器。
| 资产 | 许可 / 使用范围 | 说明 |
|---|---|---|
| 检测器权重(训练于 SKU-110K) | Trax 许可:学术与非商用;第 (iii) 条禁止衍生作品 | use_scope: academic-only、redistributable: false |
| 嵌入器权重(微调于京东 Products-10K) | 非商用研究与教育 | use_scope: non-commercial、redistributable: false |
facebook/dinov2-base、facebook/dinov2-small 骨干 | Apache-2.0 | 骨干可商用,限制来自训练数据 |
| Grocery Store Dataset | MIT | 只用于检索评测;这一组里唯一可商用的数据集 |
| RPC、Unitail-OCR、GroZi-120 | CC BY-NC-SA 4.0 / 仅学术 / 许可需向数据集方确认 | 出现在上游评测计划里,属非商用范围 |
| 项目自身代码 | Apache-2.0 |
逐产物字段(license_id、use_scope、redistributable、source_revision、sha256)在上游的 model card 里;摘要在包内 gallery/ATTRIBUTION.md。可部署方案页上的架构图按本包的设备清单自绘。


