在一些场景中,我们可以认为这一时刻的状态依赖于上一时刻的转态,即:
xt=F∗xt−1+ϵt
其中,ϵt为误差项,ϵ∼N(0,Q)。
令Pt为协方差矩阵,那么P1=Q,且Pt=F∗xt−1∗F′+Q。
上面等式成立的原因为:cov(Ax)=Acos(x)A′。
此时,我们预测的观测值应该是μt=H∗xt,Σt=H∗Pt∗H′,即服从N(H∗xt,H∗Pt∗H′)。这里的H为当前状态和观测的转态的关系矩阵。
这个和真实的观测值是不一样的,服从N(yk,Rk)。
我们找的点就是,最大可能满足这两个分布的点。也就是两个高斯分布相乘的均值。
这里我没有推导,直说结论吧。
K=Σ0(Σ0+Σ1)−1μ′=μ0+K(μ1−μ0)Σ′=Σ0−KΣ0
那么我们的最优估计就是:
K=HPtH′(HPtH′+Rk)−1Hx^t=Hxt+K(yk−Hxt)HP^tH=HPtH′−KHPtH′
化简一下就是:
K^=PtH′(HPtH′+Rt)−1x^t=xt+K^(yt−Hxt)P^t=Pt−K^HPt
那么,卡尔曼滤波到这里就结束了。
下面是做了一个温度例子的仿真
先模拟得到温度的数据:
function [real_temperature, view_temperature] = get_kalman_data()
% 模拟温度变化的仿真
%% 参数设置
data_size = 120;
%% 空间申请
real_temperature = zeros(1, data_size); % 真实的温度值
view_temperature = zeros(1, data_size); % 观测的温度值
real_temperature(1) = 25.1;
view_temperature(1) = 24.9;
%% 噪声项
Q = 0.01;
R = 0.25;
real_guass = sqrt(Q) * randn(1, data_size);
view_guass = sqrt(R) * randn(1, data_size);
%% 模型数据变化
for t = 2:data_size
real_temperature(t) = real_temperature(t - 1) + real_guass(t - 1);
view_temperature(t) = real_temperature(t) + view_guass(t - 1);
end
end
然后根据观测值,做卡尔曼滤波:
%% 初始化
clear; close; clc;
%% 数据导入
[real_temperature, view_temperature] = get_kalman_data();
data_size = size(view_temperature);
%% 参数设置
Q = 0.01; R = 0.25;
P = zeros(data_size);
P(1) = Q;
%% 状态转移矩阵
% x_t = F_t * x_{t - 1} + B_t * u_t _ \epsilon_t
% p_t = F_t * p_{t - 1} * F_t' + Q_t
% 其中,\epsilon为误差项,\epsilon_t \sim N(0, Q_t)
F = 1;
H = 1;
I = eye(1);
%% 卡尔曼滤波
kf_temperature = zeros(data_size);
kf_temperature(1) = view_temperature(1);
for t = 2:data_size(2)
X_t = F * kf_temperature(t - 1); % 预测值
P_t = F * P(t - 1) * F' + Q; % 偏差
e = view_temperature(t) - H * X_t; % 误差值
K = P_t * H' * inv(H * P_t * H'+ R);% 计算卡尔曼增量
kf_temperature(t) = X_t + K * e; % 卡尔曼滤波值 = 预测值 + 卡尔曼增量 * 误差值
P(t) = (I - K * H) * P_t;
end
%% 数据可视化
t = 1:data_size(2);
plot(t, real_temperature, '-b', t, view_temperature, '-r', t, kf_temperature, '-ko');
legend('真实值', '观测值', '卡尔曼滤波值');
xlabel('采样时间/s'); ylabel('温度值/c');
figure;
plot(t, (view_temperature - real_temperature), '-b', t, (kf_temperature - real_temperature), '-r');
legend('测量偏差', '卡尔曼滤波偏差');
xlabel('采样时间/s'); ylabel('温度值/c');
效果如下: