本科阶段协助研究生准备华为杯时,我写了一套芯片表面缺陷检测程序。需求不只是拿一张图片跑出检测框,而是把模型放到树莓派一类边缘设备上,接入工业相机连续推理,发现异常时给出本地提示,并把结果送到数据大屏。

归档中的推理部署、海康相机接入、告警、数据库上传和大屏数据链路都由我完成。项目最终识别裂纹、边缘斑点、薄膜缺失、薄膜残留、氧化、凹坑、划痕和斑点八类问题。

我先把目标定成一条完整检测链路

flowchart LR
    A["海康工业相机"] --> B["树莓派采集进程"]
    B --> C["YOLOv11 边缘推理"]
    C --> D["检测框、类别与置信度"]
    D --> E["本地预览 / 录像 / 蜂鸣器"]
    D --> F["异常采样与 MySQL 上报"]
    F --> G["趋势、分布与最近记录大屏"]

这条链路里,模型推理只占中间一段。前面要处理相机 SDK、取帧和断线重连,后面要处理中文类别映射、告警节流、录像、数据库异常以及展示端需要的数据格式。

为树莓派准备模型和运行环境

项目保留了 PyTorch、TorchScript 和 NCNN 几种模型形式。开发机上使用 .pt 权重最方便,部署到树莓派时则优先使用 NCNN,减少对完整 PyTorch 环境的依赖,也更适合 ARM CPU 推理。

推理入口基于 Ultralytics YOLO 和 OpenCV,统一支持单张图片、图片文件夹、视频、普通摄像头、RTSP 和海康相机。这样做方便我先在电脑上复现问题,再切到树莓派与工业相机,不必为每一种输入写一套后处理。

运行环境里的数据库、蜂鸣器和海康相机模块都按可选能力加载。某个模块缺失时,程序给出警告并关闭对应功能,模型推理仍然可以继续。这一点对边缘设备很重要,驱动或外设偶尔不可用时,不能让整个检测程序在 import 阶段退出。

海康相机接入和实时推理

相机层单独封装初始化、状态和取帧接口。主循环只关心能否拿到一帧图像,不直接操作厂商 SDK。出现断线时可以触发重新连接,退出时则统一释放相机和 OpenCV 窗口。

每帧进入 YOLOv11 后,我按置信度阈值过滤结果,找到当前帧置信度最高的缺陷,完成英文类别到中文名称的映射。同时在原图上绘制检测框和标签,预览画面再叠加帧率、目标数量等运行信息。

程序可以保存单帧截图,也可以把带检测框的画面写成视频。蜂鸣器用于区分正常、异常和操作反馈,但它被放在可选模块里,不会因为音频设备初始化失败影响检测。

为什么不能每一帧都写数据库

视频中同一处缺陷可能连续出现几百帧。如果每帧都插入一条记录,数据库看到的不是“发生了多少次缺陷”,而是“这个缺陷在镜头里停留了多久”,大屏统计会被完全带偏。

我在检测端加入异常帧计数和采样策略,只在达到间隔时上传代表记录。上传内容收敛成四个字段:检测时间、是否异常、故障类型和置信度。数据库使用连接池,插入失败时可以回滚或重试,不让一次网络抖动中断相机推理。

这张最小数据表也是检测端与数据大屏之间的边界。大屏不需要知道 YOLO 的 Tensor 结构,只要按时间读取统一记录,就可以计算异常率、类别分布、置信度和近期趋势。为了在没有真实产线连续数据时验证展示效果,我还写了模拟数据生成脚本,按设定异常率和类别权重补充历史记录。

展示层和检测进程因此可以分开部署。树莓派只负责采集、推理和上报,数据大屏从数据库读取结果。即使大屏重启,边缘端仍能继续检测;相机端更新模型时,也不需要修改展示端字段。

从 Demo 到部署,问题发生在哪里

真正放到树莓派上以后,问题通常不在 model.predict() 这一行。

相机 SDK 与系统架构要匹配,OpenCV 预览不能抢占太多资源,模型格式要在速度和兼容性之间选择,录像又会增加磁盘写入压力。数据库上报也必须节流,否则网络等待会反过来拖慢取帧。

代码演进过程中,异常采样间隔曾在不同版本里出现过不同数值。更合适的做法是把阈值、上传间隔、模型路径和数据库连接全部放进配置文件或环境变量,而不是留在脚本常量里。归档中的数据库连接信息也不应该继续硬编码,正式部署必须改成环境变量并轮换凭据。

这次项目留下的经验

这个项目让我把计算机视觉从训练结果推进到了边缘应用。YOLOv11 负责识别,但工业相机、树莓派运行环境、告警、数据库和数据大屏共同决定系统是否可用。

我后来做其他视觉项目时也沿用了这套思路:先定义输入与输出协议,再隔离硬件驱动和模型逻辑;检测结果先标准化,展示端只依赖稳定字段;可能失败的外部能力允许降级。这样模型可以换,设备可以换,整条链路不用推倒重来。