IMU融合定位实战:手把手教你用ESKF搞定无人机状态估计(附Python代码)
·
IMU融合定位实战:手把手教你用ESKF搞定无人机状态估计(附Python代码)
当无人机在GPS信号丢失的室内或复杂环境中飞行时,如何仅凭IMU数据实现厘米级定位?误差状态卡尔曼滤波(ESKF)正是解决这一难题的利器。本文将带您从零实现一个完整的ESKF框架,使用EuRoC MAV数据集进行实战验证,并分享工程化过程中的关键技巧。
1. 环境准备与数据加载
在开始编码前,我们需要搭建Python科学计算环境。推荐使用Anaconda创建独立环境:
conda create -n eskf python=3.8
conda activate eskf
pip install numpy scipy matplotlib pyquaternion pandas
EuRoC MAV数据集提供了高质量的IMU和真值数据,我们以 MH_01_easy 序列为例。数据集目录结构通常包含:
├── mav0
│ ├── imu0
│ │ ├── data.csv # IMU测量值
│ ├── leica0
│ │ ├── data.csv # 真值轨迹
使用Pandas加载IMU数据的核心代码:
import pandas as pd
def load_imu_data(csv_path):
cols = ['timestamp','gyro_x','gyro_y','gyro_z','acc_x','acc_y','acc_z']
df = pd.read_csv(csv_path, header=None, names=cols)
df['timestamp'] /= 1e9 # 纳秒转秒
return df.to_numpy()
注意:实际应用中需要校准IMU的bias和scale参数,EuRoC数据已进行过标定
2. ESKF核心算法实现
2.1 状态变量定义
与传统EKF不同,ESKF维护三个状态层次:
| 状态类型 | 变量组成 | 特点 |
|---|---|---|
| 名义状态 | 位置、速度、姿态(四元数)、bias | 不含噪声的理想状态 |
| 误差状态 | δp, δv, δθ, δbg, δba | 小量,服从高斯分布 |
| 真实状态 | 名义状态 ⊕ 误差状态 | 实际物理系统状态 |
Python中的状态类实现:
class NominalState:
def __init__(self):
self.p = np.zeros(3) # 位置
self.v = np.zeros(3) # 速度
self.q = Quaternion() # 姿态(四元数)
self.bg = np.zeros(3) # 陀螺bias
self.ba = np.zeros(3) # 加速度计bias
class ErrorState:
def __init__(self):
self.dp = np.zeros(3)
self.dv = np.zeros(3)
self.dtheta = np.zeros(3)
self.dbg = np.zeros(3)
self.dba = np.zeros(3)
2.2 预测步实现
IMU的预测分为名义状态预测和误差状态预测两部分。名义状态通过IMU测量值直接积分:
def predict_nominal(imu_data, dt):
# 去除bias后的测量值
gyro = imu_data.gyro - state.bg
acc = imu_data.acc - state.ba
# 姿态更新 (四元数积分)
q_dot = 0.5 * state.q * Quaternion(0, *gyro)
new_q = state.q + q_dot * dt
# 速度更新
global_gravity = np.array([0, 0, -9.81])
global_acc = state.q.rotate(acc) + global_gravity
new_v = state.v + global_acc * dt
# 位置更新
new_p = state.p + state.v * dt + 0.5 * global_acc * dt**2
return NominalState(new_p, new_v, new_q, state.bg, state.ba)
误差状态预测需要计算状态转移矩阵F和噪声协方差Q:
def compute_F(dt, gyro, acc, state):
F = np.eye(15)
# 填充各状态间的耦合关系
F[0:3, 3:6] = np.eye(3) * dt
F[3:6, 6:9] = -skew(state.q.rotate(acc)) * dt
# ... 其他非零块矩阵
return F
def predict_error(F, Q, dt):
# 误差状态协方差预测
new_P = F @ state.P @ F.T + Q
return new_P
2.3 更新步实现
当视觉或GPS观测到来时,执行ESKF更新:
def update(z, H, R):
# 计算卡尔曼增益
S = H @ state.P @ H.T + R
K = state.P @ H.T @ np.linalg.inv(S)
# 更新误差状态
delta_x = K @ (z - predict_measurement())
state.dp += delta_x[0:3]
state.dv += delta_x[3:6]
# ... 其他状态更新
# 更新协方差
I_KH = np.eye(15) - K @ H
state.P = I_KH @ state.P @ I_KH.T + K @ R @ K.T
3. 工程实践关键技巧
3.1 数值稳定性处理
ESKF实现中容易遇到的数值问题及解决方案:
- 四元数归一化 :每次预测后执行
q.normalize() - 协方差矩阵对称性 :每次更新后执行
P = (P + P.T) * 0.5 - Cholesky分解失败 :添加小量对角矩阵
P += 1e-6 * np.eye(15)
3.2 参数调优指南
关键噪声参数对系统性能的影响:
| 参数 | 物理意义 | 调大效果 | 调小效果 |
|---|---|---|---|
| gyro_noise | 陀螺测量噪声 | 更信任观测 | 更信任IMU |
| acc_noise | 加速度计测量噪声 | 减小加速度计影响 | 增大加速度计影响 |
| bg_walk | 陀螺bias随机游走 | bias变化更灵敏 | bias更稳定 |
典型初始值设置(EuRoC数据集):
params = {
'gyro_noise': 0.005, # rad/s
'acc_noise': 0.05, # m/s²
'bg_walk': 1e-6, # rad/s²
'ba_walk': 1e-5, # m/s³
}
3.3 多传感器时间对齐
实际系统中各传感器时间戳不同步会导致性能下降。解决方法:
- 硬件同步 :使用PPS信号触发所有传感器
- 软件插值 :对IMU数据进行线性插值匹配视觉时间戳
- 运动补偿 :考虑相机曝光期间的IMU运动
时间对齐的插值实现示例:
def interpolate_imu(t_target, imu_before, imu_after):
alpha = (t_target - imu_before.t) / (imu_after.t - imu_before.t)
gyro = imu_before.gyro * (1-alpha) + imu_after.gyro * alpha
acc = imu_before.acc * (1-alpha) + imu_after.acc * alpha
return IMUData(t_target, gyro, acc)
4. 结果可视化与分析
使用Matplotlib绘制轨迹对比图:
def plot_trajectory(gt, eskf):
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.plot(gt[:,0], gt[:,1], gt[:,2], label='Ground Truth')
ax.plot(eskf[:,0], eskf[:,1], eskf[:,2], label='ESKF')
ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); ax.set_zlabel('Z [m]')
plt.legend(); plt.show()
性能评估指标计算:
def compute_ate(gt, est):
"""计算绝对轨迹误差"""
alignment = umeyama(est.T, gt.T)
aligned = (alignment[:3,:3] @ est.T + alignment[:3,3:4]).T
return np.sqrt(np.mean(np.sum((aligned - gt)**2, axis=1)))
在EuRoC数据集上的典型性能:
| 序列 | ATE (m) | 最大误差 (m) | 运行频率 (Hz) |
|---|---|---|---|
| MH_01_easy | 0.12 | 0.35 | 200 |
| MH_03_medium | 0.28 | 0.91 | 180 |
更多推荐

所有评论(0)