IMU 扩展卡尔曼滤波(EKF)姿态估计 — MATLAB 实现
用于基于 IMU(加速度计 + 陀螺仪 + 磁力计)的姿态估计。采用 四元数 + 陀螺偏差 作为状态,EKF 融合加速度计和磁力计观测,输出横滚、俯仰、偏航角。
一、算法概述
1.1 状态向量
x=[q0,q1,q2,q3,bx,by,bz]T\mathbf{x} = [q_0, q_1, q_2, q_3, b_x, b_y, b_z]^Tx=[q0,q1,q2,q3,bx,by,bz]T
- (qqq):单位四元数(标量在前)
- (bbb):陀螺仪零偏(rad/srad/srad/s)
1.2 预测模型(陀螺仪驱动)
q˙=12Ω(ωm−b)q\dot{\mathbf{q}} = \frac{1}{2} \boldsymbol{\Omega}(\boldsymbol{\omega}_m - \mathbf{b}) \mathbf{q}q˙=21Ω(ωm−b)q
其中 (ωm\boldsymbol{\omega}_mωm) 为陀螺测量值,(b\mathbf{b}b) 为偏差。
1.3 观测模型
-
加速度计:测量重力矢量在机体坐标系的分量(假设无加速度)
am=Cnb[00g]+va\mathbf{a}_m = \mathbf{C}_n^b \begin{bmatrix}0\\0\\g\end{bmatrix} + \mathbf{v}_aam=Cnb00g+va
-
磁力计:测量磁场方向(需当地磁场参考)
mm=Cnbmn+vm\mathbf{m}_m = \mathbf{C}_n^b \mathbf{m}_n + \mathbf{v}_mmm=Cnbmn+vm
二、完整 MATLAB 代码
2.1 主程序:imu_ekf_attitude.m
%% IMU 扩展卡尔曼滤波姿态估计
clear; clc; close all;
%% 1. 生成模拟IMU数据(或加载真实数据)
dt = 0.01; % 采样间隔 (s)
T = 10; % 总时长 (s)
t = 0:dt:T;
N = length(t);
% 真实姿态(欧拉角,用于生成测量和对比)
true_euler = [20*sin(2*pi*0.3*t); % 横滚 (deg)
15*sin(2*pi*0.2*t); % 俯仰
30*sin(2*pi*0.1*t)]'; % 偏航
% 转换为四元数
true_quat = eul2quat(deg2rad(true_euler), 'XYZ'); % 每行一个四元数 [w,x,y,z]
% 生成陀螺仪测量(真实角速度 + 偏差 + 噪声)
omega_true = zeros(N,3);
for i = 2:N
q_prev = true_quat(i-1,:);
q_curr = true_quat(i,:);
% 用四元数差分近似角速度(简化)
dq = quatmultiply(q_curr, quatinv(q_prev));
omega_true(i,:) = 2/dt * [dq(2), dq(3), dq(4)]; % 近似
end
omega_true(1,:) = omega_true(2,:);
gyro_bias = [0.01, -0.02, 0.015]; % 真实偏差 (rad/s)
gyro_noise_std = 0.01; % 噪声标准差 (rad/s)
gyro_meas = omega_true + gyro_bias + gyro_noise_std*randn(N,3);
% 生成加速度计测量(重力矢量 + 噪声)
g = 9.81;
accel_true = zeros(N,3);
for i = 1:N
C_nb = quat2rotm(true_quat(i,:)); % 导航系到机体系
accel_true(i,:) = (C_nb * [0;0;g])';
end
accel_noise_std = 0.2;
accel_meas = accel_true + accel_noise_std*randn(N,3);
% 生成磁力计测量(假设当地磁场指向北)
mag_north = [0.3; 0; -0.5]; % 当地磁场矢量 (nT)
mag_true = zeros(N,3);
for i = 1:N
C_nb = quat2rotm(true_quat(i,:));
mag_true(i,:) = (C_nb * mag_north)';
end
mag_noise_std = 0.05;
mag_meas = mag_true + mag_noise_std*randn(N,3);
%% 2. EKF 初始化
% 状态向量: [q0,q1,q2,q3, bx,by,bz]
x = zeros(7,1);
x(1:4) = [1;0;0;0]; % 初始四元数
x(5:7) = [0;0;0]; % 初始偏差
% 协方差矩阵
P = diag([0.1*ones(1,4), 0.01*ones(1,3)]);
% 过程噪声协方差
Q = diag([0.001*ones(1,4), 1e-6*ones(1,3)]);
% 测量噪声协方差
R_acc = accel_noise_std^2 * eye(3);
R_mag = mag_noise_std^2 * eye(3);
%% 3. EKF 主循环
x_est = zeros(7,N);
x_est(:,1) = x;
for k = 2:N
% --- 预测步骤 ---
omega = gyro_meas(k-1,:)'; % 上一时刻陀螺测量
bias = x(5:7);
omega_corrected = omega - bias;
% 四元数预测(一阶积分)
q = x(1:4);
Omega = 0.5 * [0, -omega_corrected(1), -omega_corrected(2), -omega_corrected(3);
omega_corrected(1), 0, omega_corrected(3), -omega_corrected(2);
omega_corrected(2), -omega_corrected(3), 0, omega_corrected(1);
omega_corrected(3), omega_corrected(2), -omega_corrected(1), 0];
q_pred = q + dt * Omega * q;
q_pred = q_pred / norm(q_pred); % 归一化
% 偏差预测(随机游走)
bias_pred = bias;
% 状态预测
x_pred = [q_pred; bias_pred];
% 状态转移矩阵 F
F = eye(7);
F(1:4,1:4) = eye(4) + dt * Omega;
F(1:4,5:7) = -dt * 0.5 * [q(2), q(3), q(4);
-q(1), q(4), -q(3);
-q(4), -q(1), q(2);
q(3), -q(2), -q(1)];
% 协方差预测
P_pred = F * P * F' + Q;
% --- 更新步骤(加速度计) ---
% 预测加速度
C_nb = quat2rotm(q_pred');
g_pred = C_nb * [0;0;g];
% 测量残差
z_acc = accel_meas(k,:)' - g_pred;
% 观测矩阵 H_acc (3x7)
H_acc = zeros(3,7);
% 对四元数的雅可比
qw = q_pred(1); qx = q_pred(2); qy = q_pred(3); qz = q_pred(4);
H_acc(1,1) = -2*qz*g; H_acc(1,2) = 2*qy*g; H_acc(1,3) = 2*qx*g; H_acc(1,4) = -2*qw*g;
H_acc(2,1) = 2*qw*g; H_acc(2,2) = 2*qx*g; H_acc(2,3) = 2*qy*g; H_acc(2,4) = 2*qz*g;
H_acc(3,1) = -2*qx*g; H_acc(3,2) = 2*qw*g; H_acc(3,3) = -2*qz*g; H_acc(3,4) = 2*qy*g;
% 注意:这里假设g=9.81,实际应为重力加速度
H_acc = H_acc / norm(g_pred); % 归一化(实际应为 g 的模长)
% 卡尔曼增益
S_acc = H_acc * P_pred * H_acc' + R_acc;
K_acc = P_pred * H_acc' / S_acc;
% 状态更新
x_upd = x_pred + K_acc * z_acc;
x_upd(1:4) = x_upd(1:4) / norm(x_upd(1:4)); % 归一化
% 协方差更新
P_upd = (eye(7) - K_acc * H_acc) * P_pred;
% --- 更新步骤(磁力计) ---
% 预测磁场
m_pred = C_nb * mag_north;
% 测量残差
z_mag = mag_meas(k,:)' - m_pred;
% 观测矩阵 H_mag (3x7)
H_mag = zeros(3,7);
% 对四元数的雅可比(类似加速度计,但使用 mag_north)
% 这里省略详细推导,实际应用中可数值计算或解析
% 为简化,使用数值雅可比(更稳健)
eps_q = 1e-6;
for j = 1:4
q_pert = q_pred;
q_pert(j) = q_pert(j) + eps_q;
q_pert = q_pert / norm(q_pert);
C_pert = quat2rotm(q_pert');
m_pert = C_pert * mag_north;
H_mag(:,j) = (m_pert - m_pred) / eps_q;
end
% 偏差对磁场无直接影响,H_mag(:,5:7)=0
% 卡尔曼增益
S_mag = H_mag * P_upd * H_mag' + R_mag;
K_mag = P_upd * H_mag' / S_mag;
% 状态更新
x_est_k = x_upd + K_mag * z_mag;
x_est_k(1:4) = x_est_k(1:4) / norm(x_est_k(1:4));
% 协方差更新
P = (eye(7) - K_mag * H_mag) * P_upd;
% 保存
x_est(:,k) = x_est_k;
end
%% 4. 结果提取与绘图
% 估计的四元数转欧拉角
euler_est = zeros(N,3);
for i = 1:N
euler_est(i,:) = rad2deg(quat2eul(x_est(1:4,i)', 'XYZ'));
end
figure('Position',[100 100 1200 600]);
subplot(3,1,1);
plot(t, true_euler(:,1), 'b-', 'LineWidth', 1.5); hold on;
plot(t, euler_est(:,1), 'r--', 'LineWidth', 1.5);
ylabel('Roll (deg)'); legend('True', 'EKF'); grid; title('横滚角');
subplot(3,1,2);
plot(t, true_euler(:,2), 'b-', 'LineWidth', 1.5); hold on;
plot(t, euler_est(:,2), 'r--', 'LineWidth', 1.5);
ylabel('Pitch (deg)'); grid; title('俯仰角');
subplot(3,1,3);
plot(t, true_euler(:,3), 'b-', 'LineWidth', 1.5); hold on;
plot(t, euler_est(:,3), 'r--', 'LineWidth', 1.5);
ylabel('Yaw (deg)'); grid; title('偏航角');
xlabel('Time (s)');
sgtitle('IMU EKF 姿态估计结果','FontSize',14,'FontWeight','bold');
% 偏差估计
figure;
plot(t, x_est(5,:), 'r-', 'LineWidth', 1.5); hold on;
plot(t, x_est(6,:), 'g-', 'LineWidth', 1.5);
plot(t, x_est(7,:), 'b-', 'LineWidth', 1.5);
yline(gyro_bias(1), 'r--'); yline(gyro_bias(2), 'g--'); yline(gyro_bias(3), 'b--');
xlabel('Time (s)'); ylabel('Bias (rad/s)'); legend('bx est','by est','bz est','bx true','by true','bz true');
title('陀螺偏差估计'); grid;
2.2 辅助函数(若需要自定义四元数转换)
MATLAB 2017b 及以上版本内置 quat2rotm、eul2quat 等函数。若版本较低,可用以下自定义函数:
function C = quat2rotm(q)
% q = [w,x,y,z]
w = q(1); x = q(2); y = q(3); z = q(4);
C = [1-2*(y^2+z^2), 2*(x*y-w*z), 2*(x*z+w*y);
2*(x*y+w*z), 1-2*(x^2+z^2), 2*(y*z-w*x);
2*(x*z-w*y), 2*(y*z+w*x), 1-2*(x^2+y^2)];
end
function q = eul2quat(euler)
% euler = [roll, pitch, yaw] (rad)
roll = euler(1); pitch = euler(2); yaw = euler(3);
cy = cos(yaw*0.5); sy = sin(yaw*0.5);
cp = cos(pitch*0.5); sp = sin(pitch*0.5);
cr = cos(roll*0.5); sr = sin(roll*0.5);
q = [cr*cp*cy + sr*sp*sy,
sr*cp*cy - cr*sp*sy,
cr*sp*cy + sr*cp*sy,
cr*cp*sy - sr*sp*cy]';
end
三、运行结果
运行主程序后,你将看到:
- 横滚、俯仰、偏航角的估计值与真实值高度吻合(误差通常在几度以内)。
- 陀螺偏差逐渐收敛到真实值。
参考代码 实现IMU数据的拓展卡尔曼滤波 www.youwenfan.com/contentcsv/81183.html
四、关键参数调优
| 参数 | 作用 | 建议值 |
|---|---|---|
Q(1:4,1:4) | 四元数过程噪声 | 0.001 ~ 0.01 |
Q(5:7,5:7) | 偏差过程噪声 | 1e-8 ~ 1e-6 |
R_acc | 加速度计噪声方差 | 根据传感器手册 |
R_mag | 磁力计噪声方差 | 根据传感器手册 |
五、扩展建议
- 自适应噪声:根据运动加速度动态调整
R_acc(如检测到剧烈运动时增大)。 - 磁力计校准:使用椭球拟合补偿硬铁和软铁干扰。
- GPS融合:加入位置/速度观测,构成完整的 INS/GPS 组合导航。
- 实时部署:将代码移植到 C/C++ 或 Python,用于嵌入式系统。
765

被折叠的 条评论
为什么被折叠?



