ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于YOLOv8的铁路轨道检测系统:从训练到PyQt5部署全流程

基于YOLOv8的铁路轨道检测系统:从训练到PyQt5部署全流程 简介本资源为基于YOLOv8的铁路轨道检测系统完整项目包面向计算机、人工智能、通信工程、自动化等专业的在校学生与教师适合作为毕业设计、课程设计或大作业的参考方案也可供初学者进阶学习目标检测全流程。包内共97个文件以70个Python源码文件为核心辅以4个pt权重文件、5个xml配置、12个pyc缓存及txt说明文档等压缩包约24.21MB涵盖模型训练、推理检测、可视化界面与部署说明等模块。项目已通过运行测试可生成核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图并附带可视化页面与视频检测示例便于直观展示检测效果。目前已有46人学习下载读者可据此快速复现铁路轨道检测流程理解数据组织、模型训练与界面交互的实现思路并在此基础上修改扩展功能。1. 铁路轨道检测为什么值得用 YOLOv8 重做一遍铁路轨道检测这个方向过去几年在工业视觉圈里一直是个看着简单、做起来全是坑的活。传统做法要么靠人工巡检要么用 OpenCV 那套边缘检测加霍夫变换硬拟合钢轨直线白天光照均匀还行一到隧道口、雨雾天、道砟反光或者扣件阴影叠上来阈值调到头也压不住误检。YOLOv8 这类单阶段检测器把找轨道从几何拟合变成了目标检测问题扣件缺失、轨道异物、轨面伤损都能统一到一套标注和训练流程里泛化能力比手写规则强一个量级。这套基于 YOLOv8 的铁路轨道检测系统打包了源码、完整数据集、可视化界面和部署教程定位很清楚让毕设或课程设计的人不用从零攒数据、不用自己搭 PyQt 界面、不用啃 Ultralytics 源码拿到就能跑通一条从训练到推理的完整链路。适合两类人——一类是刚接触深度学习检测、需要一个能跑通全流程的工程模板的学生另一类是想把轨道巡检从规则方案迁到模型方案、但不想在数据标注和界面开发上耗时间的工程师。下面按环境怎么搭、数据怎么处理、模型怎么训、界面怎么接、坑在哪的顺序拆开讲。2. 环境搭建与数据集结构先把地基打平2.1 用 conda 隔离 YOLOv8 运行环境铁路轨道检测这套代码依赖 Ultralytics 官方库而 Ultralytics 对 PyTorch 版本比较敏感直接装在系统 Python 里很容易和已有的 torch 版本打架。我一般用 conda 建一个独立环境Python 版本锁在 3.9 或 3.10这两个版本和 PyTorch 2.x 的兼容性最稳。# 创建独立环境Python 3.10 对 torch 2.x 支持最好 conda create -n rail_yolov8 python3.10 -y conda activate rail_yolov8 # 安装 PyTorchCPU 版本先用官方源有 GPU 再换 cu118/cu121 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 Ultralytics 和界面依赖 pip install ultralytics opencv-python pyqt5 numpy pandas matplotlib这里有个参数要留意--index-url后面跟的地址决定了装 CPU 版还是 GPU 版。如果你机器上有 NVIDIA 显卡把cpu换成cu121对应 CUDA 12.1装完用torch.cuda.is_available()验证一下返回是不是 True。没有显卡也别慌YOLOv8n 这种小模型在 CPU 上跑单张 640×640 推理大概 100~200ms做毕设演示完全够用只是训练会慢很多建议训练阶段借一台带显卡的机器或者用云上的按量实例。装完 Ultralytics 后终端敲yolo checks会打印环境自检信息重点看三行Python 版本、torch 版本、CUDA 是否可用。如果 CUDA 显示不可用但你确实有显卡八成是 torch 装成了 CPU 版重装对应 CUDA 版本的 wheel 即可。2.2 数据集目录必须按 YOLO 格式摆YOLOv8 对数据集的目录结构有硬性要求摆错了训练脚本直接报no labels found。标准结构是这样rail_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 训练集标签与图片同名 .txt │ └── val/ └── data.yaml # 数据集配置文件标签文件是每行一个目标的 txt格式为类别id 中心x 中心y 宽 高后四个值都是归一化到 0~1 的相对坐标。这一点和 VOC 的 XML、COCO 的 JSON 都不一样很多人第一次转格式就栽在这——忘了归一化或者把左上角坐标当成中心坐标写进去训练时 loss 死活不降。data.yaml是训练入口的配置文件内容长这样# 数据集根路径建议写绝对路径避免相对路径踩坑 path: /home/user/rail_dataset train: images/train val: images/val # 类别数和类别名顺序必须和标注时的 id 对应 nc: 4 names: 0: rail # 钢轨 1: fastener # 扣件 2: missing_fastener # 扣件缺失 3: obstacle # 轨道异物nc和names的对应关系是整个数据集里最容易出错的地方。标注时如果扣件缺失标成了 id 2这里 names 里 2 就必须是 missing_fastener错一位模型学出来的类别就全乱套。改完 yaml 建议用 Ultralytics 自带的校验跑一遍它会检查图片和标签是否一一对应、坐标是否越界。2.3 用脚本批量校验图片与标签配对拿到数据集后别急着开训先跑一段校验脚本把有图无标签有标签无图坐标越界这三类问题筛出来。这三类问题在训练时不会直接报错但会让模型学到噪声表现为 loss 震荡或者某类 AP 异常低。import os from pathlib import Path img_dir Path(rail_dataset/images/train) lbl_dir Path(rail_dataset/labels/train) img_stems {p.stem for p in img_dir.glob(*.jpg)} lbl_stems {p.stem for p in lbl_dir.glob(*.txt)} # 找出不配对的样本 only_img img_stems - lbl_stems only_lbl lbl_stems - img_stems print(f有图无标签: {len(only_img)} 个 - {list(only_img)[:5]}) print(f有标签无图: {len(only_lbl)} 个 - {list(only_lbl)[:5]}) # 检查坐标是否越界 bad [] for txt in lbl_dir.glob(*.txt): for line in txt.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: bad.append((txt.name, 字段数不对)) continue _, cx, cy, w, h map(float, parts) if not all(0 v 1 for v in (cx, cy, w, h)): bad.append((txt.name, f坐标越界: {cx},{cy},{w},{h})) print(f异常标签: {len(bad)} 个) for name, reason in bad[:10]: print(f {name}: {reason})这段脚本干三件事用集合差集找出图片和标签不配对的样本逐行读标签检查字段数是不是 5检查归一化坐标有没有超出 0~1。跑完如果only_img和only_lbl都是 0、bad也是 0说明数据集结构干净可以进训练环节。如果有问题only_img里的图要么补标注要么删掉坐标越界的标签要回标注工具里重新框。提示图片格式建议统一成 jpgpng 和 jpg 混用虽然 Ultralytics 能读但某些版本在缓存标签时会因为扩展名不一致漏读统一格式能省掉一类玄学问题。3. 训练配置与参数调优让模型真正学到轨道特征3.1 从预训练权重起步别从零训铁路轨道检测的数据集规模通常不大几千张到一两万张量级从零初始化训练很容易过拟合而且收敛慢。标准做法是加载 COCO 预训练的 yolov8n.pt 或 yolov8s.pt 做迁移学习让骨干网络已经学到的边缘、纹理特征直接复用。# 从预训练权重开始训练epochs 设 100imgsz 640 yolo detect train \ modelyolov8n.pt \ datarail_dataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/rail \ nameexp1参数逐个说清楚model指定预训练权重n 是最小的 nano 版本速度快适合毕设演示追求精度可以换 s 或 mepochs是训练轮数100 轮对几千张图通常够配合patience20做早停——20 轮验证指标不涨就自动停省得白跑imgsz640是输入分辨率轨道检测里小目标扣件比较多如果显存够可以提到 768 或 896小目标召回会明显改善batch16是批大小显存不够就往下调调到 8 甚至 4 都行但太小会让 BN 层统计不稳建议不低于 8lr00.01是初始学习率迁移学习场景下这个值比较稳如果 loss 一开始就炸到 nan降到 0.001 再试。训练启动后终端会打印每一轮的 box_loss、cls_loss、mAP50、mAP50-95。重点盯 mAP50-95这个指标比 mAP50 严格能反映框的定位精度。正常情况下前 10 轮 mAP 涨得很快30 轮后趋缓如果 50 轮了 mAP50 还在 0.3 以下八成是数据或配置有问题别硬等。3.2 学习率和数据增强的关键参数YOLOv8 默认开了一整套数据增强包括 mosaic、mixup、随机翻转、HSV 抖动。轨道检测场景下mosaic 增强把四张图拼成一张能提升小目标检测但轨道是长条状目标mosaic 拼接后轨道会被截断有时候反而干扰学习。我的经验是前 80% 的 epoch 开 mosaic最后 20% 关掉让模型在接近真实分布的图上收尾。# 精细调参版本关闭最后阶段的 mosaic yolo detect train \ modelyolov8s.pt \ datarail_dataset/data.yaml \ epochs150 \ imgsz768 \ batch12 \ lr00.01 \ lrf0.01 \ mosaic1.0 \ close_mosaic30 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ fliplr0.5 \ projectruns/rail \ nameexp2lrf0.01是最终学习率相对初始学习率的比例也就是从 0.01 余弦退火到 0.0001这个衰减策略对收敛稳定性帮助很大。close_mosaic30表示最后 30 轮关闭 mosaic这是 Ultralytics 内置的参数比手动改配置省事。hsv_h/s/v控制色调、饱和度、明度的抖动幅度轨道场景光照变化大这三个值适当调大能提升鲁棒性但 hsv_h 别超过 0.02否则钢轨颜色被抖得不像钢轨反而掉点。fliplr0.5是水平翻转概率轨道左右对称翻转增强安全但注意如果数据集里有方向性目标比如单向的异物翻转会引入错误标签这种情况要关掉。3.3 训练过程监控与损失曲线判读训练跑起来后runs/rail/exp2/目录下会生成results.csv和一堆曲线图。results.csv 每行是一轮列包括 train/box_loss、train/cls_loss、metrics/mAP50、metrics/mAP50-95 等。想自己画损失曲线用 pandas 读出来画就行import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/rail/exp2/results.csv) df.columns df.columns.str.strip() # 列名可能带空格先清理 fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(df[epoch], df[train/box_loss], labelbox_loss) axes[0].plot(df[epoch], df[train/cls_loss], labelcls_loss) axes[0].set_xlabel(epoch); axes[0].set_ylabel(loss); axes[0].legend() axes[1].plot(df[epoch], df[metrics/mAP50], labelmAP50) axes[1].plot(df[epoch], df[metrics/mAP50-95], labelmAP50-95) axes[1].set_xlabel(epoch); axes[1].set_ylabel(mAP); axes[1].legend() plt.tight_layout(); plt.savefig(train_curve.png, dpi150)判读曲线有几个经验点box_loss 和 cls_loss 应该整体下降中间有波动正常但如果持续上升多半是学习率太大或者标签有问题mAP50 和 mAP50-95 应该同步上升如果 mAP50 涨但 mAP50-95 不涨说明模型能找对位置但框不准可以试试提高分辨率或检查标注框是否贴合如果训练 loss 一直降但验证 mAP 早早停滞甚至下降就是过拟合了加数据增强、加 dropout 或者减模型容量。注意results.csv 的列名在不同 Ultralytics 版本里可能有细微差异比如空格、大小写。读之前先 print 一下列名别硬编码列名导致 KeyError。4. 可视化界面与推理部署把模型变成能演示的系统4.1 PyQt5 界面接推理的最小闭环毕设和课程设计通常要求有个可视化界面能选图片、点按钮、显示检测结果。PyQt5 是最省事的方案核心逻辑就三步加载模型、读图推理、把带框的结果画到 QLabel 上。import sys import cv2 from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget, QFileDialog from PyQt5.QtGui import QImage, QPixmap from ultralytics import YOLO class RailDetectWindow(QMainWindow): def __init__(self): super().__init__() self.model YOLO(runs/rail/exp2/weights/best.pt) # 加载训练好的权重 self.setWindowTitle(铁路轨道检测系统) self.label QLabel(请选择图片) self.btn QPushButton(选择图片并检测) self.btn.clicked.connect(self.detect) layout QVBoxLayout() layout.addWidget(self.label); layout.addWidget(self.btn) container QWidget(); container.setLayout(layout) self.setCentralWidget(container) def detect(self): path, _ QFileDialog.getOpenFileName(self, 选图, , Images (*.jpg *.png)) if not path: return results self.model(path, conf0.25, iou0.45) # 推理 annotated results[0].plot() # 画框返回 BGR ndarray rgb cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qimg QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(qimg).scaled(800, 600)) if __name__ __main__: app QApplication(sys.argv) win RailDetectWindow(); win.show() sys.exit(app.exec_())conf0.25是置信度阈值低于这个值的框不显示轨道检测里如果漏检多就降到 0.15误检多就提到 0.4。iou0.45是 NMS 的 IoU 阈值控制重叠框的合并轨道是长条目标框之间重叠多这个值可以适当调高到 0.5 减少误合并。results[0].plot()直接返回画好框的图省得自己写画框代码但它默认用类别名做标签中文类别名需要额外配置字体否则显示成方块。4.2 视频流推理与帧率控制图片检测跑通后视频检测是自然延伸。核心是把model(path)换成逐帧推理但要注意帧率控制否则 CPU 上跑视频会卡成幻灯片。import cv2 from ultralytics import YOLO model YOLO(runs/rail/exp2/weights/best.pt) cap cv2.VideoCapture(rail_video.mp4) fps cap.get(cv2.CAP_PROP_FPS) frame_interval max(1, int(fps / 10)) # 目标 10fps 处理跳帧 idx 0 while cap.isOpened(): ret, frame cap.read() if not ret: break if idx % frame_interval 0: results model(frame, conf0.25, verboseFalse) frame results[0].plot() cv2.imshow(Rail Detect, frame) if cv2.waitKey(1) 0xFF ord(q): break idx 1 cap.release(); cv2.destroyAllWindows()frame_interval是关键参数它决定每隔几帧做一次推理。原视频 30fps如果每帧都推理CPU 上根本跟不上跳帧到 10fps 处理显示时仍然逐帧播放视觉上流畅度够用。verboseFalse关掉 Ultralytics 每帧的日志打印不然终端会被刷屏。如果部署到 RK3588 这类嵌入式板子上思路一样只是要把 PyTorch 模型转成 ONNX 或 RKNN 格式用板子的 NPU 加速这部分转换流程和 PC 端推理是两套东西需要单独处理。4.3 模型导出与跨平台部署训练完的 .pt 权重只能在装了 PyTorch 的环境里跑要部署到没有 Python 环境的机器或者嵌入式设备得导出成通用格式。YOLOv8 支持导出 ONNX、TensorRT、OpenVINO 等多种格式。# 导出 ONNX动态 batch 和动态尺寸 yolo export modelruns/rail/exp2/weights/best.pt formatonnx dynamicTrue opset12 # 导出 OpenVINO适合 Intel CPU 加速 yolo export modelruns/rail/exp2/weights/best.pt formatopenvino halfTruedynamicTrue让导出的 ONNX 支持动态输入尺寸部署时不用固定 640×640灵活一些但推理速度略慢。opset12是 ONNX 算子集版本12 兼容性最好别盲目追新。halfTrue是 FP16 量化模型体积减半、推理加速但精度会掉一点点轨道检测这种对精度敏感的场景建议先对比 FP32 和 FP16 的 mAP 再决定。导出后在同目录会生成对应格式的文件ONNX 用 onnxruntime 加载OpenVINO 用 openvino.runtime 加载推理接口和 Ultralytics 略有不同需要改几行代码。5. 避坑与排查那些训练日志不会告诉你的问题5.1 现象训练 loss 正常下降但 mAP 一直是 0原因标签类别 id 和 data.yaml 里的 names 顺序对不上或者标签文件里出现了 data.yaml 没定义的类别 id。模型在学但学的是错的映射验证时按正确类别算 AP 自然是 0。解决跑一遍标签统计脚本把所有出现过的类别 id 列出来和 data.yaml 的 names 逐一核对。统计命令cat labels/train/*.txt | awk {print $1} | sort | uniq -c输出每个 id 的出现次数如果出现了 nc 范围外的 id就是标注时类别选错了。5.2 现象推理时框的位置整体偏移原因训练时的 imgsz 和推理时的输入尺寸不一致或者标注时用了非归一化坐标。YOLOv8 推理时会自动 resize但如果训练用 640、推理传原图不指定 imgsz内部会按默认 640 处理长宽比差异大的图会被拉伸框就偏了。解决推理时显式指定imgsz640和训练保持一致。如果原图长宽比很极端比如轨道巡检的窄长图考虑训练时就用 letterbox 填充而不是直接 resizeUltralytics 默认就是 letterbox一般不用改但要确认推理时没关掉。5.3 现象小目标扣件大量漏检原因扣件在 640×640 的输入里可能只有十几个像素YOLOv8 的 P3 特征图 stride 是 8太小的目标在深层特征图上就消失了。解决三个方向——提高输入分辨率到 896 或 1024换更大的模型yolov8m/l利用更丰富的特征在 data.yaml 里检查扣件类别的标注框是不是太小如果实际扣件在图中占比就很小可以考虑切图推理把大图切成小块分别检测再合并。5.4 现象训练到一半突然 OOM 崩溃原因mosaic 增强在训练后期会拼接不同尺寸的图某些 batch 的实际显存占用比预期高或者 dataloader 的 workers 开太多每个 worker 都缓存了一批图。解决降 batch size把workers从默认 8 降到 4 或 2开ampTrue混合精度训练显存占用能降 30% 左右如果还不行把cacheTrue关掉别把整个数据集缓存到内存。5.5 现象PyQt 界面显示检测结果时颜色错乱原因OpenCV 读图是 BGR 通道Qt 的 QImage 默认按 RGB 解析直接传 BGR 数据过去红蓝通道就反了。解决在构造 QImage 之前做一次cv2.cvtColor(img, cv2.COLOR_BGR2RGB)这一步不能省。另外 QImage 构造时要注意 bytesPerLine 参数传ch * w而不是默认值否则图片宽度不是 4 的倍数时会出现斜纹。6. 把 mAP 再往上推一档几个我反复验证过的技巧模型跑通之后大部分人卡在 mAP50-95 上不去0.6 左右就顶住了。分享几个我在轨道检测场景里反复验证有效的做法。第一个是难例挖掘。训练完第一版模型后用它在验证集上推理把漏检和误检的图挑出来人工补标或修正后加进训练集再训一轮。这个循环做两到三次mAP 通常能涨 3~5 个点。轨道检测的难例集中在隧道口逆光、雨雾天、道岔区域这些场景在原始数据集里往往样本少补进去收益最大。第二个是锚框和损失权重的微调。YOLOv8 是无锚框的但损失里 box 和 cls 的权重可以调。轨道检测里定位精度比分类更重要扣件缺失和扣件正常框的位置差一点类别就错了可以适当提高 box loss 的权重。在训练命令里加box7.5默认 7.5可以试 10观察 mAP50-95 的变化。第三个是测试时增强TTA。推理时对同一张图做翻转、多尺度缩放把多次推理结果融合能稳定涨 1~2 个点代价是推理时间翻几倍。Ultralytics 推理时加augmentTrue就能开 TTA适合对精度要求高、对速度不敏感的离线检测场景。技巧预期涨幅代价适用场景难例挖掘3~5 mAP人工标注时间有标注人力box 权重调高1~2 mAP需重新训练定位精度要求高TTA 推理1~2 mAP推理慢 3~5 倍离线检测提高输入分辨率2~4 mAP显存和耗时增加小目标多最后说个习惯每次改完参数训练我都会把 results.csv 和权重文件按日期_参数摘要命名归档比如20250115_imgsz768_closeMosaic30。轨道检测调参是个反复试的过程没有归档两周后你根本记不清哪个权重是哪个配置训出来的想复现最佳结果只能重跑这个后悔药我吃过不止一次。希望帮到你。本文还有配套的精品资源点击获取
返回列表