第4章 感知层架构设计:传感器融合、轨道占用检测、障碍物识别

各位同学,欢迎来到感知层架构设计这一章。

说实话,感知层是整个列车自动驾驶系统里,我个人觉得最「刺激」的一层。为什么?因为它是系统对物理世界的唯一窗口。你决策层再聪明,控制层再精准,如果感知层给你传回的是错误数据,那一切都白搭。我在项目里见过太多因为一个传感器误报,导致列车紧急制动、乘客摔伤的案例了。

好,我们直接进入正题。感知层要解决三个核心问题:我在哪?轨道上有什么?前面有没有障碍物? 对应到技术架构上,就是传感器融合、轨道占用检测、障碍物识别。

4.1 传感器融合:不是简单做加法

传感器融合,说白了就是把雷达、激光雷达、摄像头、惯性测量单元(IMU)这些传感器的数据揉在一起,得到比单个传感器更准确、更可靠的结果。

但这里有个常见的误区——很多人以为融合就是「取平均值」。我刚开始做项目时也这么想过,结果发现根本行不通。举个例子:摄像头说前面有障碍物,毫米波雷达说没有,你信谁?取平均?那等于什么都没说。

核心原则:传感器融合的本质是「互补」与「冗余」的平衡。

我习惯把传感器分成两类:

  • 互补型传感器:比如摄像头(纹理信息丰富,但受光照影响大)和激光雷达(距离精度高,但分辨率低)。它们各自弥补对方的短板。
  • 冗余型传感器:比如两个不同型号的毫米波雷达,同时检测同一目标。当一个失效时,另一个还能顶上。

在实际架构中,我推荐使用卡尔曼滤波作为融合的基础框架。嗯,这里要注意,卡尔曼滤波不是万能的。它假设噪声是高斯分布,但真实世界的传感器噪声往往不是。我在项目中遇到过,当列车通过隧道时,GPS信号突然变差,卡尔曼滤波的协方差矩阵会迅速发散。怎么解决?我当时的做法是引入一个「传感器健康度」因子,动态调整滤波器的观测噪声协方差。

// 伪代码:带健康度的卡尔曼滤波更新
def update_with_health(z, H, R, health_factor):
    # health_factor: 0.0 ~ 1.0
    R_adjusted = R / (health_factor + 1e-6)
    K = P * H.T * inv(H * P * H.T + R_adjusted)
    x = x + K * (z - H * x)
    P = (I - K * H) * P
    return x, P

你想想看,当健康度接近0时,R_adjusted变得极大,卡尔曼增益K趋近于0,系统几乎完全信任预测模型,而不是观测数据。这就能有效避免传感器失效时的灾难性后果。

4.2 轨道占用检测:别让两列车「撞车」

轨道占用检测,说白了就是回答一个问题:「前面那段轨道上有没有车?」

传统方法靠轨道电路,但现代列车自动驾驶系统里,我们更多依赖应答器(Balise)里程计的组合。我参与过一个项目,在一条老旧线路上改造,轨道电路经常被雨水短路。那段时间,我们不得不把应答器的布置密度从每公里2个增加到每公里5个,才勉强保证检测的可靠性。

避坑指南:我曾经在应答器安装时忽略了一个细节——应答器的安装高度偏差超过5毫米,就会导致读取失败。后来我们规定,安装后必须用专用量具校准,误差控制在±2毫米以内。

轨道占用检测的逻辑其实不复杂,但实现起来有很多坑。我总结了一个简单的状态机:

状态 触发条件 动作
空闲 无应答器触发,里程计无位移 允许后续列车进入
占用 应答器被触发,或里程计检测到位移 禁止后续列车进入
未知 传感器通信超时或数据异常 强制降速至15km/h,人工确认

这里有个关键点:「未知」状态的处理。很多系统设计者只考虑了「空闲」和「占用」,忽略了传感器故障的情况。我见过一个事故报告,就是因为应答器被积雪覆盖,系统一直显示「空闲」,结果两列车追尾了。所以,我建议在架构设计时,一定要把「未知」作为一个独立状态来处理,并且触发安全降速。

4.3 障碍物识别:从「看到」到「理解」

障碍物识别,是感知层里技术难度最高的部分。为什么?因为障碍物种类太多了——行人、动物、落石、掉落的货物、甚至是一个塑料袋(在某些系统里,塑料袋会被误识别为障碍物,导致不必要的紧急制动)。

我个人的经验是,不要试图用一个模型识别所有障碍物。更好的做法是分层处理:

  1. 第一层:快速检测。用毫米波雷达或激光雷达做粗筛,检测前方是否有「非轨道物体」。这一层要求低延迟(<100ms),可以容忍一些误报。
  2. 第二层:精细分类。用摄像头+深度学习模型,对第一层筛选出的目标进行分类。这一层要求高精度,但延迟可以放宽到200-300ms。
  3. 第三层:行为预测。判断障碍物是否具有「侵入性」。比如,一个人站在轨道旁边,和一个人站在轨道中间,处理方式完全不同。
注意:深度学习模型在列车场景中有一个致命问题——「长尾分布」。你训练时可能见过100万张行人图片,但只见过10张落石图片。一旦遇到落石,模型很可能识别失败。我建议在架构中保留一个「规则引擎」作为兜底,当模型置信度低于阈值时,回退到基于几何规则的检测方法。

说到规则引擎,我分享一个实际案例。有一次,我们的深度学习模型把轨道上的一个纸箱识别成了「人」,触发了紧急制动。后来我们加了一条规则:如果目标高度低于30厘米,且宽度大于50厘米,则优先判定为「静态障碍物」而非「行人」。这条规则虽然简单,但有效减少了误报率。

嗯,这里还要提一下时间维度。障碍物识别不能只看单帧数据。我习惯用「多帧一致性检测」——如果一个障碍物在连续5帧(约0.5秒)中都被检测到,才认为它是真实存在的。这样可以过滤掉传感器噪声和偶然的误检。

// 多帧一致性检测伪代码
class ObstacleTracker:
    def __init__(self, threshold=5):
        self.history = {}  # obstacle_id -> count
        self.threshold = threshold
    
    def update(self, detections):
        confirmed = []
        for obs in detections:
            if obs.id in self.history:
                self.history[obs.id] += 1
            else:
                self.history[obs.id] = 1
            if self.history[obs.id] >= self.threshold:
                confirmed.append(obs)
        return confirmed

最后,我想强调一点:感知层的输出,必须是「可解释」的。你不能只告诉决策层「前面有障碍物」,还要告诉它「障碍物是什么、距离多远、运动状态如何」。我在项目中吃过亏,因为感知层只输出了一个布尔值(有/无障碍物),导致决策层无法做出精细的制动策略——要么全速,要么急刹。后来我们改成了输出结构化的感知结果,包含目标类型、位置、速度、置信度等字段,决策层的表现立刻好了很多。

好,这一章的内容就到这里。下一章我们会讲决策层架构设计,到时候会用到今天讲的感知数据。记得把传感器融合和障碍物识别的代码框架自己写一遍,光看是学不会的。