最近自学了卡尔曼滤波,起初只是看了看推导过程,后来碰巧在最优控制课程上面要用它,就利用MATLAB实现了,先放个流程图,代码及推导过程后期更新。
这里直接给出传说中的黄金5条,具体的推导过程大家可以参考别人家的博客哦(文末有推荐)
参数 | 说明 |
---|---|
x ^ k − \hat{x}_{k}^{-} x^k− | 当前时刻的预测值 |
x ^ k − 1 \hat{x}_{k-1} x^k−1 | 上一时刻的最优状态估计值 |
P k − P_{k}^{-} Pk− | 当前时刻预测值的协方差矩阵 |
K k K_k Kk | 卡尔曼滤波增益 |
y k y_k yk | 当前时刻测量值 |
x ^ k + \hat{x}_{k}^{+} x^k+ | 最优状态估计值 |
Q , R Q,R Q,R | 过程噪声和测量噪声的协方差 |
设线性化的离散系统为:
x k = A k x k − 1 + B k u k − 1 + w k − 1 {{x}_{k}}={{A}_{k}}{{x}_{k-1}}+{{B}_{k}}{{u}_{k-1}}+{{w}_{k-1}} xk=Akxk−1+Bkuk−1+wk−1 y k = H k x k + v k {{y}_{k}}={{H}_{k}}{{x}_{k}}+{{v}_{k}} yk=Hkxk+vk 其中, w k {{w}_{k}} wk是协方差为 Q k {{Q}_{k}} Qk的零均值高斯白噪声,即 w k ∼ ( 0 , Q k ) {{w}_{k}}\sim \left( 0,{{Q}_{k}} \right) wk∼(0,Qk), v k {{v}_{k}} vk是协方差为 R k {{R}_{k}} Rk的零均值高斯白噪声,即 v k ∼ ( 0 , R k ) {{v}_{k}}\sim \left( 0,{{R}_{k}} \right) vk∼(0,Rk),且 w k {{w}_{k}} wk与 v k {{v}_{k}} vk不相关。 A k {{A}_{k}} Ak为非奇异状态矩阵, B k {{B}_{k}} Bk为控制输入矩阵, H k {{H}_{k}} Hk为观测矩阵, x k {{x}_{k}} xk为 k k k时刻的系统状态, y k {{y}_{k}} yk为 k k k时刻系统的量测值, u k {{u}_{k}} uk为 k k k时刻对系统的控制量, w k {{w}_{k}} wk为系统过程噪声, v k {{v}_{k}} vk为系统量测噪声。
下面通过MATLAB实现卡尔曼滤波
%% 加入卡尔曼滤波
% 初始化
H = [0 0 1 0]; % 观测矩阵
z = zeros(1,len); % 观测值
z(1) = H*x(:,1)+v(1); % 观测初始真实值,第一列的列向量,
x_kf = zeros(4,len);% kalman估计状态
x_kf(:,1) = x(:,1); % kalman估计状态初始化,第一列的列向量,
p0 = diag([0 0 0.1^2 0]); % 初始误差 协方差
err_p = zeros(N,4);
err_p(1,1) = p0(1,1);
err_p(1,2) = p0(2,2);
err_p(1,3) = p0(3,3);
err_p(1,4) = p0(4,4);
x_pre = zeros(4,len);
I = eye(4); % 4*4单位矩阵表明四维系统
% Kalman 滤波迭代过程
for k=2:len
x(:,k) = Ad*x(:,k-1) + Bd * u(k) + w(:,k);
z(k) = H*x(:,k) + v(k); % 量测
x_pre(:,k) = Ad*x_kf(:,k-1) + Bd*u(k); % 状态预测 -黄金(1)
p_pre = Ad*p0*Ad' + Qk; % 状态协方差预测 -黄金(2)
kg = p_pre*H'/(H*p_pre*H' + R); % 增益更新 -黄金(3)
x_kf(:,k) = x_pre(:,k) + kg*(z(k)- H*x_pre(:,k)); % 更新状态最优估计值 -黄金(4)
p0 = (I-kg*H)*p_pre; % 状态协方差更新 -黄金(5)
err_p(k,1) = p0(1,1); % 状态1误差协方差
err_p(k,2) = p0(2,2); % 状态2误差协方差
err_p(k,3) = p0(3,3); % 状态3误差协方差
err_p(k,4) = p0(4,4); % 状态4误差协方差
end
clc;
clear;
clf;
figwidth = 500;
figheight = 300;
%% 自行车参数
m_0 = 2; % 自行车前后轮的质量 2kg
m_1 = 18; % 车架的质量18kg
m_2 = 2.5; % 车把及转轴质量2.5kg
r_0 = 0.35; % 前后轮半径0.35m
h_0 = 0.9; % 车身质心到地面距离0.9m
h_1 = 1.2; % 车把手到地面距离1.2m
L = 0.5; % 车把长度0.5m
s = 0.04; % 车把转轴到前轮圆心距离0.04m
g = 9.802; % 重力加速度
%% 新参数
a = 1/(m_0*s^2 + 0.5*m_0*r_0^2 + (m_2*L^2)/3);
b = (m_0*s*r_0 + 0.5*m_2*L*h_1)/(m_0*s^2 + 0.5*m_0*r_0^2 + (m_2*L^2)/3);
c = -b;
d = 2*(2*m_0*g*r_0 + m_1*g*h_0 + m_2*g*h_1)/(3*m_0*r_0^2 + 2*m_1*h_0^2 + 2*m_2*h_1^2);
e = -(m_0*s*r_0 + 0.5*m_2*L*h_1)/(3*m_0*r_0^2 + 2*m_1*h_0^2 + 2*m_2*h_1^2);
% 线性化后的常数矩阵
A = [0 1 0 0;0 0 c*d/(1-c*e) 0;0 0 0 1;0 0 d/(1-c*e) 0];
B = [0 a/(1-c*e) 0 e*a/(1-c*e)]';
C =[0 0 1 0];
D = 0;
CONT=ctrb(A,B);% 可控矩阵
RankC = rank(CONT);
OBSER = obsv(A,C); % 可观矩阵
RankO = rank(OBSER);
Q = [1 0 0 0;0 1 0 0;0 0 1 0;0 0 0 1];
R = [1];
N = 0;
[K,S,e] = lqr(A,B,Q,R); % 连续
%% 加入lQR控制器后的系统矩阵
Ac = (A-B*K);
Bc = B;
Cc = C;
Dc = D;
ts = 0.1; % 采样时间
t = 0:ts:20;
len = length(t);
[Ad,Bd,Cd,Dd]=c2dm(Ac,Bc,Cc,Dc,ts,'z'); % 离散化' 加入LQR后
%% 没加噪声的响应
u = 1*ones(size(t));
x_0 = zeros(4,len);
y_0 = zeros(1,len);
x_0(:,1) = [0 0 0.1 0]'; % 状态初始化
y_0(:,1) = Cd*x_0(:,1);
for k=2:len
x_0(:,k) = Ad*x_0(:,k-1) + Bd*u(k);
y_0(k) = Cd*x_0(:,k);
end
%% 加入噪声的响应
Qc = 5*10e-6;
Qk = diag([Qc Qc Qc Qc]);
Rc = 0.001;
Rk = diag([Rc Rc]);
w = normrnd(0,Qc,4,len); % 过程噪声 产生均值为a、方差为b大小为cXd的随机矩阵
v = normrnd(0,Rc,1,len); % 量测噪声
x = zeros(4,len); % 物体真实状态
y = zeros(1,len); % 输出
x(:,1) = [0 0 0.1 0]'; % 状态初始化
y(:,1) = Cc*x(:,1);
% 加入卡尔曼滤波
% 初始化
H = [0 0 1 0]; % 观测矩阵
z = zeros(1,len); % 观测值
z(1) = H*x(:,1)+v(1); % 观测初始真实值,第一列的列向量,
x_kf = zeros(4,len);% kalman估计状态
x_kf(:,1) = x(:,1); % kalman估计状态初始化,第一列的列向量,
p0 = diag([0 0 0.1^2 0]); % 初始误差 协方差
err_p = zeros(N,4);
err_p(1,1) = p0(1,1);
err_p(1,2) = p0(2,2);
err_p(1,3) = p0(3,3);
err_p(1,4) = p0(4,4);
x_pre = zeros(4,len);
I = eye(4); % 4*4单位矩阵表明四维系统
% Kalman 滤波迭代过程
for k=2:len
x(:,k) = Ad*x(:,k-1) + Bd * u(k) + w(:,k);
z(k) = H*x(:,k) + v(k); % 量测
x_pre(:,k) = Ad*x_kf(:,k-1) + Bd*u(k); % 状态预测 黄金(1)
p_pre = Ad*p0*Ad' + Qk; % 状态协方差预测 黄金(2)
kg = p_pre*H'/(H*p_pre*H' + R); % 增益更新 黄金(3)
x_kf(:,k) = x_pre(:,k) + kg*(z(k)- H*x_pre(:,k)); % 更新状态最优估计值 黄金(4)
p0 = (I-kg*H)*p_pre; % 状态协方差更新 黄金(5)
err_p(k,1) = p0(1,1); % 状态1误差均方值
err_p(k,2) = p0(2,2); % 状态2误差均方值
err_p(k,3) = p0(3,3); % 状态1误差均方值
err_p(k,4) = p0(4,4); % 状态2误差均方值
end
%% ---画图 协方差
figure(1)
plot(t,err_p(:,3),'b','LineWidth',1.5)
title('车身倾角的误差协方差变化曲线','FontWeight','bold')
xlabel('time/s','FontWeight','bold','FontSize',10,'Interpreter','latex')
ylabel('$$P$$','FontWeight','bold','FontSize',14,'Interpreter','latex')
savefig(gcf,figwidth,figheight,'fig\P.jpg')
%% 绘制车把倾角 在有噪声跟没噪声下的阶跃响应
figure(2)
subplot(3,1,1)
plot(t,y_0,'color',[0.99, 0.49, 0.00],'LineWidth', 1.5)
title('车身倾角角\theta变化曲线','FontWeight','bold')
xlabel('time/s','FontWeight','bold','FontSize',10,'Interpreter','latex')
ylabel('$$\theta$$','FontWeight','bold','FontSize',14,'Interpreter','latex')
legend('无噪声','FontSize',12,'FontWeight','bold')
set(gca, 'FontSize',10) % 设置坐标轴字体是 8
set(gca, 'Linewidth',1) % 设置坐标轴字体是 8
%%
subplot(3,1,2)
plot(t,z,'color','b','LineWidth', 1.5)
title('车身倾角\theta变化曲线','FontWeight','bold')
xlabel('time/s','FontWeight','bold','FontSize',10,'Interpreter','latex')
ylabel('\theta','FontWeight','bold','FontSize',14)
legend('有噪声','FontSize',12,'FontWeight','bold')
set(gca, 'FontSize',10) % 设置坐标轴字体是 8
set(gca, 'Linewidth',1) % 设置坐标轴字体是 8
%% 绘制车身滚角 在有噪声跟没噪声下的阶跃响应
subplot(3,1,3)
plot(t,x_kf(3,:),'r','LineWidth', 1.5)
title('车身倾角\theta变化曲线','FontWeight','bold')
xlabel('time/s','FontWeight','bold','FontSize',10,'Interpreter','latex')
ylabel('\theta','FontWeight','bold','FontSize',14)
legend('有噪声且有滤波','FontSize',12,'FontWeight','bold')
set(gca, 'FontSize',10) % 设置坐标轴字体是 8
set(gca, 'Linewidth',1) % 设置坐标轴字体是 8
set(gcf,'color','white');
savefig(gcf,1000,600,'fig\CSDN_1.jpg')
%%
filepath = 'fig\CSDN_2.jpg'
figure(3)
title('滤波前后的比较')
plot(t,z,'r','LineWidth',1.5)
hold on
plot(t,x_kf(3,:),'b','LineWidth',1.5)
title('车身倾角\theta滤波前后变化曲线','FontWeight','bold')
xlabel('time/s','FontWeight','bold','FontSize',10,'Interpreter','latex')
ylabel('\theta','FontWeight','bold','FontSize',14)
legend('滤波前','滤波后')
savefig(gcf,figwidth,figheight,filepath)
代码中的savefig函数链接
卡尔曼滤波详细推导
知乎