边缘端商品识别系统构建实战:基于YOLO系列的货架缺货检测方案从训练到部署完整记录
一、项目背景与需求定义
零售门店的货架缺货检测是库存管理中的核心痛点。传统方案依赖人工巡检,平均每店每日耗费2.5小时巡检工时,漏检率高达18%。将视觉检测能力部署至边缘设备,可在摄像头端实时判定缺货状态并上报云端,将巡检响应周期从小时级缩短至秒级。
本项目目标参数如下:
| 缺货检测准确率 | ≥95%(mAP@0.5) |
| 单帧推理延迟 | ≤120ms(ARM Cortex-A72平台) |
| 模型参数量 | ≤8MB(INT8量化后) |
| 连续运行功耗 | ≤5W |
| 日均上报频率 | 每15分钟一次状态快照 |
硬件选型为瑞芯微RK3568(4×A72@2.0GHz + NPU 0.8TOPS),配合2MP宽动态摄像头模组。NPU支持INT8卷积加速,但后处理算子需在CPU侧完成,这是后续部署调优的关键约束点。
二、数据采集与模型训练
数据集构建流程:
实际采集覆盖6家门店、42个货架位,共标注12,800帧图像。缺货样本占比约22%,在架56%,遮挡22%。数据增强策略模拟了货架灯光闪烁(亮度±30%)、顾客遮挡(随机矩形掩码)以及排列不齐(水平平移±5%)等真实场景干扰。
训练配置选用YOLOv8s(11.2M参数),输入分辨率640×640,batch=32,学习率cosine衰减从0.01至0.001,共300 epoch。验证集mAP@0.5达到0.963,测试集0.951,满足部署指标。
# 训练启动脚本关键配置
import ultralytics
model = ultralytics.YOLO("yolov8s.pt")
result = model.train(
data="shelf_empty.yaml", # 数据集描述文件
epochs=300,
imgsz=640,
batch=32,
lr0=0.01,
lrf=0.001,
augment=True,
device="0", # GPU训练
workers=8,
project="shelf_detect",
name="yolov8s_v1",
)
if result is None:
raise RuntimeError("训练过程异常终止,请检查数据集路径与GPU状态")
三、模型量化与边缘部署
ONNX模型经onnxruntime验证无误后,通过RKNN-Toolkit2完成INT8量化。量化校准数据取验证集的200帧,覆盖不同光照条件。
量化后模型体积从28.3MB压缩至7.8MB,mAP@0.5从0.951降至0.931(下降2.1%),仍在可接受范围内。板端单帧推理耗时98ms,满足≤120ms指标。
// RKNN模型加载与推理核心代码
#include "rknn_api.h"
int run_shelf_detect(const char *model_path, const uint8_t *frame_buf,
int width, int height, rknn_output *outputs)
{
rknn_context ctx = 0;
int ret = rknn_init(&ctx, model_path, NULL, 0, NULL);
if (ret != RKNN_SUCC) {
fprintf(stderr, " rknn_init 失败, 错误码: %d\\n", ret);
return -1;
}
rknn_input input = {
.index = 0,
.buf = frame_buf,
.size = width * height * 3,
.pass_through = 0,
.type = RKNN_TENSOR_UINT8,
.fmt = RKNN_TENSOR_NHWC,
};
ret = rknn_inputs_set(ctx, 1, &input);
if (ret != RKNN_SUCC) {
fprintf(stderr, " rknn_inputs_set 失败, 错误码: %d\\n", ret);
rknn_destroy(ctx);
return -2;
}
ret = rknn_run(ctx, NULL);
if (ret != RKNN_SUCC) {
fprintf(stderr, " rknn_run 失败, 错误码: %d\\n", ret);
rknn_destroy(ctx);
return -3;
}
ret = rknn_outputs_get(ctx, 3, outputs, NULL);
if (ret != RKNN_SUCC) {
fprintf(stderr, " rknn_outputs_get 失败\\n");
rknn_destroy(ctx);
return -4;
}
rknn_destroy(ctx);
return 0;
}
四、后处理优化与系统集成
NPU输出的原始检测结果需经NMS(非极大值抑制)过滤后方可用。原始NMS在A72单核耗时约15ms,通过以下优化降至4ms:
// 单类NMS快速实现(整数IoU近似)
static int fast_nms(box_t *boxes, int num, float iou_thresh, box_t *out, int max_out)
{
if (num <= 0 || boxes == NULL || out == NULL) {
fprintf(stderr, " fast_nms 输入参数异常\\n");
return 0;
}
// 按置信度降序排序(部分排序,取前max_out个即可)
int effective = (num < max_out) ? num : max_out;
for (int i = 0; i < effective; i++) {
int best = i;
for (int j = i + 1; j < num; j++) {
if (boxes[j].score > boxes[best].score) best = j;
}
box_t tmp = boxes[i]; boxes[i] = boxes[best]; boxes[best] = tmp;
}
int keep = 0;
uint8_t suppressed[256] = {0}; // 标记被抑制的候选框
for (int i = 0; i < effective; i++) {
if (suppressed[i]) continue;
out[keep++] = boxes[i];
for (int j = i + 1; j < effective; j++) {
// 整数IoU近似: (交集面积 * 256) / (并集面积)
int inter = box_intersection_q8(boxes[i], boxes[j]);
int union_ = box_area_q8(boxes[i]) + box_area_q8(boxes[j]) – inter;
if (union_ == 0) continue;
int iou_q8 = (inter << 8) / union_;
int thresh_q8 = (int)(iou_thresh * 256);
if (iou_q8 > thresh_q8) suppressed[j] = 1;
}
}
return keep;
}
系统整体集成采用进程间MQ通信架构:推理进程每15分钟提取一帧,执行检测后将缺货层数写入共享内存,上报进程读取后通过MQTT发布至云端。设备端平均功耗4.2W(CPU降频至1.5GHz时),满足≤5W约束。
五、总结
本方案从数据标注、模型训练、INT8量化到板端部署,完整覆盖了边缘端货架缺货检测系统的构建流程。关键数据汇总:
| 训练 | mAP@0.5 | 0.951 |
| 量化 | 精度损失 | 2.1% |
| 模型体积 | INT8后 | 7.8MB |
| 板端推理 | 单帧延迟 | 98ms |
| NMS后处理 | 耗时 | 4ms |
| 系统功耗 | 日均 | 4.2W |
量化精度损失控制在2%以内,推理延迟优于120ms目标,说明YOLOv8s + RK3568 NPU的组合在零售货架场景具备工程可行性。后续改进方向包括:引入时序帧融合以降低遮挡误判率,以及尝试YOLOv8n进一步压缩模型体积以适配更低算力平台。
网硕互联帮助中心

评论前必须登录!
注册