4、感知数据流设计:目标检测、语义分割、实例分割的数据流拓扑
好,咱们接着聊感知数据流。这一章我打算把目标检测、语义分割、实例分割这三条线的数据流拓扑彻底讲透。说实话,很多同学在学端到端时,容易把感知模块当成一个黑盒往里灌数据。但实际做项目时,数据流怎么走、谁先谁后、中间要不要做对齐,这些细节直接决定了你的系统能不能稳定跑起来。
4.1 三条感知任务的核心差异
先理清概念。目标检测、语义分割、实例分割,它们虽然都叫感知,但输出的数据形态完全不同。
- 目标检测:输出的是一个个边界框(Bounding Box),每个框带一个类别标签和置信度。说白了,就是告诉系统「那里有辆车,置信度0.95」。
- 语义分割:输出的是像素级的分类图。每个像素都被分配一个类别,比如路面、车道线、行人。但它分不清同一类里的不同个体——两辆挨着的车,在语义分割图里是同一片红色。
- 实例分割:比语义分割更进一步。它不仅要给每个像素分类,还要区分出不同的个体。同样是车,实例分割能告诉你「这是车A,这是车B」。
我当年刚入行时,觉得语义分割和实例分割差不多,直到有一次在高速场景下做融合,发现语义分割把两辆并排的车当成了一堵墙,规控模块直接报了紧急制动。嗯,从那以后我再也不敢把这两个概念混为一谈了。
4.2 目标检测的数据流拓扑
目标检测的数据流,我个人习惯把它分成三个阶段:特征提取 → 候选区域生成 → 后处理。
先看特征提取。输入是一张图像(比如 1280×720×3),经过 backbone 网络(ResNet、EfficientNet 之类的)提取特征图。这一步没什么特别的,但要注意特征图的分辨率。我建议保留至少 1/8 原图大小的特征图,否则小目标(比如远处的行人)很容易漏检。
然后是候选区域生成。这里分两派:
- 两阶段法(Faster R-CNN 为代表):先由 RPN 网络生成一堆候选框,再对每个框做分类和回归。数据流是串行的,精度高但慢。
- 单阶段法(YOLO、SSD 为代表):直接在特征图上划分网格,每个网格预测几个框。数据流是并行的,速度快但小目标容易丢。
最后是后处理。核心就是 NMS(非极大值抑制)。这一步其实很关键,我见过不少同学在 NMS 的 IoU 阈值上踩坑。阈值设高了,重复框多;设低了,会把挨着的两辆车合并成一个。我的经验是:城区场景用 0.5,高速场景用 0.7。为什么?城区车多且密集,阈值低一点能保留更多框;高速上车少,阈值高一点能减少误检。
数据流拓扑示意(目标检测)
输入图像 (1280x720x3)
↓
Backbone 特征提取 (特征图 160x90x256)
↓
检测头 (分类 + 回归)
↓
NMS 后处理
↓
输出: [x1, y1, x2, y2, class, confidence] 列表
4.3 语义分割的数据流拓扑
语义分割的数据流,说白了就是一个「编码器-解码器」结构。编码器负责下采样提取语义信息,解码器负责上采样恢复分辨率。
这里有个坑:下采样太狠,小物体就没了。我在项目中遇到过,用 MobileNet 做编码器,下采样到 1/32 原图大小,结果车道线断成了好几截。后来我改用 DeepLabV3+ 的 ASPP 模块,多尺度特征融合一下,效果好了很多。
语义分割的输出是一张单通道的类别图。比如有 10 个类别,每个像素的值就是 0~9 的整数。但注意,这个输出通常要经过 argmax 操作,把每个像素的 softmax 概率转成具体的类别 ID。
小技巧:如果你在做端到端训练,建议保留 softmax 概率图而不是 argmax 后的类别图。因为概率图保留了不确定性信息,规控模块可以利用这个信息做更安全的决策。比如某个像素的类别概率是 0.6,说明模型不太确定,那规控就应该保守一点。
数据流拓扑长这样:
输入图像 (1280x720x3)
↓
编码器 (下采样至 40x23x512)
↓
ASPP / 特征金字塔 (多尺度融合)
↓
解码器 (上采样至 1280x720)
↓
Softmax + Argmax
↓
输出: 类别图 (1280x720, 每个像素 0~9)
4.4 实例分割的数据流拓扑
实例分割是三者中最复杂的。目前主流方案有两种:Mask R-CNN 系列和基于 Transformer 的方法(如 Mask2Former)。
拿 Mask R-CNN 举例,它的数据流是:
- 先走目标检测的流程,生成候选框。
- 对每个候选框做 RoI Align,把不同大小的框统一成固定尺寸(比如 14×14)。
- 然后并行跑两个分支:一个做分类和框回归,另一个做像素级的 mask 预测。
你想想看,这里的数据流其实有个瓶颈——RoI Align 是串行的。如果一张图里有 100 个候选框,就要做 100 次 RoI Align。这在实时性要求高的场景下很要命。
我建议的做法是:控制候选框数量。训练时可以用 256 个框,但推理时我一般只保留前 100 个置信度最高的框。再多的话,延迟就上去了,而且很多低置信度的框其实都是噪声。
注意:实例分割的输出数据量很大。一张 1280×720 的图像,如果有 50 个实例,每个实例对应一个 1280×720 的 mask(二值图),那光 mask 数据就有 50×1280×720 ≈ 4600 万个像素。这在传输到下游模块时,带宽压力非常大。我建议只保留实例的轮廓点(polygon)或者压缩后的 mask,而不是原始的二值图。
数据流拓扑:
输入图像 (1280x720x3)
↓
Backbone 特征提取
↓
RPN 生成候选框 (Top-100)
↓
RoI Align (统一尺寸 14x14)
↓
┌─────────────────┬─────────────────┐
│ 分类 + 框回归 │ Mask 预测 │
│ (全连接层) │ (全卷积网络) │
└─────────────────┴─────────────────┘
↓ ↓
类别 + 边界框 二值 mask (28x28)
↓ ↓
输出: 实例列表 (每个实例: 类别, 框, mask)
4.5 三条数据流的对齐与融合
在实际的端到端系统中,这三条数据流往往是并行跑的。但问题来了——它们的输出怎么对齐?
举个例子。目标检测输出的是「车」的边界框,语义分割输出的是「路面」的像素区域,实例分割输出的是「那辆红色车」的 mask。这三个输出在空间上是有重叠的,但它们的坐标系、分辨率、时间戳可能都不一样。
我的做法是:统一到鸟瞰图(BEV)坐标系下。先把所有感知结果通过相机内外参投影到 BEV 空间,然后再做融合。这样目标检测的框、语义分割的区域、实例分割的 mask 就都在同一个空间里了。
但这里有个细节要注意——时间对齐。不同感知模块的处理速度不一样。目标检测可能 30ms 出一帧,语义分割 50ms 出一帧,实例分割 80ms 出一帧。如果你不做时间对齐,规控模块拿到的就是不同时刻的感知结果,那决策就会乱套。
我建议在感知模块后面加一个时间同步缓冲区。缓冲区里存最近 100ms 内的所有感知结果,然后以规控模块的触发时刻为准,取时间戳最接近的那一组数据。说白了,就是「谁先到就用谁,但保证时间差不超过 50ms」。
总结一下三条数据流的关键点
| 任务 | 输出格式 | 主要瓶颈 | 我的建议 |
|---|---|---|---|
| 目标检测 | 边界框列表 | NMS 阈值 | 城区 0.5,高速 0.7 |
| 语义分割 | 像素类别图 | 下采样倍数 | 保留 softmax 概率图 |
| 实例分割 | 实例 mask 列表 | RoI Align 串行 | 推理时只取 Top-100 框 |
好了,这一章的内容就到这里。下一章我会讲融合数据流的设计,到时候咱们再聊聊怎么把这三条数据流真正拧成一股绳。记住,感知数据流的设计没有银弹,关键是根据你的场景和硬件资源做取舍。你想想看,是不是这个理?