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

基于YOLOv8的轨道异物检测系统:从数据标注到PyQt5部署全流程

基于YOLOv8的轨道异物检测系统:从数据标注到PyQt5部署全流程 ★ FEATURED ARTICLE
前阵子把一个“基于YOLOv8的轨道异物检测系统”从零到一整了下来算是把数据标注、模型训练、可视化展示、PyQt5界面设计和最后的环境部署全流程都趟了一遍。这套东西听起来是个不小的工程但拆开看其实就是“数据集 模型 界面 部署”四件事真正的难点反而不在YOLOv8本身而在数据质量和工程落地时的各种衔接细节。这篇文章把我在项目里的完整流程和踩坑记录写出来给正在做类似巡检项目或者拿这个方向当毕设的同学一份能直接参考的实操记录。我尽量把每个环节的“为什么”也讲清楚比如为什么YOLO格式的标签长这样、为什么PyQt5里必须开线程做推理、为什么训练时mAP会卡住不动。你照着做至少能跑通一条完整链路少走几周弯路。1. 项目整体设计与思路拆解1.1 这个系统到底要解决什么问题轨道异物检测说白了就是盯着铁轨或者轨道周边区域发现有不该出现的东西就报警。场景很常见比如轨道上掉了石块、施工工具、树枝甚至行人闯入。传统方式是人工盯监控画面但长时间盯屏容易疲劳漏报风险高所以用计算机视觉做自动检测是刚需。这类场景有几个比较棘手的地方目标普遍偏小比如一块砖在画面里可能就几十个像素光照变化大顺光、逆光、夜间补光差异明显异物类别杂同一类物体不同角度长得差异很大实时性要求高报警晚几秒钟都可能引发问题。选YOLOv8作为检测模型主要是看中它在速度和精度之间的平衡。对轨道巡检这种需要快速响应、又要求能部署到普通工控机上的场景YOLOv8的系列模型从n到x都有量级从几兆到几十兆按硬件水平选就行。而且ultralytics框架把训练、验证、导出封装得很顺手不用自己造车轮子。1.2 整体技术栈与系统组成整个系统按数据流理解很清晰数据层摄像头采集视频帧或者直接读图片文件标注层用标注工具生成带标签数据集格式统一成YOLO的txt训练层基于YOLOv8训练检测模型输出权重应用层PyQt5写界面加载模型做实时推理把结果画在画面里配上告警逻辑部署层配置好Python环境和依赖打包成可以双击运行的应用。这里最容易被忽略的是“数据层”和“标注层”占用的时间。我这个项目里训练模型只花了两三天调参但前前后后整理数据、清洗标注、核对类别花了快两周。不要觉得数据处理是杂活就糊弄过去检测效果天花板其实是数据决定的模型只是把这个上限尽量逼近而已。1.3 为什么选择PyQt5做展示界面有人会问为什么不用Web前端或者直接用OpenCV的窗口我自己的理由很直接PyQt5是桌面应用离线环境运行稳定不依赖浏览器和网络后续要接声光告警、串口闸机联动这类硬件逻辑桌面端处理起来更顺手界面控件成熟QThread做多线程比较方便不像纯OpenCV窗口那样什么都得自己画打包成exe在工控机上跑比搭一套Web服务省心得多。当然PyQt5也有缺点比如界面样式比较老气但监控类工具的界面核心是数据清晰、操作简单不需要花哨。真正让我在项目里花时间调试的不是控件布局而是“视频解码、模型推理、界面渲染”这三者之间的线程配合。这个问题后面单独用一小节讲。2. 带标签数据集的制作从采集到标注2.1 数据采集的来源与注意事项我先说结论数据质量比数据量重要尤其是异物检测这种小目标场景样本“像不像真实场景”直接决定检测效果。数据来源大体分三路真实场景采集用现场监控摄像头截取视频帧覆盖不同时间段、不同天气、不同光照模拟场景拍摄在类似轨道环境中放置异物样本用手机或工业相机拍摄公开通用数据集预训练先用通用目标检测数据集训练一遍拿到预训练权重做迁移学习再用自己的轨道数据微调。这里面最容易踩的坑是“样本太单一”。很多项目图省事只在同一个角度、同一个光照下拍了几百张就开训结果测试时换个角度就漏检。我的做法是同一类异物至少保证三个角度、两种亮度、两种距离以上宁可每类只有三四百张高质量图片也不要一千张全是同一个视角的相似图。负样本也要收这是很多新手忽略的。空轨道、只有道砟、只有轨枕的画面也需要一些让模型知道“没有异物的时候别乱报”。我最后的数据集里放了大概15%的负样本对降低误报有明显的帮助。2.2 标注工具与YOLO格式说明标注工具我用过LabelImg也试过labelme最后固定在LabelImg配合YOLO模式直接输出txt文件。LabelImg操作简单画矩形框后选类别就行缺点是切到YOLO模式前要先准备好类别名。YOLO格式的标签是一个txt文件跟图片同名放在同一个目录下。每一行代表一个目标格式是class x_center y_center width height注意x_center、y_center、width、height都是相对于图片宽高的归一化坐标取值在0到1之间。类别从0开始编号。举个例子一张640x480的图片里有一个石块的检测框左上角是(100, 200)右下角是(200, 300)那么它对应的YOLO标签行就是0 0.234375 0.520833 0.156250 0.208333计算过程x_center (100 200) / 2 / 640 150 / 640 0.234375y_center (200 300) / 2 / 480 250 / 480 0.520833width (200 - 100) / 640 0.156250height (300 - 200) / 480 0.208333建议标注完做一次脚本检查主要查两类问题一是坐标是否越界二是某些图片漏标了。我吃过一次亏训练到一半发现有一批图片的标签文件是空的模型把空图当成负样本损失曲线一直下不去后来用脚本筛出来重新标注才正常。2.3 数据增强、划分与验证集数据增强我直接在ultralytics框架里配置。YOLOv8默认自带Mosaic、翻转、HSV变换等增强策略配置文件里都可以调。我实际用下来Mosaic对小目标检测帮助明显但要注意如果异物场景有很强的尺度固定性Mosaic之后目标可能缩得很小反而学不到稳定特征。所以我在Mosaic基础上没有开太多亮度扰动因为轨道场景夜间和白天差别太大我干脆用白天数据做主要训练夜间单独准备了一批数据做验证。数据划分我习惯按8:1:1分成训练集、验证集、测试集。目录结构清晰一些方便后续复用dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml内容是关键特别是names的顺序一定要跟标注类别数字对应train: dataset/images/train val: dataset/images/val test: dataset/images/test nc: 3 names: [stone, tool, person]我之前犯过一个低级错误标注时把“tool”当成第2类但names里写成了第一个结果模型训练时所有类别错位置信度看起来很高检测框位置也对但类别全乱了。这种问题光看loss曲线根本发现不了一定要在训练前去验证集上跑一次肉眼检查预测的类别是否跟实际目标一致。3. 模型训练与可视化调优3.1 环境准备与训练脚本训练环境我用的是conda创建独立环境避免不同项目之间的包冲突。主要的包是ultralytics、torch、opencv-python。如果你有NVIDIA显卡先装好CUDA和cuDNN再用GPU版本的PyTorch。装完后可以用一行代码检查显卡是否可用import torch print(torch.cuda.is_available())输出True才能用GPU训练否则就在默默用CPU硬扛训练速度会慢到怀疑人生。训练脚本用ultralytics的Python接口写比命令行直观一些from ultralytics import YOLO model YOLO(yolov8n.pt) model.train( datadataset/data.yaml, epochs100, batch16, imgsz640, workers4, device0, )这里有个选型问题。YOLOv8有n、s、m、l、x几个版本我项目里先用了n跑通流程后面换成s因为轨道异物检测对精度要求更高但工控机性能有限。如果你显存充足、推理设备也够强可以上m。但从工程角度我建议先小模型跑通再根据实测效果升级模型不要一上来就搞l或x训练和部署都痛苦。3.2 训练过程指标与可视化结果解读训练结束后ultralytics会在run目录下生成一堆可视化文件其中最常见的是results.png和confusion_matrix.png。新手拿到手最容易蒙圈我一个个说。results.png里有几根曲线train/box_loss边框回归损失正常是下降趋势train/cls_loss分类损失下降且最终收敛val/cls_loss、val/box_loss验证集上的损失比训练集损失略高是正常的metrics/precision、metrics/recall精确率和召回率越高越好metrics/mAP50、metrics/mAP50-95核心指标。我看模型的习惯是先看mAP50也就是IoU阈值0.5下的平均精度再看mAP50-95后者更严格、对框的定位精度更敏感。对于异物检测这类偏“找出目标”的任务mAP50达到90以上就是可用的状态。mAP50-95受小目标影响大轨道场景里目标偏小这个指标如果低于50也不用太慌重点看实际测试效果。confusion_matrix.png可以看看哪些类别之间容易混淆。我项目里“tool”和“stone”在远距离时经常互相认错因为小尺寸下纹理信息丢失两者轮廓又相似。后来我通过增加远距离样本以及把置信度阈值调高误报才降下来。3.3 调参与常见误区很多新手喜欢到处调超参我自己的经验是大部分参数保持默认就可以重点处理以下几个imgsz。轨道目标小我建议至少用640如果显存允许试试768或896小目标检测会有明显提升。但推理速度会下降要平衡。batch。显存不够就调小比如从16降到8。不要因为batch小就慌了YOLOv8自动学习率调度会帮你补偿一部分。early stopping。ultralytics默认有早停机制patience设为默认就行。不要一看loss降得慢就手动中断给训练多留一点耐心。数据增强。只在效果不稳的时候调不要一开始就开太多花活。还有一个典型误区是“训练集损失一直降但mAP不动”。这通常是数据问题比如某个类别样本太少或者标注框不准确。别急着加深模型先回到数据层面查一下。3.4 导出模型并测试训练完的权重文件是best.pt部署的时候我习惯导出成ONNX格式因为ONNX在CPU上推理速度比原始PyTorch模型快而且后续可以转成其他平台格式。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) model.export(formatonnx, imgsz640)导出后才能拿真实场景测试一波。用一段简单的推理代码把检测结果画到图片上from ultralytics import YOLO model YOLO(best.onnx) results model.predict(test_01.jpg, conf0.25) result results[0] result.save(filenameoutput.jpg)这一步看起来简单却在项目里很关键。用PyQt5做界面时推理部分其实就是这个predict调用只不过从摄像头取帧、循环调用、实时绘制。如果你在这里能稳定输出检测结果那后面做界面就顺了。4. PyQt5界面设计与可视化展示4.1 界面布局、交互与组件PyQt5界面的核心是“控制端”和“显示端”分离。我是这样布局的左侧主区域QLabel显示实时画面检测框和类别文字直接画在帧上右侧控制面板模式选择本地图片、视频文件、摄像头开始/停止按钮置信度阈值滑条告警状态指示灯日志输出区显示检测记录、当前帧率、告警信息。控件整体用QVBoxLayout和QHBoxLayout组合起来就行布局代码本身不复杂。真正要注意的是不要把检测逻辑塞进UI事件里。比如你在“开始”按钮的点击槽函数里直接写一个while循环读摄像头那么界面会在循环期间完全卡死窗口拖不动、按钮点不了。4.2 实时视频推理线程设计PyQt5的界面运行在主线程也就是事件循环线程。所有耗时操作比如摄像头读取和解码、YOLO模型推理都应该放到子线程子线程处理完再通过信号把结果传回主线程更新界面。我用的方案是创建两个QThread子类也可以合并成一个VideoThread负责读取视频流/摄像头把原始帧信号发给DetectThreadDetectThread收到帧后做模型推理再把绘制了检测框的帧信号发回主线程显示。主线程收到信号后用setPixmap更新QLabel即可。线程之间传递数据用pyqtSignal内部用队列缓冲避免积压。这里有一个细节模型第一次推理时需要加载权重、做预热首帧耗时明显长于后续帧。所以启动时可以先丢一帧给模型“暖机”不然界面显示的可能是延迟很大的画面。4.3 画面绘制与检测结果展示OpenCV默认读出来的图像是BGR格式而Qt显示要的是RGB中间需要一个转换。这一行代码我几乎每次都要写贴出来方便直接抄from PyQt5.QtGui import QImage rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape qt_image QImage(rgb_image.data, w, h, ch * w, QImage.Format_RGB888) pixmap QPixmap.fromImage(qt_image)检测框的绘制我建议直接在OpenCV上完成然后再转成QImage。不要在Qt侧重新绘制框那样又要管理坐标映射容易出偏差。YOLOv8的predict结果里有boxes.xyxy取出坐标后直接用cv2.rectangle画就行。告警逻辑可以这样设计当检测到某类异物且置信度超过阈值时通过信号让主线程把状态指示灯的样式改为红色并在日志区写入告警记录。如果项目还要做声光联动可以在信号触发的位置预留一个硬件控制函数接口后续接串口或GPIO都方便。5. 环境配置与常见问题排查实录5.1 环境部署清单最后部署阶段环境配置是个小门槛。我整理一份当前项目里用到的核心依赖清单依赖版本建议说明Python3.10兼容性好PyQt5和ultralytics都支持PyQt55.15.x稳定版本直接用pip装ultralytics8.xYOLOv8训练和推理框架torch2.x CUDAGPU训练必需CPU部署可降低opencv-python4.8图像读取、处理、绘制onnxruntime1.16CPU/GPU推理ONNX模型建环境的命令也很简单conda create -n rail_env python3.10 conda activate rail_env pip install pyqt5 ultralytics opencv-python onnxruntime如果要用GPU训练再单独装对应版本的torch不然用CPU跑一百个epoch会比较煎熬。5.2 新手最容易踩的坑我把实际碰到的几个高频问题整理成一张排查表基本覆盖了从启动到运行的常见坑问题现象原因解决办法程序启动后闪退无报错Qt平台插件异常或OpenCV DLL冲突检查环境变量避免混装多个OpenCV版本用纯Python环境重装PyQt5摄像头打不开画面黑屏摄像头被占用或分辨率过高先释放其他进程设置摄像头分辨率如1280x720降低采集压力模型文件加载失败路径含中文或空格所有路径改成英文模型文件和数据集路径都不要放桌面训练时报CUDA out of memorybatch或imgsz太大调低batch到4或8imgsz降到640必要时用GPU显存清理工具推理帧率很低模型太大或input尺寸太高换YOLOv8n/s导出FP16的ONNX检测间隔加几帧画面颜色偏蓝偏绿BGR和RGB没有转换在QImage转换之前先做cv2.cvtColor界面卡死无响应推理写在了主线程用QThread把检测逻辑和UI分离5.3 实测效果与优化心得我最后用一套模拟轨道现场的视频做测试包含三个主要类别的异物样本。整体效果是在1280x720分辨率下GPU推理能稳定跑到40到50帧每秒CPU上使用ONNX大约10帧每秒检测精度上近处大目标基本全部能框住远处小目标会有漏检但置信度阈值从0.25调到0.15之后召回率明显提升代价是误报变多。这里有个经验不要只看单张图片的效果连续视频流里的抖动更值得关注。模型在单帧里偶尔漏一两次没关系但如果是连续性漏报就要考虑是不是这一帧的图像出现了反光、模糊或者目标严重遮挡。针对这些情况我加了一个简单的帧间平滑逻辑同一个目标连续几帧都出现了才触发告警如果只出现一帧则暂时记录不报警。这个逻辑大大降低了现场误报率实现也不复杂就是在PyQt5里维护一个目标ID到连续帧数的字典而已。最后再分享一个小技巧在界面里除了实时显示画面我建议顺手加一个“保存告警截图”的功能。每次检测到有异物且置信度超过告警阈值时就把当前帧保存到单独目录文件名带上时间戳和类别。这功能写起来只要几行代码但后续调试模型、复盘漏报误报时这些截图就是最好的第一手素材。我项目里有一半的调优思路都是靠翻告警截图找出来的。
阅读完成 · 觉得有帮助?
咨询建站