YOLOv11轨道缺陷检测实战:从数据标注到PySide6桌面应用部署
这阵子一直在搞一套铁路轨道缺陷识别的检测系统从最初只想把YOLOv11模型跑通到最后做成一个带登录注册、图片/视频/实时检测、结果导出界面的完整桌面工具前后折腾了差不多两个月。中间踩了不少坑也攒下不少可以直接照抄的经验。如果你正在做类似的工业视觉检测项目或者刚接触YOLO系列、想找一个从数据到部署的完整落地案例这篇应该对你有用。这个项目最核心的一件事不是把模型精度刷得多高而是把所有环节串起来数据怎么来、标签怎么定、模型怎么改、界面怎么不卡、导出怎么才能让人真正拿去用。我尽量按实际做项目的过程来讲每一段都是当时遇到的真问题。1. 项目从哪里来轨道巡检场景下的真实需求与数据构建1.1 轨道检测为什么需要视觉方案轨道缺陷这件事平时不坐火车的人可能完全无感但在铁路运维里是实打实的刚需。钢轨长期承受列车碾压会出现轨面裂纹、剥离掉块、光带异常还有扣件松动、螺栓缺失这类“小毛病”。传统做法主要靠人工巡检天窗时间内工务段的人带着仪器在轨道上走又慢又累而且细小裂纹肉眼很容易漏。这个项目的目标很直接用摄像头代替一部分人眼重复劳动基于深度学习的YOLOv11模型自动识别缺陷类型并在画面里框出来。表面看是算法Demo实际上它是个完整的小系统——检测只是核心外面还套了账号体系、可视化界面、结果保存这些工程化内容这样才符合真实使用习惯。1.2 数据集不是一次到位公开集打底、自采补充、增强兜底做检测项目第一道坎永远是数据。轨道缺陷不像猫狗数据集那么好找正样本本来就少缺陷种类又碎。我采用的是“开源缺陷样本 模拟环境补拍 针对性增强”的组合方式总共整理出约1.2万张可用图片。先说明一下纯靠公开数据有个问题公开集大多是实验室转盘拍摄的轨面特写角度固定、光照单一、背景干净。拿这种数据训练出来的模型一到现场就露馅。所以我从两个方面做了补强在模拟轨道模型上架手机和工业相机用不同角度、远近、顺光逆光拍了一批素材重点补充“暗光”“反光”“阴影遮挡”这类难例。用Albumentations做在线增强包括随机仿射、亮度对比度扰动、马赛克拼接、小目标复制粘贴。其中复制粘贴对轨道缺陷特别有效因为很多裂纹在整张图里只占很小区域喂给网络的目标太少。最终数据集里缺陷类别我定了5类轨面裂纹、轨头剥离、扣件缺失、扣件断裂、螺栓缺失。之所以没有把“光带异常”这种模糊概念加进来是因为标注一致性太差模型学到的东西会非常不稳定。类别宁少勿杂这个原则在缺陷检测里尤其重要。1.3 标注环节最容易埋雷我的做法是“多轮校准”标注是整个项目里最容易被低估的环节。YOLO格式本身很简单——每张图对应一个txt文件每行是“类别id x_center y_center width height”坐标做了归一化。但数据量大起来以后不同人标出来的框风格完全不同有的喜欢紧贴目标有的习惯留白模型会被这种不一致性搞糊涂。我的处理方法是标注完成后随机抽几百张图出来做交叉检查把同一类别的框尺寸分布画出来如果发现某个类别的框宽高比特别离散基本就是标注标准不统一。轨面裂纹这种细长目标尤其容易出现这种问题后来我干脆重新统一了一遍规则裂纹沿延伸方向包紧、剥离区域按实际掉块轮廓、螺栓类只看可见部分。还有一个细节值得提类别不平衡。扣件缺失这种样本好找但轨头剥离的正样本可能就几百张。训练时如果直接硬跑模型会严重偏向样本多的类别。我的做法是先按类别数量统计给样本少的类别在损失计算里加权重同时用复制粘贴增强把稀缺类目标复制多份让每一轮训练都能稳定见到它们。2. YOLOv11选型逻辑以及针对轨道场景做了哪些改造2.1 为什么选YOLOv11而不是别的检测模型YOLO系列发展到现在能选的版本很多。我自己之前主力用的是YOLOv5和YOLOv8这次换成YOLOv11核心原因有三条推理框架成熟、小目标能力有优化、官方仓库支持直接出模型。先看一个粗糙的选型对比这是我当时在几个候选模型之间做的评估模型推理速度T4上ms/张小目标效果部署复杂度结论YOLOv5约8-12ms一般低足够用但旧YOLOv8约7-10ms较好低可选项YOLOv11约6-9ms小目标检测层更友好低最终选择RT-DETR约10-15ms中上中速度吃亏两阶段类Faster R-CNN20ms以上强但慢高现场不合适轨道缺陷检测有个特点缺陷往往小、细长、对比度低对速度要求又比较高因为现场希望实时或近实时出结果。YOLOv11在结构上对多尺度特征融合做了进一步优化搭配合适的分辨率比上一代在小目标上更容易出效果而且官方训练推理链路是目前最顺的后面做界面、导出ONNX都很省事。不过要注意选YOLOv11不代表什么都不用改Detection Head默认配置是为通用目标服务的轨道场景必须做针对性调整。2.2 针对细长裂纹和反光表面做的具体修改第一处修改是输入分辨率。默认训练尺寸一般是640x640但轨道裂纹很多只有几十个像素宽缩放以后特征基本消失了。我把训练和推理分辨率提到1280x1280虽然显存和耗时都有增加但小目标召回率明显提升。如果你显卡显存小于8G建议用896或1024折中一下。第二处修改是加了更浅层的检测分支。YOLOv11本身有P3、P4、P5这几个检测尺度对应8倍、16倍、32倍下采样。轨道上的小缺陷在8倍下采样的特征图上仍然太小我额外加了一个P2检测层对应4倍下采样专门用来捕捉高频细节。代价是计算量增加但实测mAP50-95能涨两三个点这钱花得值。第三处是注意力模块。我在Backbone的倒数第二层后面加了一个轻量的注意力模块帮助网络重点关注轨面区域而不是枕木、道砟等背景。这不算什么创新结构但确实抑制了把石块反光当裂纹的误检。2.3 训练超参数怎么定以及怎么压住过拟合训练配置是这套系统里最有参考价值的部分。我直接用YOLOv11的官方训练仓库配置文件里核心参数如下train: dataset/train/images val: dataset/val/images nc: 5 names: [crack, spall, clip_missing, clip_broken, bolt_missing]yolo detect train datarail_defect.yaml modelyolo11m.pt epochs150 imgsz1280 batch8 device0 optimizerAdamW lr00.0005几个参数背后的考虑模型用yolo11m比nano精度高比large省显存对于这个任务够用。优化器选AdamW而不是默认的SGD收敛更快尤其在这种小数据集上更稳。训练轮数设150但是加了早停实际跑到第80轮左右验证集指标就不再明显上升果断停掉避免过拟合。输入尺寸1280意味着batch不能太大8比较稳再大容易爆显存。训练过程中我每10轮导出一轮验证集的可视化预测图肉眼观察有没有把道砟误判成剥离、把水渍误判成裂纹这类问题。这比只看mAP好用得多因为mAP是总体指标掩盖了很多局部问题。3. 从模型到系统UI界面和登录注册模块的设计思路3.1 为什么选PySide6做桌面界面系统做出来不是给程序员自己玩的目标使用场景是工务人员或者检测工程师在电脑上操作。Web界面当然也可以但部署起来麻烦还得考虑摄像头能否被浏览器正常调用。最后选了PySide6做桌面端主要原因有三个Python生态里和模型推理库集成最顺跨平台Windows/Linux都能跑开发速度快拖控件布局比从零写Web前端省事得多。用PySide6还有一个隐性优势和PyTorch/ONNX Runtime在同一个Python进程里图像数据从QImage转成numpy数组直接喂给模型少了一层HTTP通信的延迟实测单张图片从点击到显示结果在1秒以内。3.2 登录注册模块账号存储与密码安全处理这个模块看起来跟“轨道缺陷识别”没什么关系但真正给用户使用的时候必须有。不同的检测人员要有独立账号管理员需要知道谁在哪个时间段跑了哪些检测。我设计的是“管理员普通用户”两级角色管理员能查看所有检测历史和账号管理普通用户只能做检测、看自己的记录。账号存储直接用SQLite没有引入独立数据库服务因为单机部署场景不需要。密码绝不存明文用的是加盐SHA256哈希。下面这段是把注册逻辑抽出来的核心片段import hashlib import os def register_user(username, password): salt os.urandom(16).hex() digest hashlib.sha256((salt password).encode()).hexdigest() # 写入SQLiteinsert into users (username, salt, password) values (?,?,?) return True def verify_user(username, password, salt, stored_digest): digest hashlib.sha256((salt password).encode()).hexdigest() return digest stored_digest实际项目里我还加了三件事密码最少8位且包含字母和数字连续输错5次锁定对应账号15分钟登录成功后返回一个带过期时间的用户令牌界面跳转时携带这个令牌。这些细节不复杂但能让系统看起来像个正经软件而不是课程设计。3.3 检测主界面怎么布局用户才不会一头雾水主界面的布局原则是“打开就会用”。整个窗口分四块区域左侧是操作面板包含“打开图片”“打开视频”“打开摄像头”三个入口以及置信度阈值滑杆。中间是显示区原图和标注结果叠加显示检测框旁边实时显示类别名称和置信度。右侧是结果列表每条检测记录包含缺陷类别、置信度、坐标、时间戳。底部是统计条显示当前文件的缺陷数量、各类别分布、耗时信息。另外做了一个“一键导出报告”按钮把检测结果输出成带图片批注的PDF或Excel这个功能在实际调研时被反复提到比想象中刚需。界面上最需要注意的事情是把“置信度阈值”做成可调滑杆而不是写死。因为不同场景下要求完全不同巡检初筛希望宁可错报也别漏报那就把阈值调低到0.25出了疑似缺陷后人工复查希望减少无意义报警可以调到0.5以上。4. 训练和部署落地时最折磨人的几个坑4.1 验证集指标很漂亮现场却没法用的“假阳性”问题这是整个项目里最值得写的一段。模型训练完在验证集上mAP50到了0.94我当时很高兴结果拿到真实的轨检车拍摄视频里一测立刻被打脸——连续把钢轨接缝处的阴影、轨枕上的水渍、甚至铁锈色的道砟识别成了“轨面裂纹”。原因其实很典型我的验证集和训练集共用同一批采集条件下的数据背景分布高度一致模型根本没有学会“轨面”和“非轨面”的边界它只是记住了“和训练样本纹理相近的东西”。排查过程我分了三步走先看混淆矩阵发现错误主要集中在crack和spall两个类别与background之间证明是背景误检而不是类别混淆。再抽错样本把预测置信度前20的错误图拿出来发现都是暗色背景加局部亮斑。最后做了一组实验单图输入尺寸从1280降到640误检反而变多证明问题不在分辨率而在数据分布。修复措施是在训练集里加入了大量裁切后的背景负样本并且对正常样本做了随机仿射和HSV扰动模拟现场光照。重新训练后误检直接降了七成。4.2 界面卡顿与摄像头实时性不足的根因界面做好之后第一次联调发现打开摄像头预览时拖动窗口就能把程序卡死。原因很清晰我在主线程里直接做了“取帧-推理-绘制”这一整条链路PySide6的界面事件循环被推理阻塞界面自然就不动了。解决办法是把推理放到单独的QThread里界面线程只管显示。关键逻辑是class DetectThread(QThread): frame_ready Signal(np.ndarray) result_ready Signal(dict) def run(self): while self.running: frame self.cap.read() results model(frame) # 这个操作放到子线程 self.result_ready.emit(results)除了线程分离还加了帧率控制摄像头输入实时帧率可能有几十fps但模型推理在1280分辨率下只能跑到十几fps如果每帧都推理会造成队列堆积画面延迟越来越大。我的做法是丢帧策略只对最新的一帧推理旧帧直接丢弃这样界面看到的永远是接近实时的结果。4.3 导出ONNX时踩的算子兼容问题模型训练好以后我打算导出ONNX然后用ONNX Runtime推理这样可以摆脱PyTorch环境部署体积更小、启动更快。第一次导出倒是很顺利但一跑就发现输出和PyTorch对不上检测框位置偏了不少。排查了半天问题出在“输出尺寸动态化”上。导出的输入尺寸允许动态变化但我用来解析输出的代码却写死了shape。YOLOv11在非最大尺寸输入时输出的特征图尺寸会变直接reshape必然出错。后面我把导出参数改成指定固定尺寸yolo export modelbest.pt formatonnx imgsz1280 dynamicFalse simplifyTrue并且后处理里的坐标解析改成读取实际输出shape而不是写死。另外因为ONNX自带的NMS处理起来比较麻烦我干脆在导出时关闭了模型内NMS把原始预测输出拿到Python里自己用置信度过滤和非极大值抑制处理逻辑完全可控排查问题也容易得多。5. 实测效果、运行环境与可复现的启动步骤5.1 验证集指标和真实场景表现最终模型在自建验证集上的指标如下缺陷类别mAP50mAP50-95备注轨面裂纹0.930.76细长目标1280输入下表现尚可轨头剥离0.910.72样本量最少仍有提升空间扣件缺失0.960.83形状规则最容易识别扣件断裂0.940.79依赖局部细节螺栓缺失0.950.82背景简单误检少这里多说一句mAP50-95比mAP50更能反映真实检测质量因为它对框的位置精度更严格。工业场景一定要同时看这两个指标只看mAP50容易被“框大概在附近”的假精度骗了。推理速度上用一块中端显卡跑1280分辨率单张图片大约耗时18-25ms视频流大约12-15fps跟预期一致。如果换成TensorRT做FP16加速还有至少30%以上的提升空间但这个坑如果不熟的话不建议第一版就上。5.2 源码拿到手后怎么跑起来整个工程目录大致是这样的rail_defect_system/ ├── app/ │ ├── main.py # 入口启动登录界面 │ ├── ui/ # 登录窗口、主窗口代码 │ ├── core/ # 模型推理、数据处理逻辑 │ └── database.db # SQLite数据库 ├── models/ │ ├── best.pt │ └── best.onnx ├── dataset/ │ ├── train/ │ └── val/ ├── requirements.txt └── rail_defect.yaml运行前先建一个Python 3.10的虚拟环境然后pip install -r requirements.txt python app/main.pyrequirements里最重要的几个包是pyqt6或pyside6、torch、onnxruntime-gpu、opencv-python、ultralytics——注意如果只用ONNX推理PyTorch其实可以不装但为了方便二次训练脚本通常还是装上。第一次启动后先注册一个管理员账号再重新登录就能进入主界面。检测模型文件默认加载models/best.onnx如果没有ONNX Runtime环境则自动回退到best.pt。5.3 这个系统后续还能往哪些方向扩展做完一版之后我觉得后面可以扩展的方向至少有这几个一是接入视频流和抓拍联动不只是单张图片和本地视频可以对接现场的轨检摄像头做成长时间无人值守的监控模式异常才抓拍留存。二是缺陷分级和尺寸估算现在只输出缺陷类别和位置后续可以通过标定把像素尺寸换算成实际毫米值然后按严重程度分为轻、中、重三级这对用户排检修计划帮助很大。三是多路并行优化如果同时检测左右两条钢轨可以用两个推理线程分别在两张图上并行跑控制好显存分配整体吞吐能翻倍。四是不断用现场数据做增量训练把每次人工复核后的结果回填到训练集里定期重训这样系统会越用越贴合自己现场的工况。这也是我觉得工业检测项目最应该养成的习惯——算法模型不是一次性交付物它是需要持续喂养和迭代的。最后再说点实际操作中的体会如果让我重新做一遍这个项目我最想一开始就做对的事情是先把“现场背景长什么样”搞清楚再去设计数据集和模型。我第一次就是在公开数据集上花了大精力结果现场测试发现背景根本不匹配后面补样本重训浪费了很多时间。另一个体会是深度学习检测系统能不能真正被用起来关键往往不在模型精度而在界面交互和部署流程顺不顺手。一个工程师愿意每张图都点开看和嫌操作麻烦不愿意用这两个结果对项目价值的评价是完全不一样的。这套系统的模型精度并不是天花板但它让我把从数据标注到界面交付的整条链路走通了后面再换场景、换模型都只是替换中间模块的问题。