航姿传感器——扩展卡尔曼滤波(EKF)初步(一)

本文介绍使用MPU6050传感器与EKF算法实现基础航姿传感器的过程。通过VC++实现EKF,详细解释代码结构与初始化,以及如何读取数据更新状态量。实验结果显示,与加速度计和积分计算相比,EKF结果更稳定平滑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

利用手头的MPU6050传感器中的三轴陀螺仪及三轴加速度计实现一个基础的航姿传感器。阶段目标,VC++上实现EKF算法。
主要参考的是A Double-Stage Kalman Filter for Orientation Tracking With an Integrated Processor in 9-D IMU这篇论文。

代码

1、结构体初始化
  • 卡尔曼滤波器结构体初始化。主要是几个变量的初始化,由于是EKF算法,所以多出了dH和Zkk_1这两个变量。EKF在进行kalman增益计算和协方差矩阵更新的时候,是近似看作普通的KF的,故这两个式子中的观测矩阵要求偏导数及原始观测阵的Jacobian Matrix,代码中用dH表示。而在残差(residual)或者说是新息的计算中应该是用 观测值-推测值,这里的推测值是用原始的观测阵推出的,代码中用Zkk_1表示。
  • 可能有几个变量不涉及后续的计算。
typedef struct testKF {
	Eigen::VectorXd X;	// 状态向量	
	Eigen::MatrixXd PHI;// 状态转移矩阵
	Eigen::MatrixXd H;	// 观测矩阵
	Eigen::MatrixXd dH;	// 观测矩阵Jacobian Matrix
	Eigen::MatrixXd P;	// 状态协方差矩阵
	Eigen::MatrixXd W;	// 过程噪声矩阵Q
	Eigen::MatrixXd V;	// 观测噪声矩阵R
	Eigen::VectorXd Z;	// 观测向量
	Eigen::VectorXd Zkk_1;// 先验观测估计
	Eigen::MatrixXd K;	// kalman增益矩阵
	testKF() {
		X.resize(4);
		PHI.resize(4, 4);
		H.resize(3, 4);
		dH.resize(3, 4);
		P.resize(4, 4);
		W.resize(4, 4);
		V.resize(3, 3);
		Z.resize(3);
		Zkk_1.resize(3);
		K.resize(4, 3);
	}
}testKF;
2、kalman变量初始化
  • 主要是 X,P,W,V这四个变量的初始赋值
  • 具体赋值的多少可以参考论文中最后给出的几个数值,这里我稍微调节了下
  • 后续会探究下P,W,V这三个矩阵该如何赋值,主要是背后的物理含义数学含义到底是什么这是个关键!!
void ekf_init(testKF* kf)
{
	double q[4] = {1,0,0,0};
	//double w[3] = { 0, 0, 0};
	double acc[3] = { 0, 0, -1 };
	//初始化 X,P
	kf->X << q[0], q[1], q[2], q[3];
	//复制论文中的数值
	kf->P <<0.125, 0.0003, 0.0003, 0.0003,
			0.0003, 0.125, 0.0003, 0.0003,
			0.0003, 0.0003, 0.125, 0.0003,
			0.0003, 0.0003, 0.0003, 0.125;
	double DQ = 0.05*DEG2RAD*0.05*DEG2RAD;
	kf->W << DQ*Eigen::MatrixXd::Identity(4,4);
	kf->V << 0.05 * Eigen::MatrixXd::Identity(3, 3);
}
3、数值读取
  • 这部分主要是读取原始数据
  • 更新状态量X,观测矩阵dH,先验状态观测值Zkk_1
std::vector<std::vector<double>> ekf_test(std::vector<std::vector<double>>&data)
{
	std::vector<std::vector<double>> ekfout;
	testKF* kf = &QEKF;
	ekf_init(kf);
	size_t count = 0;
	for (auto line : data)
	{	
		double wx, wy, wz;
		wx = line[4]; wy = line[5]; wz = line[6];
		kf->Z << -line[7], -line[8], -line[9];
		// 状态转移阵
		double ts = S_IN_TIME;//采样间隔
		kf->PHI <<	0, -wx, -wy, -wz,
					wx, 0, wz, -wy,
					wy, -wz, 0, wx,
					wz, wy, -wx, 0;
		kf->PHI = Eigen::MatrixXd::Identity(4, 4) + kf->PHI*ts / 2;
		// 状态量
		Eigen::VectorXd Xkk_1(4);
		Xkk_1 = kf->PHI*kf->X;
		double q0, q1, q2, q3;
		q0 = Xkk_1[0]; q1 = Xkk_1[1]; q2 = Xkk_1[2]; q3 = Xkk_1[3];
		// 观测矩阵(Jabobain Matrix)
		kf->dH << -q2, q3, -q0, q1,
			q1, q0, q3, q2,
			q0, -q1, -q2, q3;
		kf->dH = 2 * kf->dH;
		// 先验观测估计
		kf->Zkk_1 << 2 * q1*q3 - 2 * q0*q2, 2 * q0*q1 + 2 * q2*q3, q0*q0 - q1 * q1 - q2 * q2 + q3 * q3;

		ekf_update(kf);

		double qx[4];
		qx[0] = kf->X[0]; qx[1] = kf->X[1]; qx[2] = kf->X[2]; qx[3] = kf->X[3];
		quaternions_norm(qx);
		kf->X[0] = qx[0]; kf->X[1] = qx[1]; kf->X[2] = qx[2]; kf->X[3] = qx[3];
		std::vector<double>vec = ekf_test_out(kf->X);
		double eul[3];
		Quaternions2Eular(qx,eul);
		vec.push_back(eul[0]);
		vec.push_back(eul[1]);
		vec.push_back(eul[2]);
		for (auto val : vec)
		{
			line.push_back(val);
			//std::cout << val;
		}
		ekfout.push_back(line);
	}
	return ekfout;
}
5、卡尔曼滤波更新
  • 套用公式运算,没啥好说的
void ekf_update(testKF* kf)
{
	Eigen::VectorXd Xkk_1 = kf->PHI*kf->X;
	Eigen::MatrixXd Pkk_1 = kf->PHI*kf->P*kf->PHI.transpose() + kf->W;
	//EKF kalman增益计算时用H阵的Jacobian matrix
	kf->K = Pkk_1 * kf->dH.transpose()*((kf->dH*Pkk_1*kf->dH.transpose() + kf->V).inverse());
	kf->X = Xkk_1 + kf->K*(kf->Z - kf->Zkk_1);//EKF X更新时残差用 原始观测阵
	kf->P = (Eigen::MatrixXd::Identity(kf->K.rows(), kf->K.rows())-kf->K*kf->dH)*Pkk_1;
}

试验结果

滚转角
滚转角:绿色加速度计计算,蓝色积分计算,红色EKF计算
俯仰角
俯仰角:绿色加速度计计算,蓝色积分计算,红色EKF计算

初步结论

  • 可以看到当俯仰角的积分计算出现发散时,ekf计算结果保持收敛
  • 相比加速度计得出的计算结果,EKF得出的结果要平滑许多。
  • 尝试调节初始化中的W,V矩阵,当W(过程噪声矩阵)保持不变时,减小V(观测噪声矩阵)时,ekf滤波结果更加靠近直接由加速度计计算出的结果,反之则更加靠近陀螺仪积分得出的结果。可以做一些定性的分析和感性的理解(不准确的理解)
    1. V其实是测量的误差的反应,V越大表明误差越大,在经过数次迭代后,滤波结果中对观测值的“参考”就不是那么多。
    2. W则是整个模型递推过程中建模误差的反应,W越大,说明模型得出的结论越不可信,在经过数次迭代后,滤波结果中对模型推导值的“参考”就会下降。

下一步计划

  • 理解PWV这三个量的含义,尝试给出定量的值。
  • 与一个精确的航姿传感器进行对比。
  • 加入磁力计进行计算
### 扩展卡尔曼滤波EKF)原理 扩展卡尔曼滤波种用于非线性系统的状态估计技术,它基于标准卡尔曼滤波进行了改进以适应非线性的动态系统和观测模型。对于非线性函数,EKF采用泰勒级数展开的方式,在当前最佳估计点附近近似这些非线性关系为线性形式[^1]。 具体来说,当处理非线性过程方程 \( \mathbf{x}_{k} = f(\mathbf{x}_{k-1},\mathbf{u}_k)+w_k \) 和测量方程 \( z_k=h(\mathbf{x}_k)+v_k\) 时,其中\( w_k \) 是过程噪声而 \( v_k \) 表示测量误差,则可以通过雅可比矩阵计算局部导数值来进行预测与更新操作: #### 预测阶段 \[ \hat{\mathbf{x}}^-_k=f(\hat{\mathbf{x}}_{k-1},\mathbf{u}_k)\quad (1)\] \[ P^{-}_k=F_kP_{k-1}{F^{T}_k}+Q_k\qquad(2)\] 这里 \( F_k=\frac{\partial f}{\partial x}| _{{\hat {\mathbf {x}}} _{k-1},{\mathbf {u}} _k}\),即在给定点处求得的过程模型关于状态变量的阶偏微分;\( Q_k \)代表过程噪音协方差矩阵。 #### 更新/校正阶段 \[ K_k=P^{-}_kH^{T}(HP^{-}_kH^{T}+R)^{-1}\qquad(3)\] \[ \hat{\mathbf{x}}_k=\hat{\mathbf{x}}^{-}_k+K_k(z_k-h(\hat{\mathbf{x}}^{-}_k))\quad(4)\] \[ P_k=(I-K_kH)P^{-}_k(I-K_kH)^{T}+KRK^{T}\qquad(5)\] 上述公式中的 \( H=\left.\dfrac{\partial h}{\partial x}\right|_{\hat{\mathbf{x}}^{-}_k}\), 它是在估计的状态向量位置上对观察模型取阶偏导的结果; 而 \( R \) 则表示观测量的不确定性程度或者说测量噪声的协方差矩阵. ### 实现方法 针对实际应用场合下的传感器数据融合问题,比如姿态解算中涉及到的角度变化率由陀螺仪提供,而绝对方向信息则来自加速度计和磁力计读数的情况,可以构建如下所示的时间迭代估计流程: ```python import numpy as np def ekf_predict(x_hat_prev, u, A, B, Q): """Predict step of the Extended Kalman Filter.""" # Predict state and covariance matrix x_pred = A @ x_hat_prev + B @ u # Compute Jacobian matrices at predicted point F = compute_jacobian_f(x_pred, u) P_pred = F @ P_prev @ F.T + Q return x_pred, P_pred def ekf_update(x_pred, y_meas, C, R, P_pred): """Update step of the Extended Kalman Filter.""" # Measurement prediction from model y_model = predict_measurement(x_pred) # Compute residual between measurement & its expectation under current estimate res = y_meas - y_model # Calculate Jacobians needed for update equations H = compute_jacobian_h(x_pred) S = H @ P_pred @ H.T + R K = P_pred @ H.T @ np.linalg.inv(S) # Update estimated state vector using gain factor times innovation term x_estimated = x_pred + K @ res I_KH = np.eye(len(K)) - K @ H P_updated = I_KH @ P_pred @ I_KH.T + K @ R @ K.T return x_estimated, P_updated ``` 此代码片段展示了如何在个简单的Python环境中执行次完整的EKF循环,包括预测和更新两个主要步骤。注意这里的`compute_jacobian_f`, `predict_measurement`, 及 `compute_jacobian_h` 函数需根据具体的物理场景定义相应的数学表达式来完成。 ### 应用实例 个典型的应用案例是对惯性导系统(INU)内的多源传感设备所采集的数据进行同步处理并得到更精确的姿态角度输出。例如,在飞行器控制系统里,通过组合使用IMU内部集成的各种类型的敏感元件——如三轴加速计、陀螺仪以及电子罗盘等装置获取的信息,借助于EKF算法能够有效地消除各单独信号中存在的随机波动成分,进而提高整体定位精度和服务质量[^3].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值