1. 为什么工业项目会盯上 YOLOv5 v6.2 实例分割
1.1 矩形框在工业视觉里为什么“不够用”
很多做机器视觉的工程师对目标检测都很熟:输入一张图,模型给你一堆矩形框,每个框带一个类别和置信度。这套逻辑用在快速定位、计数、分类场景下没问题,但一旦涉及到精确测量、缺陷面积统计、复杂轮廓提取,矩形框就开始露怯了。
一个很典型的例子是铸件表面的气孔检测。气孔的形状不规则,有的细长,有的接近圆形,如果只用检测框卡,框里除了气孔还包含大量正常金属表面。你拿着这个框去算缺陷面积,结果一定是偏大的,而且不同角度的气孔形状一旦变化,框的尺寸也跟着抖,根本无法做稳定的量化判断。
有经验的工程师会想到传统方案:阈值分割、边缘提取、连通域分析。这些方法在某些固定打光、固定工位的场景下也能跑,但对光照变化、反光干扰非常敏感。一个产品换了个批次,表面颜色略有差异,分割参数就得重新调,维护成本很高。
实例分割不一样。它在目标检测的基础上,多输出了一层像素级的轮廓信息。模型不只告诉你“这个位置有一个缺陷”,还直接把缺陷的每一个像素点都标出来。你可以直接基于轮廓计算面积、周长、形状因子,甚至判断缺陷是否在允许的工艺范围之内。这种“语义+轮廓”的组合,正好命中工业视觉最刚需的痛点。
1.2 为什么偏偏是 v6.2 这个版本
YOLOv5 从 2020 年开源之后更新了很多版本,v6.2 是第一个把实例分割能力正式整合进官方仓库的版本。它发布的那个时间点,正好处于社区生态最成熟的阶段:训练脚本稳定、导出工具齐全、各种推理部署方案也都有人踩过坑。对工业项目这种“求稳求首版见效”的场景,选它比追最新版更合理。
也有人问,为什么不直接用 YOLOv8 或者更新的模型?我的回答很直接:v6.2 的资料覆盖面和第三方支持是最稳妥的。工业项目的周期通常很短,团队里可能有多个工程师在并行工作,选择一个自己熟、社区里有大量现成代码可参考的版本,比贪图模型性能上那百分之几的提升更重要。而且 v6.2 的实例分割性能到今天仍能覆盖大部分工业场景,后面再换 TensorRT 加速时,转 ONNX 的踩坑成本也低。
1.3 LabVIEW 在里面的真实角色
LabVIEW 在工业自动化里的地位不用多说,PLC 通信、数据库记录、人机界面、NI 视觉生态的调用都很顺手。但它的短板也非常明显:深度学习推理不是它的主场。让 LabVIEW 去写 Python 的训练脚本、做实例分割的推理计算,既不现实也没必要。
我在实际项目里采用的思路是“混合架构”:训练和推理交给 Python 完成,LabVIEW 负责图像采集、流程调度、结果显示和上位机通信。两者通过 Python 节点或者 TCP 长连接通信。这样 LabVIEW 做自己擅长的事,Python 做自己擅长的事,各自都不难受。
这套组合最初是我在一个模拟项目里验证的,后来逐步扩展成比较通用的流程。如果你的产线上已经有 Visual Studio 或者 C++ 团队,也可以把推理封装成 DLL 给 LabVIEW 调,但这套方案的前期开发量明显更大,而且没有 Python 方案灵活。所以,除非你后续要走非常极端的工业部署,否则先用 Python 混合方案把流程跑通,是少走弯路的第一步。
2. 整体架构设计:LabVIEW 与 Python 之间的通信方案
2.1 三种通信路线对比
把 Python 模型接进 LabVIEW,不是只有一种办法。我自己常用的是三种路线,按开发难度和部署形态做取舍:
| 方案 | 连接方式 | 优点 | 缺点 | 部署形态 |
|---|---|---|---|---|
| Python 节点 | LabVIEW 内置 Python 节点调用 .py 脚本 | 开发快,调试直观 | 进程内调用,模型加载会占住调用线程;LabVIEW 与 Python 位数必须一致 | 开发调试首选 |
| TCP 长连接 | Python 起一个 socket 服务,LabVIEW 发图像过去 | 双方进程隔离,LabVIEW 卡死不影响推理;可跨机器 | 需要设计协议、粘包处理、图像编码解码 | 现场部署常用 |
| DLL 封装 | Python 脚本打包成动态库,C 接口导出 | 无 Python 环境依赖,部署干净 | 打包复杂,OpenCV、Torch 等依赖容易冲突 | 正式交付备选 |
我见过很多工程师一上来就选 DLL 方案,结果被依赖地狱折磨得焦头烂额。真实项目里,首版验证阶段用 Python 节点完全够用,先把算法效果确认了,再决定要不要为了部署复杂度去封装 DLL。
2.2 我推荐的长连接协议设计
现场部署我倾向于 TCP 长连接。它不是最快的,但足够稳定,而且调试比共享内存直观。协议很简单:每次推理请求发一个 JPEG 压缩后的图像数据,前面加一个固定字节数的长度头,服务端解析图像、跑模型、返回 JSON 格式的结果。
用 JPEG 而不是原始 BMP 或者 YUV,是因为网络传输速率有限。对于工业相机常见的一两百万像素图像,JPEG 压缩能压掉大量无关信息,传输延迟可以控制在几毫秒到十几毫秒之间。如果后续对实时性要求更高,再切换到共享内存方案不迟。
JSON 只返回检测框、类别、置信度和关键面积数据。Mask 的逐像素数据如果要传回 LabVIEW,会比较大,我通常不直接传,而是让 Python 端把 mask 叠加在原始图上,生成一张可视化预览图,再把预览图传回去。这样 LabVIEW 端只管显示,不需要做繁重的矩阵运算。
2.3 流水线设计:采集、推理、显示三线并行
工业现场最怕的是“串行等待”:相机采一帧,推理一帧,显示一帧,循环跑起来帧率惨不忍睹。更合理的做法是把采集、推理、显示拆成独立线程。
LabVIEW 的队列机制很适合做这件事。采集线程负责拿图,把图像指针丢进队列;推理线程从队列取图,交给 Python 推理,把结果放进另一个队列;显示线程只负责在界面上刷新。这样即使某一帧推理超时,采集线程也不会停,下一帧可以继续往里送,至少保证画面不卡住。
我在模拟项目里测过,这种流水线能让吞吐量提升一倍以上。原理也不复杂:GPU 推理是异步的,排队请求越多,越能压满 GPU 利用率;同步串行模式下,GPU 每次都等着 CPU 送图,大部分时间都是空转的。
3. 环境搭建与模型训练实操
3.1 基础环境清单
整个环境分成两块:训练环境和部署环境。训练可以放在自己电脑上跑,部署放到工控机上。如果条件允许,最好训练环境用 Linux,部署环境用 Windows,因为现场工控机基本跑 Windows。
一个我现在比较推荐的组合:
- NVIDIA 显卡,显存 6GB 以上
- CUDA 11.6 或更高版本
- PyTorch 1.12 或 1.13
- Python 3.8 或 3.9,64 位
- LabVIEW 2020 及以上版本(Python 节点需要 64 位环境和对应 Python 版本)
特别注意一点:LabVIEW 的 Python 节点要求 LabVIEW 和 Python 都是 64 位,或者都是 32 位,混用会直接报错。很多人在这一步卡了很久,其实是位数不一致。
3.2 数据集标注:实例分割和检测标注逻辑完全不同
实例分割的数据标注比目标检测复杂得多。目标检测只需画矩形框,实例分割需要沿着目标边缘画多边形。标注一个复杂缺陷的时间,可能是框选的三倍以上。
我这边用的标注流程是:先用标注工具画出每个实例的多边形,保存成 JSON 格式,再写脚本转换成 YOLO 实例分割训练需要的 txt 格式。
YOLO 分割格式每一行是这样的:
class_id x1 y1 x2 y2 x3 y3 ...其中坐标都是归一化到 0~1 之间的小数,用多边形顶点坐标表示。一个标注文件里可以有多行,对应同一张图里的多个实例。
这里有个容易忽略的细节:多边形顶点的数量不固定,所以每个实例的坐标个数也不一样。训练脚本读取时会动态解析,标注时尽量让顶点贴合轮廓,但也没必要追求太多顶点,能表达轮廓特征就够了,否则数据文件会变得很大,训练速度也受影响。
3.3 训练参数与命名建议
在 v6.2 目录下,训练命令大概是这样的:
python train.py --data data.yaml --weights yolov5s-seg.pt --img 640 --batch-size 16 --epochs 100新手最常纠结的是选哪个模型规格。工业项目里面,我一般从 yolov5s-seg 开始。它速度快,显存占用小,如果精度不够再换 yolov5m-seg,而不是一上来就训练最大的模型。工业视觉场景通常目标数量不多、背景相对固定,小模型往往已经能跑出很不错的指标。
训练过程中主要看两个指标:mAP_0.5 和 mAP_0.5:0.95。前者更贴近实际可用性,后者更严格。如果你的成果 mAP_0.5 能到 0.95 以上,部署到现场一般就有很强的参考价值了。
训练完成后,模型权重默认存在 runs/segment/exp 目录下,里面有 best.pt 和 last.pt。工业部署只用 best.pt,last.pt 是中间产物,没必要用。
3.4 导出 ONNX 或 TensorRT 模型
如果只是想验证效果,用 PyTorch 直接加载 best.pt 就能跑。但到了现场部署,我更建议导出成 ONNX,再用 TensorRT 转成 engine。
v6.2 自带的导出脚本已经支持 ONNX:
python export.py --weights runs/segment/exp/weights/best.pt --include onnx --opset 12导出时注意 opset 版本不能太高,否则某些部署端不支持。转到 ONNX 以后,如果在工控机上装了 TensorRT,可以进一步转成 FP16 精度的 engine,推理速度能提升不少。不过这一步也容易踩坑,建议放到后面优化阶段再碰。
如果部署环境性能吃紧,还有一个折中方案:用 OpenVINO 转一次,它的安装和部署比 TensorRT 门槛低,兼容性好,对工业现场很友好。
4. LabVIEW 端集成:把模型接入上位机
4.1 Python 节点的使用要点
LabVIEW 内置的 Python 节点可以调用 Py 脚本里的函数。界面路径一般是“函数 -> 互连接口 -> Python -> Python 节点”。
使用它最大的坑就是环境路径。LabVIEW 需要指定 Python 可执行文件的位置,并且会从那个路径加载 Python 以及安装了 torch、opencv 等包的环境。如果电脑上有多个 Python 版本,必须明确指定带好依赖包的那个解释器。
其次,Python 节点的函数不能是脚本里的顶层逻辑,必须定义成 def 函数,LabVIEW 才能按参数调用。比如:
def predict(image: np.ndarray, conf_thres: float, iou_thres: float): # 执行推理,返回结果 return boxes, masks, labels调用时 LabVIEW 这边需要手动匹配输入输出的数据类型。图像数据通常传 U8 数组,返回结果建议用 Tuple 或者 List 接收,再解析。
4.2 图像数据传递:别把时间浪费在拷贝上
在 LabVIEW 里拿到相机图像后,传给 Python 的方式很影响性能。如果你用的是 PIL 或者 OpenCV 直接读取文件,那每帧都要做一次磁盘 I/O,工业场景下是不可接受的。
正确的做法是把图像数组直接传给 Python。LabVIEW 里可以先做一次 IMAQ ImageToArray,把图像转成二维 U8 数组,然后作为参数传给 Python 节点。Python 端拿到数组后转成 numpy 数组,注意通道顺序:OpenCV 默认是 BGR,YOLOv5 训练时用的也是 BGR,但个别颜色空间处理流程会不小心变成 RGB,导致检测效果明显变差,画面颜色看起来偏蓝红。
我建议在 Python 脚本里保持一个约定:所有输入图像统一走 BGR 通道顺序,这样模型行为最可预期。
4.3 模型加载与推理封装
模型加载不要放在每次调用时做,那样每帧都会重新载入权重,速度会非常慢。正规做法是一次性加载模型,之后进入常驻状态,LabVIEW 每次调用只是往推理函数里传图,推理函数内部通过全局缓存拿到模型对象。
一个简单的推理类结构如下:
class Segmentor: def __init__(self, weight_path): self.model = torch.hub.load('ultralytics/yolov5', 'custom', path=weight_path, force_reload=False) self.model.conf = 0.35 self.model.iou = 0.45 def predict(self, image): results = self.model(image) return resultsLabVIEW 第一次调用时先把模型加载一次,后续就不重复加载了。这个细节很多初稿项目会忽略,结果现场一跑,每帧推理时间忽高忽低,实际上就是反复加载模型造成的。
4.4 推理结果如何可视化与决策
实例分割的结果包含 mask。最简单的方式是让 Python 端生成一张叠加了彩色半透明轮廓的预览图,返回给 LabVIEW 显示。这样 LabVIEW 端不需要解析每一条 mask 的坐标,能省不少事。
如果要在 LabVIEW 端做具体决策,比如算缺陷面积、判断 OK/NG,我建议直接在 Python 端把面积、长度、位置这些数值算好,通过 JSON 返回。LabVIEW 拿到数值以后,只做阈值判断和逻辑联锁。
典型的返回值结构:
{ "count": 2, "defects": [ {"class_id": 0, "class_name": "scratch", "score": 0.96, "area_pixels": 356.2, "bbox": [12, 30, 80, 45]} ] }这样做的好处是直观、好查、好记录。工控机上做了 OK/NG 判断以后,直接把结果写到 PLC 或者数据库里,整个流程就闭环了。
5. 工业应用中的性能调优与问题排查
5.1 推理速度:从 480 毫秒到 30 毫秒的路径
在实际落地里,我遇到的最高频问题就是“算法准,但是太慢”。慢不一定全是 GPU 的问题,很多时候是模型大小、输入分辨率、推理引擎没选对。
先把几个关键方向的先后顺序理清楚:
- 第一,输入分辨率。YOLOv5 训练默认 640,但很多工业场景下目标占图面积大,用 640 足够;测试时改成 480 或者 512,推理帧率有明显提升。如果目标太小,再考虑 768,但会有明显的速度损失。
- 第二,模型规格。确认小模型是否够用,尽量用 yolov5s-seg 而不是 yolov5m-seg。工业场景中模型体积对长期维护影响很大。
- 第三,TensorRT FP16。如果 PyTorch 推理在 100 毫秒左右,转 TensorRT 后通常能压到 30 到 50 毫秒,这是收益最明显的一步。
- 第四,批处理。工业相机通常是单帧触发的,batch 设为 1 就够了,不要盲目加大 batch,否则显存占用和延迟都会有负面影响。
我见过一个典型的场景:一套表面缺陷检测系统,最初用 PyTorch 直接跑 768 分辨率,单帧耗时约 300 毫秒,产线节拍完全跟不上。后来把输入降到 640,模型换成 s 规格,转 TensorRT FP16,推理耗时降到 28 毫秒左右,整个产线瞬间就活过来了。
5.2 显存与内存管理:长期运行的隐患
工业现场要求的是 7x24 小时运行,内存泄漏、显存碎片化都是隐藏雷。推理程序如果在循环里不断创建新 tensor 而不释放,python 进程的内存会缓慢爬升,跑几天后系统越来越卡。
几个控制策略:数据集在推理开始前固定到模型内置常数,不要每次动态构建;每次推理结束显式调用 del 释放容器数据;如果使用 TensorRT engine,注意分配好的显存不要被 Python 的缓存机制占用。
另外,如果用 Python 节点方案,LabVIEW 与 Python 在同一个进程里,Python 侧的显存占用也会体现在进程内存里。如果发现内存涨得厉害,优先查一查是不是每帧都调用了类似 torch.load 之类的东西。
5.3 常见问题速查表
| 现象 | 原因 | 解决方法 |
|---|---|---|
| Python 节点调用时报错“类型不匹配” | LabVIEW 与 Python 位数不一致,或者 LabVIEW 传入数组类型不正确 | 统一装 64 位,数组类型改成 U8 |
| 图像颜色发蓝发红 | BGR/RGB 通道顺序被切换 | 检查 OpenCV 和 numpy 转换逻辑 |
| 检测效果差,目标漏检 | 置信度阈值太高或训练样本覆盖不足 | 调低 conf 阈值,补充现场样张 |
| 每帧推理时间忽高忽低 | 模型被重复加载或显存碎片化 | 一次性加载模型,检查内存释放 |
| 偶发卡死无响应 | TCP 通信阻塞,图像数据没有完整接收 | 设置超时重连,数据包加长度校验 |
| 工控机没装显卡驱动 | PyTorch 检测不到 GPU | 装好驱动后用 torch.cuda.is_available() 验证 |
这里我想特别强调一下图像数据粘包问题。TCP 传输图像数据时,如果发送端和接收端的分包逻辑写得不对,偶尔会合并或拆分数据包,导致图像无法解码。最简单有效的处理方式就是长度头协议,发送前先把图像字节长度写进一个固定长度的包头,接收端先读包头,再按长度读完整包。这个方案在绝大多数项目里都很靠谱。
5.4 光照变化引起的模型漂移
工业现场最隐蔽的问题是“模型在测试集上很好,一到现场就崩”。绝大多数原因是光照差异。实验室的照片、旧产线的照片、新产线的照片,在亮度、色温、反光程度上差别很大。
这里有两种补救思路。
第一种是数据层面:做训练集时,把不同时段、不同灯具状态下的图像都放进去。更狠一点的做法是直接做亮度扰动、对比度扰动、高斯噪声。YOLOv5 自带的马赛克、随机仿射增强已经覆盖了部分变化,但对工业场景的专项光照变化,还是得自己补数据。
第二种是工程层面:在相机端做曝光和增益自动调优,尽量让不同时段拍出来的图像在整体亮度上保持一致。如果现场不允许自动调曝光,至少要做一次白平衡。很多时候模型本身没问题,是输入图像漂移了。
实操上,我习惯在推理脚本里加一个非常轻量的预处理:转灰度、计算均值,如果整图均值偏离训练集的统计均值太多,就做一个线性亮度校正,再送进模型。这样可以对抗大部分亮度波动。
6. 踩坑几次之后,我沉淀下来的几点体会
在这套 YOLOv5 v6.2 实例分割与 LabVIEW 的混合架构里,我最大的体会是:不要迷信模型,也不要低估工程。模型再强,也挡不住现场光照的剑走偏锋;LabVIEW 再顺手,也弥补不了通信方案没设计好的缺陷。真正让实例分割在工业场景里表现“惊艳”的,是把模型能力、通信延迟、数据处理、显示调度这几件事协调好。
另一个很实际的建议是:哪怕时间再紧,也要把推理模块做成分离服务的形态。你可以先用 Python 节点验证效果,但目录结构、协议定义、返回码这些从一开始就按“未来要部署成独立服务”的标准来设计。否则后面一旦从开发机搬到工控机,你会发现到处都是硬编码路径和鸡蛋筐一样的依赖,想拆都拆不开。
如果你正在做类似的工业视觉项目,给自己留两天时间专门做性能测试和长期稳定性测试。第一天把模型跑通,第二天专门盯着显存、内存、帧率数据,往往比继续调模型参数更能发现问题。模型算对一张图只是开始,产线连续跑三天不出毛病才是真的验收标准。
最后分享一个小技巧:在部署目录里放一个简单的推理自检脚本,手动给一张打满标注的图像,跑一次推理,输出检测框数量和每个目标置信度。现场工程师遇到问题时,先跑这个脚本确认环境是否正常,再排查相机与通信,效率会高很多。这个细节帮我解决过好几轮既不是算法也不是硬件的“疑难杂症”。