☰
熔炼炉烟尘AI视觉识别:三分类方案与边缘部署实战
2026/10/2 11:08:16 网站建设 项目流程

1. 炉前烟尘识别到底在解决什么问题

第一次接触这个需求是在一个铜熔炼车间,炉长拉着我看炉口,说“你帮我看看现在这个烟算浓还是淡”。我当时愣了一下——这玩意儿肉眼判断全凭经验,老师傅说浓就浓,说淡就淡,换个人来看结论可能完全相反。更麻烦的是,环保数据要留痕,加料时机要卡点,全靠人盯炉口,一个班下来眼睛都花了,还容易漏看。

熔炼炉烟尘AI视觉识别要干的事情,说白了就是用摄像头替代人眼,把炉前烟尘状态实时分成浓烟、淡烟、无烟三类,输出结构化信号给中控系统。它解决的核心痛点有三个:第一,人工判断主观性强、标准不统一;第二,炉前环境高温高尘,人不能长时间靠近;第三,烟尘状态直接关联加料节奏、燃烧效率和排放合规,需要秒级响应。

这套方案适合谁参考?做工业视觉落地的算法工程师、冶金/铸造行业的自动化改造负责人、以及计算机视觉方向想找真实场景练手的学生。我后面会从方案选型、数据采集、模型训练、部署调优到现场踩坑,完整拆一遍,代码和参数都给到能直接抄的程度。

注意:工业现场和实验室跑demo完全是两码事。实验室里准确率99%的模型,到炉口可能连60%都保不住,原因后面会细说。

2. 整体方案设计与技术选型思路

2.1 为什么是“三分类”而不是“浓度回归”

很多人第一反应是做个回归模型,输出一个0到1的烟雾浓度值,看起来更精细。我一开始也这么想,但实际跑下来发现两个致命问题。

第一,标注一致性无法保证。让十个老师傅给同一段视频打浓度分,分数能差出0.3。你说0.6算浓还是淡?没有客观基准,回归目标的噪声太大,模型学出来的东西不可信。

第二,业务侧根本不需要连续值。中控系统要的是决策信号:浓烟了要不要加大引风?淡烟了要不要准备加料?无烟了是不是可以进入下一工序?三个离散状态足够驱动逻辑,反而更稳定、更好验证。

所以三分类是业务需求倒推出来的最优解,不是技术上的偷懒。分类边界清晰,标注可仲裁,模型输出可以直接映射到PLC动作。

2.2 视觉方案选型:可见光还是红外

炉前环境有两个极端:一是强光,炉火本身是巨大光源,普通相机容易过曝;二是烟尘遮挡,可见光在浓烟下穿透力有限。

我对比过三种方案:

方案优点缺点适用场景
工业可见光相机成本低、色彩信息丰富受炉火强光干扰大、浓烟下失效无烟/淡烟区分
红外热成像穿透烟尘能力强、不受可见光影响成本高、分辨率低、纹理信息少浓烟/无烟区分
可见光+红外融合互补性强、鲁棒性最好标定复杂、数据同步要求高高要求场景

最终我选了工业可见光相机+窄带滤光片的方案。原因很实际:预算有限,而且三分类里“淡烟”这个中间态主要靠纹理和透明度判断,红外图像在这块信息不足。滤光片选的是850nm窄带,把炉火里最强的可见光波段压下去,同时保留烟尘的散射特征。

相机安装位置也有讲究。我试过正对炉口、侧45度、顶部俯视三个角度,最后定在侧上方30度、距离炉口约3.5米。正对容易被喷溅物打坏镜头,俯视看不到烟的扩散形态,侧上方既能拍到烟柱轮廓,又能避开大部分飞溅。

2.3 模型选型:轻量级CNN还是Transformer

2024年了,Transformer在视觉领域确实火,但工业部署要考虑推理延迟和硬件成本。炉前监测要求单帧推理小于50ms,才能做到真正的实时。

我最终选了MobileNetV3-Small做主干,输入分辨率224x224,参数量约2.5M,在Jetson Xavier NX上单帧推理约18ms。为什么不用ResNet50?因为现场部署的是边缘设备,功耗和散热都受限,大模型跑起来风扇狂转,车间温度又高,稳定性反而下降。

Transformer方案我也试过ViT-Tiny,准确率比MobileNetV3高约1.5个百分点,但推理时间翻倍到40ms左右,而且对训练数据量要求更高。在工业场景里,稳定性和延迟比那1.5个点重要得多。

实操心得:不要盲目追新架构。工业落地的第一原则是“够用就好”,模型小、推理快、容易维护,比刷榜重要。

3. 数据采集与标注的实操细节

3.1 现场采集:怎么拍到有用的数据

数据采集这一步,我踩的坑最多。第一次去现场,架好相机录了三天,回来一看,80%的帧都是无效的——要么炉门关着,要么画面里全是火花,要么曝光过度一片白。

后来总结出一套采集规范:

  • 采集时段覆盖完整生产周期:加料、熔化、扒渣、出料,每个阶段都要有。浓烟主要出现在加料和扒渣阶段,无烟出现在出料后,淡烟是过渡态。
  • 每类至少采集2000帧有效图像,且要覆盖不同班次、不同炉料、不同天气(车间光照受外界影响)。我最终攒了约12000帧原始数据,三分类大致均衡。
  • 同步记录工艺参数:加料时间、引风频率、炉温。这些后面用来做数据校验,比如“引风开到80%还出现浓烟”就是异常样本,要单独分析。
  • 固定相机参数:曝光、增益、白平衡全部手动锁定。自动模式在不同光照下会漂移,导致模型学到的是相机参数变化而不是烟尘变化。

采集脚本我用的是OpenCV,每500ms抓一帧,同时写时间戳:

import cv2 import time import os cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 手动锁定曝光,具体值现场调 cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25) cap.set(cv2.CAP_PROP_EXPOSURE, -6) save_dir = "raw_data" os.makedirs(save_dir, exist_ok=True) count = 0 while True: ret, frame = cap.read() if not ret: break ts = time.strftime("%Y%m%d_%H%M%S") cv2.imwrite(f"{save_dir}/{ts}_{count:06d}.jpg", frame) count += 1 time.sleep(0.5)

3.2 标注规范:让三个人标出一样的结果

标注是三分类项目里最容易被低估的环节。我一开始让两个实习生标,结果一致性只有70%左右。后来制定了明确的标注规则,一致性提到92%以上。

规则核心是看三个维度:烟柱的不透明度、扩散范围、边缘清晰度。

  • 浓烟:烟柱完全不透明,看不到炉口背景,扩散范围超过炉口宽度2倍,边缘模糊。
  • 淡烟:烟柱半透明,能隐约看到炉口轮廓,扩散范围在炉口宽度1到2倍之间,边缘有一定清晰度。
  • 无烟:画面中无明显烟柱,或仅有极淡的热气流扰动,炉口背景清晰可见。

边界样本处理原则:拿不准的归到相邻更严重的类别。比如浓烟和淡烟之间拿不准,标浓烟。因为业务上漏报浓烟的代价比误报大得多。

标注工具我用的是LabelImg改的分类版本,每张图只打一个类别标签。12000张图,两个人标了约一周,交叉校验了两轮。

注意:标注时一定要把图像分成“训练集”和“验证集”两个文件夹分别标,避免同一段视频的相邻帧同时出现在训练和验证里,否则验证准确率会虚高。

4. 模型训练与调优的完整流程

4.1 数据增强:模拟现场的各种干扰

工业现场的图像变化比公开数据集复杂得多。我用了一套组合增强策略,在训练时动态应用:

import albumentations as A from albumentations.pytorch import ToTensorV2 train_transform = A.Compose([ A.RandomResizedCrop(224, 224, scale=(0.7, 1.0)), A.HorizontalFlip(p=0.5), A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, p=0.7), A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=20, val_shift_limit=20, p=0.5), A.GaussNoise(var_limit=(10.0, 50.0), p=0.3), A.MotionBlur(blur_limit=5, p=0.2), A.CoarseDropout(max_holes=8, max_height=16, max_width=16, p=0.3), A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ToTensorV2() ])

这里有几个针对性的设计:

  • RandomResizedCrop:模拟相机焦距变化和烟尘远近差异。
  • RandomBrightnessContrast:模拟炉火强弱和曝光波动。
  • MotionBlur:模拟烟尘快速流动时的运动模糊。
  • CoarseDropout:模拟飞溅物短暂遮挡镜头。

验证集只用Resize和Normalize,不做增强,保证评估结果反映真实分布。

4.2 训练配置与类别权重

三分类虽然均衡,但实际生产中浓烟样本偏少(因为浓烟时段短),所以我在损失函数里加了类别权重。权重按类别频率的倒数计算:

import torch import torch.nn as nn from torchvision.models import mobilenet_v3_small # 假设训练集三类样本数为 [4000, 4500, 3500] class_counts = [4000, 4500, 3500] total = sum(class_counts) weights = [total / (3 * c) for c in class_counts] # 约 [1.0, 0.89, 1.14] class_weights = torch.tensor(weights, dtype=torch.float32).to(device) model = mobilenet_v3_small(pretrained=True) model.classifier[3] = nn.Linear(1024, 3) model = model.to(device) criterion = nn.CrossEntropyLoss(weight=class_weights, label_smoothing=0.1) optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30)

训练超参:batch size 64,epoch 30,初始学习率1e-3,余弦退火。在RTX 3060上大约训练40分钟。

关键技巧:label_smoothing=0.1。工业数据标注难免有噪声,标签平滑能防止模型对边界样本过度自信,实测验证集准确率提升约2个百分点。

4.3 训练结果与混淆矩阵分析

最终模型在验证集上的表现:

类别精确率召回率F1
浓烟0.960.940.95
淡烟0.910.930.92
无烟0.970.960.96

整体准确率约94%。混淆矩阵显示主要错误集中在淡烟和浓烟的边界,以及淡烟和无烟的边界。这符合预期,因为这两个边界本身就是连续过渡的。

我针对边界样本做了第二轮专项采集,在炉口加装了辅助光源,让淡烟的纹理更清晰,补充了约1500张边界样本重新训练,淡烟F1提升到0.94。

实操心得:不要只看整体准确率。工业场景里,漏报浓烟的代价最大,所以浓烟的召回率要单独盯。我最终把浓烟召回率调到0.97以上才敢上线。

5. 边缘部署与实时推理优化

5.1 模型导出与量化

训练完的PyTorch模型要部署到Jetson Xavier NX上,直接跑FP32推理延迟约35ms,不够理想。我做了两步优化:

第一步,导出ONNX:

dummy_input = torch.randn(1, 3, 224, 224).to(device) torch.onnx.export( model, dummy_input, "smoke_cls.onnx", input_names=["input"], output_names=["output"], dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}}, opset_version=11 )

第二步,用TensorRT做FP16量化:

trtexec --onnx=smoke_cls.onnx \ --saveEngine=smoke_cls_fp16.engine \ --fp16 \ --workspace=1024 \ --minShapes=input:1x3x224x224 \ --optShapes=input:1x3x224x224 \ --maxShapes=input:1x3x224x224

量化后单帧推理降到12ms,加上预处理和后处理,端到端约20ms,满足50ms的实时要求。精度损失不到0.5个百分点,完全可接受。

5.2 推理服务与平滑策略

单帧分类结果会有抖动,比如浓烟和淡烟之间来回跳。直接输出给PLC会导致执行机构频繁动作。我加了一个滑动窗口投票:

from collections import deque import numpy as np class SmoothPredictor: def __init__(self, window_size=5, threshold=0.6): self.window = deque(maxlen=window_size) self.threshold = threshold def update(self, probs): self.window.append(probs) avg = np.mean(self.window, axis=0) pred = int(np.argmax(avg)) conf = float(avg[pred]) if conf < self.threshold: return -1 # 不确定,保持上一状态 return pred

窗口大小5,对应约100ms的平滑。阈值0.6,低于这个值就输出“保持”,避免频繁切换。实测下来,状态切换频率从每秒3到4次降到每分钟2到3次,中控侧反馈很稳。

5.3 与PLC的通信对接

推理服务输出三分类结果后,通过Modbus TCP写给PLC。我用的是pymodbus:

from pymodbus.client import ModbusTcpClient client = ModbusTcpClient('192.168.1.10', port=502) client.connect() # 寄存器地址0x0001,0=无烟,1=淡烟,2=浓烟 def write_state(state): client.write_register(0x0001, state)

PLC侧根据状态执行逻辑:浓烟触发引风加大,淡烟准备加料,无烟进入下一工序。整个链路从图像采集到PLC动作,端到端延迟约80ms。

注意:Modbus寄存器地址和数据类型一定要和PLC工程师对齐,我因为地址偏移搞错了一次,导致引风逻辑反了,炉子差点出问题。上线前务必做联调测试。

6. 现场常见问题与排查速查

6.1 模型在现场掉点怎么办

这是最典型的问题。实验室94%,现场可能只有80%出头。原因通常有三个:

第一,光照变化。车间白天和晚上光照差异大,虽然锁了相机参数,但外界光还是会漏进来。解决办法是在炉口加装遮光罩,同时训练时加入更强的亮度增强。

第二,镜头污染。炉前飞溅物和粉尘会逐渐糊住镜头,图像变模糊。我加了每4小时自动吹扫的气路,镜头前加保护玻璃,每周更换一次。同时训练时加入高斯模糊增强,让模型对轻度污染有鲁棒性。

第三,炉料变化。换了一种炉料后,烟的颜色和形态可能不同。解决办法是持续采集新数据,做增量训练。我一般每两周收集一批新样本,微调最后一层分类头。

6.2 常见问题速查表

现象可能原因排查方法解决措施
准确率骤降镜头污染查看原始图像清晰度吹扫/更换保护玻璃
浓烟漏报曝光过度检查图像是否过曝调低曝光或加滤光片
状态频繁切换平滑窗口太小观察输出日志增大窗口或提高阈值
推理延迟高未量化检查推理引擎转TensorRT FP16
通信失败地址错误用Modbus调试工具核对寄存器地址
夜间误报多光照不足对比白天夜间图像补光或增强训练数据

6.3 独家避坑技巧

技巧一:保留“不确定”状态。三分类之外,我实际部署时加了一个隐式的第四态——置信度低于阈值时输出“保持”。这比强行分类要安全得多,避免模型在边界上乱跳。

技巧二:用工艺参数做交叉校验。如果引风频率已经开到90%还报浓烟,大概率是模型误判,可以触发人工复核。这个逻辑帮我过滤了约30%的误报。

技巧三:模型版本管理要严格。每次更新模型都要记录训练数据版本、超参、验证指标。我有一次直接覆盖了线上模型,结果新模型在某个班次表现很差,想回滚都找不到旧版本。后来用MLflow做版本管理,再没出过这个问题。

技巧四:现场调试带个便携屏。炉前环境嘈杂,抱着笔记本蹲在炉口调参很不现实。我后来带了一个带HDMI的便携屏和无线键鼠,在安全区域远程调试,效率高很多。

7. 后续可扩展的方向

这套三分类框架跑通后,其实可以往几个方向延伸。一是烟尘浓度分级细化,比如把浓烟再分成“浓”和“极浓”,对应不同的引风档位。二是多炉口联动,一个车间多个炉子,统一调度引风资源。三是结合炉温预测,烟尘状态和炉内反应阶段强相关,可以用来辅助判断熔化进度。

我目前在做的是把分类结果和能耗数据关联分析,看看能不能找到“烟尘状态-引风能耗-炉温”的最优控制曲线。初步数据看,淡烟阶段适当降低引风频率,能耗能降约8%,同时不影响排放指标。这个方向还在验证,有结果再单独写一篇。

代码和配置我都整理在了一个仓库里,核心就是MobileNetV3加TensorRT量化加滑动窗口平滑,没有花哨的东西,但现场跑了半年多,稳定性没问题。工业视觉落地,简单可靠比先进重要,这是我最大的体会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询