一、update() 主循环结构
ekf.cpp 的 update() 函数是 EKF 算法的最高级入口,每次 IMU 降采样完成后由 EKF2.cpp 调用一次(约 250 Hz):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| bool Ekf::update() { if (!_imu_buffer.pop_first_older_than(_time_delayed_us, &_imu_sample_delayed)) { return false; }
if (!_filter_initialised) { _filter_initialised = initialiseFilter(); if (!_filter_initialised) return false; }
_dt_ekf_avg = 0.998f * _dt_ekf_avg + 0.002f * _imu_sample_delayed.delta_ang_dt;
predictState(_imu_sample_delayed);
predictCovariance(_imu_sample_delayed);
controlFusionModes(_imu_sample_delayed);
_output_predictor.correctOutputStates(_imu_sample_delayed.time_us, getQuaternion(), getVelocity(), getPosition(), getGyroBias());
return true; }
|
二、初始化条件 initialiseFilter()
EKF 初始化是一个积累静止数据的过程,需满足多个条件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| flowchart TD Start("[首次调用]") --> CheckIMU{"IMU 数据<br/>是否充足?"} CheckIMU -->|否| WaitIMU["等待积累足够 IMU 样本"] CheckIMU -->|是| CalcTilt["用加速度计计算 roll / pitch"] CalcTilt --> CheckYaw{"有可靠航向源?"} CheckYaw -->|磁力计有效| MagYaw["用磁力计初始化 yaw"] CheckYaw -->|GPS 双天线| GpsYaw["用 GPS 航向初始化 yaw"] CheckYaw -->|无磁无GPS| ZeroYaw["yaw = 0, 等待校正"] MagYaw --> InitCov["初始化协方差 P"] GpsYaw --> InitCov ZeroYaw --> InitCov InitCov --> CheckGPS{"GPS 可用且<br/>预检通过?"} CheckGPS -->|是| SetOrigin["设置本地坐标原点"] CheckGPS -->|否| BaroOrigin["用气压计高度设定 Z 轴原点"] SetOrigin --> Done("[初始化完成]") BaroOrigin --> Done
|
初始化失败的主要原因:
- IMU 在运动(加速度计不稳定,无法估计重力方向)
- 磁力计数据异常(软铁干扰大)
- GPS 预检失败(精度差、卫星数不足)
三、predictState() 状态预测数学
状态预测基于 IMU 的角增量 和速度增量 (加速度计积分 - 已去除估计重力):
3.1 姿态预测
姿态用四元数表示,角增量转换为旋转四元数后与名义四元数相乘:
代码实现:
1 2
| const Quatf dq(AxisAnglef{imu.delta_ang - _state.gyro_bias * imu.delta_ang_dt}); _state.quat_nominal = (_state.quat_nominal * dq).normalized();
|
注意:角增量在应用前减去了陀螺仪零偏估计 _state.gyro_bias * dt,这是 EKF 实时补偿传感器偏差的体现。
3.2 速度预测
速度在导航(NED)坐标系下积分,加速度增量需从机体系转到导航系:
其中 是机体到导航系的旋转矩阵(由 _state.quat_nominal 提取),(NED 坐标系重力朝 D 轴正方向)。
1 2 3 4 5 6
| const Vector3f delta_vel_nav = _R_to_earth * (imu.delta_vel - _state.accel_bias * imu.delta_vel_dt) + _earth_rate_NED % imu.delta_vel * imu.delta_vel_dt;
_state.vel += delta_vel_nav; _state.vel(2) += CONSTANTS_ONE_G * imu.delta_vel_dt;
|
3.3 位置预测
位置用梯形积分(避免纯后向欧拉的滞后):
1
| _state.pos += (_state.vel + 0.5f * delta_vel_nav) * imu.delta_vel_dt;
|
3.4 零偏/磁场/风速预测
这些缓慢变化的量使用随机游走模型(不预测,保持不变,仅在协方差矩阵中增加过程噪声):
四、水平加速度健康监测
预测中会实时检测加速度计异常:
1 2 3 4 5 6 7 8
| const bool delta_vel_clipped = imu.delta_vel_clipping[0] || imu.delta_vel_clipping[1] || imu.delta_vel_clipping[2];
if (delta_vel_clipped) { clearInhibitedStateKalmanGains(); }
|
五、controlFusionModes() 调用时机
在每步 IMU 预测后,controlFusionModes() 检查各传感器 RingBuffer 是否有延迟时刻的新数据:
1 2 3 4 5 6 7 8 9 10
| flowchart LR predict["predictState +<br/>predictCovariance"] --> ctrl["controlFusionModes"] ctrl --> gps["controlGpsFusion<br/>位置/速度"] ctrl --> mag["controlMagFusion<br/>航向/磁场"] ctrl --> baro["controlBaroFusion<br/>高度"] ctrl --> rng["controlRangeFinderFusion<br/>高度"] ctrl --> flow["controlOpticalFlowFusion<br/>光流速度"] ctrl --> ev["controlEvFusion<br/>外部视觉"] ctrl --> height["controlHeightFusion<br/>多源高度仲裁"] ctrl --> yaw["controlGpsYawFusion<br/>GPS航向"]
|
每个 control*Fusion() 函数决定:
- 是否有新数据需要融合
- 是否通过新息检验(未通过则拒绝)
- 是否调用
measurementUpdate() 执行卡尔曼更新
六、小结
ekf.cpp 的 update() 实现了预测 → 融合的标准 EKF 节拍:
- 预测阶段:IMU 积分驱动,每步固定执行,高频(250 Hz)
- 更新阶段:由传感器数据到达时机决定,各传感器独立异步触发
- 零偏估计:通过协方差传播和卡尔曼更新逐步收敛,不在预测阶段显式积分
理解 update() 的执行流程是读懂后续 covariance.cpp 和 control.cpp 的前提。
上一篇:EKF2 应用管理层
下一篇:协方差矩阵预测 covariance.cpp
系列总目录:PX4 v1.16 源码解读总目录