第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个,才勉强保证检测的可靠性。
轨道占用检测的逻辑其实不复杂,但实现起来有很多坑。我总结了一个简单的状态机:
| 状态 | 触发条件 | 动作 |
|---|---|---|
| 空闲 | 无应答器触发,里程计无位移 | 允许后续列车进入 |
| 占用 | 应答器被触发,或里程计检测到位移 | 禁止后续列车进入 |
| 未知 | 传感器通信超时或数据异常 | 强制降速至15km/h,人工确认 |
这里有个关键点:「未知」状态的处理。很多系统设计者只考虑了「空闲」和「占用」,忽略了传感器故障的情况。我见过一个事故报告,就是因为应答器被积雪覆盖,系统一直显示「空闲」,结果两列车追尾了。所以,我建议在架构设计时,一定要把「未知」作为一个独立状态来处理,并且触发安全降速。
4.3 障碍物识别:从「看到」到「理解」
障碍物识别,是感知层里技术难度最高的部分。为什么?因为障碍物种类太多了——行人、动物、落石、掉落的货物、甚至是一个塑料袋(在某些系统里,塑料袋会被误识别为障碍物,导致不必要的紧急制动)。
我个人的经验是,不要试图用一个模型识别所有障碍物。更好的做法是分层处理:
- 第一层:快速检测。用毫米波雷达或激光雷达做粗筛,检测前方是否有「非轨道物体」。这一层要求低延迟(<100ms),可以容忍一些误报。
- 第二层:精细分类。用摄像头+深度学习模型,对第一层筛选出的目标进行分类。这一层要求高精度,但延迟可以放宽到200-300ms。
- 第三层:行为预测。判断障碍物是否具有「侵入性」。比如,一个人站在轨道旁边,和一个人站在轨道中间,处理方式完全不同。
说到规则引擎,我分享一个实际案例。有一次,我们的深度学习模型把轨道上的一个纸箱识别成了「人」,触发了紧急制动。后来我们加了一条规则:如果目标高度低于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
最后,我想强调一点:感知层的输出,必须是「可解释」的。你不能只告诉决策层「前面有障碍物」,还要告诉它「障碍物是什么、距离多远、运动状态如何」。我在项目中吃过亏,因为感知层只输出了一个布尔值(有/无障碍物),导致决策层无法做出精细的制动策略——要么全速,要么急刹。后来我们改成了输出结构化的感知结果,包含目标类型、位置、速度、置信度等字段,决策层的表现立刻好了很多。
好,这一章的内容就到这里。下一章我们会讲决策层架构设计,到时候会用到今天讲的感知数据。记得把传感器融合和障碍物识别的代码框架自己写一遍,光看是学不会的。