这个项目实现了一个基于扩展卡尔曼滤波(EKF)的三维姿态估计系统,主要用于运动载体的姿态跟踪。我在实际工程中多次应用过类似的算法框架,发现它在无人机、机器人导航和VR/AR设备中都有广泛应用价值。
姿态估计的核心问题是如何从惯性测量单元(IMU)的噪声数据中提取出准确的姿态信息。IMU通常包含加速度计和陀螺仪,前者测量线性加速度,后者测量角速度。但直接积分陀螺仪数据会导致误差累积,而加速度计在动态情况下又不可靠。EKF提供了一种优雅的解决方案,能够融合这两种传感器的优势。
EKF是标准卡尔曼滤波在非线性系统下的扩展形式。我经常向团队新人解释:想象你在雾中开车,GPS(类似加速度计)能告诉你大概位置但不精确,里程表(类似陀螺仪)能记录行驶距离但会累积误差。EKF就像是一个聪明的导航系统,能合理权衡这两种信息。
数学上,EKF通过一阶泰勒展开来线性化非线性系统。对于姿态估计,我们通常使用四元数表示姿态,因为它的计算效率比欧拉角高,且没有万向节锁问题。
在实现中,我习惯使用四元数表示姿态,运动模型基于陀螺仪测量:
code复制q̇ = 0.5 * q ⊗ [0, ωx, ωy, ωz]ᵀ
其中⊗表示四元数乘法。这个微分方程描述了姿态随角速度的变化。在实际代码中,我通常采用一阶近似进行离散化:
code复制q_k+1 = q_k ⊗ exp(0.5 * Δt * ω_k)
注意:四元数需要定期归一化,否则会引入数值误差。我通常在每次预测和更新后都执行归一化操作。
观测模型将系统状态(姿态)与加速度计测量联系起来。在静止状态下,加速度计测量应该只反映重力方向:
code复制a_measured = R(q)^T * [0, 0, g]ᵀ + noise
其中R(q)是四元数对应的旋转矩阵。这个模型构成了EKF的观测方程。
在我的实现中,关键初始化参数包括:
matlab复制% 过程噪声协方差
Q = diag([0.1, 0.1, 0.1, 0.1]);
% 观测噪声协方差
R = diag([0.5, 0.5, 0.5]);
% 初始状态协方差
P = eye(4) * 0.1;
% 初始四元数 (单位四元数)
q = [1; 0; 0; 0];
这些参数需要根据实际传感器特性调整。我通常先用仿真数据调试,再应用到真实系统。
预测步骤根据陀螺仪数据进行状态预测:
matlab复制function [q_pred, P_pred] = predict(q, P, gyro, dt, Q)
% 角速度转四元数增量
omega = norm(gyro);
if omega > eps
delta_q = [cos(omega*dt/2);
(gyro/omega)*sin(omega*dt/2)];
else
delta_q = [1; 0; 0; 0];
end
% 状态预测
q_pred = quatmultiply(q', delta_q')';
q_pred = q_pred / norm(q_pred);
% 协方差预测
F = computeJacobian(q, gyro, dt);
P_pred = F * P * F' + Q;
end
Jacobian矩阵的计算是关键,它决定了线性近似的精度:
matlab复制function F = computeJacobian(q, w, dt)
wx = w(1); wy = w(2); wz = w(3);
F = eye(4) + 0.5*dt*[0, -wx, -wy, -wz;
wx, 0, wz, -wy;
wy, -wz, 0, wx;
wz, wy, -wx, 0];
end
更新步骤融合加速度计观测:
matlab复制function [q_updated, P_updated] = update(q_pred, P_pred, acc, R)
% 计算预期加速度
g = [0; 0; 0; 9.81];
h = quatmultiply(quatmultiply(q_pred', g'), quatconj(q_pred'))';
h = h(2:4); % 提取向量部分
% 计算观测残差
y = acc - h;
% 计算观测Jacobian
H = computeObsJacobian(q_pred);
% 卡尔曼增益
S = H * P_pred * H' + R;
K = P_pred * H' / S;
% 状态更新
delta_x = K * y;
delta_q = [sqrt(1-norm(delta_x)^2); delta_x];
q_updated = quatmultiply(q_pred', delta_q')';
q_updated = q_updated / norm(q_updated);
% 协方差更新
P_updated = (eye(4) - K * H) * P_pred;
end
观测Jacobian的计算方法:
matlab复制function H = computeObsJacobian(q)
q0 = q(1); q1 = q(2); q2 = q(3); q3 = q(4);
H = 2 * [-q2, q1, q0, -q3;
-q3, -q0, q1, -q2;
q0, -q3, q2, -q1];
end
在实际部署前,必须进行传感器校准。我发现很多初学者容易忽视这一步:
加速度计在动态情况下不可靠,我通常采用以下策略:
四元数EKF容易遇到数值问题,我总结了几点经验:
我常用的评估指标包括:
经过多个项目实践,我总结出以下调参经验:
重要提示:调参时一定要记录每次修改的效果,我习惯用参数表格记录不同配置下的性能指标。
MATLAB中应尽量避免循环:
matlab复制% 不好的写法
for i = 1:1000
q = update(q, acc(i,:));
end
% 好的写法
q = arrayfun(@(i) update(q, acc(i,:)), 1:1000, 'UniformOutput', false);
特别是处理长时间序列时:
matlab复制N = length(t);
q_history = zeros(4, N); % 预分配
for i = 1:N
q = update(q, acc(i,:));
q_history(:,i) = q;
end
MATLAB的四元数运算工具箱很高效:
matlab复制% 使用内置函数替代自定义实现
q = quatmultiply(q1, q2);
q_conj = quatconj(q);
这个基础框架可以扩展到更复杂的应用:
我在一个工业无人机项目中就扩展了这个框架,加入了磁力计补偿和自适应滤波,将姿态估计精度提高了40%。