
简介基于YOLOv5与UNet的工业仪表读数识别系统面向深度学习方向的毕业设计、课程设计和期末大作业专注于解决工业场景中仪表读数依赖人工巡检、效率较低的问题。项目通过YOLOv5实现仪表与指针的快速检测利用UNet结构对表盘区域和刻度进行精细分割核心代码与UI界面可支撑实时读数识别。压缩包共103个文件包含52个Python源码、23个pyc编译文件及模型权重、UI界面、Dockerfile、shell脚本、配置文件和说明文档整体大小约20.51MB目录结构较清晰便于按模块复现与二次开发。资源还提供了容器化部署文件、训练/推理脚本和示例结果图可帮助理解从数据准备、模型训练到界面展示的完整项目链路对学习目标检测与图像分割结合应用很有参考价值。目前已有34人学习/下载适合需要快速搭建仪表识别系统或借鉴项目架构的开发者。1. 基于yolov5_unet的工业仪表读数识别系统为什么检测加分割才是完整答案很多同行第一次看到“基于yolov5_unet的工业仪表读数识别系统”这个标题第一反应是yolov5做检测Unet做分割两个网络串在一起是不是有点重但真去现场看过老旧指针式仪表的人会明白只做目标检测根本不够——检测框能告诉你表盘在哪却读不出指针角度和刻度值。工业仪表读数识别系统的核心矛盾在于表盘是半刚体指针会旋转刻度分布不均匀单纯分类或检测无法输出连续数值。这个方案的思路是让yolov5负责“找到表盘并定位关键区域”让Unet负责“把指针和刻度从复杂背景里抠出来”再通过几何换算得到读数。它适合三类人做工业自动化改造的集成商、搞设备巡检算法的工程师、以及想在树莓派或RK3568这类边缘设备上跑表计识别的研究者。下面按我实际落地这个方案的经验从选型到踩坑一步步拆开讲。2. 先想清楚再动手yolov5和Unet各自解决什么问题2.1 为什么检测选yolov5而不选更轻量的模型在工业仪表场景里yolov5几乎是默认起点。它本身不是为仪表识别设计的但其网络结构图决定了它在小目标检测上有优势——表盘在整幅巡检画面里往往只占很小面积而yolov5的PANet特征融合能保留浅层细节。实际测试中yolov5s在GTX 1660上推理一张640×640的图大约需要6到8毫秒检测精度足够框住表盘外沿和数码管区域。有人问为什么不用YOLOv8或更小的nano版本我的经验是如果后续要量化到RK3568或树莓派4Byolov5s的C3结构在INT8量化后精度损失比v8更可控。还有一个现实原因——yolov5训练自己的数据集的教程和工具链最成熟标注格式、预训练权重、超参数调整都有大量现成经验项目周期紧的时候这些积累能省很多时间。2.2 Unet在仪表识别里的真正角色不是分割整张图很多人误以为Unet要把整个表盘区域都做像素级分割这是最常见的认知偏差。在工业仪表读数识别系统里Unet只做两件事分割指针区域分割刻度线区域。指针是细长结构刻度线是短线段两者在Unet网络结构图的编码器-解码器对称设计下都能保留较好边缘信息尤其是跳跃连接skip connection能把低层纹理直接传给解码器这对细线结构尤其重要。如果只靠yolov5的检测框来推断指针角度光照变化和阴影会让框的尺寸抖动读数误差放大到肉眼可见。而Unet输出的掩码可以直接提取指针中轴线和刻度线的质心坐标再通过最小二乘拟合得到角度值。这套“检测定位分割提取几何计算”的流程才是这个标题背后的完整技术栈。2.3 两个网络是串联还是并联——数据流向设计常见做法是严格串联yolov5先跑一遍全图输出表盘检测框然后把框内的图像裁剪出来缩放后送入UnetUnet输出指针和刻度的二值掩码最后在掩码上做骨架提取和角度计算。为什么不并联因为并联需要两个网络都处理全图算力翻倍但收益为零——刻度特征只有表盘区域内才有意义。串联的代价是延迟叠加yolov5s的8毫秒加上Unet的15到20毫秒单帧总耗时要到30毫秒左右在巡检机器人场景中完全够用。如果现场有多个表盘yolov5会输出多个检测框每个框独立走一遍Unet逻辑上天然支持多表同时识别。这里有个容易翻车的点yolov5检测框如果包含大量背景Unet的分割质量会明显下降所以检测框的置信度阈值要调高一些我一般设到0.45以上。3. 把数据集做成能用的样子标注策略与合成数据补足3.1 真实样本不够时用合成数据撑起指针角度分布工业仪表的真实照片往往只有几百张而且指针角度分布极不均匀——大部分时间表盘指针在中间区域极限位置的照片很少。如果直接拿这些数据训练Unet模型会对中间角度过拟合两端角度分割效果极差。我常用的做法是收集表盘模板图用程序生成不同指针角度的合成样本。具体思路是剥离表盘背景把指针作为独立图层用仿射变换旋转到任意角度叠加高斯噪声、模糊和光照变化。合成数据能覆盖0到360度全角度范围配合真实照片做微调Unet的分割精度能提升12到15个百分点。合成比例我一般控制在7:3合成数据过多会让模型对真实纹理不敏感。3.2 标注格式转换的四个边界坑yolov5训练自己的数据集要求YOLO格式的TXT标注Unet要求PNG掩码图这两套标注经常需要来回转。常见的转换流程是先用LabelImg标检测框导出YOLO格式再用Labelme标分割掩码导出JSON最后写成脚本把JSON转成PNG灰度图。这里有四个坑第一YOLO格式的归一化坐标是相对于整张图的裁剪表盘区域后必须重新计算坐标第二Unet的掩码图必须是单通道灰度图但很多标注工具导出的是三通道RGB直接训练会报通道错误第三标注时指针的厚度要保持一致如果有的标了2像素有的标了4像素分割结果会不稳定第四刻度线标注要把主刻度和次刻度分开否则后面计算量程时会乱。建议写一个统一的转换脚本输入是标注JSON和原图输出YOLO Txt和PNG掩码所有路径和类别映射写死在配置文件里。3.3 数据增强的取舍——哪些增强会毁掉仪表特征工业仪表识别里数据增强不是越多越好。yolov5训练时自带马赛克增强这对检测框是有帮助的但对Unet分割来说马赛克增强会破坏表盘的圆形结构和刻度分布我用过一次之后分割精度掉了8个点。Unet训练时我只用三类增强随机亮度扰动、随机对比度扰动、轻微随机旋转。亮度扰动模拟早晚光线变化对比度扰动应对逆光场景旋转增强让小角度偏移不至于让模型过度敏感。不要用随机裁剪和随机遮挡仪表盘边缘在裁剪后容易被误判为指针遮挡会让刻度线断掉。如果你用的是合成数据增强力度可以更小因为合成数据本身已经带了光照变化。4. 模型训练的必调参数从yolov5超参数到Unet损失函数4.1 yolov5超参数的几个关键旋钮yolov5超参数调整是门玄学但工业表盘场景下有几个参数值得优先调。第一个是img_size表盘在画面中是小目标训练分辨率设在640而不是默认的416检测精度能提升不少如果算力紧张可以训练时用640、推理时用800实际效果反而更好。第二个是anchor表盘的宽高比接近1:1默认anchor里有大量长条形不适合用k-means重新聚类anchor我聚类出来的结果通常是0.8到1.2的宽高比区间。第三个是mosaic参数前面提到马赛克对检测有用但不要全程开启建议训练到第100个epoch之后关闭mosaic让模型在真实分布上收敛。还有一个容易被忽略的是workers和batch_size的配合在Windows上workers设8可能会崩设4比较稳。4.2 Unet的损失函数Dice Loss加边界惩罚项Unet在仪表分割场景最怕的是指针断裂和刻度粘连。标准的BCE loss在这种细线结构上表现很差尤其当指针只有几个像素宽时BCE会倾向于把所有前景像素都预测为背景。我比较常用的组合是Dice Loss加二元交叉熵权重配比1:1Dice Loss解决前景背景不平衡BCE保留像素级精度。如果追求更稳的分割边缘可以在损失函数里加一个边界惩罚项——对靠近指针边缘的像素加大权重。实现上不难用OpenCV的Canny提取标注掩码的边缘生成一个权重图把这个权重图乘到BCE loss上就行。这个改动让指针的连续性提升了明显一个档次刻度线也不容易断裂了。Unet模型改进方向上我试过把编码器换成ResNet34精度提升有限但显存占用多了两倍对于工业部署不划算。4.3 训练命令与迭代节奏中断续训和早停策略# yolov5训练命令示例hyp.scratch.yaml为超参数文件 python train.py --data instrument.yaml --weights yolov5s.pt --img 640 \ --batch 16 --epochs 200 --hyp hyp.scratch.yaml --patience 30 # Unet训练命令示例自定义train_unet.py python train_unet.py --data_dir ./datasets/instrument --mask_dir ./masks \ --backbone resnet34 --loss dice_bce --lr 1e-4 --epochs 300 \ --batch_size 8 --augment brightness contrast rotation第一条命令里--patience 30的含义是验证集指标连续30个epoch不提升就自动停止这能防止过拟合并节省时间。第二条命令里--loss dice_bce指定了损失函数类型--lr 1e-4是Unet训练比较稳的初始学习率如果loss下降太慢可以前50个epoch用3e-4之后切回1e-4做精细收敛。两个网络都建议定期保存checkpointyolov5会在runs/train/exp*/weights/下自动存last.pt和best.ptUnet的脚本里也应该每20个epoch保存一次。训中断电是常态尤其是用远程服务器训练时所以断点续训能力必须验证过再跑长任务。5. 部署与推理把模型压进边缘设备并跑通读数换算5.1 从PyTorch到ONNX再到RKNN/ONNX Runtime的转换链路工业现场最常见的部署目标是树莓派4B和RK3568这样的设备。树莓派4B部署yolov5可以用ONNX Runtime配合ARM的CPU推理一张640的图大约450毫秒加上Unet需要额外700毫秒单帧总耗时超过1秒了只能做定时巡检。RK3568这类带NPU的板子必须走RKNN工具链具体步骤是先把yolov5的PyTorch权重导出为ONNX再用rknn-toolkit2转成RKNN格式。这里有两个坑必须提前规避。第一个坑是yolov5的导出命令python export.py --include onnx默认输出带NMS的版本NPU不支持NMS必须加上--simplify并在后处理代码里手工实现NMS。第二个坑是RK3568上Unet的输入尺寸必须是32的倍数我用的是256×256指针分割精度虽然比320低一些但速度能维持在50毫秒以内。# RKNN转换的简化流程示例 from rknn.api import RKNN rknn RKNN() rknn.config(target_platformrk3568, quantized_dtypew8a8) rknn.load_onnx(modelyolov5s_instrument.onnx) rknn.build(do_quantizationTrue, datasetdataset_quant.txt) rknn.export_rknn(yolov5s_instrument.rknn)这段代码里quantized_dtypew8a8表示权重和激活都做8位量化dataset_quant.txt里每一行放一张有代表性的真实图片路径量化校准集至少要放20张不同光照条件下的表盘图否则量化后的精度损失会大到不可接受。量化后精度校验建议在开发板上直接跑而不是在PC上模拟——模拟结果和实机结果经常不一致以实机为准。如果量化后精度掉太多优先检查校准集是否符合现场分布其次检查模型里是否有不支持的算子比如上采样用Upsample而不是Interpolate能减少算子转换问题。5.2 指针读数的几何换算骨架提取加上角度到数值映射分割出指针掩码后读数换算这一步藏着整个系统最容易被忽视的误差来源。我的做法分三步走第一步用OpenCV的cv2.threshold把Unet输出的概率图转成二值图阈值取0.5第二步用cv2.erode去掉孤立的噪声点再用cv2.HoughLinesP提取指向表心的线段拟合出指针中轴线第三步以表盘圆心为原点计算指针直线与表盘零刻度线之间的夹角套用仪表的量程线性映射公式得到读数。# 角度换算核心逻辑的关键片段 import cv2 import numpy as np mask (unet_output 0.5).astype(np.uint8) * 255 lines cv2.HoughLinesP(mask, 1, np.pi/180, threshold50) # 用最长线段近似指针方向 angle np.degrees(np.arctan2(lines[0][0][1] - center_y, lines[0][0][0] - center_x)) # 消除角度跳变真实仪表指针始终在连续区域 if angle 0: angle 360 value (angle - zero_angle) / (max_angle - zero_angle) * range_max这段逻辑里zero_angle和max_angle必须通过标注数据标定不能拍脑袋设0和90。我在现场踩过一个坑某台压力表的零刻度线并不是垂直向上的而是偏了8度用默认起点导致所有读数整体偏低。建议部署前在每台表上做一个标定环节——人工记录三个已知读数对应的指针角度用最小二乘拟合角度到数值的线性关系看起来多花半小时实际可以省掉后续大量调试。5.3 树莓派4B部署yolov5的实测参数参考树莓派4B部署yolov5时如果暂不量化、直接跑FP32的ONNX模型输入分辨率调成320反而比640更划算——表盘检测框在320下已经足够稳定速度却从450毫秒降到200毫秒。Unet的输入可以进一步降到192×192指针分割仍然不会断。如果你要同时跑两个网络建议用多进程而不是多线程Python的GIL会让多线程推理在树莓派上几乎没提升。模型推理进程和结果上报进程分开用队列通信这样单个进程卡住不会拖垮整个系统。内存方面树莓派4B的4GB版本跑这两个模型还剩700MB左右可用空间如果用8GB版本就更从容。此外树莓派上的功率限制会导致精度推理时CPU降频建议在/boot/config.txt里把arm_freq固定为1750实测可以避免推理速度波动20%以上的问题。6. 避坑手册我在这个系统上翻过的5个具体问题6.1 现象Unet分割的指针在边缘处反复出现断裂分割结果中指针根部总能识别到但接近表盘边缘的部分经常断裂。原因排查后发现是训练数据里指针和背景对比度不够尤其是在表盘边缘有装饰性刻线的情况下Unet把指针和刻线混淆了。解决方法是两个方向同时推进一是在标注时把指针掩码整体加粗2像素二是调整损失函数里的边界权重让靠近指针端点的像素在这次迭代里被更大力地拉动。加粗掩码后再次训练断裂现象明显减少。后来我在推理侧也加了后处理——如果检测到指针线段长度小于表盘半径的三分之一就把方向延长到表盘边缘。6.2 现象yolov5在同一块表盘附近输出多个重复检测框重复框通常不是模型问题而是NMS阈值设置不合理。我在部署时把yolov5的NMS的IOU阈值从默认的0.45调低到0.3重复框明显减少。还有一个更深层的原因如果采集的训练照片里表盘有明显反光模型会把高光区域误认为独立目标它的特征和表盘太像了。解决思路是扩充数据集时优先加入多角度、多光照的照片而不是依赖后处理去重。NMS阈值调低后要复查是否会把相邻的两块仪表当成一个目标我遇到过一个场景是两块表盘安装得很近阈值调太低后成了漏检。6.3 现象表盘玻璃反光导致指针分割出现大面积误检这个坑几乎无法完全避免只能缓解。反光区域在图像上的亮度梯度变化大Unet很容易把玻璃反射的倒影当成指针。我在推理链路上加了一个预处理步骤先把图像从RGB转成HSV把V通道亮度值超过95百分位数的区域做个标记这一区域送进Unet之前先做局部直方图均衡化让高光区域的对比度回落到正常范围。实测这个操作能把反光导致的误分割面积减少大约60%。如果反光太严重就要考虑在硬件侧加偏振片这是治本方案但成本要向甲方说明。6.4 现象离线验证精度高部署到现场后读数波动大离线测试集和现场环境的差异是导致这个现象的主因。在现场部署时我习惯先连续采集2小时真实环境照片不标注直接让模型跑一遍把预测结果按时间轴拉出来看读数稳定性。如果读数在短时间内大幅跳跃优先排查是不是相机自动曝光导致的图像亮度突变——工业相机的自动曝光调节频率有时达到几百毫秒一次和在离线环境测试时的固定曝光差异很大。解决方法是锁定相机曝光时间和白平衡如果场景光照确实会变化那就开一个定时器每5分钟重新计算一次曝光基准。另外一个常见原因是yolov5检测框的抖动——前一帧框住了表盘下一帧框偏了零点几度Unet看到的内容就不同导致读数波动。处理办法是给检测框加一个时序滤波用滑动窗口平均检测框的中心和尺寸实测效果显著。6.5 现象RK3568量化后Unet输出全黑或全白的掩码这是部署过程中最让人懊恼的问题因为它在转换环节不报错而是在板子上运行时刻才暴露。这条问题我在转换手法上吃过亏Unet中使用的ReLU算子在某些版本的RKNN工具链里量化后数值偏移特别是当上述算子紧跟着BN层时输出会变成单一值。解决方法是训练完成后把BN层折叠进卷积层再导出ONNX用torch.quantization.fuse_modules处理折叠后量化误差明显小得多。如果折叠太麻烦还有一个临时替代方案把Unet换成不带BN层的精简版结构分割精度略降但量化后不容易炸。7. 进阶用法同一套模型迁移到数码管读数识别与读数校验回读数码管表计在工业现场占了近一半比例而迁移到这一类时不用重训两个网络。yolov5的检测头不变把Unet的分割目标从指针改成数码管每个数字段的亮暗区域Unet输出的是7段掩码然后对每一段做开闭运算判断亮灭状态映射到0到9的数字。整个迁移只需要改标注和换一下合成数据的生成脚本。我在迁移时发现一个好处数码管的7段结构比指针更规则Unet的Dice Loss在训练时收敛更快通常50个epoch就能到0.95以上的准确率而且光照变化的鲁棒性比指针模式好。另一个值得投入的方向是关于读数校验回读系统输出一个数值同时还输出了检测框的置信度、指针分割的前景像素占比、角度拟合的残差。这三个指标结合起来判断当前读数可不可信——如果角度拟合残差超过3度或者指针前景占比低于阈值说明这次读数大概率不可靠系统把这次结果标记为待人工复核而不是继续上报数据。这个习惯是我在被甲方要求处理一次误报后才养成的比任何折线图告警都实际。最后一个建议整套系统跑通后用一台不参与训练的仪表做长期稳定性测试至少连续运行一周记录每天的读数和环境温度。你会发现温度对仪表的机械零点漂移影响比想象中大得多这类慢变量的处理方法是定期用人工读数做一次校准回归把漂移量找补回来。做工业视觉项目最值钱的经验往往不在模型结构里而在这些肉眼不易察觉的细枝末节里。希望这套方案能帮你在自己的表盘上少走几条弯路。本文还有配套的精品资源点击获取