首页 / 资讯中心 / 文章详情

云边端协同的AI质检员:工业缺陷检测从训练到边缘推理落地

云边端协同的AI质检员:工业缺陷检测从训练到边缘推理落地 ★ FEATURED ARTICLE
简介这份PDF案例研究面向工业制造企业的质检负责人、AI方案架构师与数字化转型团队系统梳理百度智能云联合英特尔打造工业智能质检方案的落地路径。内容从人工质检效率、精度与成本痛点切入归纳工业质检在严苛碎片化标准、边缘部署、未知缺陷识别、少样本冷启动及高精度语义分割等方面的瓶颈并给出云边端一体化架构云端零代码训练平台负责数据标注、模型训练与迭代闭环边缘端借助英特尔酷睿处理器与OpenVINO工具套件完成视觉推理优化支持3C、化纤、纺织、汽车等场景及第三方光学、机械、电气硬件接入。文中还结合小仙炖燕窝原料杂质智能挑拣等案例说明良品率与产能提升效果。资源包共1个PDF文件约1.42MB已有118人学习适合用于方案选型、技术汇报与项目立项参考。1. 从「人眼挑 0.1mm 杂质」说起一套 AI 质检员到底由哪些件拼起来一条滋补食品原料挑拣线上老师傅盯着传送带把 0.1mm 的绒毛从原料里挑出来一天下来眼睛发花。换成一台装着工业相机和边缘计算盒的机台同样的活儿单机每小时能处理 700g 原料杂质拣出率超过 80%损耗压在 2% 以内。这就是现在常说的 AI 质检员——它不是一个买回来插电就能用的盒子而是「云端训练平台 边缘推理设备 现场光学机械」三件套咬合在一起的一整套工程。这套东西真正要解决的是三件事把缺陷判得准分割精度到亚毫米级、把节拍跟得上单图推理毫秒级、把新缺陷接得住少样本甚至零样本冷启动。它适合三类人被人工目检卡住产能的产线工程师、做机器视觉集成的方案商以及手上只有几十张良品图、不知道怎么冷启动的算法同学。2. 云边端三段式架构训练为什么必须放在云上、推理为什么必须压到边缘2.1 「全云」和「全边缘」都试过最后都翻了车最早一批做工业质检的项目思路很直接既然模型训练在机房、算力在机房那推理也放机房算了产线相机拍完照走厂内网络上云结果回传再驱动挑拣机构。这条路在演示环境里跑得很顺到了真实产线就原形毕露。某集成商的项目上线第一周赶上厂区网络做调整单张图的往返时延从 80ms 抖到 600ms挑拣机构的吸嘴已经到位了判定结果还没回来误拣率直接翻倍。工业质检的节拍是硬的包装线不会等你的网络。反过来的「全边缘」同样不成立。把所有训练都放到边缘盒子上第一是算力不够一个分割模型在边缘侧训一轮要按天算第二是数据孤岛边缘盒子只能看见自己这一台机台拍到的图新缺陷样本永远凑不齐第三是版本管理失控几十台设备各自为战出了问题连哪台跑的是哪个模型都说不清。所以实际能落地的形态只能是「云上训练闭环 边缘推理执行」云端负责数据沉淀、标注、训练、测试、模型管理和批量下发边缘负责采集、推理、控制执行机构并把难例和新样本按策略回传。这条链路里边缘是手云是脑中间靠模型版本和回流数据连起来。2.2 云端训练平台的四个必填模块真正决定这套方案好不好用的不是模型结构多花哨而是云端平台有没有把四个模块做扎实。少一个项目就会在某个环节卡住。模块缺了会怎样实际要盯住的点视觉对齐标注坐标和实物对不上模型学到的全是错位特征实物图与盲标图逐点对齐光学方案参数工作距离、镜头、光源角度随图存证智能预标注全靠人标一个新缺陷类别标一周用既有模型出预标注框人工只做增删改标注效率能差出好几倍训练与测试没有独立测试集模型好坏全凭感觉训练集/测试集按批次切分不能随机切否则同批次图片泄漏导致指标虚高模型管理与下发现场跑的是哪个版本说不清回滚没有后悔药每次训练产出带版本号、指标、训练集指纹下发支持按设备分组灰度「视觉对齐」这个模块最容易被忽略也最要命。产线上相机是斜装的、镜头有畸变、光源有角度你标注的时候看到的是一张校正后的盲标图模型推理时拿到的却是原始畸变图两边对不上模型学出来的边界永远是偏的。常见做法是在平台侧固定一套标定流程先用标定板算出相机内参和畸变系数把原始图校正成标准视图再在这个视图上做标注和推理标定参数跟着工位一起存档换镜头必须重新走一遍。2.3 边缘侧算力选型CPU 加集成显卡到底够不够工业现场选边缘算力绕不开三个约束机柜空间、散热条件、总体成本。很多方案第一反应是塞一张独立 GPU 加速卡进去性能确实好但功耗上去了、机箱要改、风扇噪音大、粉尘环境下故障率也高。这几年另一条路走通了用移动级 CPU 加集成显卡做推理靠指令集层面的低精度加速把性能顶上来。方案单模型时延912×608 分割FP16功耗量级环境适应性算控一体备注独立 GPU 加速卡最低高需要独立风道需额外控制器性能余量大成本与功耗压力大移动级 CPU 集成显卡约 3060ms低无风扇也可做可直接跑控制逻辑需做低精度量化专用 NPU 加速盒约 3050ms低好通常只做推理生态与算子支持受限于厂商表里的时延是区间值随模型结构和输入分辨率浮动。关键在于第三列到第五列CPU 方案最大的优势是「算控一体」——推理进程和运动控制、IO 驱动跑在同一台设备上省掉一套 PLC 或者运动控制器硬件方案一下子简化了。传统 Linux 计算盒要另配控制器才能驱动机台这一层省下来整机成本和故障点都少一截。提示算控一体的前提是推理不能把 CPU 吃满。挑拣机构的控制周期通常是毫秒级推理进程一旦抢占过多核心控制指令就会抖动表现出来是「有时候挑偏一点」非常难查。2.4 一条最小可跑通的数据闭环链路不讲概念直接给一条能跑起来的最小链路。目录结构、上传、训练、下发四步缺一步闭环就断了。# 工位侧目录约定按 日期/工位号/批次 三级存放回流时直接打包这一层 /data/edge/ └── 20240612/ └── st-03/ ├── good/ # 良品图用于良品建模与基线校准 ├── ng/ # 已确认缺陷图带 defect_type 子目录 └── suspect/ # 模型置信度落在灰区间的难例人工复核后再分流 # 回流只传 suspect 和按比例抽样的 good别把整条产线的原图全推上云 tar -czf st-03-20240612.tgz -C /data/edge/20240612 st-03这段脚本做了两件事一是把落盘规则固化下来良品、已确认缺陷、灰区难例分三个目录人工复核只需要看suspect工作量可控二是回流时只打包当天、单工位的数据避免一次性推几百 GB 上去把上行带宽打满。实际项目里good的抽样比例一般按 1%5% 走suspect全量传ng全量传但要去重。模型训练完成后下发同样要带校验# 下发前在边缘侧做一次 dry-run确认输入 shape、预处理参数与设备配置一致 edge-cli model verify --pkg cutter-v37.pkg \ --expect-input 1x3x608x912 \ --expect-mean 123.675,116.28,103.53 \ --expect-scale 58.395,57.12,57.375 edge-cli model deploy --pkg cutter-v37.pkg --group line-a --canary 2verify这一步看起来多余实际上省掉了大量现场排查。模型包里的预处理参数和边缘服务的配置一旦不一致模型不会报错只会安静地输出一堆垃圾结果这是最典型的黑匣子故障。deploy里的--canary 2表示先给两台设备灰度观察 24 小时的过杀率和漏检率再全量推出问题能立刻回滚到上一版本。3. 边缘推理提速模型导出、FP16/INT8 量化与算控一体的负载编排3.1 导出中间格式先把算子兼容性过一遍训练框架里跑得通不代表边缘侧跑得通。边缘推理工具链支持的算子集合是有限的尤其是自定义的分割头、可变形卷积、非极大值抑制这类算子很容易在转换阶段报「unsupported op」。规范做法是在训练阶段就用受限算子集来搭网络导出前先做一次静态图检查。# export_onnx.py —— 训练框架侧导出固定输入尺寸便于后续量化 import torch model build_seg_model(num_classes2) # 输出前景/背景两通道 model.load_state_dict(torch.load(ckpt/best.pth, map_locationcpu)) model.eval() # 必须切 eval否则 BN 层统计量会漂 dummy torch.randn(1, 3, 608, 912) # 与现场裁剪后的实际分辨率一致 torch.onnx.export( model, dummy, cutter.onnx, input_names[input], output_names[logits], opset_version12, # 12 对分割类算子支持较稳 do_constant_foldingTrue, # 常量折叠减掉推理期无用节点 dynamic_axesNone, # 固定 shape边缘侧才好做静态内存规划 )这段代码里三个参数值得单独说。model.eval()不能省训练态的 BatchNorm 用的是当前 batch 的均值方差导出后如果还带着训练态标记同一张图两次推理结果可能不一样现场表现就是「时好时坏」。opset_version12是比较保守的选择更高版本对某些插值、切片算子支持更激进反而容易在边缘工具链上翻车。dynamic_axesNone意味着输入尺寸写死好处是推理引擎可以提前分配好内存池、做算子融合代价是换分辨率必须重新导出——工业场景里相机和工位是固定的这个代价可以接受。导出之后别急着往下走先拿一批图对比原框架和中间格式的输出# check_parity.py —— 同一批图跑两边逐像素比最大绝对误差 import numpy as np, onnxruntime as ort, torch sess ort.InferenceSession(cutter.onnx, providers[CPUExecutionProvider]) for path in glob(calib_300/*.png)[:20]: x preprocess(path) # 与训练完全一致的预处理 ref model(torch.from_numpy(x)).detach().numpy() got sess.run(None, {input: x})[0] diff np.abs(ref - got).max() assert diff 1e-4, f{path} 输出偏差 {diff:.6f}转换有问题1e-4这个阈值是经验值FP32 下正常转换应该在1e-5量级。如果偏差到了1e-2甚至更大八成是预处理没对齐——BGR 和 RGB 反了、有没有除 255、mean/scale 的顺序是不是 BGR这三件事每年都在不同的项目上重复翻车。3.2 FP16 与 INT8 的取舍精度掉多少速度提多少低精度量化不是越狠越好。FP16 基本是「无损提速」模型权重和激活都从 32 位浮点压到 16 位精度损失通常在小数点后三位速度能提 1.52 倍几乎所有视觉模型都可以直接上。INT8 更激进权重和激活压到 8 位整数速度能再提一截但需要校准集来确定每一层的量化尺度遇到分割这类对边界敏感的任务边缘像素的精度损失会肉眼可见。实际项目中我的选择顺序是先上 FP16 打底把时延压到节拍要求以内只有 FP16 还不够时才考虑 INT8而且必须做逐类别的精度回归确认最小缺陷尺寸那一档的召回没掉。有个容易忽略的点INT8 的加速收益在 CPU 上主要靠 VNNI 这类指令在集成显卡上主要靠 DP4a两者走的路径不一样同一份量化模型在 CPU 和核显上的表现可能差出一倍选型时要分别实测不能只看一个数。3.3 量化校准与转换的实际命令转换用工具链里的模型优化器命令本身不长关键是参数别写错。# FP16先把能无损拿的收益拿到手 mo --input_model cutter.onnx \ --input_shape [1,3,608,912] \ --mean_values [123.675,116.28,103.53] \ --scale_values [58.395,57.12,57.375] \ --data_type FP16 \ --output_dir ir/fp16 # INT8需要一份校准集300 张左右覆盖不同光照和批次 mo --input_model cutter.onnx \ --input_shape [1,3,608,912] \ --mean_values [123.675,116.28,103.53] \ --scale_values [58.395,57.12,57.375] \ --data_type INT8 \ --calibration_dataset calib_300/ \ --output_dir ir/int8mean_values和scale_values必须和训练时用的归一化完全一致顺序也要对——很多框架默认 RGB工业相机出图往往是 BGR这里顺序写反了模型不会报错只会安静地掉精度。calib_300/里的图不能随便凑要覆盖早中晚三个光照时段、至少三个原料批次、良品和缺陷按真实比例混合否则量化尺度会偏表现为「实验室好好的现场一到下午就飘」。注意不同打包版本的工具入口可能叫mo、mo.py或者新的命令行封装参数名也略有差异。别照抄网上的老命令先跑一次--help确认当前版本的参数拼写。3.4 算控一体的线程编排与实测模型转好了只是第一步边缘盒子上还有采集、回传、控制三摊子事在抢 CPU。默认配置下所有进程自由竞争推理时延会像心电图一样抖表现出来就是挑拣机构「有时候准有时候偏一点」。# 给推理进程绑核把控制线程和回传线程赶到剩下的核上 taskset -c 0-3 ./infer_service --model ir/fp16/cutter.xml --threads 4 taskset -c 4-5 ./motion_ctrl taskset -c 6-7 ./uploader # 限制 OpenMP 线程数避免推理进程自己开一堆线程互相抢 export OMP_NUM_THREADS4 export KMP_BLOCKTIME1绑核的逻辑是把 4 个物理核留给推理并固定线程数控制进程单独占两个核回传再占两个。KMP_BLOCKTIME1让线程在空闲 1 毫秒后释放而不是默认的长等待能降低控制指令的响应抖动。压测时别只看平均时延要看 P99——挑拣机构不会因为平均 35ms 就满意它只会在碰到那 1% 的 200ms 时挑偏。# 连续跑 5000 张统计 P50 / P95 / P99 for i in $(seq 1 5000); do /usr/bin/time -f %e ./infer_once --img sample_$((i % 500)).png 2 lat.log done sort -n lat.log | awk {a[NR]$1} END{print P50,a[int(NR*0.5)],P95,a[int(NR*0.95)],P99,a[int(NR*0.99)]}这套跑法比较笨但能暴露出平均值掩盖的问题。我见过一个项目平均值 38ms、P99 冲到 240ms最后查出来是回传进程每 5 分钟做一次打包压缩压缩那一瞬间把磁盘 IO 打满推理进程读模型权重被阻塞。这种问题看平均值永远看不出来。4. 亚毫米缺陷分割大图切块、良品建模与增量迭代的三个落地招4.1 大图切块推理0.05mm 的精度是拼出来的工业相机的原图动辄四五千万像素整图 resize 到 912×608 送进网络一个 0.1mm 的杂质在特征图上只剩个位数像素卷积核根本抓不住。这是小目标漏检最常见的原因也是最容易解决的一个——不要 resize切块。# tile_infer.py —— 原分辨率切块推理重叠区按概率平均合并 import numpy as np TILE, OVERLAP 224, 32 STRIDE TILE - OVERLAP # 192保证边界目标至少被完整覆盖一次 def split(img): h, w img.shape[:2] ys list(range(0, max(h - TILE, 0) 1, STRIDE)) xs list(range(0, max(w - TILE, 0) 1, STRIDE)) if ys[-1] TILE h: ys.append(h - TILE) # 补齐右/下边缘避免丢最后一条 if xs[-1] TILE w: xs.append(w - TILE) return [(y, x, img[y:yTILE, x:xTILE]) for y in ys for x in xs] def merge(shape, tiles, probs): acc np.zeros(shape, dtypenp.float32) cnt np.zeros(shape, dtypenp.float32) for (y, x, _), p in zip(tiles, probs): acc[y:yTILE, x:xTILE] p # 重叠区累加 cnt[y:yTILE, x:xTILE] 1.0 # 记录覆盖次数 return acc / np.maximum(cnt, 1e-6) # 平均而不是取最大值这段代码的核心在最后一行合并用平均而不是取最大值。取最大值会让重叠区的置信度整体偏高同一个杂质在两张块里都被检出时合并后的响应比单块强阈值一卡就会出现「同一个缺陷被判成两个、位置还错开几像素」挑拣机构会重复动作。平均之后响应平滑坐标定位稳定。OVERLAP32是个折中值太小了跨越块边界的细长缺陷会被切断模型在每块里都只看到一段判不出完整形态太大了块数变多推理时间线性上涨。32 像素大约相当于检测精度的 58 倍够用。补齐右下边缘那两行不能省工业相机的视野里缺陷经常贴着画面边缘不补齐就会整条边漏检。切块之后还有一个后处理细节坐标要映射回原图而且要考虑机械臂的坐标系。挑拣路径规划拿到的坐标是图像坐标需要经过标定矩阵转成机械坐标这一步的误差往往比模型误差还大标定没做好模型再准也挑不准。4.2 良品建模只有良品图的时候怎么冷启动新产线开线时最尴尬一堆良品图一张缺陷图都没有。缺陷的严重程度和出现频次成反比真正要命的缺陷一年出不了几次等攒够样本再训练产线早就黄了。这时候走的是良品建模的路子——只用良品训练让模型学「正常长什么样」推理时凡是偏离这个分布的就算异常。具体做法是孪生模板思路把同一工位、同一光照条件下的良品图对齐后取中位数得到一张「标准模板」推理时把待检图和模板做差分差分响应用来定位疑似缺陷区域再叠加一个分割网络做形态判别。这条路的优势是冷启动快几十张良品图就能跑起来劣势是对光照和物料底色敏感早中晚的光线一变差分响应就整体偏移过杀率飙升。所以良品建模必须配合光学锁定相机曝光、增益、白平衡、光源亮度全部写进配置文件开机自检时校验任何一项偏离就报警而不是硬跑。这套东西听起来笨但它是良品建模能不能稳住的分水岭。4.3 增量迭代数据回流之后怎么用回流数据上来之后最常见的错误做法是「攒够一批就全量重训」。全量重训的问题在于不可控——新模型在老批次上的表现可能变差而你没有独立的回归集来发现这件事。规范做法是增量迭代 版本对比步骤做什么验收标准1从suspect里筛出模型判错且人工确认的样本每类至少 30 张才能进训练集2混入固定比例的存量数据通常 1:3防止灾难性遗忘3在老批次回归集上跑新模型老批次召回不下降超过 1 个点4灰度下发到 2 台设备跑 24 小时过杀率、漏检率都在阈值内5全量下发并保留上一版本出问题 5 分钟内能回滚第 3 步的回归集是整个流程的锚点必须从项目一开始就固定下来按批次分层抽样之后每次迭代都用同一份。很多人图省事每次重新切分结果指标来回跳根本判断不了模型是真变好还是测试集变简单了。版本管理的粒度也要想清楚。模型包至少包含四样东西权重、输入 shape、预处理参数、训练集指纹。前三个决定能不能跑第四个决定出了问题能不能追溯——现场说「这两天误判变多了」你打开平台一看训练集指纹变了就知道是新批次数据引进去的排查方向立刻明确。5. 避坑与排查五条从产线带回来的血泪记录5.1 成像与数据侧的三次翻车现象一换了原料批次模型一致率从 97% 掉到 78%过杀率翻了三倍。原因是新批次原料底色偏黄归一化之后的灰度分布整体右移良品建模的差分基线全偏了。模型本身没有错是输入分布变了。解决办法有两条短期把新批次的良品图回流后重算基线模板同时把白平衡从自动改成手动锁定长期在训练集里按批次分层采样每个批次至少留 5% 的良品图。现象二同一张图两次推理结果不一样边界差一两个像素。这个最玄学查了两天才定位到。模型导出时没切eval()模式BatchNorm 还在用推理时的 batch 统计量单张推理时 batch 里只有一张图统计量就是这张图自己的均值方差等于每张图用了不同的归一化。重新用model.eval()导出后问题消失。从那以后我每次导出后都强制跑一遍 parity 校验。现象三标注看着都对模型学出来的边界系统性偏移 3 个像素。原因是标注用的是校正后的盲标图推理用的是原始畸变图两边坐标系差了镜头畸变。解决办法是把标定参数固化进预处理链路边缘侧推理前先做 undistort和标注端用同一套参数。这件事的血泪教训是光学方案必须随图存证换了镜头不更新标定等于整个项目从头再来。5.2 部署与推理侧的两处暗坑现象四边缘盒子跑了一周推理时延从 35ms 缓慢涨到 210ms重启就恢复。典型的资源泄漏长相。查下来是回传线程把图写进本地缓存队列网络不好时队列只进不出慢慢把页缓存吃满推理进程读模型权重被 swap 到磁盘。改成独立进程 有界队列 满队列丢弃最旧数据同时给推理进程加了内存锁定问题消失。这个坑的隐蔽之处在于前几天完全正常等发现的时候现场已经在骂了。现象五模型下发显示成功推理服务起不来日志里只有一行「input shape mismatch」。下发流程只校验了模型包完整性没校验设备和模型包的输入配置是否匹配。有一台设备是早期版本配置里的输入尺寸还是 640×640新模型是 608×912服务启动直接失败。改进做法是把 shape、mean、scale 写进模型包的元数据边缘服务启动时逐项比对不一致就拒绝加载并明确报出哪一项不匹配而不是抛一个笼统的 shape mismatch。6. 上线前的最后一道关用一批「坏样本」把验收标准钉死方案跑到这一步模型指标好看、演示流程顺畅很容易就急着上线。我踩过的坑是实验室里 98% 的准确率到了产线第一个月漏检率超标三倍。原因是验收用的测试集太「干净」——都是挑出来的典型缺陷光照均匀、形态完整、位置居中。真实产线上缺陷可能只露出半个、可能贴着画面边缘、可能被反光盖住。所以上线前我固定会做一件事准备一批「坏样本」——不是坏图是刻意构造的极端场景。包括贴在视野边缘的缺陷、被强反光部分遮挡的缺陷、直径贴近检测下限的缺陷、以及连续十张全良品的空跑批次。用这批样本跑完整链路看的不是准确率是四件事验收项怎么测及格线边缘缺陷召回把已知缺陷平移到画面四边各 5 个位置与中心位置召回差不超过 3 个点遮挡鲁棒性在缺陷区域叠加 30%50% 面积的模拟反光召回下降不超过 10 个点最小尺寸检出用标定板生成贴近下限的合成缺陷至少 8/10 检出空跑误报连续 500 张纯良品误报不超过 2 张第三项用合成缺陷是有讲究的真实的小缺陷样本根本攒不够用图像合成把已知形态的缺陷按比例缩到检测下限附近、贴到真实良品的随机位置既能测出模型的下限又不会引入合成痕迹导致的假阴性。第四项的空跑批次最容易被忽略但它直接对应现场最敏感的那个指标——过杀率。挑拣机构误动作一次就要人工复核一次误报多了产线班组会直接把设备关掉。还有一个笨办法但很有效把整条链路的时间戳全打出来。从相机触发、图像采集、预处理、推理、后处理、指令下发到机构动作每一段打一个时间戳落进日志。上线后但凡出现「挑偏了」先把这段时间戳拉出来看是哪一段超时。绝大多数所谓的「模型不准」拆开看都是某一段链路抖了——图像传输慢了 80ms机构已经走过去了模型判得再准也没用。从那以后我每次交付工业质检项目不管模型指标多漂亮都强制把这份坏样本清单和全链路时间戳走一遍一项不过就不签字。这套流程救过我至少两次其中一次是边缘缺陷召回掉了 12 个点最后查明是切块补齐逻辑没覆盖右下角改了三行代码解决。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站