做钢轨表面缺陷检测这个方向快一年了,从最开始对着采集回来的图像一头雾水,到后来把一套基于YOLOv5的训练流程稳定跑起来并应用到现场测试,中间确实把该踩的坑都踩了一遍。这个项目说白了就三件事:整理一套干净可用的钢轨表面缺陷数据集、把YOLOv5模型训练到能用的精度、再把模型部署到检测设备上实时跑起来。如果你是做工业视觉、铁路工务巡检,或者刚接触缺陷检测的小白,这篇内容应该能帮你少走不少弯路。整个流程不复杂,但细节非常多——数据怎么标、参数怎么定、小目标缺陷检测不上怎么办,每一个环节都会直接影响最终效果。
1. 钢轨缺陷检测的难点与YOLOv5选型逻辑
1.1 钢轨表面缺陷为什么难检测
钢轨表面缺陷检测和普通工业零件表面检测有个很大的区别:缺陷种类多,而且每一类的形态都不太一样。常见的有轨面裂纹、剥离掉块、擦伤、锈蚀、压溃这几大类。裂纹是细长曲折的暗色线条,剥离是块状凹坑,擦伤是沿轨向延伸的条状痕迹,锈蚀则是大面积的表面颜色和纹理异常。同类缺陷在不同磨损阶段、不同轨型上长得也不一样,这对检测算法的泛化能力要求很高。
第二个难点是背景干扰严重。钢轨表面不是干净的,长期运行后有油污、水渍、锈斑、焊痕、光带变化,甚至灰尘和水雾。这些干扰在图像上的表现和真实缺陷非常接近。我训练初期就踩过这个坑,模型把好几种反光水渍全识别成了裂纹,误检率高得没法看。后面专门花了两周时间整理负样本,才把误检压下来。
第三个难点是缺陷尺寸小。一条宽度只有1到3毫米的疲劳裂纹,在某些相机视野下可能只占几个像素。你可以算一下:假设相机视野覆盖400毫米宽的轨面,图像横向有4096个像素,那么每个像素对应的物理尺寸大约0.098毫米,1毫米宽的裂纹还能覆盖约10个像素;但如果把视野拉到1000毫米,每像素对应0.24毫米,裂纹就只剩下4个像素左右,特征已经非常弱了。小目标检测是这类项目的硬骨头,后面我会专门讲怎么优化。
还有一点是现场环境不稳定。白天逆光、夜间补光、列车经过后的震动、夏季的高温反光,都会让图像质量波动。传统的人工巡检依赖经验,但劳动强度大,而且天窗时间有限,不可能高频次全覆盖;传统图像处理里的阈值分割、边缘检测在受控环境下能用,一放到现场,光照稍微一变阈值就全崩了。所以这个场景天然适合用深度学习的检测模型来做。
1.2 方案选型:YOLOv5为什么合适
检测方案可以粗分成两类:两阶段检测器,比如比较早的Faster R-CNN系列,精度高但推理速度慢,一张图要跑几十甚至上百毫秒,很难满足现场实时处理的要求;单阶段检测器,以YOLO系列为代表,把检测当成一次回归直接输出位置和类别,速度和精度平衡得很好。
在YOLOv5、YOLOX、还有后来的YOLOv8这些候选里,我最终选了YOLOv5,主要是看中几点:
第一,模型规模选择灵活。YOLOv5有s、m、l、x四个尺寸,从轻量级到高精度都有,部署到不同算力的设备上很方便。刚开始探索时先用s跑通流程,后面再换m或者l追求更高精度,不用改代码结构。
第二,迁移学习体验好。官方仓库提供了在大型通用数据集上预训练好的权重,钢轨缺陷这种专用小数据集,直接基于预训练权重微调,收敛速度快得多。我自己从空权重训练过,损失下降非常慢,一万张数据跑下来效果还是不行;换用预训练权重后,几百个epoch内精度就追了上来。
第三,训练生态完整。数据标注、格式转换、训练参数配置、结果可视化这一套工具链都很成熟,实验结果导出也方便,做工程落地很省心。
我用的整体流程是:现场图像采集 -> 缺陷标注 -> 数据集划分与增强 -> YOLOv5训练 -> 指标评估 -> 模型导出 -> 部署推理。后面所有章节都按这条线展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据集构建——模型上限的决定者
2.1 现场图像采集的几个硬指标
项目最开始,我犯过一个本末倒置的错误:急着去调模型参数,结果模型精度死活上不去,回头才发现是数据本身的问题。采集阶段需要关注几件事,这里按重要性排个序。
成像分辨率是第一位的。上面算了像素尺寸,检测目标至少要覆盖3到5个像素才能被稳定识别。我的建议是:根据你要检测的最小缺陷尺寸倒推相机分辨率,而不是反过来让算法去迁就模糊的图像。如果最小缺陷是1毫米,那么视野内每像素物理尺寸最好控制在0.1毫米左右,也就是1毫米缺陷占10个像素以上。
光照方案是第二位的。钢轨表面是金属材质,反光严重,普通的顶灯照明会产生大面积高光,把细小裂纹完全盖住。现场用得比较多的是低角度照明和条形光源,让光贴着轨面打,裂纹这类凹陷区域会因为阴影对比被突出出来。如果条件限制只能用环境光,那就要尽量把采集时段固定,同时增加样本的多样性。
数据量方面,我的实际经验是:单类缺陷至少准备几百个实例,整个数据集建议在3000张以上。缺陷检测有个特征,正样本(有缺陷的图)和负样本(没有缺陷的图)都要有比例。很多人只顾着标缺陷,忽略了纯背景的负样本,结果模型上线后到处误检。我一共凑了大约5000张现场图,其中负样本占两三成,这个配比后续被证明非常关键。
最后是场景覆盖。不同型号的钢轨、不同磨损阶段、晴天阴天、早上傍晚的光线,都要尽量采集到。数据集的多样性决定了模型泛化能力的上限,这一点在后续的评估里会体现得非常明显。
2.2 缺陷类别定义与标注规范
类别定义决定了模型的能力边界,建议一开始就把类别定清楚,后期反复修改类别会浪费大量标注成本。我这个项目按现场检修的实际需求,定了下面几类:
| 类别名称 | 典型外观特征 | 标注建议 |
|---|---|---|
| crack(裂纹) | 细长、曲折的暗色线条,宽度小 | 弯曲厉害的用不规则框,尽量贴合轨迹 |
| spalling(剥离/掉块) | 块状凹坑,边缘不齐,有阴影 | 矩形框包住整个损伤区 |
| scratch(擦伤) | 沿轨向延伸的条状痕迹,连续或断续 | 矩形框包含整段痕迹 |
| rust(锈蚀) | 表面大面积颜色异常,纹理粗糙 | 矩形框框住明显锈蚀区域 |
标注工具我用的是开源工具LabelImg和Label Studio。前者轻量,适合纯检测框标注;后者功能更全,适合团队协作和多轮质检。标注完导出成YOLO格式,也就是每个缺陷对应一个txt文本,里面的内容是:
code复制类别id 中心点x 中心点y 框宽w 框高h
坐标全部是相对于图片宽度和高度的归一化值,范围0到1。YOLOv5训练时会按这个格式读取每个标注文件。
标注规范定死几条:标注框要和目标边缘贴合,最多留3个像素的余量;两个缺陷挨得近但实际上是分开的,必须分开标;目标被遮挡了一半,只标可见部分;拿不准的宁可多标也不要漏标,因为漏标会直接把模型带偏。标注完成以后一定要做质检,我习惯抽检5%的图逐张看框是否贴合,遇到连续标错的批次直接打回重标。
2.3 数据增强与正负样本平衡的方法
标注完成后,第一版训练我直接把原始数据丢进去,发现crack这一类怎么都学不好。原因很直接:裂纹样本本身少,而且形态单一。YOLOv5内置了Mosaic增强,把四张图拼成一张训练,对小目标检测帮助很大。HSV通道扰动、随机翻转、旋转、缩放这些我也开了,但有一点要特别注意:钢轨表面是灰暗金属材质,不要过分调亮和调饱和,否则增强出来的样本完全脱离真实分布,训练时指标看着不错,一到现场就露馅。
YOLOv5官方训练策略里有个很实用的细节:前一大半训练阶段开启Mosaic增强,最后50个epoch关掉它,模型更容易在真实分布上收敛稳定。我对比过,这个操作对最终精度确实有改善,机制上可以理解成:Mosaic负责扩大尺度和位置多样性,后半程去掉后模型能专注于逼真样本的精细化拟合。
正负样本平衡方面,除了采集阶段增加纯背景负样本,还可以用难例挖掘的思路。具体做法是:用第一版模型去跑一批现场视频,把所有置信度不高或者明明有缺陷却没检出来的图收集起来,人工复核后重新标注,补进训练集。这一轮补数据往往比调任何参数都管用,我在crack类别上就靠这个办法把召回率提升了十几个点。复制粘贴合成也是一种办法,把某个图片里的缺陷区域抠出来,用随机位置、随机尺度贴到背景图上,但要注意保持光照方向一致,否则模型会学到不自然的边界痕迹。
3. YOLOv5模型训练全流程与关键参数
3.1 环境搭建与数据目录组织
环境这块不用太纠结,Python 3.8以上配PyTorch框架,再装对应版本的CUDA就可以。我的建议是:别用最新版PyTorch,选一个和GPU驱动、CUDA都兼容的稳定版本,能省掉很多环境报错。没有GPU也能训练,但速度会慢到让人怀疑人生,如果有条件还是尽量用单张16G显存级别的GPU。
数据集目录结构比较规范的做法是这样:
text复制dataset/
├── images/
│ ├── train/
│ ├── val/
│ └── test/
├── labels/
│ ├── train/
│ ├── val/
│ └── test/
└── rail_defect.yaml
images和labels里存放对应的图片和txt标注文件,文件名一一对应。划分比例我习惯用8:1:1,训练集、验证集、测试集各司其职。有一个很多人忽略的坑:划分的时候要按采集时段分层抽样,不能简单地随机切。因为现场连续采集的视频帧非常相似,如果同一段视频的相邻帧同时进了训练集和验证集,验证指标会被严重高估,实际部署后才发现精度没达标。我把这个数据泄漏问题处理掉之后,验证集mAP掉了几个点,但反而松了一口气——因为这才是真实水平。
3.2 配置文件修改详解
训练前要改两个配置文件。第一个是数据集配置文件,指定图片路径和类别信息,YOLOv5用yaml格式:
yaml复制path: /data/rail_defect/dataset
train: images/train
val: images/val
nc: 4
names:
0: crack
1: spalling
2: scratch
3: rust
第二个是模型配置文件。以yolov5s为例,把其中的nc字段从80改成4,别的结构不用动,YOLOv5会自动根据这个nc数调整最后的输出头。
这里有个机制值得说清楚:YOLOv5在训练前会自动基于当前训练集的标注框做k-means聚类,重新生成锚框尺寸。所以即使你的缺陷尺寸比例和通用数据集差异很大,也不需要手工去调锚框参数,直接训练就行。训练日志里会打印聚类后的锚框数值,你可以看一眼是否和标注框的大致尺度吻合。
3.3 训练命令与参数选择的思路
训练命令是核心,我常用的启动命令长这样:
bash复制python train.py \
--data rail_defect.yaml \
--weights yolov5s.pt \
--img 640 \
--batch 16 \
--epochs 100 \
--device 0
逐个说参数。--weights这里我指定的是官方预训练权重,而不是从零训练。迁移学习的价值在数据量不充裕的情况下特别明显,预训练权重已经学到了通用的边缘、纹理、形状特征,微调只需要让模型适应钢轨的专属特征。如果数据集只有几千张,从零训练的结果往往很差。
--img是输入分辨率。640是默认值,训练速度适中。如果你的目标是小缺陷,可以考虑提高到1280。但要算一笔账:分辨率提高一倍,输入图像的像素数量变为原来的4倍,训练显存需求和训练时间大致也会往这个量级走。我自己的经验是:先把640跑通,确认流程没问题,再考虑上1280做精度优化,不要一上来就追求高分辨率。
--batch要结合显卡显存来定。单张16G显存跑yolov5s,640输入,batch 16比较稳妥,batch 32就接近极限了;如果换m模型,batch建议降到8到12。训练时报OOM就减batch,没有别的捷径。
--epochs定多少要配合早停机制。YOLOv5自带早停,patience设20,也就是连续20轮验证集没有提升就自动停止。我一般先定300,实际跑到100到150轮就停了,因为后期过拟合风险变大,训练收益反而变小。学习率方面,默认的0.01配余弦退火策略够用,数据量小或者标注质量一般的时候调到0.005更稳。训练初期YOLOv5自动开启warmup,前几轮学习率从很小逐渐爬升,这个机制能避免开局就震荡。
还有一个常用技巧:冻结主干网络前几层。钢轨缺陷检测和通用目标检测的低层特征有共性,冻结前10层可以加快训练、减少过拟合,尤其在数据量不超过一万张时效果明显。YOLOv5里通过--freeze 10参数就能实现。
3.4 训练过程监控与评估指标解读
训练过程里我几乎每天都会打开训练输出目录下的results图看一眼。这张图包含了box_loss、obj_loss、cls_loss三条损失曲线的下降情况,以及Precision、Recall、mAP这些评估指标的变化。
先看损失曲线:训练损失如果整体震荡下行,说明模型在正常学习;如果训练损失下降但验证损失掉头回升,那就是过拟合信号;如果训练损失从一开始就不怎么降,大概率是学习率设置不对,或者标注数据有系统性错误。再看评估指标:mAP@0.5是IoU阈值取0.5下的平均精度,mAP@0.5:0.95则是在0.5到0.95多个IoU阈值下的平均,后者更严格,对小目标框的回归精度更敏感。
钢轨缺陷检测的实际使用场景里,我的目标定得比较明确:mAP@0.5尽量到0.9以上,crack这类漏检代价高的类别Recall要尽量高。注意Precision和Recall是一对矛盾,调高置信度阈值能提升Precision,但会牺牲Recall;现场巡检宁可多报几个疑似缺陷让人工复核,也不愿意漏掉真实缺陷,所以我会偏向保Recall。
训练完成后还要看混淆矩阵。我在这上面发现过crack和scratch两类混得很严重——细长的裂纹和条状擦伤在局部图像上确实难分。针对这类问题,光调参数没用,得回到数据层面,补充两类之间边界情况的样本,或者把标注规范再收紧一点。
4. 针对钢轨场景的专项优化与部署落地
4.1 小目标缺陷检测的三种有效手段
钢轨裂纹这种小目标,是项目里最让人头疼的问题。我试下来,三个办法组合使用效果最明显。
第一个是提高输入分辨率。把训练和推理的--img从640提到1280,小目标的像素占比翻了几倍,检测率提升非常直观。代价是显存占用约4倍,推理时间也变长。我通常先把小目标类的漏检集中样本拿出来对比测试,确认收益确实明显才会上分辨率。
第二个是添加P2检测头。YOLOv5的标准结构里有P3、P4、P5三个检测尺度,P2是额外添加的更浅层特征图,分辨率更高,对小尺寸物体更友好。代价是计算量增加,推理速度变慢,实际使用时要看部署设备的算力承受能力。
第三个是我最推荐的组合策略:切图检测,也叫tiling策略。高分辨率现场图像先不直接整张送进模型,而是切成一堆小块,比如每块512乘512,相邻块之间留一段重叠区域避免目标正好被切在边界上,我的经验是重叠留10%到20%。每个小块分别检测,再把检测框坐标映射回原图坐标,最后对重叠区域重复检出的框做NMS合并。这个方案的好处是,既享受了高分辨率的小目标细节,又不需要模型去处理极端大的输入,训练和推理都能用常规模型。代价是整体计算量增加,需要写坐标映射逻辑,但它是我在工程上解决小尺寸裂纹漏检最稳妥的方案。
4.2 样本不均衡与难例挖掘的实战处理
钢轨缺陷的类别分布天然是不均衡的。我自己统计过一次,rust类别占了全部标注框的一半以上,而crack只占不到一成。如果直接训练,模型会把主要精力用在rust上,crack学不好。
处理思路有几个层次。最简单的做法是调整类别采样权重,让样本少的类别在每个batch里出现得更频繁。YOLOv5支持通过数据集目录组织方式来变相实现,把稀有类别图片复制若干份,或者用增强策略把稀有类别样本多变换几次。
更进一步是复制粘贴合成。把crack这类稀有缺陷的像素块抠出来,贴到只有背景的负样本图上,注意旋转角度和亮度要与场景匹配。我这里说的抠图不是简单拼贴,最好做一点边缘羽化,否则模型会学会识别“贴图边缘”这种伪特征。
难例挖掘是最后一招,也是最费人力但最有效的一招。用当前模型去跑没有参与训练的新增视频,把所有漏检和置信度低的图捞出来,找专业人员逐张复核,确认真实缺陷后标注进数据集,迭代到下一轮训练。我做了两轮难例挖掘之后,crack类别的召回率从0.78涨到了0.93,这个提升是任何网络结构调整都很难做到的。
4.3 模型导出与推理性能优化
模型训练完不是终点,现场设备上能不能跑起来才是关键。我的导出流程是:把PyTorch训练出的best.pt权重导出成ONNX格式,这样可以用ONNX格式的运行时做加速推理,也是很多部署平台通用的中间格式。
导出时注意两点:一是设置好opset版本,太低了有些算子不支持,不利于量化;二是根据现场推理设备的CPU或GPU情况决定是否开启动态batch维度。如果要在嵌入式计算板卡上跑,FP16精度也可以考虑,显存占用和推理速度都会改善,精度损失对钢轨检测来说通常可接受。
后处理逻辑也要跟上。检测模型输出的是归一化坐标,要还原成实际图像坐标;如果要输出缺陷的物理尺寸,还需要做像素到毫米的标定换算。现场界面上我会把检测框叠加到原始图像上,同时记录每个框的类别、置信度和像素位置,生成一条条缺陷记录,方便检修人员定位复核。
4.4 高频误检案例分析
我把自己踩过的误检坑整理成三类,供你对照排查。
第一类是把螺栓、扣件识别成裂纹。钢轨接头的螺栓轮廓和裂纹在局部图像上很像,加上枕木区域纹理复杂,很容易误报。解决办法是采集大量纯背景负样本塞进训练集,尤其是包含螺栓和扣件的图,让模型认识到这些是正常结构。后处理上也可以加一条位置过滤规则:检测框中心落在轨头轮廓之外的框直接丢弃。
第二类是焊缝区域被识别成剥离。焊缝有规则的鱼鳞纹,阴影重的时候和剥落掉块确实不好区分。我加了各种光照条件下的焊缝样本,并调整标注规范,把表面正常波形但成像像缺陷的情况专门标记为负样本,误检率明显下降。
第三类是水渍反光产生假阳性。雨后轨面半干不干的时候,反光区域呈亮条形,模型会当成擦伤或裂纹。除了补数据,还可以用后处理规则:结合置信度阈值,对低置信度的细长条目标做额外审查。光照方案上尽量用低角度照明压制镜面反光,从成像源头降低误检概率。
5. 常见问题排查与避坑速查
5.1 训练阶段典型问题速查表
训练阶段的问题大多能归因到数据、参数和配置三类。我把经历过的典型情况整理成了速查表:
| 问题现象 | 可能原因 | 优先排查方向 |
|---|---|---|
| 训练损失长期不降 | 学习率过高或过低 | 调学习率,确认warmup正常 |
| 损失曲线震荡剧烈 | 数据增强过强 | 关闭Mosaic做对比实验 |
| 训练损失降、验证损失不降 | 过拟合 | 加数据量、调低epochs、早停 |
| 验证mAP远低于预期 | 数据集划分泄漏或标注错误 | 检查同源帧泄漏,抽检标注质量 |
| 某一类指标特别差 | 样本量不足或类别混淆 | 补样本、难例挖掘、调整采样权重 |
| 训练中途报NaN | 学习率过大或数据有异常 | 降低学习率,检查标签是否有越界坐标 |
排错有个基本思路:先验证数据的干净程度,再动训练参数。我见过一次训练不收敛,排查了两天才发现是一批标注文件的坐标归一化出现了数值异常,和模型参数一点关系都没有。所以训练前先写个脚本检查所有txt标注:坐标是否在0到1区间、类别id是否在合法范围内、有没有空标注文件对应到有目标图片,这些预检能省大量时间。
5.2 检测效果与部署问题排查
模型训练完了,现场部署又会冒出一批新问题。漏检小目标是高频问题,主要对策就是4.1里说的三条路:提高输入分辨率、加P2检测头、切图检测。误检率高则优先检查负样本是否充足、后处理规则是否合理。推理速度慢的时候,先把输入分辨率降下来看能否满足需求,再考虑ONNX导出、FP16、模型换小尺寸等优化手段。显存OOM没有悬念,减batch或者降低分辨率。还有一个现场特有的问题:相机视野光照和设备装调条件跟采集训练数据时不一致,导致效果打折。解决思路是在部署现场采集一批图像做快速校准测试,如果效果差距过大,就把它当作新的数据集来源纳入下一轮迭代。
5.3 我总结的一套稳妥流程
这个项目做完后,我觉得最稳妥的落地节奏是这样的:第一步,用最小可用的数据集和yolov5s把训练、评估、导出的全流程跑通,目标是建立基线;第二步,重点解决数据质量问题,补足各类别的样本数量和负样本比例,做难例挖掘;第三步,才轮到底层优化,比如输入分辨率、P2头、切图策略这些手段;第四步,现场小批量测试,收集部署环境的真实失败案例,再回到数据层面迭代。
每轮实验我都会把训练用的yaml配置、数据集版本说明、训练参数和最终指标记录在案。缺陷检测项目变量太多,不记录版本,一个月后回头看就说不清精度提升到底是哪个改动带来的。
最后说点个人体会。这个项目做下来,我最大的感受是:不要一上来就折腾模型结构。钢轨表面缺陷检测的绝大多数问题,根源都在数据质量和工程细节上。模型结构只要选一个成熟稳定的方案,把数据、标注、参数这些基本功打磨到位,效果自然就出来了。还有一个小建议要送给你:每次训练前抽300张原始图像亲眼看一下标注质量,这比看十遍训练曲线都管用。基层工作做扎实,钢轨缺陷检测并没有想象中那么玄乎。
