IMU 扩展卡尔曼滤波(EKF)姿态估计 — MATLAB 实现

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Ω(ωmb)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 及以上版本内置 quat2rotmeul2quat 等函数。若版本较低,可用以下自定义函数:

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磁力计噪声方差根据传感器手册

五、扩展建议

  1. 自适应噪声:根据运动加速度动态调整 R_acc(如检测到剧烈运动时增大)。
  2. 磁力计校准:使用椭球拟合补偿硬铁和软铁干扰。
  3. GPS融合:加入位置/速度观测,构成完整的 INS/GPS 组合导航。
  4. 实时部署:将代码移植到 C/C++ 或 Python,用于嵌入式系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值