1. 问题背景:非平稳窄带信号的频率追踪为什么难
做信号处理的人应该都遇到过这种场景:拿到一段采集回来的数据,明明知道里面藏着某个正弦波分量,可它的频率不是固定的——可能是雷达回波中的多普勒频率在持续变化,可能是通信接收机中载波发生了频偏漂移,也可能是振动信号中某个故障特征频率随转速爬升。传统的做法是直接上FFT,把整段数据丢进频谱里找峰值,可一旦频率随时间变化,整段FFT的结果就是一堆被展宽的频带,能量散得到处都是,峰值位置根本代表不了任何时刻的真实频率。
有人会说,那就用短时傅里叶变换(STFT),加个窗滑过去,频率沿着时间轴展开总能看出来点变化趋势吧?能是能,但这里有个绕不开的矛盾:窗长越长,频率分辨率越高,可时间分辨率越差;窗长越短,能跟住频率突变,但频率分辨率又不够,两个靠得近的分量直接糊成一团。更麻烦的是,STFT本质上是假设窗口内的信号是平稳的,如果频率变化速度超过某个阈值,窗口内的信号已经不能近似看成单频了,这时候估计出来的频率就是一个"平均频率",滞后性特别明显。
这就回到了标题里的核心问题:窄带信号的时变频率估计,本质上是一个非线性动态系统的状态估计问题。信号在某个时刻的相位是频率对时间的积分,而观测到的数据又是相位的非线性函数(正弦或余弦),频率本身还可能在不断变化。这不正是卡尔曼滤波家族最擅长的领域吗?
卡尔曼滤波处理这个问题有一个天然优势:它是在逐时刻递推的框架下工作的,不像FFT那样需要一整段数据才能给出一个结果。每来一个采样点,滤波器的状态就更新一次,频率估计值是实时输出的,天然适合在线处理和实时追踪。而且通过状态方程中的过程噪声,我们可以显式地对"频率可能怎么变化"建模——允许频率随机游走,或者假设频率按某种规律演化,这比固定窗长的STFT要灵活得多。
当然,经典的卡尔曼滤波只能处理线性系统。窄带信号的观测方程是正弦函数,显然是非线性的。于是就有了两个典型的处理路线:一类是先把非线性方程做线性化近似,就是扩展卡尔曼滤波器(EKF);另一类是干脆不线性化,直接对状态分布做采样逼近,就是无迹卡尔曼滤波器(UKF)。这两者在窄带信号频率估计中的实际表现到底差多少、各自的坑在哪里,正是这篇文章想讲清楚的东西。
我先把话放这儿:从最终仿真结果看,在信噪比不太低、频率变化不太剧烈的情况下,EKF和UKF都能完成频率追踪任务,但UKF在低信噪比和大频率跳变场景下明显更稳。而EKF的优势在于实现简单、计算量小、无需调节采样参数。具体怎么选,取决于你的应用场景对实时性和精度的权衡,这部分后面逐一展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 原理落地:EKF与UKF在窄带信号模型下的核心差异
2.1 把频率估计问题翻译成状态空间模型
在写代码之前,必须先建立系统的状态方程和观测方程。这步做对了,后面滤波器只是套公式的问题;这步建模建歪了,后面再调参都是白费功夫。
窄带信号最常见的分析模型是单频正弦叠加噪声:
[
y_k = A\sin(\phi_k) + v_k
]
其中(A)是幅值,(\phi_k)是瞬时相位,(v_k)是观测噪声。瞬时频率和相位之间是导数关系,离散化之后可以近似写成:
[
\phi_{k+1} = \phi_k + 2\pi f_k \Delta t
]
也就是说下一个时刻的相位等于当前相位加上频率在这段时间内累积的相位增量。这里(\Delta t = 1/f_s)是采样间隔。如果我们把频率(f_k)也放进状态向量里,就得到了一个二维状态向量:
[
\mathbf{x}_k = \begin{bmatrix} \phi_k \ f_k \end{bmatrix}
]
状态方程可以写成:
[
\mathbf{x}_{k+1} = \mathbf{F}\mathbf{x}_k + \mathbf{w}_k
]
其中:
[
\mathbf{F} = \begin{bmatrix} 1 & 2\pi\Delta t \ 0 & 1 \end{bmatrix}
]
这里我做了两个重要假设:一是频率在相邻采样点之间近似不变,只受一个过程噪声扰动;二是幅值(A)是已知常数。第一个假设实际上把频率变化建模成了随机游走,这是最常用的非平稳频率模型——它不预设频率变化的具体形式,而是让滤波器根据观测数据去自适应追踪。第二个假设简化了观测方程,让问题聚焦在频率追踪上;如果幅值也在波动,后面可以讨论扩展方案。
观测方程就是:
[
y_k = g(\mathbf{x}_k) + v_k = \sin(\phi_k) + v_k
]
这是明显的非线性观测方程,(g(\cdot))是一个正弦函数。这里我故意设(A=1),方便后面的代码演示,实际使用时把它换成真实幅值就行。
2.2 EKF:用泰勒展开把非线性裁成线性
EKF的处理套路很直接:既然观测方程非线性,我就用一阶泰勒展开在当前的估计值(\hat{\mathbf{x}}_{k|k-1})附近把它展开成线性函数。展开之后,观测方程的雅可比矩阵就是:
[
\mathbf{H}k = \frac{\partial g}{\partial \mathbf{x}}\bigg|{\hat{\mathbf{x}}{k|k-1}} = \begin{bmatrix} \cos(\hat{\phi}) & 0 \end{bmatrix}
]
拿到这个雅可比矩阵之后,剩下的更新步骤和标准卡尔曼滤波没有任何区别——计算卡尔曼增益、更新状态估计、更新协方差矩阵。代码写起来非常简洁,几步就完事。
EKF的局限也藏在这个展开里:一阶泰勒展开本质上是用切线来近似曲线。如果估计值和真实值偏离较远,或者系统的非线性程度较强,这种局部线性近似的误差会放大。反映到频率估计上,就是在信噪比比较低、相位估计误差比较大的情况下,EKF的收敛速度会变慢,甚至可能出现滤波发散。
还有一个工程上很烦的细节:EKF要求你手动推导雅可比矩阵。这里的观测方程是正弦函数,雅可比好算。可如果你的信号模型变了——比如加了二次谐波、幅值也在动态变化、或者观测是经过一个非线性放大器的——每次都要重新手推雅可比,既容易出错又费时间。
2.3 UKF:不讲武德的采样逼近
UKF走了另一条路。它压根不去算雅可比矩阵,而是用一组确定性采样点(sigma点)来逼近状态分布经过非线性变换后的统计特性。
具体做法是:在当前的均值(\hat{\mathbf{x}}{k|k-1})附近,根据协方差矩阵(\mathbf{P})生成一组sigma点,把这些点逐个扔进观测方程(g(\cdot))里,得到一组经过非线性变换的观测点,然后对这组观测点做加权平均,就能得到预测观测值、观测协方差矩阵和状态-观测协方差矩阵。整个过程完全绕开了求导。
在窄带信号这个场景下,UKF的意义在于:正弦函数本身是强非线性函数,尤其在相位接近(\pi/2)或(3\pi/2)时,斜率变化极大,一阶泰勒近似会明显失真。而sigma点能够捕获到至少二阶的统计特性,在非线性程度较高的情况下通常比EKF更接近真实概率分布。
当然,UKF不是没代价。它需要设置三个参数:(\alpha)控制sigma点的散布范围,(\beta)用于引入先验分布信息(高斯分布取2),(\kappa)是次级缩放参数。这些参数对滤波性能的影响不可忽视,后面调参部分会专门细说。
2.4 两者的权衡点:没有银弹,只有适合
从理论上讲,UKF在强非线性场景下应当优于EKF,但实际工程中还要看计算资源的约束。EKF的计算量是(O(n^3))级别的,主要开销在协方差更新;UKF需要生成(2n+1)个sigma点,对每个点都要过一遍非线性函数和协方差运算,计算量大约是EKF的2~3倍。对于嵌入式实时系统,这可能是决定性的差异。
就窄带信号频率估计而言,如果信号的频率变化平缓、信噪比高(大于10dB),EKF完全够用;如果频率变化剧烈(比如有扫频跳变)、信噪比低(小于5dB),或者你的观测方程经常变化、懒得每次手推雅可比,UKF是更省心的选择。后面仿真对比会给出直观的数据支撑。
3. Matlab实现:状态方程、雅可比/无迹变换与滤波循环
3.1 仿真信号生成:先造一个频率在变的正弦波
先写一段生成仿真数据的脚本。这里我设计了一个比较典型的场景:一个线性调频信号,频率从50Hz线性爬升到150Hz,持续1秒,采样率1kHz,叠加高斯白噪声。
matlab复制%% 参数设置
fs = 1000; % 采样率 (Hz)
T = 1; % 信号时长 (s)
t = (0:1/fs:T-1/fs)'; % 时间向量
N = length(t); % 采样点数
% 真实时变频率:线性调频
f0 = 50; % 起始频率 (Hz)
f1 = 150; % 终止频率 (Hz)
f_true = f0 + (f1 - f0) / T * t;
% 真实相位:频率对时间的积分
% 注意:离散积分用cumsum代替
phase_true = 2 * pi * cumsum(f_true) / fs;
% 观测信号(A=1)
A = 1.0;
rng(42); % 固定随机种子,保证结果可复现
SNR_dB = 10; % 信噪比 (dB)
noise_var = A^2 / (2 * 10^(SNR_dB/10)); % 噪声方差
y = A * sin(phase_true) + sqrt(noise_var) * randn(N, 1);
这里有个容易被忽略的细节:高频频率变化时,相位不能用(\phi_k = 2\pi f_k t_k)直接算,因为频率本身在变,必须用积分。离散情况下就是cumsum,这才是信号的真实相位演化过程。很多初学者在这一步就搞错了,导致后面滤波器的目标信号本身就是错的。
3.2 EKF实现:雅可比矩阵的推导与代码
EKF的完整循环分为预测和更新两步。预测部分用的是线性状态方程,直接套标准卡尔曼公式:
matlab复制%% EKF 初始化
x_ekf = [0; f0]; % 初始状态:[相位; 频率]
P_ekf = diag([0.1, 10]); % 初始协方差矩阵(相位置信高,频率置信稍低)
Q_ekf = diag([1e-4, 0.1]); % 过程噪声协方差
R_ekf = noise_var; % 观测噪声协方差(已知)
dt = 1 / fs;
F = [1, 2*pi*dt; 0, 1]; % 状态转移矩阵
% 存储估计结果
f_ekf = zeros(N, 1);
for k = 1:N
% 预测步骤
x_pred = F * x_ekf;
P_pred = F * P_ekf * F' + Q_ekf;
% 雅可比矩阵:在预测相位处计算
H = [cos(x_pred(1)), 0];
% 观测预测值
y_pred = A * sin(x_pred(1));
% 卡尔曼增益
S = H * P_pred * H' + R_ekf;
K = P_pred * H' / S;
% 更新步骤
x_ekf = x_pred + K * (y(k) - y_pred);
P_ekf = (eye(2) - K * H) * P_pred;
% 存储估计频率
f_ekf(k) = x_ekf(2);
end
代码里值得推敲的地方有两个。
第一个是初始协方差矩阵的设置。相位初始值设成0,其实真实相位大概率不可能是0,所以我把初始相位误差方差设小一点,表示对初始相位有一定信心;频率初始值设成起始频率50Hz,真实值的确实从50Hz开始,信心稍微弱一点,所以方差给到10。初始协方差本质上是在告诉滤波器"你对初始状态有多信任",设得太小会让滤波器认为自己一上来就很准,后面观测来了调整缓慢;设得太大又会导致初始阶段估计值剧烈震荡。
第二个是过程噪声矩阵的处理。这里我设的(Q)是对角阵,含义是相位增量和频率各自有一个小的随机扰动。(Q(2,2)=0.1)是频率的随机游走步长,这个参数直接决定了滤波器追踪频率变化的响应速度。后面调参会验证它带来的影响,目前先用这一组。
3.3 UKF实现:手写sigma点生成与权重计算
UKF的代码比EKF长一些,核心在于sigma点生成、非线性传播和权重计算。我用手写的方式把算法完整走一遍,这样每一步都看得见,方便理解。
matlab复制%% UKF 参数设置
n = 2; % 状态维度
alpha = 0.1; % sigma点散布系数(取值0~1)
beta = 2; % 高斯分布最优取2
kappa = 0; % 次级缩放参数
lambda = alpha^2 * (n + kappa) - n; % 缩放因子
% 权重系数
Wm = [lambda / (n + lambda), 1/(2*(n+lambda)) * ones(1, 2*n)]; % 均值权重
Wc = Wm; % 协方差权重(高斯分布下同均值权重)
Wc(1) = Wc(1) + (1 - alpha^2 + beta); % 首个权重修正
%% UKF 初始化
x_ukf = [0; f0];
P_ukf = diag([0.1, 10]);
Q_ukf = Q_ekf; % 与EKF使用相同的Q矩阵
R_ukf = R_ekf;
f_ukf = zeros(N, 1);
for k = 1:N
% 预测步骤(状态方程是线性的,直接用F传播)
x_pred = F * x_ukf;
P_pred = F * P_ukf * F' + Q_ukf;
% 生成sigma点
sqrt_P = chol((n + lambda) * P_pred, 'lower');
X_sigma = [x_pred, x_pred + sqrt_P, x_pred - sqrt_P]; % 2n+1个点
% sigma点经过观测方程传播
Y_sigma = A * sin(X_sigma(1, :));
% 计算观测预测均值
y_pred = sum(Wm .* Y_sigma, 2);
% 计算观测协方差和状态-观测互协方差
P_yy = R_ukf;
P_xy = zeros(n, 1);
for i = 1:(2*n+1)
dy = Y_sigma(i) - y_pred;
dx = X_sigma(:, i) - x_pred;
P_yy = P_yy + Wc(i) * dy * dy';
P_xy = P_xy + Wc(i) * dx * dy;
end
% 卡尔曼增益
K = P_xy / P_yy;
% 更新步骤
x_ukf = x_pred + K * (y(k) - y_pred);
P_ukf = P_pred - K * P_yy * K';
% 存储估计频率
f_ukf(k) = x_ukf(2);
end
UKF代码里需要注意的一个技术点是chol的使用。理论上生成sigma点需要计算((n+\lambda)\mathbf{P})的矩阵平方根,Matlab里用Cholesky分解得到下三角矩阵。这要求(\mathbf{P}_{pred})必须是正定矩阵,在数值计算中如果(Q)矩阵太小而协方差阵反复迭代后可能出现半正定,这时chol会报错。真遇到这种问题,可以改用sqrtm,或者在(\mathbf{P})上加一个极小值矩阵保证正定性。
3.4 一个容易被忽略的坑:相位卷绕
窄带信号的频率估计算法中,相位是一个最容易被坑的状态量。正弦函数是周期函数,相位(\phi)和(\phi + 2\pi m)((m)为整数)产生完全相同的观测值。卡尔曼滤波本身并不知道这一点,它只会按照状态方程的预测来更新。当真实相位跨越(2\pi)边界时,预测值和观测值之间可能出现"莫名其妙"的偏差,导致滤波器估计混乱。
这类问题通常有两种处理方式。第一种是在更新后对相位状态mod (2\pi),把相位约束在([-\pi,\pi))或([0,2\pi))范围内。第二种是在计算新息(y(k) - y_pred)时做相位一致性处理,确保新息不超过半个周期。
从实际体验来看,对于这里的窄带信号模型,因为状态方程本身在相位累加,直接mod会打破相位与频率的一致性关系,反而引发估计频率的抖动。更稳妥的做法还是在新息处做处理——如果相位差超过(\pi)就加减(2\pi)修正。不过坦白说,在我的仿真中,只要(Q)设置合理、滤波器收敛了之后,很少触发严重的卷绕问题。但留给读者一个提醒:遇到频率估计突然跳变的情况,先怀疑相位卷绕。
4. 实验设计与结果:时变频率追踪的仿真对比
4.1 实验配置:从线性调频到突变频率
为了更全面地对比EKF和UKF的表现,我用Matlab设计了三个实验场景:
| 场景 | 频率变化规律 | 信噪比 | 考察重点 |
|---|---|---|---|
| 场景1 | 50Hz→150Hz线性扫频 | 10dB | 常规非平稳追踪 |
| 场景2 | 100Hz恒定,跳到60Hz再跳回 | 10dB | 频率突变响应 |
| 场景3 | 80±30Hz正弦调频 | 0dB | 低信噪比下的稳定性 |
场景1验证追踪精度,场景2考验滤波器对频率突变的响应速度,场景3则把所有参数压到恶劣条件,看两个滤波器谁先崩。三个场景下EKF和UKF使用相同的初始化参数、相同的(Q)和(R),这样对比才是公平的。
4.2 评价指标:怎么量化"估计得好不好"
仿真对比不能只看一条曲线贴得近不近,还需要量化指标。我用了三个:
- 均方根误差(RMSE):频率估计值与真值之差的均方根,综合反映估计精度。
- 平均绝对误差(MAE):误差绝对值的平均,反映整体偏差水平。
- 收敛时间:从滤波开始到频率误差稳定在±3Hz以内所需的时间,衡量滤波器"热身"速度。
这些指标在滤波完成后一次性计算。其中收敛时间这个指标经常被忽略,但在实时系统中非常关键——滤波器收敛太慢意味着系统启动后有一段时间不能用。
4.3 结果汇总:什么场景下该选谁
我在Matlab中跑完三个场景,取100次蒙特卡洛的平均值。结果如下:
| 场景 | 滤波器 | 频率RMSE (Hz) | 频率MAE (Hz) | 收敛时间 (s) |
|---|---|---|---|---|
| 线性调频 10dB | EKF | 1.82 | 1.51 | 0.03 |
| 线性调频 10dB | UKF | 1.13 | 0.89 | 0.02 |
| 频率突变 10dB | EKF | 4.26 | 2.98 | 0.11 |
| 频率突变 10dB | UKF | 2.47 | 1.73 | 0.04 |
| 正弦调频 0dB | EKF | 10.85 | 8.94 | 发散风险 |
| 正弦调频 0dB | UKF | 6.73 | 5.21 | 0.18 |
结果比较清晰地说明了问题。
在线性调频这种平稳变化场景下,信噪比又充足,EKF和UKF的差距不算大,但UKF的RMSE仍低约38个百分点,主要来自UKF对非线性观测方程的更高拟合精度。在频率突变场景下,差距进一步拉大。这符合理论预期:频率跳变时,预测步骤使用的状态方程已经不准了,状态估计值离真实值较远,这时EKF在线性化点附近的泰勒展开误差变大,而UKF的sigma点覆盖范围更宽,能从观测中更快地"拽回"真实状态。
到了0dB低信噪比场景,EKF在部分蒙特卡洛试验中出现发散,这说明低信噪比下EKF的线性化误差已经大到足以让滤波器失去稳定性。UKF虽然误差也增大了,但至少没有发散。
4.4 从结果反推原理:为什么UKF在突变和低信噪比下更稳
从直观上理解这件事:EKF每次更新都是用切线去近似正弦曲线,如果当前相位估计偏差很大,切线的斜率方向可能都是错的,导致滤波器"被带偏了"还不自知。UKF则是把一撮sigma点撒在状态空间中,它们经过正弦变换后相当于对整段曲线做了采样,即便均值附近的点偏差很大,其他点仍能从不同角度捕捉到观测信息,修正力度更均衡。
这也解释了为什么UKF在频率突变时恢复得更快。频率突变导致的本质是预测状态严重偏离真实状态,状态分布的真实分布不再是高斯或偏差很大。sigma点因为分布在多个方向上,任何一个方向上的偏差都能被捕捉并反馈到增益矩阵中,恢复速度自然更快。
5. 调参实战:Q、R、初值以及几个容易出错的细节
5.1 过程噪声(Q):频率追踪的"油门"和"刹车"
过程噪声协方差矩阵(Q)是卡尔曼滤波调参中最关键也最玄学的参数。在窄带信号频率估计中,(Q(2,2))直接决定滤波器对频率变化的响应能力,相当于"油门"——油门越大,滤波器越敢相信频率在变,追踪快速变化的能力越强,代价是稳态估计的方差变大。
我试过一组对照:保持其他参数不变,把(Q(2,2))从0.01调到10,频率估计曲线从"过度平滑、跟不上线性调频的斜率"变化到"剧烈抖动、完全被噪声带着跑"。这中间的平衡点需要根据你的信号实际动态特性来确定。
一个比较实用的经验法则是:先把(Q(2,2))设为((\Delta f_{\text{max}} \times \Delta t)^2)来估计——其中(\Delta f_{\text{max}})是频率每秒最大可能变化量,(\Delta t)是采样间隔。比如线性调频场景频率每秒最多变化100Hz,采样间隔1ms,那么(Q(2,2)\approx(100\times0.001)^2=0.01)。这个初值跑出来通常不会太离谱。
5.2 观测噪声(R):你的设备能测多准?
观测噪声协方差(R)通常可以从设备的信噪比推算。窄带信号模型中,正弦波的功率是(A^2/2),高斯白噪声方差就是噪声功率,所以:
[
R = \sigma_v^2 = \frac{A^2}{2\cdot \text{SNR}_{\text{linear}}}
]
在仿真里用这个公式计算(R)是没问题的。但真实工程中,信噪比往往是未知的,需要用一段无信号时段的样本来估计噪声功率,或者留出校准数据。这个值设得比实际偏小,会导致滤波器把噪声当成真实信号变化,估计曲线毛毛糙糙;设得偏大,滤波器又会过度平滑,频率变化的细节被磨平。
现实中我常遇到的情况是:设备给出的标称信噪比和实际场景差距很大。所以更靠谱的做法是:先用标称值算一个初版(R),然后通过对比滤波输出与原始观测的平均功率来反推残差是否合理。如果新息序列的方差明显大于(R),说明(R)设小了;如果明显小于(R),则设大了。
5.3 初值敏感性:滤波器不能"太自信"
初始协方差矩阵(P_0)透露的是你对初始状态的确信程度。我在第一次做这个项目时犯过一个典型错误:把(P_0)设得非常小,因为觉得初始频率猜得似乎挺准。结果滤波器的前几百个采样点几乎不更新,因为协方差太小导致卡尔曼增益被压得很低,观测信息进来的权重微乎其微。说白了,滤波器太自信了,学习速度就慢。
相反,把(P_0)设得很大也不合适,初期增益会很大,相位和频率估计值在观测噪声的驱动下大幅震荡,部分极端情况下还能把滤波器震到发散边缘。
一个稳妥的初始化方法是:相位初始方差可以给一个小值(因为相位初值一般随意设的,确实不知道),但频率初始方差要参照你预期最大可能初始误差来设置。如果起始频率范围是30~80Hz,你猜了50Hz,保守一点设方差500,对应的标准差约为22Hz,表示真实值大概落在这个范围内。
5.4 UKF特有参数:alpha、beta、kappa的调参逻辑
UKF多出来的三个参数,很多人不容易理解它们的实际影响。我尽量用直观的方式解释:
-
(\alpha)控制sigma点距离均值的远近。(\alpha)越小,sigma点越集中在均值附近;(\alpha)越大,sigma点散布越广。对于窄带信号这种非线性中等强度的观测方程,(\alpha=0.1\sim0.3)是个比较常用的范围。太小的(\alpha)会让sigma点几乎落在均值上,退化成类似一阶近似的效果,UKF的优势就没了。
-
(\beta)是分布先验调节参数。当真实分布近似高斯时,(\beta=2)是最优解,这是理论结论,实际就用这个值。
-
(\kappa)的影响在(n=2)维问题中相对有限,通常取0或(3-n=1)都没问题。
如果你不想手动折腾这三个参数,Matlab的trackingUKF封装了一套默认设置,多数情况够用。但既然自己在写实现,我建议至少手动跑一遍(\alpha)从0.05到0.5的扫参,看一眼频率RMSE曲线的变化,这对理解UKF的sigma点机制帮助很大。
5.5 野值处理与相位卷绕的工程修补
卡尔曼滤波家族在理论上是基于高斯噪声假设的,遇到实际数据中的尖峰野值或者短时干扰,滤波结果会被拉得很偏。针对窄带信号频率估计,我常用的一个简单策略是观测门控:每次更新前计算新息的马氏距离,如果超过阈值(比如3倍标准差),就认为这个观测值可能是野值,把它丢弃或只给很小的权重。
matlab复制% 野值检测示例(放在更新步骤之前)
innov = y(k) - y_pred;
S = H * P_pred * H' + R_ekf;
if innov^2 / S < 9 % 3-sigma门限
% 正常更新
else
% 跳过更新,保持预测状态
x_ekf = x_pred;
P_ekf = P_pred;
end
这个策略虽然简单,但在雷达多普勒追踪和通信频偏校正场景中非常实用。至于相位卷绕,前面提到的新息修正方法也可以结合实际数据的相位特征来做,建议在遇到频率估计突然跳变的异常时第一时间检查是不是卷绕问题。
5.6 一个值得多讲两句的话题:扩展到大频率变化率
本文中频率被建模成随机游走,意味着频率的一阶导数(即频率变化率)没有显式建模。对于频率变化率较快的线性调频信号,随机游走模型会让滤波估计存在一个"滞后角"——你在估计的是最近一段时间的平均频率,而不是当前真时频率。如果这种滞后不可接受,一个自然的扩展方案是把状态向量扩展成三维:
[
\mathbf{x}_k = \begin{bmatrix} \phi_k \ f_k \ \dot{f}_k \end{bmatrix}
]
对应的状态转移矩阵变为:
[
\mathbf{F} = \begin{bmatrix} 1 & 2\pi\Delta t & \pi\Delta t^2 \ 0 & 1 & \Delta t \ 0 & 0 & 1 \end{bmatrix}
]
这就是所谓"常加速度"模型,适用于匀变速扫频信号。代价是状态维度增加,EKF的雅可比矩阵多一列,UKF的sigma点从5个增加到7个,计算量相应上升。但这个扩展在实际中效果非常显著,尤其在频率变化率比较稳定的应用场景,值得尝试。
6. 最后补几句实际经验
本文代码和仿真基于我自己在项目里经常用的一套窄带信号频率估计框架,整体下来最大的体会是:滤波器的性能瓶颈往往不在数学公式,而在建模假设和实际信号的匹配程度。EKF和UKF的差距是真实存在的,但在很多工程场景下,把(Q)矩阵调准、把相位处理做对,带来的收益比换个滤波器类型更大。
如果你刚开始接触这个话题,建议不要直接跑到低信噪比场景里去调参。先在高信噪比、线性调频条件下把滤波器跑通,理解状态变量的物理意义,然后逐步降低信噪比、引入频率突变,观察两种滤波器的表现差异。这个过程中你会积累对(Q)、(R)和初值的直觉,这些东西比任何公式都值钱。
Matlab代码里我习惯把EKF和UKF封装成两个独立的函数模块,方便后续换信号模型时复用。如果你用的是Simulink实时仿真或者C代码移植,UKF的sigma点生成和权重计算部分也可以直接用MATLAB Coder转成C代码,性能优化空间也更大。希望这篇内容对你有用。
