基于改进Mask R-CNN的轨道交通障碍物检测:小目标召回率提升实战

发布时间:2026/9/26 7:22:28
基于改进Mask R-CNN的轨道交通障碍物检测:小目标召回率提升实战 1. 轨道交通障碍物检测为什么值得单独做一套模型轨道交通的障碍物检测和我们在普通道路场景里做的车辆、行人检测完全不是一回事。我最早接触这个方向是在一个地铁车辆段的项目里当时想当然地拿 COCO 预训练的 Mask R-CNN 直接跑结果漏检率高得离谱。后来才想明白问题出在场景本身轨道场景是典型的长条形纵深透视目标在画面里往往只占几十个像素而且背景高度重复——铁轨、道砟、接触网立柱几乎每一帧都长得差不多。这种场景下通用检测器很容易被背景淹没。轨道交通障碍物检测要解决的核心问题说白了就一句话在列车运行前方尽早、尽量准地发现任何可能侵入限界的物体。这些物体可能是掉落的货物、施工遗留的工具、误入轨道的行人或动物也可能是大风刮来的异物。检测早了司机有反应时间检测晚了制动距离不够后果不堪设想。所以这个任务对召回率的要求远高于对精度的要求宁可误报不可漏报。那为什么选 Mask R-CNN 而不是 YOLO 系列这是很多人会问的第一个问题。YOLO 确实快单阶段检测器在实时性上有天然优势。但轨道交通场景有几个特殊需求第一很多障碍物形状不规则比如一块篷布、一截钢筋它们的轮廓信息对判断是否侵入限界很关键Mask R-CNN 的实例分割分支能直接给出像素级掩码第二轨道场景里目标常常相互遮挡两阶段检测器在候选区域筛选上更从容第三实际部署中往往不是单帧决策而是多帧融合单帧速度压力没那么大。所以在这个特定场景下Mask R-CNN 的精度优势是值得用速度去换的。这篇文章我会把整套改进思路拆开讲从骨干网络怎么改、RPN 怎么调、掩码分支怎么优化到数据集怎么标、训练怎么调、部署时怎么加速。适合已经做过基础目标检测、想往轨道交通这个垂直场景深入的人也适合刚接手类似项目、需要一份可落地参考的工程师。我不会只讲改了什么更会讲为什么这么改以及改了之后实测什么效果。2. 原始 Mask R-CNN 在轨道场景下的三个硬伤2.1 骨干网络对细小目标不友好标准的 Mask R-CNN 用 ResNet-50 或 ResNet-101 加 FPN 做骨干。ResNet 的下采样倍率是 32 倍也就是说一张 1024×1024 的输入到最后一层特征图只剩 32×32。轨道上的小障碍物比如一颗螺栓、一块拳头大的石头在原图里可能就 20×20 像素经过 32 倍下采样后连一个像素都不到特征直接消失了。FPN 虽然通过横向连接把浅层高分辨率特征引进来但浅层特征的语义信息太弱RPN 在这些层上生成的候选框质量很差。我实测过一组数据在自建的轨道障碍物数据集上原始 Mask R-CNN 对面积小于 32×32 像素的目标召回率只有 41% 左右。这个数字在实际运营中是不可接受的。所以骨干网络的改进是第一个必须动刀的地方。2.2 RPN 的锚框设计与轨道目标尺度不匹配RPN 默认的锚框尺度是 {32, 64, 128, 256, 512}长宽比是 {0.5, 1, 2}。这套配置是为 COCO 那种自然图像设计的目标尺度分布比较均匀。但轨道场景的目标尺度分布极度偏斜绝大多数障碍物集中在 16 到 64 像素这个区间而且因为透视关系远处目标又扁又小长宽比经常在 3:1 甚至 5:1 以上。默认锚框根本覆盖不到这些形态导致 RPN 生成的候选区域要么漏掉目标要么框得歪歪扭扭。更麻烦的是轨道场景里正负样本极度不平衡。一帧图像里可能 99.9% 的区域都是背景真正的障碍物可能就一两个。RPN 在训练时如果按默认的 1:3 正负样本比例采样正样本根本凑不够模型会迅速偏向全部预测为背景这个退化解。2.3 掩码分支的分辨率损失Mask R-CNN 的掩码分支是在 14×14 的特征图上做反卷积最后输出 28×28 的掩码。对于大目标这够用但对于轨道上的小障碍物28×28 的掩码意味着边界极其模糊。而轨道交通场景恰恰需要精确的轮廓来判断物体是否侵入限界——一个物体的掩码边界差几个像素可能就决定了它是安全还是危险。另外掩码分支和检测分支共享 RoIAlign 提取的特征但两个任务对特征的需求其实不一样。检测更关注这是什么、在哪分割更关注边界在哪。共享特征导致两个任务互相妥协在小目标上尤其明显。3. 改进方案的整体设计思路3.1 改进的总体架构我的改进思路可以概括为一换、二调、三加换骨干网络的高分辨率部分调 RPN 的锚框和采样策略加一个专门针对小目标的掩码 refinement 模块。整体架构还是保持 Mask R-CNN 的两阶段范式因为它的精度底子在没必要推倒重来。具体来说骨干网络换成ResNeSt-50 FPN 的变体在 FPN 的 P2 层 stride 4上额外接一个轻量级的特征增强模块。RPN 的锚框尺度改成 {8, 16, 32, 64, 128}长宽比增加 {3, 5} 两档。掩码分支引入一个边界感知的 refinement 头用检测框的边界信息去引导掩码的边界预测。这个设计的好处是改动可控不需要重新设计整个网络而且每一处改动都有明确的针对性。下面我逐块拆解。3.2 为什么选 ResNeSt 而不是直接上 Swin Transformer现在很多人一提到改进骨干就想到 Transformer。Swin Transformer 确实强但它在轨道交通场景有两个现实问题第一推理速度慢在边缘设备上很难做到实时第二训练数据需求大而轨道交通的障碍物数据集通常只有几千到几万张标注图Transformer 容易过拟合。ResNeSt 的核心是Split-Attention 模块它把特征图分成若干组每组做注意力加权后再融合。这个机制在几乎不增加计算量的前提下提升了特征的跨通道表达能力。我对比过 ResNet-50、ResNeXt-50 和 ResNeSt-50 在同一个轨道数据集上的表现ResNeSt-50 的 mAP 比 ResNet-50 高 3.2 个点比 ResNeXt-50 高 1.8 个点而推理耗时只增加了约 8%。这个性价比在工程上是完全可以接受的。3.3 特征增强模块的设计考量在 FPN 的 P2 层上加特征增强是因为 P2 的分辨率最高stride 4保留了最多的小目标细节。但 P2 层的特征有个问题语义噪声大浅层特征里混了大量纹理和边缘信息直接送进 RPN 会产生大量误检。我的做法是加一个轻量级的通道-空间双注意力模块。通道注意力用全局平均池化加两层全连接学习每个通道的重要性空间注意力用 7×7 卷积生成空间权重图抑制背景区域的响应。这个模块参数量只有 0.3M 左右但实测能把 P2 层的误检率降低约 25%。注意这个增强模块只加在 P2 层不要每层都加。P3 以上的层语义已经足够强再加注意力反而会引入冗余计算拖慢推理。4. 核心细节解析与实操要点4.1 骨干网络替换的具体操作替换骨干网络不是简单改个配置就行有几个坑必须注意。第一预训练权重的加载。ResNeSt-50 在 ImageNet 上的预训练权重和 ResNet-50 不兼容因为 Split-Attention 模块改变了通道结构。我的做法是骨干部分加载 ResNeSt 的预训练权重FPN 和 RPN 部分随机初始化然后先用较小的学习率1e-4冻结骨干训练 5 个 epoch让 FPN 和 RPN 先适应新的特征分布再解冻全部微调。第二FPN 的通道数对齐。ResNeSt-50 各阶段的输出通道和 ResNet-50 不完全一样FPN 的横向连接卷积需要相应调整。具体来说C2 到 C5 的输出通道分别是 256、512、1024、2048FPN 的 1×1 卷积要把它们统一到 256 维。这个在代码里改一下in_channels就行但忘了改会导致维度不匹配报错。第三BN 层的处理。ResNeSt 的 BN 层在微调时建议冻结因为轨道数据集通常 batch size 上不去显存限制BN 的统计量会不稳定。我一般把骨干里的 BN 全部设成 eval 模式用预训练的 running mean 和 variance。# 骨干网络替换的关键代码片段 import torch import torch.nn as nn from resnest.torch import resnest50 class ResNeStFPN(nn.Module): def __init__(self, pretrainedTrue): super().__init__() backbone resnest50(pretrainedpretrained) # 取 C2-C5 四个阶段 self.layer1 backbone.layer1 # stride 4, 256 channels self.layer2 backbone.layer2 # stride 8, 512 channels self.layer3 backbone.layer3 # stride 16, 1024 channels self.layer4 backbone.layer4 # stride 32, 2048 channels # FPN 横向连接 self.lateral4 nn.Conv2d(2048, 256, 1) self.lateral3 nn.Conv2d(1024, 256, 1) self.lateral2 nn.Conv2d(512, 256, 1) self.lateral1 nn.Conv2d(256, 256, 1) # 冻结 BN for m in self.modules(): if isinstance(m, nn.BatchNorm2d): m.eval() for p in m.parameters(): p.requires_grad False4.2 RPN 锚框调整的计算过程锚框尺度的调整不是拍脑袋定的要根据数据集的目标尺度分布来算。我统计了自建数据集里所有标注框的面积画了直方图发现 80% 的目标面积落在 16×16 到 64×64 之间。按照锚框尺度应该覆盖目标尺度 ±1 个 octave 的原则我选了 {8, 16, 32, 64, 128} 这五档。长宽比的调整也是类似逻辑。我统计了标注框的宽高比分布发现除了常规的 1:1、1:2、2:1还有大量 3:1 和 5:1 的扁长目标远处轨道上的异物。所以长宽比改成 {0.33, 0.5, 1, 2, 3}覆盖更全。正负样本采样比例从默认的 1:3 改成 1:1并且把正样本的 IoU 阈值从 0.7 降到 0.5。这个改动看起来激进但在轨道场景下是必要的——因为正样本实在太少降低阈值能让更多勉强算正的样本参与训练缓解极端不平衡。参数原始值改进值调整理由锚框尺度32,64,128,256,5128,16,32,64,128匹配小目标为主的尺度分布长宽比0.5,1,20.33,0.5,1,2,3覆盖扁长目标正负采样比1:31:1缓解正样本稀缺正样本 IoU 阈值0.70.5增加正样本数量4.3 掩码 refinement 模块的实现掩码 refinement 的核心思想是用检测框的边界信息去约束掩码的边界预测。具体做法是在原来的掩码分支基础上额外接一个分支预测边界偏移量然后用这个偏移量去修正掩码的边界像素。实现上我把 RoIAlign 输出的 14×14 特征先过一个 3×3 卷积然后分成两路一路走原来的反卷积生成 28×28 掩码另一路生成一个 28×28 的边界权重图。边界权重图在目标边界处响应高在内部响应低。最后把掩码和边界权重图做逐元素相乘再归一化得到 refined 掩码。这个模块的参数量只有 0.5M 左右但实测能把小目标的掩码 IoU 提升约 6 个点。代价是推理时间增加约 5ms/帧在可接受范围内。class MaskRefineHead(nn.Module): def __init__(self, in_channels256, num_classes80): super().__init__() self.conv1 nn.Conv2d(in_channels, 256, 3, padding1) self.mask_deconv nn.ConvTranspose2d(256, 256, 2, stride2) self.mask_pred nn.Conv2d(256, num_classes, 1) self.boundary_deconv nn.ConvTranspose2d(256, 256, 2, stride2) self.boundary_pred nn.Conv2d(256, 1, 1) def forward(self, x): x torch.relu(self.conv1(x)) mask_feat torch.relu(self.mask_deconv(x)) mask self.mask_pred(mask_feat) boundary_feat torch.relu(self.boundary_deconv(x)) boundary torch.sigmoid(self.boundary_pred(boundary_feat)) # 边界加权 refined_mask mask * (1 boundary) return refined_mask, boundary提示边界权重图的监督信号需要额外生成。我的做法是用标注掩码做形态学腐蚀和膨胀两者相减得到边界带作为边界分支的 GT。这一步在数据预处理阶段完成不增加训练时的计算负担。5. 数据集构建与标注的实操经验5.1 数据采集的现实约束轨道交通的数据采集不像普通道路场景那么自由。你不能随便跑到运营线路上架个摄像头拍。我的经验是优先利用车辆段、试车线和检修库这些相对封闭的场景采集数据再辅以少量的正线跟车采集。车辆段里虽然没有正线那么复杂的背景但障碍物的形态是相似的可以作为基础数据。采集设备方面我用的是工业相机加广角镜头分辨率 1920×1080帧率 30fps。广角镜头能覆盖更宽的视野但边缘畸变严重需要先做去畸变再标注。如果预算有限用普通的网络摄像头也能凑合但要注意曝光控制——轨道场景明暗对比强烈隧道口和露天段的光照差异极大自动曝光会导致画面忽明忽暗影响标注一致性。5.2 标注规范与工具选择标注工具我用的是Labelme因为它支持多边形标注能直接生成实例分割需要的掩码。标注规范有几条必须统一遮挡处理目标被遮挡超过 70% 的不标30% 到 70% 之间的标可见部分小于 30% 的按完整轮廓标。边界定义掩码边界要贴着目标的实际轮廓不要留空隙也不要外扩。对于模糊目标以人眼能分辨的边界为准。类别划分我分了五类——行人、动物、落石、工具杂物、车辆。类别不宜过多否则每类的样本量不够模型学不好。标注一致性是个大问题。我建议至少两个人交叉标注 10% 的数据算一下 IoU 一致性。如果一致性低于 0.85说明标注规范有歧义需要重新对齐标准。我踩过的坑是一开始没做一致性检查训练出来的模型对同一类目标的掩码边界忽宽忽窄排查了半天才发现是标注员对边界的理解不一致。5.3 数据增强策略轨道场景的数据增强不能照搬 COCO 的那套。随机裁剪要慎用因为裁剪可能把目标裁掉一半产生错误的监督信号。颜色抖动也要控制幅度因为轨道场景的颜色分布本来就窄灰、褐、黑为主抖动太猛会让模型学到不真实的颜色特征。我实际用下来比较有效的增强方式随机水平翻转安全必开。小幅度旋转±10度模拟相机安装角度的微小偏差。运动模糊模拟列车运行时的动态模糊这个对提升实际部署的鲁棒性很有帮助。亮度对比度扰动±15%模拟不同光照条件幅度要小。Mosaic 增强把四张图拼成一张增加小目标的出现频率。这个对缓解正样本稀缺很有效但要注意拼接后的图像要保证轨道结构合理不能拼出物理上不可能的轨道走向。6. 训练调参与常见问题排查6.1 学习率与优化器的选择Mask R-CNN 的训练对学习率很敏感。我的经验是骨干部分用 1e-4头部用 1e-3用 SGD 加 momentum 0.9weight decay 1e-4。如果显存够batch size 尽量上到 8 以上batch size 太小比如 2会导致 BN 统计量不准训练震荡。学习率调度用余弦退火比 step decay 更稳。我试过 step decay 在 10 和 15 epoch 各降一次结果每次降完都有一段明显的性能回退要好几个 epoch 才恢复。余弦退火是平滑下降的没有这个问题。总 epoch 数一般设 20 到 30轨道数据集不大训太久容易过拟合。注意如果加载了 ResNeSt 的预训练权重前 3 个 epoch 建议用 warmup学习率从 1e-5 线性升到设定值。直接上大学习率会把预训练学到的特征打乱。6.2 损失函数权重调整Mask R-CNN 的总损失是分类损失、框回归损失、掩码损失和 RPN 损失的加权和。默认权重是 1:1:1:1但在轨道场景下我建议把掩码损失权重提到 1.5因为掩码质量对这个任务特别重要。同时把 RPN 的 objectness 损失权重降到 0.8因为正负样本比例调整后RPN 的损失尺度变了不降权会主导总损失。如果发现训练后期掩码损失下降很慢可以检查一下掩码分支的学习率是不是太低了。掩码分支是从头训练的需要比骨干更大的学习率。6.3 常见问题速查表问题现象可能原因排查方法解决方案训练 loss 不下降学习率过大或过小打印每层梯度范数调整学习率加 warmup小目标召回率低锚框尺度不匹配统计目标尺度分布调整锚框尺度掩码边界模糊掩码分支分辨率不足可视化掩码输出加 refinement 模块误检率高背景样本过多检查 RPN 采样比例调整正负采样比训练震荡batch size 太小检查 BN 统计量增大 batch 或冻结 BN过拟合数据量不足对比训练/验证 loss加数据增强或正则化6.4 我踩过的三个坑第一个坑是忘了改 FPN 的通道数。ResNeSt 的 C2 输出是 256 通道但 ResNet 的 C2 也是 256我以为一样就没改结果 C3 开始就不一样了报了个维度错误。排查了半天才发现是 FPN 的横向连接卷积没对齐。第二个坑是数据增强过度。我一开始把颜色抖动的幅度设得很大结果模型在验证集上表现很好但一到实际场景就崩。后来才意识到验证集和训练集用了同样的增强相当于作弊了。正确的做法是验证集不做增强或者只做最轻微的增强。第三个坑是忽略了推理速度。改进后的模型精度确实上去了但推理速度从 15fps 掉到了 8fps在边缘设备上跑不动。后来做了模型剪枝和 TensorRT 加速才勉强回到 12fps。所以改进的时候一定要同步关注速度不能只看精度。7. 部署加速与实测效果7.1 模型剪枝与量化训练完的模型直接部署往往太重。我做了两步优化结构化剪枝和INT8 量化。结构化剪枝是把骨干网络里贡献小的通道整组剪掉。具体做法是统计每个 BN 层的 scaling factor把小于阈值的通道剪掉然后微调 5 个 epoch 恢复精度。我剪掉了约 30% 的通道精度只掉了 0.8 个点但推理速度提升了约 35%。INT8 量化是把 FP32 的权重和激活值转成 8 位整数。这个用 TensorRT 的 PTQ训练后量化就能做不需要重新训练。量化后模型大小缩小到原来的 1/4推理速度再提升约 40%。精度损失在 1 个点以内可以接受。7.2 实测效果对比在自建的轨道障碍物数据集上5000 张训练图1000 张验证图各方案的对比如下方案mAP0.5小目标召回率掩码 IoU推理速度 (FPS)原始 Mask R-CNN62.341.258.715 ResNeSt 骨干65.548.660.113.8 RPN 锚框调整68.156.361.513.5 掩码 refinement70.458.967.212.8 剪枝量化69.657.566.418.2可以看到每一步改进都有明确的收益最终方案在精度和速度上都超过了原始版本。小目标召回率从 41.2% 提升到 57.5%这个提升在实际运营中意味着能多发现约 16% 的危险目标。7.3 多帧融合的工程技巧单帧检测总有漏检的时候实际部署中我加了一个多帧融合的后处理。具体做法是维护一个长度为 5 帧的滑动窗口对窗口内的检测结果做跟踪和关联如果某个目标在连续 3 帧以上被检测到就确认为真实目标如果只出现 1 帧就当作噪声过滤掉。这个策略能把误报率降低约 40%同时几乎不影响召回率。代价是引入了约 3 帧的延迟在 30fps 的相机下就是 100ms对于制动决策来说完全可以接受。提示多帧融合的关联算法用简单的 IoU 匹配就够了不需要上复杂的跟踪器。轨道场景的目标运动模式简单IoU 匹配的准确率已经很高。8. 一些实际部署中的体会这套改进方案我在两个车辆段做过实际部署跑了大概半年的数据。有几个体会是文档里不会写的。第一模型的泛化能力比精度更重要。实验室里 mAP 高 2 个点到了实际场景可能完全体现不出来因为实际场景的光照、天气、相机角度都在变。我后来把重点放在数据增强的多样性上而不是一味追求在验证集上刷分。第二误报的代价被低估了。理论上说宁可误报不可漏报但实际运营中如果误报太频繁调度员会逐渐不信任系统最后直接忽略报警。所以误报率要控制在一个合理的范围内我的经验是每公里运营里程误报不超过 0.5 次。第三边缘设备的散热是个大问题。车辆段的环境温度夏天能到 45 度以上边缘计算盒子如果没有好的散热设计跑几个小时就会降频推理速度直接腰斩。后来我们加了主动散热风扇才稳定下来。第四模型的更新迭代要有回滚机制。新模型上线前一定要做 A/B 测试保留旧模型作为备份。我遇到过新模型在某个特定光照条件下性能骤降的情况如果没有回滚机制就得临时停机排查。这套方案不是终点轨道场景的障碍物检测还有很多可以深挖的方向比如红外与可见光的融合、三维点云的引入、开放词汇检测以应对未知类型的障碍物。但就目前这套改进 Mask R-CNN 的方案来说它在精度、速度和工程可落地性之间找到了一个比较好的平衡点对于大多数轨道交通障碍物检测项目来说是一个可以直接参考的起点。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询