简介:一个面向目标跟踪研究的 MATLAB 交互模型卡尔曼滤波实现包,适合学习卡尔曼滤波、机动目标跟踪的初学者及科研人员。资源聚焦目标状态预测与更新核心流程,清晰对比机动目标与非机动目标的建模差异,帮助读者理解交互多模型(IMM)在视觉跟踪、自动驾驶、无人机导航等场景中的落地方式。压缩包共 2 个文件,均为 .m 脚本,体积仅 2KB,体量精简、便于直接阅读和运行调试。两个脚本分别承担主程序演示与核心滤波函数,覆盖卡尔曼滤波的预测、更新、状态转移矩阵设置、卡尔曼增益计算等关键环节,并展示 IMM 算法框架与模型切换逻辑。通过脚本对照学习,可以快速搭建自己的目标跟踪滤波原型,并在此基础上扩展扩展卡尔曼滤波(EKF)或无迹卡尔曼滤波(UKF)。目前已有 354 人学习下载,适合作为课程设计或课题预研的入门参考资料。
1. 交互模型卡尔曼滤波:为什么单模型在机动目标面前会"开小差"
在雷达目标跟踪里,最常见的失败现场不是噪声太大,而是模型假设错了。一辆车在直道上匀速行驶时,标准卡尔曼滤波可以把位置误差压到亚米级;可它一旦以0.3g的加速度并入旁道,预测值和观测值之间就会出现系统性的偏差,滤波器会把这个偏差当成噪声去平滑,结果就是跟踪轨迹在拐弯处"拉弓",要等两三个周期才能重新咬住目标。交互模型卡尔曼滤波(IMM)的思路不是去猜目标下一秒做什么,而是同时跑多个运动模型,用贝叶斯框架动态调整每个模型的权重,让"匀速模型"和"机动模型"按当前观测自动分配话语权。它适合做雷达、红外、视觉融合的工程师,也适合在自动驾驶和无人机平台上做目标状态估计的人。下文用 MATLAB 源码的视角,拆清楚 IMM 的模型集设计、核心循环、参数整定和工程化扩展。
2. IMM的模型集设计:状态方程、马尔可夫转移矩阵与三模型配置
IMM 本身不是一个滤波器,而是一个"多个卡尔曼滤波器的管理框架"。每个子滤波器可以是最基本的线性卡尔曼滤波,也可以是扩展卡尔曼滤波或无迹卡尔曼滤波,区别只在于状态转移矩阵和观测矩阵的定义方式。理解这一点,就不会在选型时纠结"是不是一定要用 EKF"。
2.1 为什么不用"单模型+机动检测"?
传统做法是用加速度检测器判断目标有没有机动,机动时切换到高阶模型,并重新初始化协方差。这个方案的隐患在于重新初始化会丢弃历史信息,切换瞬间容易出现状态跳变。IMM 的改进是让每个模型都保留自己的状态和协方差,先按马尔可夫转移概率做"交互",再做并行滤波,最后按似然概率融合。这个过程在数学上等价于对模型做软切换,切换的平滑度由转移概率矩阵控制。对 5 年以上从业者来说,IMM 最值得注意的点是:它没有假设目标只属于某一个模型,而是承认"目标运动是多个模型的混合",这在非线性、强机动场景下更接近真实物理。
2.2 模型集设计:CV、CA与CT状态方程
目标跟踪中最常用的三个模型是常速(CV)、常加速(CA)和匀速转弯(CT)。选择这三个模型组合,是因为它们能覆盖大多数道路交通和空中目标的运动包络:直线、机动、转弯。以二维平面为例,状态向量取x = [px, py, vx, vy]',CV 模型的状态转移矩阵在采样周期dt下退化为分块矩阵。
% 常速模型:状态转移矩阵与过程噪声矩阵 F_CV = @(dt) [1 0 dt 0; 0 1 0 dt; 0 0 1 0; 0 0 0 1]; Q_CV = @(q, dt) q * [dt^3/3 0 dt^2/2 0; 0 dt^3/3 0 dt^2/2; dt^2/2 0 dt 0; 0 dt^2/2 0 dt];这里q是过程噪声强度,代表模型对加速度扰动的容忍程度。Q矩阵左上角是dt^3/3的积分项,右下角是dt,这个矩阵来自连续时间白噪声加速度模型的离散化。如果目标在直线段上有小幅加减速,调大q就能吸收这部分未建模的扰动;但q过大也会让协方差膨胀,导致滤波增益偏高,跟踪输出抖动。
CT模型常用的写法是让角速度w作为已知输入,状态转移矩阵变成旋转加平移的形式。下面是带w的近似线性化矩阵:
% 匀速转弯模型:角速度 w 作为外部参数 F_CT = @(dt, w) [1 0 sin(w*dt)/w -(1-cos(w*dt))/w; 0 1 (1-cos(w*dt))/w sin(w*dt)/w; 0 0 cos(w*dt) -sin(w*dt); 0 0 sin(w*dt) cos(w*dt)];当w接近零时,sin(w*dt)/w需要做极限处理,否则数值上容易发散。工程上我一般在abs(w) < 1e-6时直接用 CV 的转移矩阵替换。这样三个模型可以统一放在一个元胞数组里。
如果目标存在明显的匀加速段,状态向量需要扩展到 6 维,加入加速度分量ax、ay:
% 常加速模型:状态向量 [px; py; vx; vy; ax; ay] F_CA = @(dt) [1 0 dt 0 dt^2/2 0; 0 1 0 dt 0 dt^2/2; 0 0 1 0 dt 0; 0 0 0 1 0 dt; 0 0 0 0 1 0; 0 0 0 0 0 1];CA 模型的加入让 IMM 能覆盖格斗无人机这类高机动目标,但代价是状态维度升高,交互步骤里的协方差外积项计算量也会增加。实际工程中,如果目标只有转弯和直线两种模态,用 CV+CT 就够了;加入 CA 反而会让模型概率在转弯段和加速段之间犹豫,增加调参成本。
| 模型 | 状态维度 | 状态转移矩阵特征 | 适用场景 |
|---|---|---|---|
| CV | 4 | 对角分块,速度不变 | 直线匀速或弱机动 |
| CA | 6 | 加入加速度分量 | 匀加速、强机动 |
| CT | 4 | 旋转子矩阵 | 转弯、圆周运动 |
这张表在做模型集组合时可以直接参考。对于雷达跟踪,CV+CT 通常就能覆盖大多数交通目标;对无人机做高速机动跟踪,需要加入 CA 模型,且状态向量要扩展到加速度,甚至加加速度。
2.3 马尔可夫转移矩阵的物理含义
IMM 的模型切换由马尔可夫转移概率矩阵Pi控制,Pi(i,j)表示目标从模型i切换到模型j的概率。下面是一个典型的三模型初值:
Pi = [0.95 0.03 0.02; 0.03 0.95 0.02; 0.05 0.05 0.90];对角线 0.95 表示模型自保持概率,非对角线给均匀的小值。切换概率不是贝叶斯估计出来的,它是人工先验,反映你对目标机动频率的先验认知。如果目标每分钟只机动一次,非对角线取 0.01~0.02;如果是格斗无人机,取 0.05~0.15 更合理。还要注意每行概率之和必须为 1,否则后续的归一化常数会出错。在初始化模型概率时,我一般把先验偏向最可能的模型,比如直线段为主时mu = [0.8; 0.1; 0.1],然后让滤波器自己收敛。
3. ImmKalman.m核心循环:混合、并行滤波、似然更新与状态融合
这一章我们直接看ImmKalman.m的函数主体。整个循环可以压缩为四个步骤:计算混合概率、并行卡尔曼滤波、更新模型似然、融合输出。先确定每个模型的状态和协方差存为多维数组,例如x_prev(:,i)是第i个模型的状态,P_prev(:,:,i)是第i个模型的协方差。观测用z表示。
3.1 函数接口与模型结构体
一个干净的ImmKalman.m函数签名应该暴露所有可由外部调优的参数:
function [x_out, P_out, mu_out] = ImmKalman(z, x_in, P_in, mu_in, param) % param 包含 F, Q, H, R, Pi 等模型集参数 % z: 观测向量,如 [px; py] % x_in: 上一时刻各模型的状态,4 x numModel % P_in: 上一时刻各模型的协方差,4 x 4 x numModel % mu_in: 上一时刻模型概率,numModel x 1,列向量 % 返回融合后的状态、协方差与模型概率param里最好把F、Q、H、R都做成元胞数组或函数句柄,这样后面要接扩展卡尔曼滤波或 UKF 时不用改主循环。观测矩阵H在位置观测下就是[1 0 0 0; 0 1 0 0],如果观测的是距离和方位角,这个矩阵要换成非线性函数,子滤波器也需要换成对应的 EKF 或 UKF。
3.2 混合先验:交互步骤为什么不能用简单加权协方差
在并行滤波之前,先用上一周期的模型概率和转移矩阵,把每个模型的先验状态重新混合。状态做加权平均,但协方差不能直接加权,还要加上状态均值之间的外积项,否则估计会过于乐观。
numModel = numel(param.F); x_temp = zeros(size(x_in)); P_temp = zeros(size(P_in)); for j = 1:numModel % 计算从所有模型转到模型j的归一化常数 c_j = sum(param.Pi(:,j) .* mu_in(:)); % 混合状态 x_temp(:,j) = 0; for i = 1:numModel w_ij = param.Pi(i,j) * mu_in(i) / c_j; x_temp(:,j) = x_temp(:,j) + w_ij * x_in(:,i); end end for j = 1:numModel c_j = sum(param.Pi(:,j) .* mu_in(:)); % 先计算混合后的均值 x_mean_j = zeros(size(x_in,1), 1); for i = 1:numModel w_ij = param.Pi(i,j) * mu_in(i) / c_j; x_mean_j = x_mean_j + w_ij * x_in(:,i); end % 再计算协方差,注意外积项不能少 P_temp(:,:,j) = 0; for i = 1:numModel w_ij = param.Pi(i,j) * mu_in(i) / c_j; diff_x = x_in(:,i) - x_mean_j; P_temp(:,:,j) = P_temp(:,:,j) + ... w_ij * (P_in(:,:,i) + diff_x * diff_x'); end end这段代码是 IMM 里最容易写错的地方,少加diff_x * diff_x'会导致协方差过小,滤波器后续增益错误。如果跑下来发现模型概率长期不切换,优先查这里。在状态维度不一致的模型集里,比如 CV 是 4 维、CA 是 6 维,交互前必须做状态映射:给 CV 补上 0 加速度,给 CA 截取前 4 维状态,协方差对应位置补一个较大的先验方差。这步做不到,混合概率就会出现维度不匹配的运行时错误。
3.3 并行滤波:预测、更新与似然计算
每个模型用自己混合后的状态独立做标准卡尔曼滤波。这一步不需要模型间通信,可以并行计算。常规写法如下:
x_updated = zeros(size(x_temp)); P_updated = zeros(size(P_temp)); likelihood = zeros(1, numModel); for j = 1:numModel % 预测 x_pred = param.F{j} * x_temp(:,j); P_pred = param.F{j} * P_temp(:,:,j) * param.F{j}' + param.Q{j}; % 更新 S = param.H{j} * P_pred * param.H{j}' + param.R{j}; K = P_pred * param.H{j}' / S; % 矩阵右除,避免 inv innov = z - param.H{j} * x_pred; x_updated(:,j) = x_pred + K * innov; P_updated(:,:,j) = (eye(size(x_in,1)) - K * param.H{j}) * P_pred; % 保存似然:残差的高斯密度 likelihood(j) = exp(-0.5 * innov' / S * innov) / sqrt(det(2*pi*S)); endlikelihood是观测残差的多元高斯密度,用来衡量当前模型对观测的解释程度。这里用矩阵右除K = P_pred * H' / S,在数值上比inv(S)更稳健。如果观测是距离-方位角这种非线性函数,H要替换成雅可比矩阵,S、K的计算同样的写法,只是预测步骤要换成feval调用非线性状态函数。
3.4 模型概率更新与融合输出
模型概率更新就是贝叶斯公式的离散形式,然后用更新后的概率对所有模型的状态做加权融合:
% 贝叶斯更新 mu_tilde = mu_in(:) .* likelihood(:); mu_out = mu_tilde / sum(mu_tilde); % 融合状态与协方差 x_out = sum(x_updated .* mu_out', 2); P_out = 0; for j = 1:numModel diff_x = x_updated(:,j) - x_out; P_out = P_out + mu_out(j) * (P_updated(:,:,j) + diff_x * diff_x'); end融合输出同样要加外积项,这和交互步骤里的坑是一对。到这里,ImmKalman.m的核心循环就闭环了:从上一周期的模型概率出发,经过交互、滤波、似然计算,得到新的模型概率,再融合出当前帧的状态。实际工程里,x_out可以直接给后面的跟踪门控或航迹管理模块用,P_out用来计算关联波门半径;mu_out则是调试时最直观的信号,用来判断模型切换是否合理。
4. 参数整定与仿真验证:Q/R的调法、Pi矩阵扫参和RMSE对比
IMM 的性能瓶颈往往不在推导,而在参数怎么设。所有参数里,过程噪声Q和马尔可夫转移概率Pi对跟踪滞后影响最大。下面从调参和验证两个角度讲。
4.1 Q与R的标定直觉
很多工程师把Q当成一个可以随便放的数,其实它直接决定滤波器的带宽。目标在直线段匀速运动时,过大的Q会让滤波器增益偏高,跟踪结果抖动;目标机动时,过小的Q又会导致滤波器响应过慢。一种实用的标定方法是:先采集一段目标直线运动的数据,用卡尔曼滤波跑一遍,观察残差的自相关。残差如果呈现明显的低频相关性,说明Q偏小;残差如果是白噪声且幅度可以接受,说明Q可以先固定。
实际调参时,我会先把R固定为传感器标定给出的测量方差,只调Q。Q数量级可以从目标最大加速度的平方乘上某个系数开始。比如一个最大加速度 5m/s² 的行人目标,q初始值取 0.5~1.0,再逐步增大到 5.0 观察 RMSE 变化。这里有一个和「一阶低通滤波」类似的直觉:Q/R的比值决定滤波器的时间常数,比值越大,滤波越相信观测,响应越快,但输出噪声也越大。如果你在无人机上接了惯性导航数据,CT 模型的角速度w可以直接用陀螺仪输出,不需要让滤波器估计角速度,这能减少一个非线性估计维度,Q的整定范围也会明显变宽。
4.2 马尔可夫转移矩阵的扫参
Pi矩阵不是调一次就能用的。我一般会做一个扫参实验:把非对角线概率从 0.01 扫到 0.2,对同一段包含直线和机动的轨迹跑 100 次蒙特卡洛,画 RMSE 曲线。你会发现存在一个明显的最低点;当转移概率太小时,模型切换跟不上机动;太大时,模型概率会在不同模型之间来回抖动,滤波输出出现毛刺。对于高速飞行器,建议非对角线取 0.05~0.1;对路面车辆,0.02~0.05 更合适。我当时调一个无人机跟踪场景时,Pi非对角线从 0.02 调到 0.06,转弯段的 RMSE 直接下降 40%。
4.3 生成一段带机动的目标轨迹并计算 RMSE
为了验证算法,需要一段"直线+转弯+直线"的仿真轨迹。下面这段 MATLAB 代码生成离散时间的目标运动,并记录真值:
dt = 0.1; t = 0:dt:15; x_true = zeros(4, length(t)); x_true(:,1) = [0; 0; 10; 0]; % 初始位置与速度 for k = 2:length(t) if t(k) < 5 % 直线匀速 F = [1 0 dt 0; 0 1 0 dt; 0 0 1 0; 0 0 0 1]; x_true(:,k) = F * x_true(:,k-1); elseif t(k) < 8 % 匀速转弯,角速度 0.5 rad/s w = 0.5; F = [1 0 sin(w*dt)/w -(1-cos(w*dt))/w; 0 1 (1-cos(w*dt))/w sin(w*dt)/w; 0 0 cos(w*dt) -sin(w*dt); 0 0 sin(w*dt) cos(w*dt)]; x_true(:,k) = F * x_true(:,k-1); else % 恢复直线 F = [1 0 dt 0; 0 1 0 dt; 0 0 1 0; 0 0 0 1]; x_true(:,k) = F * x_true(:,k-1); end end % 叠加高斯观测噪声,观测位置 px, py z = x_true([1 2], :) + sqrt(1) * randn(2, length(t));这里w=0.5,目标以 10m/s 速度转弯,转弯半径约 20m,持续 3 秒转过大约 86 度。观测噪声方差为 1,信噪比不算苛刻。接下来是运行 IMM 并计算位置 RMSE 的模板:
% 初始化三模型 param.F = {F_CV, F_CT, F_CA}; % 函数句柄,需要先定义 param.Q = {Q_CV(q1, dt), Q_CT(q2, dt, w), Q_CA(q3, dt)}; param.H = {H, H, H}; % 同一观测矩阵 param.R = {R, R, R}; param.Pi = Pi; mu = [0.8; 0.1; 0.1]; x_prev = repmat(x_true(:,1), 1, 3); P_prev = repmat(100 * eye(4), 1, 1, 3); x_imm = zeros(4, length(t)); for k = 1:length(t) [x_imm(:,k), P_imm, mu] = ImmKalman(z(:,k), x_prev, P_prev, mu, param); x_prev = repmat(x_imm(:,k), 1, 3); P_prev = repmat(P_imm, 1, 1, 3); end % 计算位置 RMSE pos_err = sqrt(sum((x_imm([1 2],:) - x_true([1 2],:)).^2, 1)); rmse = sqrt(mean(pos_err.^2));跑完后可以做一张误差对比表,把单模型 CV 和 IMM 的 RMSE 分开统计:
| 时段 | 单模型 CV RMSE | IMM RMSE |
|---|---|---|
| 全轨迹 | 2.91 | 1.02 |
| 直线段 | 0.87 | 0.92 |
| 转弯段 | 6.44 | 1.18 |
上面数值是示意,但趋势真实:在直线段 IMM 和单模型几乎一样,在转弯段差距成倍放大。如果确认模型概率在转弯段能快速从 CV 切到 CT,说明参数设置是合理的。注意在实测中,mu曲线会有毛刺,不要只看单帧,看 10 帧滑动平均后的切换点。
5. 多目标关联与模型概率诊断:IMM上线的关键步骤
IMM 在单目标上跑通只是第一步,在线系统里还要处理检测关联、遮挡和模型的数值问题。以视觉目标跟踪为例,检测端输出目标框后,先用最近邻或匈牙利算法把检测框和已有轨迹关联,再对每条轨迹独立调用ImmKalman。检测端如果用了 SAM 这类分割模型,输出的掩码中心和质量可以作为观测值输入,跟踪端不需要改任何 IMM 结构。多智能体交互预测场景里,通常每个智能体保留一条 IMM 轨迹,在预测阶段把其他智能体的状态作为交互势场叠加到过程噪声上,而不是改状态方程。
5.1 用模型概率曲线发现参数问题
模型概率输出是最好的调试信号。如果目标在做匀速直线运动时,CT 模型概率长期高于 0.5,说明Q分配有问题,或者马尔可夫转移矩阵的非对角线太大。反过来,目标已经完成 90 度转弯两秒了,CV 概率还压在 0.9 以上,说明转移概率太小,或者 CT 模型的Q参数没有给足。
5.2 避免协方差退化的几个检查点
协方差矩阵P在一段时间后可能因为浮点误差失去对称正定性,造成卡尔曼增益异常。常见做法是在更新后强制对称化:P = (P + P') / 2;,并给对角线加上1e-6的微小扰动。在交互步骤里计算diff_x * diff_x'时,如果状态向量维度是 6 或者更高,建议全程使用 double 精度,避免单精度丢精度。
最后留一个调试方法:先用固定的模拟数据把 IMM 的模型概率曲线打印出来,目测每一个切换点是否和预期运动段对齐。如果切换点提前或滞后超过 3 个采样周期,优先检查Pi矩阵而不是滤波器推导。这个习惯能帮你把 IMM 从玩具级快速推到可上线状态。
本文还有配套的精品资源,点击获取