毫米波雷达上报:距离42.3米、相对速度-3.2m/s,RCs约2.8,目标倾向小型车辆。摄像头在同一帧里给出的标签却是行人,置信度0.55,后面还跟着骑行者0.25、车辆0.15。激光雷达点云聚类后的尺寸反而更接近一个骑电动车的人。同一个物理目标,三个传感器给出三套答案——跑过实车或认真看过数据的人都知道,多传感器融合从来不是加分项,而是自动驾驶感知绕不开的必修环节。
解决这类分歧,工程上普遍采用目标级融合路线:每个传感器先各自完成检测与跟踪,输出目标列表,再在目标层面把状态和属性合并。状态量的合并用信息矩阵,类别的合并用Dempster-Shafer证据理论。今天这篇就围绕这个主题展开——我会先把为什么这样选型讲清楚,再拆解DS的数学内核和完整融合链路,最后给出可复现的Matlab实现思路,以及我在实际调试中踩过的坑。这篇内容适合正在做多传感器融合课程设计、毕业设计,或者在做工程预研的同学,就算你是第一次接触DS,也应该能跟着走完整个流程。
1. 为什么是目标级融合,又为什么是Dempster-Shafer
1.1 三种传感器各有各的"视力问题"
自动驾驶里最常见的三种传感器,优缺点基本上是互补的:
| 传感器 | 擅长的事 | 短板 |
|---|---|---|
| 毫米波雷达 | 测距测速直接、全天候稳定 | 角度分辨率低,基本认不出精细类别 |
| 摄像头 | 语义信息丰富,能区分车、人、交通标志 | 测距精度一般,受光照和天气影响大 |
| 激光雷达 | 空间点云精确,物体的尺寸和形状判断很准 | 雨雾天衰减严重,大型产品方案成本高 |
既然各有长短,融合自然就是"取长补短"。但融合做到什么层级,差别很大。底层数据融合要把图像像素、点云、雷达回波塞进同一个特征空间,数据密度和异构程度都非常夸张,实际工程里很少有人敢直接这么干。特征级融合介于中间,但特征定义和具体算法强绑定,换一个检测器就得重新调。
目标级融合的思路完全不同:每个传感器先独立完成检测和跟踪,输出一个目标列表——目标的位置、速度、尺寸、类别概率都在里面。系统拿到的是张"菜单"而不是原始"食材",后续融合只对目标做操作。这个层级的好处非常直接:模块解耦、计算开销可控、每个传感器单拎出来都能单独测试,出了问题也容易回溯是哪个传感器提供了错误信息。
1.2 目标级融合里的两条并行通道
目标级融合其实包含两条性质完全不同的通道,这个区分很重要:
- 状态融合通道:处理连续量,比如位置、速度、加速度、目标尺寸。最典型的做法是用信息矩阵(协方差矩阵的逆)和信息滤波器做估计合并。
- 属性融合通道:处理离散量,比如目标到底是车、行人还是骑行者。Dempster-Shafer证据理论在这里负责合并类别层面的证据。
标题里同时提到"信息矩阵融合算法"和"Dempster-Shafer",指的正是这两条通道的组合:信息矩阵解决"目标在哪里、往哪走",DS解决"目标是什么"。有人会问,属性融合为什么不直接用卡尔曼滤波?因为卡尔曼滤波天生处理连续量,而类别是一个离散标号,"未知"并不是某一类,而是一种需要显式建模的状态——DS恰恰提供了这样一个表达框架。
1.3 DS凭什么比"置信度平均"更可靠
先把话说在前面:不是不能用朴素贝叶斯,也不是不能把多传感器的置信度直接加权平均,工程上都能出结果。但DS在目标级融合里更合适,有几个很现实的原因:
- 贝叶斯方法需要精确的先验概率。自动驾驶的目标类别先验在不同城市、不同场景差异极大,这个先验很难取准,DS不需要先验,直接从证据出发。
- 简单加权平均缺乏理论支撑,没法表达"证据之间互相矛盾"这件事。
- DS建立在识别框架的幂集上,天然支持"不确定"和"部分支持"。比如一个目标可能是轿车也可能是卡车,摄像头拿不准,你可以把一条质量分配给子集{轿车,卡车},而不是强迫它二选一。
- DS合成结果落在同一个识别框架上,多路证据可以逐步合并,中间结果全部可解释。
说白了,当传感器输出的本身就是类别置信度时,DS的建模方式和人的直觉几乎同步。这也是它在目标级融合里被反复选择的原因。
2. DS证据理论的数学内核:从BPA到合成规则
2.1 识别框架与基本概率分配
DS的第一步是定义识别框架Θ,它包含所有可能的目标类别。假设场景里只有三类目标:Θ = {车辆A,行人B,骑行者C}。
DS不是在Θ上做概率分配,而是在Θ的幂集上做分配。幂集就是Θ所有子集的集合,这里共有8个子集:∅、{A}、{B}、{C}、{A,B}、{A,C}、{B,C}、{A,B,C}。
基本概率分配函数m,也叫mass函数,满足两个条件:m(∅) = 0;所有子集的mass之和为1。举个例子,摄像头对某个目标给出了判断:
- m({A}) = 0.6
- m({B}) = 0.3
- m({A,B,C}) = 0.1
这里的0.1分配到全集Θ上,表达的是"有10%的支持度给了未知"——摄像头看不准到底属于哪一类,但这部分信息没有被丢掉,而是被显式保留了。要注意,m({A})的字面含义不是"A的概率”,而是"证据对A的支持程度"。
2.2 信任区间:Bel与Pl
在mass函数的基础上,可以定义两个结合函数:
- 信任函数Bel(A) = Σ_{B⊆A} m(B),表示证据对A的全部支持程度。
- 似然函数Pl(A) = Σ_{B∩A≠∅} m(B),表示不反对A的程度。
对刚才的例子,Bel({A}) = 0.6,而Pl({A}) = 0.6 + 0.1 = 0.7,因为全集{m({A,B,C})}和{A}有交集,说明这部分证据不反对A。这样每条证据对目标类别的判断就变成了一个区间[Bel, Pl],区间越窄,判断越确定。这个区间在很多可视化场景里相当直观,后面的Matlab实现里可以画出来看。
2.3 Dempster组合规则与冲突归一化
两条独立证据m1和m2的合成,用Dempster组合规则:
m(A) = (1 / (1 - K)) × Σ_{B∩C=A} m1(B) × m2(C)
其中K = Σ_{B∩C=∅} m1(B) × m2(C),是所有交为空的mass乘积之和,也就是两条证据之间的冲突量。
这个公式读出来的意思就是:把两条证据的mass两两相乘,凡是交集恰好等于A的,累加到一起;凡是交集为空的,记入冲突K;最后用1/(1-K)做归一化,把冲突部分的影响消掉。整个过程就像把两个证人的证词做了一次"逻辑与"合并。
2.4 手算一个真实的融合例子
回到开头的场景,毫米波雷达和摄像头分别给出证据。识别框架简化为Θ = {车V,行人P},全集记作{V,P}。
雷达证据m1:
- m1({V}) = 0.7
- m1({P}) = 0.1
- m1({V,P}) = 0.2
摄像头证据m2:
- m2({V}) = 0.6
- m2({P}) = 0.3
- m2({V,P}) = 0.1
逐对计算交集和mass乘积:
| 雷达子集 | 摄像头子集 | 交集 | 乘积 |
|---|---|---|---|
| {V} | {V} | {V} | 0.42 |
| {V} | {P} | ∅ | 0.21 |
| {V} | {V,P} | {V} | 0.07 |
| {P} | {V} | ∅ | 0.06 |
| {P} | {P} | {P} | 0.03 |
| {P} | {V,P} | {P} | 0.01 |
| {V,P} | {V} | {V} | 0.12 |
| {V,P} | {P} | {P} | 0.02 |
| {V,P} | {V,P} | {V,P} | 0.02 |
冲突量K = 0.21 + 0.06 = 0.27,归一化系数1/(1-K) = 1/0.73。
合成结果:
- m({V}) = (0.42 + 0.07 + 0.12) / 0.73 ≈ 0.8356
- m({P}) = (0.03 + 0.01 + 0.02) / 0.73 ≈ 0.0822
- m({V,P}) = 0.02 / 0.73 ≈ 0.0274
融合后"车"的mass从雷达的0.7和摄像头的0.6提升到0.8356,两个传感器的一致意见被强化,冲突部分被归一化处理。这就是DS一个非常直观的特性:证据互相支持时,结论的确定性会上升。后面如果还有激光雷达的证据,可以把当前结果和新证据继续合成,形成多传感器逐步融合。
3. 目标级融合的整体链路:从目标列表到BPA
3.1 各传感器先各自"做作业"
目标级融合的前提,是每个传感器先把单模态的目标检测做好。以三个传感器为例:
- 毫米波雷达:经过CFAR检测、点迹聚类和跟踪滤波,输出目标的位置、速度、RCs(雷达散射截面),部分雷达还能给出一个粗分类置信度。
- 摄像头:检测网络输出目标框和类别概率,通常包括车、行人、骑行者、路障等。
- 激光雷达:经过地面滤除、点云聚类和特征提取,输出目标的中心、尺寸、点云形状特征。
这些输出统称"目标列表",每个目标是一个元组,包含时间戳、目标ID、状态向量和类别置信度。这一步做完,后续融合就只处理列表中的元素,不再碰原始点云或图像。
3.2 时间同步、坐标统一与航迹关联
融合之前有两件脏活累活必须先干:时间对齐和空间对齐。
时间对齐方面,三个传感器采样率不一样,比如雷达10到20Hz、相机30Hz、激光雷达10Hz,需要把时刻不同的观测插值到统一时间戳上,或者取最近邻时间戳。我在调试中遇到过融合轨迹来回跳的情况,查到最后就是毫米波雷达和摄像头的时间戳错了一个帧周期,50ms的偏差直接导致关联错配。这个环节看起来不高级,却是整个融合链路里最容易埋雷的地方。
空间对齐方面,每个传感器的目标都在自己的坐标系里,需要通过外参标定把它们转换到车身坐标系。雷达坐标系、相机坐标系、激光坐标系各转一次,这一步代码量大但逻辑简单,关键是标定参数必须准。
航迹关联方面,传感器各自维护目标ID,融合中心需要判断"雷达的3号目标和摄像头的7号目标是不是同一个物理目标"。常用方法是基于状态残差的马氏距离计算目标匹配度,再用匈牙利算法做最优分配。新目标启动新航迹,连续丢失的目标标记删除。
我的经验是:关联错误是融合失效的最大来源,远大于融合算法本身的问题。如果雷达的A目标被分配给了摄像头的B目标,后面DS再精巧也只是把错误信息合并得更完整而已。
3.3 质量函数设计:从置信度到BPA
传感器输出的类别置信度不能直接当mass用,要做一次映射,最常用的构造方式是:
m_j({A_i}) = p_i × d_j m_j(Θ) = 1 - Σ_i (p_i × d_j)
其中p_i是传感器输出的第i类置信度,d_j是传感器j的可靠性折扣系数。
为什么必须有折扣d_j?因为分类网络的softmax输出经常会过度自信,模型在无目标场景或遮挡严重时,可能直接给某个类别打出0.99的高置信度。直接当mass用,相当于把模型的自信当成真实世界的可靠证据,后果很严重。加一个折扣系数,把不可靠的部分推到全集Θ上,表示为认知不确定性,融合结果会更稳。
举个例子,摄像头输出p = [车0.6,行人0.3,骑行者0.1],摄像头可靠性折扣d取0.85:
- m({车}) = 0.6 × 0.85 = 0.51
- m({行人}) = 0.3 × 0.85 = 0.255
- m({骑行者}) = 0.1 × 0.85 = 0.085
- m({全集}) = 0.15
这个折扣系数怎么定?通常会在部署前用一段标注数据统计传感器在不同距离、天气下的实际准确率,再换算成折扣值。这个标定工作很耗时,但非常值得。我在后面第五部分还会展开讲。
4. Matlab实现与工程化细节
4.1 主程序与函数模块划分
Matlab实现建议按功能拆成五个文件:
- main_DS_Fusion.m:主程序,模拟多传感器目标列表并执行整条融合链路
- bpa_construction.m:把传感器置信度转换为mass函数
- ds_combine.m:Dempster合成规则的核心实现
- information_fusion.m:信息矩阵状态融合
- decision_making.m:基于合成后的mass做类别决策
主程序里先模拟或读取三个传感器的目标列表,做时空对齐和航迹关联,然后逐目标执行"状态融合 + 属性融合"两条通道。状态融合输出目标位置和速度的最终估计,属性融合输出类别mass,最后通过decision_making给出类别判定。
4.2 DS合成函数:从直觉实现到位掩码
先说最直觉的实现方式:用cell数组存放识别框架的每个子集,逐个求交集。这样写逻辑最清楚,但效率不行。识别框架有N个元素时,幂集有2^N个子集,当N到8到10的时候,cell数组的反复比较会非常慢。
更工程化的做法是位掩码编码。假设识别框架Θ = {车辆=1,行人=2,骑行者=4},每个子集用一个整数表示,集合的交集就是按位与bitand,空集就是0。比如{车, 行人} = 3,{行人, 骑行者} = 6,两者交集是bitand(3,6) = 2,对应{行人}。
位掩码版DS合成函数:
function [m_out, elems_out] = ds_combine_mask(m1, elems1, m2, elems2) % 输入两条证据的mass向量和对应的子集掩码 % 输出合成后的mass向量和子集掩码 n_frames = 2^3; % 假设识别框架有3类,共8个子集 acc = zeros(1, n_frames); K = 0; for i = 1:length(elems1) for j = 1:length(elems2) inter = bitand(elems1(i), elems2(j)); prod = m1(i) * m2(j); if inter == 0 K = K + prod; else acc(inter + 1) = acc(inter + 1) + prod; end end end acc = acc / (1 - K); idx = find(acc > 0); elems_out = idx - 1; m_out = acc(idx); end这个实现里acc数组长度是固定的2^N,所有中间结果都直接累加到对应索引上,避免了动态扩展数组。识别框架类别数N不超过10时,数组长度最多1024,完全在可控范围内,也方便后面做mass裁剪。
4.3 信息矩阵融合的状态估计部分
状态融合通道用信息滤波器思路,代码非常简单。两个传感器对同一个目标各自给出状态估计x1、x2和协方差P1、P2,融合结果由信息矩阵相加得到:
function [x_fused, P_fused] = information_fusion(x1, P1, x2, P2) % 计算信息矩阵 Omega1 = inv(P1); Omega2 = inv(P2); % 信息状态向量 xi1 = Omega1 * x1(:); xi2 = Omega2 * x2(:); % 信息融合:信息矩阵和信息状态直接相加 Omega_fused = Omega1 + Omega2; xi_fused = xi1 + xi2; % 转回状态空间 P_fused = inv(Omega_fused); x_fused = Omega_fused \ xi_fused; end这个方法的原理其实很优雅:两个高斯分布的乘积,在信息形式下就是信息矩阵和信息状态向量的加法。某个传感器协方差大(信息量少),它贡献的信息就少;协方差小的传感器自然占权重更大。相比直接做矩阵加权平均,信息形式不需要手动计算权重矩阵,工程实现干净利落。
不过要说明,这个版本隐含假设两个传感器估计误差相互独立。现实中如果两个传感器都依赖了历史融合结果,误差会相关。严格做法要引入交叉协方差或互协方差修正,简单Demo里可以先接受独立性假设,工程落地时再补这块。
4.4 融合实例的完整数值过程
假设毫米波雷达和摄像头同时对前方目标进行了状态估计,结果如下:
- 雷达:x1 = [42.3; -3.2],P1 = [1.0 0; 0 0.5]
- 摄像头:x2 = [43.1; -3.5],P2 = [2.0 0; 0 1.0]
信息矩阵分别是:
- Omega1 = diag([1.0, 2.0])
- Omega2 = diag([0.5, 1.0])
融合后:
- Omega_fused = diag([1.5, 3.0])
- xi_fused = [42.3×1 + 43.1×0.5; -3.2×2 + (-3.5)×1] = [63.85; -9.9]
- x_fused = [63.85/1.5; -9.9/3.0] = [42.57; -3.30]
- P_fused = diag([0.667, 0.333])
融合后的位置42.57更靠近雷达估计值42.3,因为雷达的协方差更小、信息量更大;而摄像头的位置估计43.1把融合结果拉向右方一部分。融合后方差从单个传感器的1.0和2.0下降到0.667,不确定性明显收缩,这就是状态融合的价值。
属性通道方面,按之前手算的DS结果,合成后的mass为m({车})≈0.8356、m({行人})≈0.0822、m({车,行人})≈0.0274。如果要做最终判决,常见做法是取最大mass类,或者用pignistic概率:
BetP({车}) = 0.8356 + 0.0274/2 ≈ 0.8493 BetP({行人}) = 0.0822 + 0.0274/2 ≈ 0.0959
最终系统判定该目标为车辆,并把融合后的状态向量和类别信息一起输出给下游规划模块。
5. 实测中踩过的几个坑
5.1 证据冲突会让合成结果反直觉
DS最经典的问题出现在两条证据高度冲突的时候。举个极端例子:
- 雷达说:m({车}) = 0.99,m({行人}) = 0.01
- 摄像头说:m({车}) = 0.01,m({行人}) = 0.99
冲突量K = 0.99×0.99 + 0.01×0.01 = 0.9802,归一化系数1/(1-K) ≈ 50.5。合成结果变成:
- m({车}) ≈ 0.5
- m({行人}) ≈ 0.5
两条原本高度自信的证据,合成后反而变成完全不确定。这个反直觉现象在证据理论文献中是个经典悖论,实际工程里也真实存在——当某个传感器因为干扰出现系统性偏差时,就会出现类似状况。如果直接把这样的结果拿去决策,系统会在"车"和"行人"之间反复摇摆。
5.2 冲突处理三板斧
处理冲突我梳理出三种工程上验证有效的方法:
- 折扣因子法:在冲突较大或传感器可靠性降低时,适当降低对应传感器的折扣系数d,把不信任分配给全集Θ。这个方法逻辑最清晰,只需要额外标定一个参数,实车项目里最常用。
- 冲突重新分配法:不归一化冲突,直接把冲突质量累加到全集上,这就是Yager类规则的核心思路。冲突被解释为"未知"而不是"互相否定",结论更保守,适合安全敏感场景。
- 加权平均法:先把多条证据做加权平均,再执行Dempster合成。这个方法能明显提升多源冲突时的稳定性,代价是损失一些数学上的严谨性,但实际效果往往不错。
我在项目里一般先观察K值的长时间走势,K持续偏高往往意味着某个传感器进入了异常状态,这个指标本身就可以作为系统的健康度监控信号。
5.3 焦元爆炸、时序抖动、关联错配
焦元爆炸发生在识别框架类别数较大时。N个类别对应2^N个子集,如果类别数到10,幂集就是1024个,还不算大,但连续多帧合成后,如果所有焦元都保留,结果会越来越多、越来越慢。解决办法是每次合成后裁剪掉mass小于0.01的焦元,并把裁剪掉的mass归给全集Θ,保证总mass仍然为1。类别数再大的场景,可以改用层级识别框架,比如第一层只分"动态目标/静态目标",第二层再分"车辆/行人/骑行者",每一层最多几个分支,焦元数量完全可控。
时序抖动是另一个常见问题。单帧DS很容易受传感器偶发错误影响,类别在帧与帧之间闪烁,这一帧判成行人、下一帧判成骑行者。我常用的手段有三个:对每类mass做指数平滑,m_t = α×m_raw + (1-α)×m_{t-1};或者用滑动窗口做多帧DS合成,让时间维度也参与融合;还可以在决策端加迟滞,要求连续N帧都是同一类别才切换输出。这几种办法各有适用场景,我自己最常用的是指数平滑加决策迟滞的组合。
关联错配这条我前面已经强调过,再补一个具体场景。有一次测试中,雷达和视觉的航迹关联在一辆大车旁边的小行人目标上频繁出错,两根航迹反复跳变,最终融合结果输出的位置在大车和小行人之间震荡。问题根源不在DS融合,而是马氏距离门限设置过宽,导致两个目标被交替匹配。把门限收紧后,融合立刻稳定了。所以排查融合问题时,一定先检查关联,再检查融合。
6. 一些实操经验与后续扩展思路
调试阶段我非常建议先把录制的传感器数据回放,把每个传感器目标框和关联结果做成动态可视化。肉眼看到"雷达目标3号"和"摄像头目标7号"是否真的对应同一个物理目标,比看任何数值指标都直观。关联对了再去看BPA曲线和Bel区间,问题会定位得很快。
调试分类冲突时,把每个目标的BPA和K值打印出来会发现很多信息。K值在一段时间内持续偏高,不只是融合算法要处理的问题,更是传感器健康度下滑的信号。我在实际项目里会把K值作为一条独立诊断通道输出,接入监控系统。
还要记住一点:DS适合做属性融合,不适合做状态估计。连续量的最优融合仍然是信息滤波或卡尔曼滤波族方法。硬把DS用在位置上,既没有理论优势,也得不到更好的精度。
如果代码已经跑通,后续可以往几个方向扩展:
- 把时间维度引入DS,用时间折扣因子让旧证据随帧数自然衰减,多帧合成更平滑。
- 把DS结果与跟踪框架结合,用mass值辅助管理航迹的存活置信度,比纯阈值判定更鲁棒。
- 做层级DS融合,先合并两个同质传感器,再跨模态融合,减少高冲突证据直接碰撞的概率。
- 把状态融合的精确协方差反馈到关联门控中,形成"关联-融合-反馈"闭环,匹配精度会有明显提升。
我个人的体会是,目标级融合这个方向,数学本身并不难,难的是把两条融合通道和一张关联矩阵安排得明明白白。当你把DS的折扣、裁剪、时序稳定都调完,回头看,最有价值的往往不是融合公式本身,而是对"每个传感器到底凭什么说这句话"的理解。先把数据对齐、关联、可信度标定做好,信息矩阵和DS才会真正变成锦上添花,而不是一句空谈。