简介:这份PDF文档面向轨道交通运维人员、计算机视觉学习者与安全系统开发者,围绕YOLOv11在道岔异物检测与列车进站预警中的落地应用展开,帮助读者理解如何用单阶段目标检测算法替代低效人工巡检,提升轨道交通安全管理的智能化水平。资源包共1个PDF文件,大小约1.93MB,支持目录章节跳转、阅读器左侧大纲显示与章节快速定位,查阅体验完整流畅。文档共27页,内容涵盖YOLOv11网络结构、训练策略与损失函数,道岔异物检测系统的硬件选型、摄像头布局、图像采集与预警模块设计,列车进站预警的分层架构、数据采集处理与算法融合,以及系统集成、测试优化和城市轨道、高铁站等应用案例分析。已有90人学习关注,适合希望掌握目标检测工程化流程、构建轨道交通安全预警方案的读者参考借鉴。
1. 道岔异物检测为什么值得单独做一套 YOLOv11 系统
道岔是轨道线路上结构最复杂、工况最恶劣的一段,尖轨与基本轨之间的缝隙天然就是异物收集器——螺栓、道砟碎块、塑料布、编织袋、甚至旅客掉落的行李箱轮子,都可能卡在转辙机动作范围内。一旦尖轨无法密贴,轻则进路无法建立,重则列车脱轨。传统做法靠巡道工和司机目视,夜间、雨雾、弯道条件下漏检率极高,而列车进站前留给司机的反应时间往往只有十几秒。
这套「轨道交通安全-YOLOv11道岔异物检测与列车进站预警系统」要解决的就是把「人眼盯」换成「模型盯 + 分级预警」:在道岔区域固定安装摄像机,用 YOLOv11 做异物目标检测,把检测结果换算成轨道坐标,再结合列车进站信号触发不同等级的预警。它适合三类人:做轨道交通智能运维的算法工程师、想把 YOLOv11 落到工业现场的视觉开发者、以及需要给既有线路加装低成本预警装置的集成商。整套方案的核心难点不在模型本身,而在小目标、夜间成像和误报抑制这三件事上。
2. YOLOv11 检测道岔异物的选型理由与数据准备
2.1 为什么是 YOLOv11 而不是 v8 或 RT-DETR
道岔异物检测的工程约束很明确:边缘设备算力有限、要求实时、目标尺度跨度大。YOLOv11 相比 YOLOv8 在同等精度下参数量更少,C3k2 模块替换了部分 C2f,颈部结构对多尺度特征的融合更充分,这对「一颗螺栓」和「一整块塑料布」同时出现在一帧里的场景很关键。RT-DETR 精度不错,但 Transformer 解码器在 Jetson 这类设备上延迟不稳定,工业现场最怕的就是偶发卡顿导致漏帧。
我一般会按这个顺序选型:先确认部署硬件,再确认帧率下限,最后才比精度。如果现场是 Jetson Orin Nano 级别,YOLOv11n 或 YOLOv11s 是甜点;如果是带独立显卡的工控机,可以上到 YOLOv11m。别一上来就追大模型,道岔场景的瓶颈从来不是模型容量,而是数据质量和后处理逻辑。
2.2 数据集怎么标:类别设计与小目标标注规范
道岔异物的类别不要按「物体是什么」分,要按「危害等级」分。我通常设四类:bolt(螺栓类金属件)、debris(道砟、石块)、soft(塑料布、编织袋)、unknown(其他)。这样后处理时可以直接按类别映射预警等级,不用再查表。
标注规范有三条血泪经验:第一,小于 16×16 像素的目标必须放大原图后再标,否则标注框抖动会直接毁掉小目标召回;第二,被尖轨部分遮挡的异物要标可见部分,不要脑补完整框;第三,同一异物在连续帧里只标关键帧,避免近似重复样本导致过拟合。
# 数据集目录结构建议(YOLO 格式) # datasets/switch/ # ├── images/ # │ ├── train/ # 训练集图片 # │ ├── val/ # 验证集图片 # │ └── test/ # 测试集图片 # ├── labels/ # │ ├── train/ # 对应 txt 标注 # │ ├── val/ # │ └── test/ # └── switch.yaml # 数据集配置文件上面这个结构是 YOLOv11 官方训练脚本默认读取的格式,images和labels必须严格同名对应,否则训练时会出现「找不到标签」的静默失败——它不会报错,只是把那批图当负样本,这是最常见的翻车点之一。
2.3 switch.yaml 的关键参数与类别映射
# switch.yaml path: /data/datasets/switch # 数据集根目录,建议用绝对路径 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 test: images/test # 测试集相对路径 names: 0: bolt # 螺栓类金属件,危害等级高 1: debris # 道砟石块,危害等级中 2: soft # 塑料布编织袋,危害等级中 3: unknown # 其他异物,危害等级待定path用绝对路径能避免在不同机器上训练时路径解析不一致的问题。names的顺序一旦确定就不能改,改了之后旧权重全部作废。类别数量直接决定检测头输出通道数,4 类对应输出4 + 5 = 9个通道(4 个类别概率 + 1 个置信度 + 4 个框坐标),这个数字在排查模型输出异常时经常要用到。
3. 训练配置、小目标优化与推理结果保存
3.1 从零训练一条可复现的命令
# 使用 YOLOv11n 在道岔数据集上训练 yolo detect train \ model=yolo11n.pt \ # 预训练权重,没有就写 yolo11n.yaml 从零开始 data=switch.yaml \ # 数据集配置 epochs=200 \ # 道岔数据量通常不大,200 轮够收敛 imgsz=1280 \ # 关键:小目标必须放大输入尺寸 batch=8 \ # 显存不够就降到 4 device=0 \ # 单卡写 0,多卡写 0,1 workers=8 \ # 数据加载线程,机械硬盘别超过 4 project=runs/switch \ # 输出目录 name=exp01 \ # 实验名,方便对比 patience=50 \ # 50 轮无提升就早停 cache=True # 内存够就开,能显著加速imgsz=1280是这套方案里最重要的一个参数。道岔异物里螺栓可能只占原图千分之几的面积,用默认 640 训练,特征图下采样到 1/32 后目标只剩几个像素,召回率会惨不忍睹。代价是显存和推理时间翻倍,所以要在硬件和精度之间做取舍。patience=50配合epochs=200是防止过拟合的后悔药,道岔场景负样本(无异物帧)占比高,模型很容易在后期把噪声学成特征。
3.2 小目标优化的三个可落地手段
第一个手段是输入分辨率,上面已经说了。第二个手段是数据增强策略调整:默认的mosaic对小目标有帮助,但mixup在道岔场景反而有害,因为异物和背景的边界会被混得模糊。建议在default.yaml里把mixup设为 0,mosaic保持 1.0,scale控制在 0.5 以内。
第三个手段是检测头层面的改进。YOLOv11 默认三个检测头分别对应 80×80、40×40、20×20(以 640 输入为例),小目标主要靠 P3 头。如果现场异物普遍小于 20 像素,可以加一个 P2 检测头,代价是参数量和推理时间上升。我一般先用默认结构跑一版基线,看验证集里小目标的召回率,低于 0.6 再考虑加 P2。
# 验证集小目标召回率快速统计 from ultralytics import YOLO import numpy as np model = YOLO("runs/switch/exp01/weights/best.pt") metrics = model.val(data="switch.yaml", imgsz=1280, conf=0.25) # 按目标面积统计召回,定位小目标短板 # metrics.box.maps 给出每个类别的 mAP50-95 for i, name in enumerate(metrics.names.values()): print(f"{name}: mAP50-95 = {metrics.box.maps[i]:.4f}")这段代码跑完能直接看出哪个类别拖后腿。如果bolt的 mAP 明显低于其他类,基本可以判定是小目标问题,优先调imgsz和增强参数,而不是盲目加数据。
3.3 推理结果保存与预警触发逻辑
热词里「yolov11保存推理结果」和「yolov11预测后保存」是高频需求,工业现场确实需要把每一帧的检测结果落盘,用于事后追溯和模型迭代。
from ultralytics import YOLO import cv2 import json import time model = YOLO("runs/switch/exp01/weights/best.pt") cap = cv2.VideoCapture("switch_cam.mp4") save_dir = "runs/infer" frame_id = 0 # 危害等级映射:类别 -> 预警级别 LEVEL = {0: 2, 1: 1, 2: 1, 3: 0} # 2=紧急 1=警告 0=记录 while cap.isOpened(): ret, frame = cap.read() if not ret: break frame_id += 1 results = model.predict(frame, imgsz=1280, conf=0.3, verbose=False) records = [] max_level = 0 for box in results[0].boxes: cls = int(box.cls[0]) conf = float(box.conf[0]) xyxy = box.xyxy[0].tolist() level = LEVEL.get(cls, 0) max_level = max(max_level, level) records.append({ "cls": cls, "conf": round(conf, 3), "bbox": [round(v, 1) for v in xyxy], "level": level }) # 保存带框图片 annotated = results[0].plot() cv2.imwrite(f"{save_dir}/frame_{frame_id:06d}.jpg", annotated) # 保存结构化结果,方便后续检索 with open(f"{save_dir}/frame_{frame_id:06d}.json", "w") as f: json.dump({"frame": frame_id, "ts": time.time(), "max_level": max_level, "objects": records}, f) # 预警触发:只有紧急级别才联动进站信号 if max_level == 2: print(f"[ALERT] frame {frame_id} 检测到高危异物,触发进站预警")这段代码的逻辑分三层:检测、落盘、预警。conf=0.3是推理阈值,比训练时的默认值低,因为工业场景宁可多报也不能漏报,误报可以靠后续帧确认过滤。LEVEL映射把类别转成预警等级,只有bolt这类金属件才触发最高级预警,塑料布只记录不联动信号,这是避免误报导致列车无故停车的关键设计。落盘同时保存图片和 JSON,JSON 用于检索和统计,图片用于人工复核。
4. 列车进站预警的联动逻辑与部署要点
4.1 从检测框到轨道坐标的换算
检测框只是像素坐标,要触发预警必须知道异物在轨道的哪个位置。常见做法是在道岔区域预先标定四个轨道关键点,用透视变换把像素坐标映射到轨道平面坐标。
import cv2 import numpy as np # 图像中道岔区域的四个角点(需现场标定) src_pts = np.float32([[320, 480], [960, 480], [1200, 900], [80, 900]]) # 轨道平面上的对应坐标,单位毫米 dst_pts = np.float32([[0, 0], [4000, 0], [4000, 3000], [0, 3000]]) M = cv2.getPerspectiveTransform(src_pts, dst_pts) def pixel_to_track(x, y): """把检测框中心点换算成轨道平面坐标""" pt = np.array([[[x, y]]], dtype=np.float32) mapped = cv2.perspectiveTransform(pt, M) return mapped[0][0].tolist() # 判断异物是否落在尖轨动作区(危险区) def in_danger_zone(track_x, track_y): return 1500 < track_x < 2500 and 500 < track_y < 2000src_pts必须现场实测,不能凭感觉填。标定误差会直接放大成预警误判,我见过因为角点偏了 20 像素导致整个危险区偏移半米的案例。in_danger_zone的阈值根据实际道岔型号调整,不同型号的尖轨行程不一样。
4.2 进站预警的分级触发策略
预警不能一检测到异物就拉闸,那样现场会被误报淹没。我一般设三级:一级是「记录」,任何检测结果都落盘;二级是「警告」,异物进入道岔区域但未进入尖轨动作区,推送值班员;三级是「紧急」,异物进入尖轨动作区且列车进站信号已触发,直接联动信号系统。
列车进站信号可以从既有联锁系统取,也可以用一个简单的视觉判断:在进站方向画一条虚拟线,检测到列车越过就置位。后者成本低但可靠性差,只适合作为辅助。
4.3 Jetson Nano 部署 YOLOv11 的实操步骤
热词里「jetson nano 部署yolov11详细步骤」是很多人的刚需,这里给一条能跑通的路径。注意 Nano 算力有限,建议用 YOLOv11n 并把输入降到 640。
# 1. 刷好 JetPack 后确认环境 sudo apt update && sudo apt install -y python3-pip libopenblas-dev # 2. 安装 PyTorch(版本要和 JetPack 对应,别直接 pip install torch) # 以 JetPack 5.x 为例,用 NVIDIA 官方 wheel pip3 install torch-2.x.x-cp38-cp38-linux_aarch64.whl # 3. 安装 ultralytics pip3 install ultralytics # 4. 导出 TensorRT 引擎,推理速度能提升 2-3 倍 yolo export model=best.pt format=engine imgsz=640 half=True device=0 # 5. 用引擎推理 yolo detect predict model=best.engine source=0 imgsz=640 conf=0.3导出 TensorRT 时half=True开启 FP16,Nano 上能明显提速,但要注意精度损失,导出后必须用验证集重新测一遍 mAP,掉超过 2 个点就改回 FP32。device=0在 Nano 上就是它唯一的 GPU,不用改。
5. 避坑与排查:道岔异物检测最常见的五个翻车点
5.1 夜间红外画面下模型几乎全漏
现象:白天验证集 mAP 0.85,夜间实测召回不到 0.3。原因是训练集全是白天可见光图像,模型没学过红外成像的灰度分布。解决办法是补采夜间红外数据,至少占训练集 30%,并且在增强里加hsv_h=0(红外没有色调),只保留亮度和对比度扰动。
5.2 螺栓和道砟被反复混淆
现象:bolt和debris两个类别互相误判,mAP 都上不去。原因是两类目标在低分辨率下纹理接近,都是小尺寸高对比度斑点。解决办法有两个:一是提高输入分辨率到 1280 以上,让纹理差异显现;二是在标注阶段严格区分,金属件必须有明显反光特征才标bolt,否则归debris。
5.3 推理结果保存拖慢帧率
现象:开了逐帧保存后帧率从 25 掉到 8。原因是每帧都做 JPEG 编码和磁盘写入,I/O 成了瓶颈。解决办法是异步落盘:检测线程只把结果塞进队列,单独一个线程负责写盘,或者只在检测到目标时才保存,无异物帧只记 JSON 不存图。
5.4 预警频繁误触发导致值班员麻木
现象:一天触发几十次紧急预警,全是误报。原因是conf阈值设太低,加上没有做多帧确认。解决办法是引入时序滤波:连续 3 帧检测到同一位置的高危异物才触发紧急预警,单帧只记录。这个逻辑用简单的 IoU 跟踪就能实现,不需要上复杂的目标跟踪算法。
5.5 模型换机器后精度骤降
现象:训练机上 mAP 0.85,部署到现场工控机后掉到 0.6。原因是推理时的预处理不一致——训练用了 letterbox 填充,部署代码直接 resize 导致长宽比失真。解决办法是统一用 ultralytics 的predict接口,或者自己写预处理时严格复现 letterbox 逻辑,包括填充颜色和缩放比例。
6. 用 TensorRT 加速和时序滤波把误报压到可接受范围
部署到现场后,真正决定这套系统能不能活下来的不是 mAP,而是误报率。我踩过最深的一个坑是:模型精度明明够,但值班员用了两周就把它关了,因为误报太多。后来我把重心从「提精度」转到「压误报」,效果立竿见影。
第一个手段是 TensorRT 加速换来的帧率余量。YOLOv11n 在 Jetson Orin Nano 上 FP32 推理 640 输入大概 15 FPS,导出 FP16 引擎后能到 35 FPS 以上。多出来的帧率不要浪费,用来做多帧投票:维护一个长度为 5 的滑动窗口,只有同一目标在窗口内出现超过 3 次才上报。这个逻辑用检测框的 IoU 做关联即可,代码不到 30 行。
from collections import deque class TemporalFilter: def __init__(self, window=5, hit=3, iou_thr=0.5): self.window = deque(maxlen=window) # 保存最近若干帧的检测框 self.hit = hit self.iou_thr = iou_thr def _iou(self, a, b): # 计算两个框的 IoU x1, y1 = max(a[0], b[0]), max(a[1], b[1]) x2, y2 = min(a[2], b[2]), min(a[3], b[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) area_a = (a[2]-a[0]) * (a[3]-a[1]) area_b = (b[2]-b[0]) * (b[3]-b[1]) return inter / (area_a + area_b - inter + 1e-6) def update(self, boxes): """boxes: 当前帧的检测框列表,返回确认上报的框""" self.window.append(boxes) confirmed = [] for box in boxes: count = sum(1 for frame in self.window for b in frame if self._iou(box, b) > self.iou_thr) if count >= self.hit: confirmed.append(box) return confirmedwindow=5和hit=3是经验值,帧率高可以加大窗口,帧率低就减小。iou_thr=0.5对静止异物够用,如果异物会滚动,要降到 0.3 并配合位置预测。这个滤波器能把单帧噪声导致的误报压掉八成以上,代价是预警延迟 2 到 3 帧,在 30 FPS 下不到 100 毫秒,完全可以接受。
第二个手段是分区域设置不同阈值。道岔区域可以再细分成「尖轨动作区」「连接区」「外围区」,越靠近尖轨阈值越低(宁可误报),外围区阈值调高(减少干扰)。这个分区信息在标定阶段就一起做了,不增加运行时开销。
第三个手段是定期用现场数据回流重训。现场跑一个月攒下的误报样本,人工标一遍加进训练集,下一版模型误报率通常能再降一半。这个闭环才是工业视觉系统长期可用的根本,模型不是一次训练就完事的。
我现在的习惯是:每套现场系统上线前,先拿一周的离线视频跑一遍,统计误报次数,超过每天 5 次就不上线,回去调滤波参数和阈值。这个门槛看起来严,但能保证系统上线后不被关掉。希望帮到你。
本文还有配套的精品资源,点击获取