简介:这份文档面向轨道交通智能化方向的研究人员、工程技术人员及人工智能学习者,系统梳理计算机视觉技术在轨道交通领域的落地路径。内容从研究背景与国内外现状切入,先介绍图像采集、增强复原、分割与特征提取等基础原理,以及传统图像处理、深度学习和目标检测识别等常用算法,再分场景展开应用:信号系统中涵盖信号灯状态识别、信号机故障检测与列车定位跟踪;线路维护中涉及轨距水平检测、轨道变形与表面缺陷识别、道岔状态评估及桥梁隧道结构健康监测;运营管理部分则延伸至客流量实时监测等方向。资源为1个docx文档,压缩包约170KB,目录层级完整、章节编号清晰,便于按模块检索与引用。已有31人学习,适合作为课题调研、方案设计或论文写作的参考底稿。
1. 计算机视觉在轨道交通的落地全景:从站台监控到轨道巡检的完整技术链路
第一次拿到《计算机视觉技术在轨道交通领域的应用》这份文档时,我正蹲在某地铁车辆段的检修地沟边上,手里攥着一台刚烧掉保险的工业相机。那会儿我们想用视觉方案替代人工巡检轨道扣件,结果现场光照一变,模型直接“失明”。这份文档恰好把从信号系统、线路维护到运营管理、乘客服务的完整应用链路梳理了一遍,覆盖了目标检测、图像分割、行为识别、视觉定位等核心任务,也点出了环境适应性、实时性、数据标注这些真实工程里绕不开的坎。它适合两类人:一是刚接触轨道交通场景的视觉算法工程师,需要快速建立场景认知;二是做智能运维、智慧车站方案的集成商,想找技术选型依据。如果你正在纠结“视觉到底能在这行干哪些活、干到什么程度”,这份材料能帮你省掉至少两周的文献翻找时间。
2. 场景拆解与算法选型:信号识别、轨道缺陷、客流统计分别该用什么模型
2.1 信号灯状态识别:为什么传统颜色阈值法在隧道里会翻车
信号灯状态识别看起来是最简单的任务——红黄绿三分类,用颜色阈值加轮廓检测就能跑。但实际在隧道口、弯道、逆光条件下,颜色空间完全偏移,HSV阈值调一天也稳不住。文档里提到信号灯状态自动识别和信号机故障检测,我一般会直接上轻量级分类网络,把整张信号灯区域裁出来做分类,而不是先检测再分类。
具体做法是:用YOLOv8n或MobileNetV3做主干,输入尺寸压到320×320,在信号灯区域标注时留出足够背景,让模型自己学光照不变性。训练时加RandAugment和ColorJitter,色温偏移范围拉到±40%,亮度±30%。推理阶段用TensorRT量化到FP16,单帧耗时能压到8ms以内,满足实时性。
import torch import torchvision.transforms as T from PIL import Image # 信号灯分类推理示例(MobileNetV3) model = torch.hub.load('pytorch/vision:v0.10.0', 'mobilenet_v3_small', pretrained=False) model.classifier[3] = torch.nn.Linear(1024, 3) # 红、黄、绿三类 model.load_state_dict(torch.load('signal_light.pth', map_location='cpu')) model.eval() transform = T.Compose([ T.Resize((320, 320)), T.ToTensor(), T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) img = Image.open('signal_crop.jpg') tensor = transform(img).unsqueeze(0) with torch.no_grad(): logits = model(tensor) pred = logits.argmax(dim=1).item() print(f"信号灯状态: {['红', '黄', '绿'][pred]}")这段代码的关键参数是输入尺寸和归一化均值方差。320×320是在精度和速度之间折中的结果,再小会丢信号灯圆斑细节,再大推理耗时线性增长。归一化参数用ImageNet的就行,因为主干是在ImageNet上预训练的。实际部署时我会把模型转成ONNX再用TensorRT跑,FP16量化后精度掉不到0.5%。
注意:信号灯区域裁剪不要用固定ROI,隧道里相机抖动会导致ROI偏移。常见做法是用一个轻量检测器先定位信号灯,再送分类网络,两级级联比单阶段检测分类更稳。
2.2 轨道表面缺陷检测:从边缘检测到语义分割的演进路线
轨道表面缺陷——裂纹、剥落、磨耗——是线路维护的核心检测项。文档里把轨距水平检测、轨道变形监测、表面缺陷检测分开讲,实际工程中这三项往往用同一套视觉采集系统,只是后处理算法不同。轨距和水平靠结构光或双目视觉测几何量,缺陷检测靠纹理分析。
早期方案用Canny边缘检测加形态学闭运算,在实验室干净轨道上效果不错,一到现场就废——油污、水渍、道砟反光全被当成裂纹。后来转语义分割,用U-Net或DeepLabV3+,把轨道表面分成“正常、裂纹、剥落、油污”四类。训练数据用LabelMe标注,每张图至少标200个像素以上的缺陷区域,小目标用放大镜工具抠。
import segmentation_models_pytorch as smp import torch # U-Net语义分割模型,用于轨道表面缺陷检测 model = smp.Unet( encoder_name="resnet34", # 主干网络,轻量且精度够用 encoder_weights="imagenet", # 预训练权重加速收敛 in_channels=3, # RGB输入 classes=4, # 正常、裂纹、剥落、油污 activation=None # 训练时用logits,推理时加softmax ) # 损失函数:交叉熵 + Dice,缓解类别不平衡 dice_loss = smp.losses.DiceLoss(mode='multiclass') ce_loss = torch.nn.CrossEntropyLoss() def combined_loss(pred, target): return ce_loss(pred, target) + dice_loss(pred, target)encoder选resnet34是因为在轨道缺陷这种纹理任务上,更深的网络收益递减,而推理速度会拖垮巡检车的处理能力。classes=4是经验值,实际项目中我会把“油污”单独拎出来,因为油污和裂纹在灰度图上容易混,但物理意义完全不同——油污不用报警,裂纹必须。损失函数用交叉熵加Dice,是因为缺陷像素占比通常不到5%,纯交叉熵会让模型偏向全预测背景。
提示:轨道缺陷检测的标注一致性比模型结构重要得多。同一个裂纹,不同标注员画的宽度可能差一倍。我一般会写一个标注规范文档,规定最小标注宽度不低于3像素,裂纹两端各外扩5像素,减少边界模糊。
2.3 客流统计与密度分析:为什么密度图回归比检测框更实用
车站客流统计,文档里列了客流量实时监测、乘客密度分析、拥挤程度评估。用目标检测框人再计数,在稀疏场景没问题,一到早晚高峰,人贴人,检测框重叠严重,NMS一压就漏。我一般用密度图回归,输入原图,输出一张和原图尺寸一致的密度热力图,积分求和就是人数。
实现上用CSRNet或MCNN的简化版,前端用VGG16前10层做特征提取,后端用空洞卷积扩大感受野。训练数据用ShanghaiTech或自建标注,标注方式是每个人头点一个点,高斯核卷积成密度图。推理时密度图求和,再乘以一个标定系数。
import torch.nn as nn import torch.nn.functional as F class DensityNet(nn.Module): def __init__(self): super().__init__() # 前端:VGG16前10层,输出512通道 self.frontend = nn.Sequential( nn.Conv2d(3, 64, 3, padding=1), nn.ReLU(), nn.Conv2d(64, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.Conv2d(128, 128, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(128, 256, 3, padding=1), nn.ReLU(), nn.Conv2d(256, 256, 3, padding=1), nn.ReLU(), nn.Conv2d(256, 512, 3, padding=1), nn.ReLU() ) # 后端:空洞卷积,保持分辨率 self.backend = nn.Sequential( nn.Conv2d(512, 256, 3, padding=2, dilation=2), nn.ReLU(), nn.Conv2d(256, 128, 3, padding=2, dilation=2), nn.ReLU(), nn.Conv2d(128, 64, 3, padding=2, dilation=2), nn.ReLU(), nn.Conv2d(64, 1, 1) # 输出单通道密度图 ) def forward(self, x): x = self.frontend(x) x = self.backend(x) # 上采样回原图尺寸 x = F.interpolate(x, scale_factor=8, mode='bilinear', align_corners=False) return x前端VGG16只取到第10层,是因为再往下池化倍率太大,密度图分辨率不够,小人群会糊成一片。后端空洞卷积的dilation=2,感受野能覆盖到原图约100×100像素区域,对应站台上一小簇人。输出上采样8倍是因为前端有三次池化,总下采样8倍。实际部署时输入尺寸建议1280×720,再大显存吃不住,再小密度图精度掉得厉害。
注意:密度图回归的绝对人数需要标定。不同相机高度、俯仰角,同一个密度积分对应的人数不同。我一般会在现场用人工计数做一次线性拟合,得到缩放系数,之后固定不变。
3. 从标注到部署:轨道交通视觉项目的完整工程链路
3.1 数据采集与标注:站台、隧道、车顶三种场景的采集差异
轨道交通视觉项目的数据采集,场景差异极大。站台场景光照相对稳定,但人流密度变化大,需要覆盖早晚高峰、平峰、节假日;隧道场景光照极不均匀,车灯打过去一块亮一块暗,需要HDR相机或多曝光融合;车顶场景(受电弓、空调机组)振动大,快门速度必须拉到1/2000秒以上,否则运动模糊直接毁掉缺陷检测。
我一般会按场景分文件夹,每个场景至少采集2000张原始图,覆盖不同时段、天气、光照。标注用LabelImg或CVAT,检测任务标矩形框,分割任务标多边形。标注完做一次交叉验证,随机抽10%让另一个人重标,IoU低于0.7的退回重标。
# 数据采集脚本示例:用OpenCV拉流并按时间戳存图 import cv2 import os import time cap = cv2.VideoCapture('rtsp://camera_ip/stream') # 替换为实际相机流地址 save_dir = './platform_data' os.makedirs(save_dir, exist_ok=True) frame_count = 0 while True: ret, frame = cap.read() if not ret: break # 每30帧存一张,避免冗余 if frame_count % 30 == 0: timestamp = time.strftime('%Y%m%d_%H%M%S') cv2.imwrite(f'{save_dir}/{timestamp}_{frame_count}.jpg', frame) frame_count += 1 if frame_count > 30000: # 采够就停 break cap.release()这段脚本的关键参数是采样间隔和总帧数。每30帧存一张,对应1秒一张(假设30fps),既能覆盖时间变化,又不会让存储爆炸。总帧数30000对应约1000秒,实际采集时我会分多次,每次采15分钟,换不同时段。
提示:隧道场景采集一定要用全局快门相机,卷帘快门在车灯频闪下会出现条纹,后期修不掉。站台场景可以用卷帘快门,成本低不少。
3.2 模型训练与调参:学习率、批次大小、数据增强的实操组合
轨道交通视觉模型的训练,和通用CV任务最大的区别是数据量少、场景单一、类别不平衡。我一般用迁移学习,主干加载ImageNet预训练权重,学习率设1e-4,批次大小根据显存来,RTX 3090上YOLOv8n可以跑batch=32,U-Net跑batch=8。
数据增强用Albumentations,站台场景加RandomBrightnessContrast、HueSaturationValue、MotionBlur;隧道场景加RandomGamma、CLAHE;车顶场景加GaussNoise、ISONoise。不要用随机裁剪,因为缺陷和信号灯的位置有物理意义,裁掉就变负样本了。
import albumentations as A from albumentations.pytorch import ToTensorV2 # 站台场景数据增强 platform_aug = A.Compose([ A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, p=0.5), A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=20, val_shift_limit=20, p=0.5), A.MotionBlur(blur_limit=5, p=0.3), A.Resize(640, 640), A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ToTensorV2() ]) # 隧道场景数据增强 tunnel_aug = A.Compose([ A.RandomGamma(gamma_limit=(80, 120), p=0.5), A.CLAHE(clip_limit=2.0, tile_grid_size=(8, 8), p=0.5), A.Resize(640, 640), A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ToTensorV2() ])亮度对比度限制设0.3,是因为再大就会把正常图像也压暗,模型学不到真实特征。CLAHE的clip_limit=2.0是经验值,再高会放大噪声。Resize到640×640是YOLO系列的标配,U-Net可以到512×512。
注意:训练集和验证集必须按时间段划分,不能随机打乱。同一时段的图像高度相似,随机划分会导致验证集精度虚高,实际部署掉点严重。
3.3 模型部署与加速:TensorRT、ONNX、OpenVINO怎么选
训练完的模型要落到边缘设备上,常见选择有TensorRT(NVIDIA GPU)、OpenVINO(Intel CPU/VPU)、ONNX Runtime(通用)。轨道交通现场工控机大多带NVIDIA Tesla T4或Jetson Xavier,TensorRT是首选,FP16量化后YOLOv8n能跑到2ms以内。
转换流程是PyTorch→ONNX→TensorRT。ONNX导出时注意opset版本选11或13,动态轴只开batch维度,空间维度固定,否则TensorRT优化会受限。
# PyTorch转ONNX python -c " import torch from model import DensityNet model = DensityNet() model.load_state_dict(torch.load('density.pth')) model.eval() dummy = torch.randn(1, 3, 720, 1280) torch.onnx.export(model, dummy, 'density.onnx', opset_version=11, input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'batch'}, 'output': {0: 'batch'}}) " # ONNX转TensorRT trtexec --onnx=density.onnx --saveEngine=density.trt --fp16 --workspace=2048trtexec的--fp16开启半精度,--workspace=2048是显存工作空间,T4上够用。如果模型有自定义层,TensorRT不支持,就得回退到ONNX Runtime,性能损失约30%。
提示:Jetson设备上建议用DeepStream做推理流水线,它内置了TensorRT和视频解码,比手写GStreamer省事得多。
4. 避坑与排查:轨道交通视觉项目里那些血泪经验
4.1 现象:模型在实验室精度99%,到现场掉到60%
原因:训练数据全是晴天正午采集的,现场有逆光、雨雾、夜间补光。解决:训练集必须覆盖至少三种光照条件,每种不少于500张。如果现场已经部署,用在线难例挖掘,把置信度低的样本回传重新标注。
4.2 现象:推理延迟忽高忽低,平均50ms但偶尔飙到500ms
原因:CPU预处理和GPU推理串行,图像解码占了大头。解决:用NVDEC硬件解码,预处理放到GPU上做,或者用DALI库做数据加载。另一个常见原因是显存碎片,TensorRT引擎初始化时预分配显存,不要动态申请。
4.3 现象:同一段视频,两次推理结果不一致
原因:模型里有Dropout或BatchNorm在推理时没切到eval模式。解决:导出ONNX前必须调model.eval(),并且用torch.no_grad()包住推理。如果是TensorRT,检查是否有非确定性层,比如某些版本的TopK。
4.4 现象:小目标缺陷漏检严重,裂纹像素只有十几个
原因:下采样倍率太大,小目标在特征图上只剩一个点。解决:用FPN结构,把浅层高分辨率特征和深层语义特征融合。或者用切片推理,把大图切成512×512重叠切片,每片单独推理再合并。
4.5 现象:相机标定参数漂移,轨距测量误差越来越大
原因:振动导致相机外参变化,或者温度变化导致镜头畸变系数偏移。解决:在轨道旁放标定板,每天运营前自动拍一张做在线标定。如果做不到,至少每月手动标定一次,用棋盘格或圆点靶标。
5. 进阶技巧:用半监督学习把标注成本砍掉一半
标注是轨道交通视觉项目最贵的环节。一张站台图标200个人头,熟练标注员要15分钟,一万张就是2500小时。我后来用半监督学习,先用少量标注数据训一个教师模型,对未标注数据生成伪标签,置信度高于0.9的才保留,再和学生模型一起训练。一轮下来,标注量减少60%,精度只掉1.5%。
具体流程是:先标2000张,训教师模型;用教师模型推理剩余8000张,保留高置信度伪标签;把2000张真标签和8000张伪标签混在一起训学生模型;学生模型再反过来更新伪标签,迭代两轮。关键参数是置信度阈值和伪标签权重。阈值设0.9,低于这个的伪标签噪声太大;伪标签损失权重设0.5,真标签权重1.0,让模型更信任人工标注。
# 半监督训练循环示例 for epoch in range(num_epochs): # 有标签数据正常训练 for imgs, labels in labeled_loader: preds = model(imgs) loss_sup = criterion(preds, labels) loss_sup.backward() optimizer.step() # 无标签数据用伪标签训练 for imgs, _ in unlabeled_loader: with torch.no_grad(): pseudo_labels = teacher_model(imgs) # 只保留高置信度伪标签 mask = pseudo_labels.max(dim=1)[0] > 0.9 preds = model(imgs) loss_unsup = criterion(preds, pseudo_labels.argmax(dim=1)) * mask.float() loss_unsup = loss_unsup.mean() * 0.5 # 伪标签权重0.5 loss_unsup.backward() optimizer.step()这个循环里,教师模型不更新梯度,只生成伪标签。mask过滤掉低置信度区域,避免噪声累积。伪标签权重0.5是经验值,太高会带偏模型,太低则半监督没效果。迭代两轮后,教师模型用学生模型的权重更新,再生成新一轮伪标签。
注意:半监督对类别不平衡很敏感。如果缺陷样本只占1%,伪标签里缺陷几乎全被过滤掉,学生模型学不到缺陷。我一般会对缺陷类单独设低阈值,比如0.7,保证缺陷伪标签能保留下来。
从那以后我每次做轨道交通视觉项目,都强制先跑一遍半监督流程,哪怕标注预算充足,也留20%数据做验证。这套方法帮我把交付周期从三个月压到六周,现场精度还稳在95%以上。希望帮到你。
本文还有配套的精品资源,点击获取