chore: 导入 Simba-OmniInfantry 基线(云台/底盘双工程)
- 云台(ROBOT_TYPE_GIMBAL)与底盘(ROBOT_TYPE_CHASSIS)完整固件源码 - 新增根级 .gitignore,排除 Keil MDK-ARM 构建产物(.o/.d/.crf/.axf/.lnp/.map/.htm/.sct/.dep/.iex/.lst 等) 与 *.uvguix.* / DebugConfig/(与机器绑定的 UI 布局) - core.autocrlf=false,避免固件源码在检出时被改动换行符
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file QuaternionEKF.c
|
||||
* @author Wang Hongxi
|
||||
* @version V1.2.0
|
||||
* @date 2022/3/8
|
||||
* @brief attitude update with gyro bias estimate and chi-square test
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 1st order LPF transfer function:
|
||||
* 1
|
||||
* ———————
|
||||
* as + 1
|
||||
******************************************************************************
|
||||
*/
|
||||
#include "QuaternionEKF.h"
|
||||
|
||||
QEKF_INS_t QEKF_INS;
|
||||
|
||||
const float IMU_QuaternionEKF_F[36] = {1, 0, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 0,
|
||||
0, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 1};
|
||||
float IMU_QuaternionEKF_P[36] = {100000, 0.1, 0.1, 0.1, 0.1, 0.1,
|
||||
0.1, 100000, 0.1, 0.1, 0.1, 0.1,
|
||||
0.1, 0.1, 100000, 0.1, 0.1, 0.1,
|
||||
0.1, 0.1, 0.1, 100000, 0.1, 0.1,
|
||||
0.1, 0.1, 0.1, 0.1, 100, 0.1,
|
||||
0.1, 0.1, 0.1, 0.1, 0.1, 100};
|
||||
float IMU_QuaternionEKF_K[18];
|
||||
float IMU_QuaternionEKF_H[18];
|
||||
|
||||
static float invSqrt(float x);
|
||||
static void IMU_QuaternionEKF_Observe(KalmanFilter_t *kf);
|
||||
static void IMU_QuaternionEKF_F_Linearization_P_Fading(KalmanFilter_t *kf);
|
||||
static void IMU_QuaternionEKF_SetH(KalmanFilter_t *kf);
|
||||
static void IMU_QuaternionEKF_xhatUpdate(KalmanFilter_t *kf);
|
||||
|
||||
/**
|
||||
* @brief Quaternion EKF initialization and some reference value
|
||||
* @param[in] process_noise1 quaternion process noise 10
|
||||
* @param[in] process_noise2 gyro bias process noise 0.001
|
||||
* @param[in] measure_noise accel measure noise 1000000
|
||||
* @param[in] lambda fading coefficient 0.9996
|
||||
* @param[in] lpf lowpass filter coefficient 0
|
||||
*/
|
||||
void IMU_QuaternionEKF_Init(float process_noise1, float process_noise2, float measure_noise, float lambda, float lpf)
|
||||
{
|
||||
QEKF_INS.Initialized = 1;
|
||||
QEKF_INS.Q1 = process_noise1;
|
||||
QEKF_INS.Q2 = process_noise2;
|
||||
QEKF_INS.R = measure_noise;
|
||||
QEKF_INS.ChiSquareTestThreshold = 1e-8;
|
||||
QEKF_INS.ConvergeFlag = 0;
|
||||
QEKF_INS.ErrorCount = 0;
|
||||
QEKF_INS.UpdateCount = 0;
|
||||
if (lambda > 1)
|
||||
{
|
||||
lambda = 1;
|
||||
}
|
||||
QEKF_INS.lambda = lambda;
|
||||
QEKF_INS.accLPFcoef = lpf;
|
||||
|
||||
// 初始化矩阵维度信息
|
||||
Kalman_Filter_Init(&QEKF_INS.IMU_QuaternionEKF, 6, 0, 3);
|
||||
Matrix_Init(&QEKF_INS.ChiSquare, 1, 1, (float *)QEKF_INS.ChiSquare_Data);
|
||||
|
||||
// 姿态初始化
|
||||
QEKF_INS.IMU_QuaternionEKF.xhat_data[0] = 1;
|
||||
QEKF_INS.IMU_QuaternionEKF.xhat_data[1] = 0;
|
||||
QEKF_INS.IMU_QuaternionEKF.xhat_data[2] = 0;
|
||||
QEKF_INS.IMU_QuaternionEKF.xhat_data[3] = 0;
|
||||
|
||||
// 自定义函数初始化,用于扩展或增加kf的基础功能
|
||||
QEKF_INS.IMU_QuaternionEKF.User_Func0_f = IMU_QuaternionEKF_Observe;
|
||||
QEKF_INS.IMU_QuaternionEKF.User_Func1_f = IMU_QuaternionEKF_F_Linearization_P_Fading;
|
||||
QEKF_INS.IMU_QuaternionEKF.User_Func2_f = IMU_QuaternionEKF_SetH;
|
||||
QEKF_INS.IMU_QuaternionEKF.User_Func3_f = IMU_QuaternionEKF_xhatUpdate;
|
||||
|
||||
// 设定标志位,用自定函数替换kf标准步骤中的SetK(计算增益)以及xhatupdate(后验估计/融合)
|
||||
QEKF_INS.IMU_QuaternionEKF.SkipEq3 = TRUE;
|
||||
QEKF_INS.IMU_QuaternionEKF.SkipEq4 = TRUE;
|
||||
|
||||
memcpy(QEKF_INS.IMU_QuaternionEKF.F_data, IMU_QuaternionEKF_F, sizeof(IMU_QuaternionEKF_F));
|
||||
memcpy(QEKF_INS.IMU_QuaternionEKF.P_data, IMU_QuaternionEKF_P, sizeof(IMU_QuaternionEKF_P));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Quaternion EKF update
|
||||
* @param[in] gyro x y z in rad/s
|
||||
* @param[in] accel x y z in m/s²
|
||||
* @param[in] update period in s
|
||||
*/
|
||||
void IMU_QuaternionEKF_Update(float gx, float gy, float gz, float ax, float ay, float az, float dt)
|
||||
{
|
||||
// 0.5(Ohm-Ohm^bias)*deltaT,用于更新工作点处的状态转移F矩阵
|
||||
static float halfgxdt, halfgydt, halfgzdt;
|
||||
static float accelInvNorm;
|
||||
if (!QEKF_INS.Initialized)
|
||||
{
|
||||
IMU_QuaternionEKF_Init(10, 0.001, 1000000 * 10, 0.9996 * 0 + 1, 0);
|
||||
}
|
||||
|
||||
/* F, number with * represent vals to be set
|
||||
0 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 33 34 35
|
||||
*/
|
||||
QEKF_INS.dt = dt;
|
||||
|
||||
QEKF_INS.Gyro[0] = gx - QEKF_INS.GyroBias[0];
|
||||
QEKF_INS.Gyro[1] = gy - QEKF_INS.GyroBias[1];
|
||||
QEKF_INS.Gyro[2] = gz - QEKF_INS.GyroBias[2];
|
||||
|
||||
// set F
|
||||
halfgxdt = 0.5f * QEKF_INS.Gyro[0] * dt;
|
||||
halfgydt = 0.5f * QEKF_INS.Gyro[1] * dt;
|
||||
halfgzdt = 0.5f * QEKF_INS.Gyro[2] * dt;
|
||||
|
||||
// 此部分设定状态转移矩阵F的左上角部分 4x4子矩阵,即0.5(Ohm-Ohm^bias)*deltaT,右下角有一个2x2单位阵已经初始化好了
|
||||
// 注意在predict步F的右上角是4x2的零矩阵,因此每次predict的时候都会调用memcpy用单位阵覆盖前一轮线性化后的矩阵
|
||||
memcpy(QEKF_INS.IMU_QuaternionEKF.F_data, IMU_QuaternionEKF_F, sizeof(IMU_QuaternionEKF_F));
|
||||
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[1] = -halfgxdt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[2] = -halfgydt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[3] = -halfgzdt;
|
||||
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[6] = halfgxdt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[8] = halfgzdt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[9] = -halfgydt;
|
||||
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[12] = halfgydt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[13] = -halfgzdt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[15] = halfgxdt;
|
||||
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[18] = halfgzdt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[19] = halfgydt;
|
||||
QEKF_INS.IMU_QuaternionEKF.F_data[20] = -halfgxdt;
|
||||
|
||||
// accel low pass filter,加速度过一下低通滤波平滑数据,降低撞击和异常的影响
|
||||
if (QEKF_INS.UpdateCount == 0) // 如果是第一次进入,需要初始化低通滤波
|
||||
{
|
||||
QEKF_INS.Accel[0] = ax;
|
||||
QEKF_INS.Accel[1] = ay;
|
||||
QEKF_INS.Accel[2] = az;
|
||||
}
|
||||
QEKF_INS.Accel[0] = QEKF_INS.Accel[0] * QEKF_INS.accLPFcoef / (QEKF_INS.dt + QEKF_INS.accLPFcoef) + ax * QEKF_INS.dt / (QEKF_INS.dt + QEKF_INS.accLPFcoef);
|
||||
QEKF_INS.Accel[1] = QEKF_INS.Accel[1] * QEKF_INS.accLPFcoef / (QEKF_INS.dt + QEKF_INS.accLPFcoef) + ay * QEKF_INS.dt / (QEKF_INS.dt + QEKF_INS.accLPFcoef);
|
||||
QEKF_INS.Accel[2] = QEKF_INS.Accel[2] * QEKF_INS.accLPFcoef / (QEKF_INS.dt + QEKF_INS.accLPFcoef) + az * QEKF_INS.dt / (QEKF_INS.dt + QEKF_INS.accLPFcoef);
|
||||
|
||||
// set z,单位化重力加速度向量
|
||||
accelInvNorm = invSqrt(QEKF_INS.Accel[0] * QEKF_INS.Accel[0] + QEKF_INS.Accel[1] * QEKF_INS.Accel[1] + QEKF_INS.Accel[2] * QEKF_INS.Accel[2]);
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
{
|
||||
QEKF_INS.IMU_QuaternionEKF.MeasuredVector[i] = QEKF_INS.Accel[i] * accelInvNorm; // 用加速度向量更新量测值
|
||||
}
|
||||
|
||||
// get body state
|
||||
QEKF_INS.gyro_norm = 1.0f / invSqrt(QEKF_INS.Gyro[0] * QEKF_INS.Gyro[0] +
|
||||
QEKF_INS.Gyro[1] * QEKF_INS.Gyro[1] +
|
||||
QEKF_INS.Gyro[2] * QEKF_INS.Gyro[2]);
|
||||
QEKF_INS.accl_norm = 1.0f / accelInvNorm;
|
||||
|
||||
// 如果角速度小于阈值且加速度处于设定范围内,认为运动稳定,加速度可以用于修正角速度
|
||||
// 稍后在最后的姿态更新部分会利用StableFlag来确定
|
||||
if (QEKF_INS.gyro_norm < 0.3f && QEKF_INS.accl_norm > 9.8f - 0.5f && QEKF_INS.accl_norm < 9.8f + 0.5f)
|
||||
{
|
||||
QEKF_INS.StableFlag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
QEKF_INS.StableFlag = 0;
|
||||
}
|
||||
|
||||
// set Q R,过程噪声和观测噪声矩阵
|
||||
QEKF_INS.IMU_QuaternionEKF.Q_data[0] = QEKF_INS.Q1 * QEKF_INS.dt;
|
||||
QEKF_INS.IMU_QuaternionEKF.Q_data[7] = QEKF_INS.Q1 * QEKF_INS.dt;
|
||||
QEKF_INS.IMU_QuaternionEKF.Q_data[14] = QEKF_INS.Q1 * QEKF_INS.dt;
|
||||
QEKF_INS.IMU_QuaternionEKF.Q_data[21] = QEKF_INS.Q1 * QEKF_INS.dt;
|
||||
QEKF_INS.IMU_QuaternionEKF.Q_data[28] = QEKF_INS.Q2 * QEKF_INS.dt;
|
||||
QEKF_INS.IMU_QuaternionEKF.Q_data[35] = QEKF_INS.Q2 * QEKF_INS.dt;
|
||||
QEKF_INS.IMU_QuaternionEKF.R_data[0] = QEKF_INS.R;
|
||||
QEKF_INS.IMU_QuaternionEKF.R_data[4] = QEKF_INS.R;
|
||||
QEKF_INS.IMU_QuaternionEKF.R_data[8] = QEKF_INS.R;
|
||||
|
||||
// 调用kalman_filter.c封装好的函数,注意几个User_Funcx_f的调用
|
||||
Kalman_Filter_Update(&QEKF_INS.IMU_QuaternionEKF);
|
||||
|
||||
// 获取融合后的数据,包括四元数和xy零飘值
|
||||
QEKF_INS.q[0] = QEKF_INS.IMU_QuaternionEKF.FilteredValue[0];
|
||||
QEKF_INS.q[1] = QEKF_INS.IMU_QuaternionEKF.FilteredValue[1];
|
||||
QEKF_INS.q[2] = QEKF_INS.IMU_QuaternionEKF.FilteredValue[2];
|
||||
QEKF_INS.q[3] = QEKF_INS.IMU_QuaternionEKF.FilteredValue[3];
|
||||
QEKF_INS.GyroBias[0] = QEKF_INS.IMU_QuaternionEKF.FilteredValue[4];
|
||||
QEKF_INS.GyroBias[1] = QEKF_INS.IMU_QuaternionEKF.FilteredValue[5];
|
||||
QEKF_INS.GyroBias[2] = 0; // 大部分时候z轴通天,无法观测yaw的漂移
|
||||
|
||||
// 利用四元数反解欧拉角
|
||||
QEKF_INS.Yaw = atan2f(2.0f * (QEKF_INS.q[0] * QEKF_INS.q[3] + QEKF_INS.q[1] * QEKF_INS.q[2]), 2.0f * (QEKF_INS.q[0] * QEKF_INS.q[0] + QEKF_INS.q[1] * QEKF_INS.q[1]) - 1.0f) * 57.295779513f;
|
||||
QEKF_INS.Pitch = atan2f(2.0f * (QEKF_INS.q[0] * QEKF_INS.q[1] + QEKF_INS.q[2] * QEKF_INS.q[3]), 2.0f * (QEKF_INS.q[0] * QEKF_INS.q[0] + QEKF_INS.q[3] * QEKF_INS.q[3]) - 1.0f) * 57.295779513f;
|
||||
QEKF_INS.Roll = asinf(-2.0f * (QEKF_INS.q[1] * QEKF_INS.q[3] - QEKF_INS.q[0] * QEKF_INS.q[2])) * 57.295779513f;
|
||||
|
||||
// get Yaw total, yaw数据可能会超过360,处理一下方便其他功能使用(如小陀螺)
|
||||
if (QEKF_INS.Yaw - QEKF_INS.YawAngleLast > 180.0f)
|
||||
{
|
||||
QEKF_INS.YawRoundCount--;
|
||||
}
|
||||
else if (QEKF_INS.Yaw - QEKF_INS.YawAngleLast < -180.0f)
|
||||
{
|
||||
QEKF_INS.YawRoundCount++;
|
||||
}
|
||||
QEKF_INS.YawTotalAngle = 360.0f * QEKF_INS.YawRoundCount + QEKF_INS.Yaw;
|
||||
QEKF_INS.YawAngleLast = QEKF_INS.Yaw;
|
||||
QEKF_INS.UpdateCount++; // 初始化低通滤波用,计数测试用
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 用于更新线性化后的状态转移矩阵F右上角的一个4x2分块矩阵,稍后用于协方差矩阵P的更新;
|
||||
* 并对零漂的方差进行限制,防止过度收敛并限幅防止发散
|
||||
*
|
||||
* @param kf
|
||||
*/
|
||||
static void IMU_QuaternionEKF_F_Linearization_P_Fading(KalmanFilter_t *kf)
|
||||
{
|
||||
static float q0, q1, q2, q3;
|
||||
static float qInvNorm;
|
||||
|
||||
q0 = kf->xhatminus_data[0];
|
||||
q1 = kf->xhatminus_data[1];
|
||||
q2 = kf->xhatminus_data[2];
|
||||
q3 = kf->xhatminus_data[3];
|
||||
|
||||
// quaternion normalize
|
||||
qInvNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
|
||||
for (uint8_t i = 0; i < 4; i++)
|
||||
{
|
||||
kf->xhatminus_data[i] *= qInvNorm;
|
||||
}
|
||||
/* F, number with * represent vals to be set
|
||||
0 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 33 34 35
|
||||
*/
|
||||
// set F
|
||||
kf->F_data[4] = q1 * QEKF_INS.dt / 2;
|
||||
kf->F_data[5] = q2 * QEKF_INS.dt / 2;
|
||||
|
||||
kf->F_data[10] = -q0 * QEKF_INS.dt / 2;
|
||||
kf->F_data[11] = q3 * QEKF_INS.dt / 2;
|
||||
|
||||
kf->F_data[16] = -q3 * QEKF_INS.dt / 2;
|
||||
kf->F_data[17] = -q0 * QEKF_INS.dt / 2;
|
||||
|
||||
kf->F_data[22] = q2 * QEKF_INS.dt / 2;
|
||||
kf->F_data[23] = -q1 * QEKF_INS.dt / 2;
|
||||
|
||||
// fading filter,防止零飘参数过度收敛
|
||||
kf->P_data[28] /= QEKF_INS.lambda;
|
||||
kf->P_data[35] /= QEKF_INS.lambda;
|
||||
|
||||
// 限幅,防止发散
|
||||
if (kf->P_data[28] > 10000)
|
||||
{
|
||||
kf->P_data[28] = 10000;
|
||||
}
|
||||
if (kf->P_data[35] > 10000)
|
||||
{
|
||||
kf->P_data[35] = 10000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在工作点处计算观测函数h(x)的Jacobi矩阵H
|
||||
*
|
||||
* @param kf
|
||||
*/
|
||||
static void IMU_QuaternionEKF_SetH(KalmanFilter_t *kf)
|
||||
{
|
||||
static float doubleq0, doubleq1, doubleq2, doubleq3;
|
||||
/* H
|
||||
0 1 2 3 4 5
|
||||
6 7 8 9 10 11
|
||||
12 13 14 15 16 17
|
||||
last two cols are zero
|
||||
*/
|
||||
// set H
|
||||
doubleq0 = 2 * kf->xhatminus_data[0];
|
||||
doubleq1 = 2 * kf->xhatminus_data[1];
|
||||
doubleq2 = 2 * kf->xhatminus_data[2];
|
||||
doubleq3 = 2 * kf->xhatminus_data[3];
|
||||
|
||||
memset(kf->H_data, 0, sizeof_float * kf->zSize * kf->xhatSize);
|
||||
|
||||
kf->H_data[0] = -doubleq2;
|
||||
kf->H_data[1] = doubleq3;
|
||||
kf->H_data[2] = -doubleq0;
|
||||
kf->H_data[3] = doubleq1;
|
||||
|
||||
kf->H_data[6] = doubleq1;
|
||||
kf->H_data[7] = doubleq0;
|
||||
kf->H_data[8] = doubleq3;
|
||||
kf->H_data[9] = doubleq2;
|
||||
|
||||
kf->H_data[12] = doubleq0;
|
||||
kf->H_data[13] = -doubleq1;
|
||||
kf->H_data[14] = -doubleq2;
|
||||
kf->H_data[15] = doubleq3;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 利用观测值和先验估计得到最优的后验估计
|
||||
* 加入了卡方检验以判断融合加速度的条件是否满足
|
||||
* 同时引入发散保护保证恶劣工况下的必要量测更新
|
||||
*
|
||||
* @param kf
|
||||
*/
|
||||
static void IMU_QuaternionEKF_xhatUpdate(KalmanFilter_t *kf)
|
||||
{
|
||||
static float q0, q1, q2, q3;
|
||||
|
||||
kf->MatStatus = Matrix_Transpose(&kf->H, &kf->HT); // z|x => x|z
|
||||
kf->temp_matrix.numRows = kf->H.numRows;
|
||||
kf->temp_matrix.numCols = kf->Pminus.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->H, &kf->Pminus, &kf->temp_matrix); // temp_matrix = H·P'(k)
|
||||
kf->temp_matrix1.numRows = kf->temp_matrix.numRows;
|
||||
kf->temp_matrix1.numCols = kf->HT.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_matrix, &kf->HT, &kf->temp_matrix1); // temp_matrix1 = H·P'(k)·HT
|
||||
kf->S.numRows = kf->R.numRows;
|
||||
kf->S.numCols = kf->R.numCols;
|
||||
kf->MatStatus = Matrix_Add(&kf->temp_matrix1, &kf->R, &kf->S); // S = H P'(k) HT + R
|
||||
kf->MatStatus = Matrix_Inverse(&kf->S, &kf->temp_matrix1); // temp_matrix1 = inv(H·P'(k)·HT + R)
|
||||
|
||||
q0 = kf->xhatminus_data[0];
|
||||
q1 = kf->xhatminus_data[1];
|
||||
q2 = kf->xhatminus_data[2];
|
||||
q3 = kf->xhatminus_data[3];
|
||||
|
||||
kf->temp_vector.numRows = kf->H.numRows;
|
||||
kf->temp_vector.numCols = 1;
|
||||
// 计算预测得到的重力加速度方向(通过姿态获取的)
|
||||
kf->temp_vector_data[0] = 2 * (q1 * q3 - q0 * q2);
|
||||
kf->temp_vector_data[1] = 2 * (q0 * q1 + q2 * q3);
|
||||
kf->temp_vector_data[2] = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3; // temp_vector = h(xhat'(k))
|
||||
|
||||
// 计算预测值和各个轴的方向余弦
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
{
|
||||
QEKF_INS.OrientationCosine[i] = acosf(fabsf(kf->temp_vector_data[i]));
|
||||
}
|
||||
|
||||
// 利用加速度计数据修正
|
||||
kf->temp_vector1.numRows = kf->z.numRows;
|
||||
kf->temp_vector1.numCols = 1;
|
||||
kf->MatStatus = Matrix_Subtract(&kf->z, &kf->temp_vector, &kf->temp_vector1); // temp_vector1 = z(k) - h(xhat'(k))
|
||||
|
||||
// chi-square test,卡方检验
|
||||
kf->temp_matrix.numRows = kf->temp_vector1.numRows;
|
||||
kf->temp_matrix.numCols = 1;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_matrix1, &kf->temp_vector1, &kf->temp_matrix); // temp_matrix = inv(H·P'(k)·HT + R)·(z(k) - h(xhat'(k)))
|
||||
kf->temp_vector.numRows = 1;
|
||||
kf->temp_vector.numCols = kf->temp_vector1.numRows;
|
||||
kf->MatStatus = Matrix_Transpose(&kf->temp_vector1, &kf->temp_vector); // temp_vector = z(k) - h(xhat'(k))'
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_vector, &kf->temp_matrix, &QEKF_INS.ChiSquare);
|
||||
// rk is small,filter converged/converging
|
||||
if (QEKF_INS.ChiSquare_Data[0] < 0.5f * QEKF_INS.ChiSquareTestThreshold)
|
||||
{
|
||||
QEKF_INS.ConvergeFlag = 1;
|
||||
}
|
||||
// rk is bigger than thre but once converged
|
||||
if (QEKF_INS.ChiSquare_Data[0] > QEKF_INS.ChiSquareTestThreshold && QEKF_INS.ConvergeFlag)
|
||||
{
|
||||
if (QEKF_INS.StableFlag)
|
||||
{
|
||||
QEKF_INS.ErrorCount++; // 载体静止时仍无法通过卡方检验
|
||||
}
|
||||
else
|
||||
{
|
||||
QEKF_INS.ErrorCount = 0;
|
||||
}
|
||||
|
||||
if (QEKF_INS.ErrorCount > 50)
|
||||
{
|
||||
// 滤波器发散
|
||||
QEKF_INS.ConvergeFlag = 0;
|
||||
kf->SkipEq5 = FALSE; // step-5 is cov mat P updating
|
||||
}
|
||||
else
|
||||
{
|
||||
// 残差未通过卡方检验 仅预测
|
||||
// xhat(k) = xhat'(k)
|
||||
// P(k) = P'(k)
|
||||
memcpy(kf->xhat_data, kf->xhatminus_data, sizeof_float * kf->xhatSize);
|
||||
memcpy(kf->P_data, kf->Pminus_data, sizeof_float * kf->xhatSize * kf->xhatSize);
|
||||
kf->SkipEq5 = TRUE; // part5 is P updating
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // if divergent or rk is not that big/acceptable,use adaptive gain
|
||||
{
|
||||
// scale adaptive,rk越小则增益越大,否则更相信预测值
|
||||
if (QEKF_INS.ChiSquare_Data[0] > 0.1f * QEKF_INS.ChiSquareTestThreshold && QEKF_INS.ConvergeFlag)
|
||||
{
|
||||
QEKF_INS.AdaptiveGainScale = (QEKF_INS.ChiSquareTestThreshold - QEKF_INS.ChiSquare_Data[0]) / (0.9f * QEKF_INS.ChiSquareTestThreshold);
|
||||
}
|
||||
else
|
||||
{
|
||||
QEKF_INS.AdaptiveGainScale = 1;
|
||||
}
|
||||
QEKF_INS.ErrorCount = 0;
|
||||
kf->SkipEq5 = FALSE;
|
||||
}
|
||||
|
||||
// cal kf-gain K
|
||||
kf->temp_matrix.numRows = kf->Pminus.numRows;
|
||||
kf->temp_matrix.numCols = kf->HT.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->Pminus, &kf->HT, &kf->temp_matrix); // temp_matrix = P'(k)·HT
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_matrix, &kf->temp_matrix1, &kf->K);
|
||||
|
||||
// implement adaptive
|
||||
for (uint8_t i = 0; i < kf->K.numRows * kf->K.numCols; i++)
|
||||
{
|
||||
kf->K_data[i] *= QEKF_INS.AdaptiveGainScale;
|
||||
}
|
||||
for (uint8_t i = 4; i < 6; i++)
|
||||
{
|
||||
for (uint8_t j = 0; j < 3; j++)
|
||||
{
|
||||
kf->K_data[i * 3 + j] *= QEKF_INS.OrientationCosine[i - 4] / 1.5707963f; // 1 rad
|
||||
}
|
||||
}
|
||||
|
||||
kf->temp_vector.numRows = kf->K.numRows;
|
||||
kf->temp_vector.numCols = 1;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->K, &kf->temp_vector1, &kf->temp_vector); // temp_vector = K(k)·(z(k) - H·xhat'(k))
|
||||
|
||||
// 零漂修正限幅,一般不会有过大的漂移
|
||||
if (QEKF_INS.ConvergeFlag)
|
||||
{
|
||||
for (uint8_t i = 4; i < 6; i++)
|
||||
{
|
||||
if (kf->temp_vector.pData[i] > 1e-2f * QEKF_INS.dt)
|
||||
{
|
||||
kf->temp_vector.pData[i] = 1e-2f * QEKF_INS.dt;
|
||||
}
|
||||
if (kf->temp_vector.pData[i] < -1e-2f * QEKF_INS.dt)
|
||||
{
|
||||
kf->temp_vector.pData[i] = -1e-2f * QEKF_INS.dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 不修正yaw轴数据
|
||||
kf->temp_vector.pData[3] = 0;
|
||||
kf->MatStatus = Matrix_Add(&kf->xhatminus, &kf->temp_vector, &kf->xhat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief EKF观测环节,其实就是把数据复制一下
|
||||
*
|
||||
* @param kf kf类型定义
|
||||
*/
|
||||
static void IMU_QuaternionEKF_Observe(KalmanFilter_t *kf)
|
||||
{
|
||||
memcpy(IMU_QuaternionEKF_P, kf->P_data, sizeof(IMU_QuaternionEKF_P));
|
||||
memcpy(IMU_QuaternionEKF_K, kf->K_data, sizeof(IMU_QuaternionEKF_K));
|
||||
memcpy(IMU_QuaternionEKF_H, kf->H_data, sizeof(IMU_QuaternionEKF_H));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自定义1/sqrt(x),速度更快
|
||||
*
|
||||
* @param x x
|
||||
* @return float
|
||||
*/
|
||||
static float invSqrt(float x)
|
||||
{
|
||||
float halfx = 0.5f * x;
|
||||
float y = x;
|
||||
long i = *(long *)&y;
|
||||
i = 0x5f375a86 - (i >> 1);
|
||||
y = *(float *)&i;
|
||||
y = y * (1.5f - (halfx * y * y));
|
||||
return y;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file QuaternionEKF.h
|
||||
* @author Wang Hongxi
|
||||
* @version V1.2.0
|
||||
* @date 2022/3/8
|
||||
* @brief attitude update with gyro bias estimate and chi-square test
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef _QUAT_EKF_H
|
||||
#define _QUAT_EKF_H
|
||||
#include "kalman_filter.h"
|
||||
|
||||
/* boolean type definitions */
|
||||
#ifndef TRUE
|
||||
#define TRUE 1 /**< boolean true */
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0 /**< boolean fails */
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Initialized;
|
||||
KalmanFilter_t IMU_QuaternionEKF;
|
||||
uint8_t ConvergeFlag;
|
||||
uint8_t StableFlag;
|
||||
uint64_t ErrorCount;
|
||||
uint64_t UpdateCount;
|
||||
|
||||
float q[4]; // 四元数估计值
|
||||
float GyroBias[3]; // 陀螺仪零偏估计值
|
||||
|
||||
float Gyro[3];
|
||||
float Accel[3];
|
||||
|
||||
float OrientationCosine[3];
|
||||
|
||||
float accLPFcoef;
|
||||
float gyro_norm;
|
||||
float accl_norm;
|
||||
float AdaptiveGainScale;
|
||||
|
||||
float Roll;
|
||||
float Pitch;
|
||||
float Yaw;
|
||||
|
||||
float YawTotalAngle;
|
||||
|
||||
float Q1; // 四元数更新过程噪声
|
||||
float Q2; // 陀螺仪零偏过程噪声
|
||||
float R; // 加速度计量测噪声
|
||||
|
||||
float dt; // 姿态更新周期
|
||||
mat ChiSquare;
|
||||
float ChiSquare_Data[1]; // 卡方检验检测函数
|
||||
float ChiSquareTestThreshold; // 卡方检验阈值
|
||||
float lambda; // 渐消因子
|
||||
|
||||
int16_t YawRoundCount;
|
||||
|
||||
float YawAngleLast;
|
||||
} QEKF_INS_t;
|
||||
|
||||
extern QEKF_INS_t QEKF_INS;
|
||||
extern float chiSquare;
|
||||
extern float ChiSquareTestThreshold;
|
||||
void IMU_QuaternionEKF_Init(float process_noise1, float process_noise2, float measure_noise, float lambda, float lpf);
|
||||
void IMU_QuaternionEKF_Update(float gx, float gy, float gz, float ax, float ay, float az, float dt);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/* =============================================================================
|
||||
* ins.c - 惯导姿态解算封装实现(见 ins.h 说明)
|
||||
* 移植自 RoboMaster-C-Board-INS-Example-master 的 ins_task.c:
|
||||
* - 去除 BMI088driver/controller/bsp_PWM/bsp_dwt 依赖,数据源改为本工程
|
||||
* 的 bmi088 驱动全局量,温度控制仍由 IMU_Task 负责
|
||||
* - dt 由调用方传入(IMU_Task 固定 1ms),不再使用 DWT 计时
|
||||
* - 保留运动加速度解算(低通 0.0085),供后续功能扩展(如功率/跌倒检测)
|
||||
* 欧拉角约定(重要):
|
||||
* QEKF 内部变量命名与航空惯例互换——QEKF_INS.Pitch 是绕 x 轴的角,
|
||||
* QEKF_INS.Roll 是绕 y 轴的角;attitude.c 按"本工程 Pitch 反馈=QEKF.Pitch"
|
||||
* 映射(与旧 Mahony 方案的 get_roll 语义一致),勿凭名字想当然
|
||||
* ========================================================================== */
|
||||
#include "ins.h"
|
||||
#include "QuaternionEKF.h"
|
||||
#include "bmi088.h"
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
|
||||
/* z 轴静止零偏标定参数:QEKF 无法观测 z 偏置(见 ins.h),上电采 1s */
|
||||
#define INS_Z_CAL_SAMPLES 1000
|
||||
static float gyro_z_offset = 0;
|
||||
static uint8_t z_cal_done = 0;
|
||||
|
||||
INS_t INS;
|
||||
|
||||
static const float xb[3] = {1, 0, 0};
|
||||
static const float yb[3] = {0, 1, 0};
|
||||
static const float zb[3] = {0, 0, 1};
|
||||
|
||||
void ins_init(void)
|
||||
{
|
||||
memset(&INS, 0, sizeof(INS));
|
||||
|
||||
/* z 轴静止零偏标定:上电静止采 1s 取平均(x/y 偏置由 EKF 在线估计,
|
||||
* 无需校准;此期间 ins_update() 不输出,控制任务由 ins_ready() 门控) */
|
||||
float sum = 0;
|
||||
for (uint16_t i = 0; i < INS_Z_CAL_SAMPLES; i++)
|
||||
{
|
||||
sum += bmi088_gyro[2];
|
||||
HAL_Delay(1);
|
||||
}
|
||||
gyro_z_offset = sum / INS_Z_CAL_SAMPLES;
|
||||
z_cal_done = 1;
|
||||
|
||||
INS.Initialized = 1;
|
||||
|
||||
/* 与 INS 例程 demo 相同的参数组合:
|
||||
* 过程噪声1=10(四元数) 过程噪声2=0.001(零偏) 观测噪声=1e7(加速度)
|
||||
* 渐消因子=1(不渐消) 加速度低通=0(内部另有运动加速度低通) */
|
||||
IMU_QuaternionEKF_Init(10, 0.001f, 10000000, 1, 0);
|
||||
}
|
||||
|
||||
uint8_t ins_ready(void)
|
||||
{
|
||||
return z_cal_done;
|
||||
}
|
||||
|
||||
void ins_update(float dt)
|
||||
{
|
||||
if (!z_cal_done)
|
||||
return;
|
||||
|
||||
/* BMI088 驱动在 SPI DMA 完成回调中后台更新,这里直接取最新值;
|
||||
* z 轴减去上电标定的零偏(x/y 由 EKF 在线估计,不再静态校准) */
|
||||
float gyro[3] = {bmi088_gyro[0], bmi088_gyro[1],
|
||||
bmi088_gyro[2] - gyro_z_offset};
|
||||
float accel[3] = {bmi088_accel[0], bmi088_accel[1], bmi088_accel[2]};
|
||||
|
||||
/* 核心函数,EKF 更新四元数(内部含零偏估计与卡方检验) */
|
||||
IMU_QuaternionEKF_Update(gyro[0], gyro[1], gyro[2],
|
||||
accel[0], accel[1], accel[2], dt);
|
||||
|
||||
memcpy(INS.q, QEKF_INS.q, sizeof(INS.q));
|
||||
|
||||
/* 机体系基向量转换到导航系(惯性系=导航系),供扩展功能使用 */
|
||||
float xn[3], yn[3], zn[3];
|
||||
BodyFrameToEarthFrame(xb, xn, INS.q);
|
||||
BodyFrameToEarthFrame(yb, yn, INS.q);
|
||||
BodyFrameToEarthFrame(zb, zn, INS.q);
|
||||
|
||||
/* 将重力从导航系转到机体系,计算运动加速度(低通),供扩展使用 */
|
||||
const float gravity[3] = {0, 0, 9.81f};
|
||||
float gravity_b[3];
|
||||
EarthFrameToBodyFrame(gravity, gravity_b, INS.q);
|
||||
const float AccelLPF = 0.0085f;
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
{
|
||||
INS.MotionAccel_b[i] = (accel[i] - gravity_b[i]) * dt / (AccelLPF + dt) +
|
||||
INS.MotionAccel_b[i] * AccelLPF / (AccelLPF + dt);
|
||||
}
|
||||
|
||||
/* 获取最终数据(QEKF 内部已含多圈 Yaw 处理) */
|
||||
INS.Yaw = QEKF_INS.Yaw;
|
||||
INS.Pitch = QEKF_INS.Pitch;
|
||||
INS.Roll = QEKF_INS.Roll;
|
||||
INS.YawTotalAngle = QEKF_INS.YawTotalAngle;
|
||||
|
||||
INS.GyroBias[0] = QEKF_INS.GyroBias[0];
|
||||
INS.GyroBias[1] = QEKF_INS.GyroBias[1];
|
||||
INS.GyroBias[2] = QEKF_INS.GyroBias[2];
|
||||
INS.StableFlag = QEKF_INS.StableFlag;
|
||||
INS.ConvergeFlag = QEKF_INS.ConvergeFlag;
|
||||
INS.ChiSquare = QEKF_INS.ChiSquare_Data[0];
|
||||
INS.UpdateCount = QEKF_INS.UpdateCount;
|
||||
|
||||
(void)xn;
|
||||
(void)yn;
|
||||
(void)zn;
|
||||
(void)zb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform 3dvector from BodyFrame to EarthFrame
|
||||
* @param[1] vector in BodyFrame
|
||||
* @param[2] vector in EarthFrame
|
||||
* @param[3] quaternion
|
||||
*/
|
||||
void BodyFrameToEarthFrame(const float *vecBF, float *vecEF, float *q)
|
||||
{
|
||||
vecEF[0] = 2.0f * ((0.5f - q[2] * q[2] - q[3] * q[3]) * vecBF[0] +
|
||||
(q[1] * q[2] - q[0] * q[3]) * vecBF[1] +
|
||||
(q[1] * q[3] + q[0] * q[2]) * vecBF[2]);
|
||||
|
||||
vecEF[1] = 2.0f * ((q[1] * q[2] + q[0] * q[3]) * vecBF[0] +
|
||||
(0.5f - q[1] * q[1] - q[3] * q[3]) * vecBF[1] +
|
||||
(q[2] * q[3] - q[0] * q[1]) * vecBF[2]);
|
||||
|
||||
vecEF[2] = 2.0f * ((q[1] * q[3] - q[0] * q[2]) * vecBF[0] +
|
||||
(q[2] * q[3] + q[0] * q[1]) * vecBF[1] +
|
||||
(0.5f - q[1] * q[1] - q[2] * q[2]) * vecBF[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform 3dvector from EarthFrame to BodyFrame
|
||||
* @param[1] vector in EarthFrame
|
||||
* @param[2] vector in BodyFrame
|
||||
* @param[3] quaternion
|
||||
*/
|
||||
void EarthFrameToBodyFrame(const float *vecEF, float *vecBF, float *q)
|
||||
{
|
||||
vecBF[0] = 2.0f * ((0.5f - q[2] * q[2] - q[3] * q[3]) * vecEF[0] +
|
||||
(q[1] * q[2] + q[0] * q[3]) * vecEF[1] +
|
||||
(q[1] * q[3] - q[0] * q[2]) * vecEF[2]);
|
||||
|
||||
vecBF[1] = 2.0f * ((q[1] * q[2] - q[0] * q[3]) * vecEF[0] +
|
||||
(0.5f - q[1] * q[1] - q[3] * q[3]) * vecEF[1] +
|
||||
(q[2] * q[3] + q[0] * q[1]) * vecEF[2]);
|
||||
|
||||
vecBF[2] = 2.0f * ((q[1] * q[3] + q[0] * q[2]) * vecEF[0] +
|
||||
(q[2] * q[3] - q[0] * q[1]) * vecEF[1] +
|
||||
(0.5f - q[1] * q[1] - q[2] * q[2]) * vecEF[2]);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* =============================================================================
|
||||
* ins.h - 惯导姿态解算封装(移植自 RoboMaster-C-Board-INS-Example, Wang Hongxi)
|
||||
* 核心: QuaternionEKF 四元数 EKF——陀螺积分预测 + 加速度重力方向观测,
|
||||
* 在线估计 x/y 陀螺零偏,卡方检验剔除加速度异常(撞击/运动)帧
|
||||
* 对比旧 Mahony 方案: 收敛更快、零偏在线估计(无需上电静止校准)、
|
||||
* 动态加速度干扰抑制更好(卡方检验)
|
||||
* 数据源: 本工程 BMI088 驱动的 bmi088_accel/gyro(SPI DMA 后台更新)
|
||||
* 调用: ins_init() 一次,ins_update(dt) 每 1ms;dt 用固定 0.001
|
||||
* (IMU_Task 为 vTaskDelayUntil 精确 1kHz 节拍,无需 DWT 计时)
|
||||
* ========================================================================== */
|
||||
#ifndef INS_H
|
||||
#define INS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Initialized;
|
||||
|
||||
float Gyro[3]; // rad/s(零偏修正后)
|
||||
float Accel[3]; // m/s^2
|
||||
float q[4]; // 四元数 w x y z
|
||||
|
||||
float Yaw; // -180~180(°)
|
||||
float Pitch; // QEKF 命名(实际为绕 x 轴角,本工程映射见 attitude.c)
|
||||
float Roll; // 同上
|
||||
float YawTotalAngle; // 多圈连续 Yaw(°)
|
||||
|
||||
float GyroBias[3]; // EKF 在线估计的陀螺零偏(rad/s)
|
||||
float MotionAccel_b[3]; // 运动加速度(机体系,去除重力,低通)
|
||||
uint8_t StableFlag; // 1-载体静止且加速度正常(可用于 ZUPT 等扩展)
|
||||
uint8_t ConvergeFlag; // 1-卡方检验收敛(姿态可信)
|
||||
float ChiSquare; // 最近一次卡方检验值(诊断用)
|
||||
uint32_t UpdateCount; // 更新计数(诊断用)
|
||||
} INS_t;
|
||||
|
||||
extern INS_t INS;
|
||||
|
||||
/* 初始化:先做 z 轴静止零偏标定(约 1s 阻塞,车体必须静止——QEKF 的
|
||||
* 卡方观测无法感知 z 偏置,GyroBias[2] 恒为 0,不标定会导致 Yaw 持续漂移),
|
||||
* 再初始化 EKF(参数为 INS 例程 demo 值,调参见 QuaternionEKF.c 头注释) */
|
||||
extern void ins_init(void);
|
||||
|
||||
/* 姿态更新(1kHz 调用,dt 单位秒;内部读 bmi088_accel/gyro 全局量)
|
||||
* 标定完成前直接返回,不更新 */
|
||||
extern void ins_update(float dt);
|
||||
|
||||
/* 1-标定完成且已有有效姿态输出(控制任务以此为门控) */
|
||||
extern uint8_t ins_ready(void);
|
||||
|
||||
/* 坐标系变换工具(机体系 <-> 导航系) */
|
||||
extern void BodyFrameToEarthFrame(const float *vecBF, float *vecEF, float *q);
|
||||
extern void EarthFrameToBodyFrame(const float *vecEF, float *vecBF, float *q);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file kalman filter.c
|
||||
* @author Wang Hongxi
|
||||
* @version V1.2.2
|
||||
* @date 2022/1/8
|
||||
* @brief C implementation of kalman filter
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 该卡尔曼滤波器可以在传感器采样频率不同的情况下,动态调整矩阵H R和K的维数与数值。
|
||||
* This implementation of kalman filter can dynamically adjust dimension and
|
||||
* value of matrix H R and K according to the measurement validity under any
|
||||
* circumstance that the sampling rate of component sensors are different.
|
||||
*
|
||||
* 因此矩阵H和R的初始化会与矩阵P A和Q有所不同。另外的,在初始化量测向量z时需要额外写
|
||||
* 入传感器量测所对应的状态与这个量测的方式,详情请见例程
|
||||
* Therefore, the initialization of matrix P, F, and Q is sometimes different
|
||||
* from that of matrices H R. when initialization. Additionally, the corresponding
|
||||
* state and the method of the measurement should be provided when initializing
|
||||
* measurement vector z. For more details, please see the example.
|
||||
*
|
||||
* 若不需要动态调整量测向量z,可简单将结构体中的Use_Auto_Adjustment初始化为0,并像初
|
||||
* 始化矩阵P那样用常规方式初始化z H R即可。
|
||||
* If automatic adjustment is not required, assign zero to the UseAutoAdjustment
|
||||
* and initialize z H R in the normal way as matrix P.
|
||||
*
|
||||
* 要求量测向量z与控制向量u在传感器回调函数中更新。整数0意味着量测无效,即自上次卡尔曼
|
||||
* 滤波更新后无传感器数据更新。因此量测向量z与控制向量u会在卡尔曼滤波更新过程中被清零
|
||||
* MeasuredVector and ControlVector are required to be updated in the sensor
|
||||
* callback function. Integer 0 in measurement vector z indicates the invalidity
|
||||
* of current measurement, so MeasuredVector and ControlVector will be reset
|
||||
* (to 0) during each update.
|
||||
*
|
||||
* 此外,矩阵P过度收敛后滤波器将难以再适应状态的缓慢变化,从而产生滤波估计偏差。该算法
|
||||
* 通过限制矩阵P最小值的方法,可有效抑制滤波器的过度收敛,详情请见例程。
|
||||
* Additionally, the excessive convergence of matrix P will make filter incapable
|
||||
* of adopting the slowly changing state. This implementation can effectively
|
||||
* suppress filter excessive convergence through boundary limiting for matrix P.
|
||||
* For more details, please see the example.
|
||||
*
|
||||
* @example:
|
||||
* x =
|
||||
* | height |
|
||||
* | velocity |
|
||||
* |acceleration|
|
||||
*
|
||||
* KalmanFilter_t Height_KF;
|
||||
*
|
||||
* void INS_Task_Init(void)
|
||||
* {
|
||||
* static float P_Init[9] =
|
||||
* {
|
||||
* 10, 0, 0,
|
||||
* 0, 30, 0,
|
||||
* 0, 0, 10,
|
||||
* };
|
||||
* static float F_Init[9] =
|
||||
* {
|
||||
* 1, dt, 0.5*dt*dt,
|
||||
* 0, 1, dt,
|
||||
* 0, 0, 1,
|
||||
* };
|
||||
* static float Q_Init[9] =
|
||||
* {
|
||||
* 0.25*dt*dt*dt*dt, 0.5*dt*dt*dt, 0.5*dt*dt,
|
||||
* 0.5*dt*dt*dt, dt*dt, dt,
|
||||
* 0.5*dt*dt, dt, 1,
|
||||
* };
|
||||
*
|
||||
* // 设置最小方差
|
||||
* static float state_min_variance[3] = {0.03, 0.005, 0.1};
|
||||
*
|
||||
* // 开启自动调整
|
||||
* Height_KF.UseAutoAdjustment = 1;
|
||||
*
|
||||
* // 气压测得高度 GPS测得高度 加速度计测得z轴运动加速度
|
||||
* static uint8_t measurement_reference[3] = {1, 1, 3}
|
||||
*
|
||||
* static float measurement_degree[3] = {1, 1, 1}
|
||||
* // 根据measurement_reference与measurement_degree生成H矩阵如下(在当前周期全部测量数据有效情况下)
|
||||
* |1 0 0|
|
||||
* |1 0 0|
|
||||
* |0 0 1|
|
||||
*
|
||||
* static float mat_R_diagonal_elements = {30, 25, 35}
|
||||
* //根据mat_R_diagonal_elements生成R矩阵如下(在当前周期全部测量数据有效情况下)
|
||||
* |30 0 0|
|
||||
* | 0 25 0|
|
||||
* | 0 0 35|
|
||||
*
|
||||
* Kalman_Filter_Init(&Height_KF, 3, 0, 3);
|
||||
*
|
||||
* // 设置矩阵值
|
||||
* memcpy(Height_KF.P_data, P_Init, sizeof(P_Init));
|
||||
* memcpy(Height_KF.F_data, F_Init, sizeof(F_Init));
|
||||
* memcpy(Height_KF.Q_data, Q_Init, sizeof(Q_Init));
|
||||
* memcpy(Height_KF.MeasurementMap, measurement_reference, sizeof(measurement_reference));
|
||||
* memcpy(Height_KF.MeasurementDegree, measurement_degree, sizeof(measurement_degree));
|
||||
* memcpy(Height_KF.MatR_DiagonalElements, mat_R_diagonal_elements, sizeof(mat_R_diagonal_elements));
|
||||
* memcpy(Height_KF.StateMinVariance, state_min_variance, sizeof(state_min_variance));
|
||||
* }
|
||||
*
|
||||
* void INS_Task(void const *pvParameters)
|
||||
* {
|
||||
* // 循环更新
|
||||
* Kalman_Filter_Update(&Height_KF);
|
||||
* vTaskDelay(ts);
|
||||
* }
|
||||
*
|
||||
* // 测量数据更新应按照以下形式 即向MeasuredVector赋值
|
||||
* void Barometer_Read_Over(void)
|
||||
* {
|
||||
* ......
|
||||
* INS_KF.MeasuredVector[0] = baro_height;
|
||||
* }
|
||||
* void GPS_Read_Over(void)
|
||||
* {
|
||||
* ......
|
||||
* INS_KF.MeasuredVector[1] = GPS_height;
|
||||
* }
|
||||
* void Acc_Data_Process(void)
|
||||
* {
|
||||
* ......
|
||||
* INS_KF.MeasuredVector[2] = acc.z;
|
||||
* }
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "kalman_filter.h"
|
||||
|
||||
uint16_t sizeof_float, sizeof_double;
|
||||
|
||||
static void H_K_R_Adjustment(KalmanFilter_t *kf);
|
||||
|
||||
/**
|
||||
* @brief 初始化矩阵维度信息并为矩阵分配空间
|
||||
*
|
||||
* @param kf kf类型定义
|
||||
* @param xhatSize 状态变量维度
|
||||
* @param uSize 控制变量维度
|
||||
* @param zSize 观测量维度
|
||||
*/
|
||||
void Kalman_Filter_Init(KalmanFilter_t *kf, uint8_t xhatSize, uint8_t uSize, uint8_t zSize)
|
||||
{
|
||||
sizeof_float = sizeof(float);
|
||||
sizeof_double = sizeof(double);
|
||||
|
||||
kf->xhatSize = xhatSize;
|
||||
kf->uSize = uSize;
|
||||
kf->zSize = zSize;
|
||||
|
||||
kf->MeasurementValidNum = 0;
|
||||
|
||||
// measurement flags
|
||||
kf->MeasurementMap = (uint8_t *)user_malloc(sizeof(uint8_t) * zSize);
|
||||
memset(kf->MeasurementMap, 0, sizeof(uint8_t) * zSize);
|
||||
kf->MeasurementDegree = (float *)user_malloc(sizeof_float * zSize);
|
||||
memset(kf->MeasurementDegree, 0, sizeof_float * zSize);
|
||||
kf->MatR_DiagonalElements = (float *)user_malloc(sizeof_float * zSize);
|
||||
memset(kf->MatR_DiagonalElements, 0, sizeof_float * zSize);
|
||||
kf->StateMinVariance = (float *)user_malloc(sizeof_float * xhatSize);
|
||||
memset(kf->StateMinVariance, 0, sizeof_float * xhatSize);
|
||||
kf->temp = (uint8_t *)user_malloc(sizeof(uint8_t) * zSize);
|
||||
memset(kf->temp, 0, sizeof(uint8_t) * zSize);
|
||||
|
||||
// filter data
|
||||
kf->FilteredValue = (float *)user_malloc(sizeof_float * xhatSize);
|
||||
memset(kf->FilteredValue, 0, sizeof_float * xhatSize);
|
||||
kf->MeasuredVector = (float *)user_malloc(sizeof_float * zSize);
|
||||
memset(kf->MeasuredVector, 0, sizeof_float * zSize);
|
||||
kf->ControlVector = (float *)user_malloc(sizeof_float * uSize);
|
||||
memset(kf->ControlVector, 0, sizeof_float * uSize);
|
||||
|
||||
// xhat x(k|k)
|
||||
kf->xhat_data = (float *)user_malloc(sizeof_float * xhatSize);
|
||||
memset(kf->xhat_data, 0, sizeof_float * xhatSize);
|
||||
Matrix_Init(&kf->xhat, kf->xhatSize, 1, (float *)kf->xhat_data);
|
||||
|
||||
// xhatminus x(k|k-1)
|
||||
kf->xhatminus_data = (float *)user_malloc(sizeof_float * xhatSize);
|
||||
memset(kf->xhatminus_data, 0, sizeof_float * xhatSize);
|
||||
Matrix_Init(&kf->xhatminus, kf->xhatSize, 1, (float *)kf->xhatminus_data);
|
||||
|
||||
if (uSize != 0)
|
||||
{
|
||||
// control vector u
|
||||
kf->u_data = (float *)user_malloc(sizeof_float * uSize);
|
||||
memset(kf->u_data, 0, sizeof_float * uSize);
|
||||
Matrix_Init(&kf->u, kf->uSize, 1, (float *)kf->u_data);
|
||||
}
|
||||
|
||||
// measurement vector z
|
||||
kf->z_data = (float *)user_malloc(sizeof_float * zSize);
|
||||
memset(kf->z_data, 0, sizeof_float * zSize);
|
||||
Matrix_Init(&kf->z, kf->zSize, 1, (float *)kf->z_data);
|
||||
|
||||
// covariance matrix P(k|k)
|
||||
kf->P_data = (float *)user_malloc(sizeof_float * xhatSize * xhatSize);
|
||||
memset(kf->P_data, 0, sizeof_float * xhatSize * xhatSize);
|
||||
Matrix_Init(&kf->P, kf->xhatSize, kf->xhatSize, (float *)kf->P_data);
|
||||
|
||||
// create covariance matrix P(k|k-1)
|
||||
kf->Pminus_data = (float *)user_malloc(sizeof_float * xhatSize * xhatSize);
|
||||
memset(kf->Pminus_data, 0, sizeof_float * xhatSize * xhatSize);
|
||||
Matrix_Init(&kf->Pminus, kf->xhatSize, kf->xhatSize, (float *)kf->Pminus_data);
|
||||
|
||||
// state transition matrix F FT
|
||||
kf->F_data = (float *)user_malloc(sizeof_float * xhatSize * xhatSize);
|
||||
kf->FT_data = (float *)user_malloc(sizeof_float * xhatSize * xhatSize);
|
||||
memset(kf->F_data, 0, sizeof_float * xhatSize * xhatSize);
|
||||
memset(kf->FT_data, 0, sizeof_float * xhatSize * xhatSize);
|
||||
Matrix_Init(&kf->F, kf->xhatSize, kf->xhatSize, (float *)kf->F_data);
|
||||
Matrix_Init(&kf->FT, kf->xhatSize, kf->xhatSize, (float *)kf->FT_data);
|
||||
|
||||
if (uSize != 0)
|
||||
{
|
||||
// control matrix B
|
||||
kf->B_data = (float *)user_malloc(sizeof_float * xhatSize * uSize);
|
||||
memset(kf->B_data, 0, sizeof_float * xhatSize * uSize);
|
||||
Matrix_Init(&kf->B, kf->xhatSize, kf->uSize, (float *)kf->B_data);
|
||||
}
|
||||
|
||||
// measurement matrix H
|
||||
kf->H_data = (float *)user_malloc(sizeof_float * zSize * xhatSize);
|
||||
kf->HT_data = (float *)user_malloc(sizeof_float * xhatSize * zSize);
|
||||
memset(kf->H_data, 0, sizeof_float * zSize * xhatSize);
|
||||
memset(kf->HT_data, 0, sizeof_float * xhatSize * zSize);
|
||||
Matrix_Init(&kf->H, kf->zSize, kf->xhatSize, (float *)kf->H_data);
|
||||
Matrix_Init(&kf->HT, kf->xhatSize, kf->zSize, (float *)kf->HT_data);
|
||||
|
||||
// process noise covariance matrix Q
|
||||
kf->Q_data = (float *)user_malloc(sizeof_float * xhatSize * xhatSize);
|
||||
memset(kf->Q_data, 0, sizeof_float * xhatSize * xhatSize);
|
||||
Matrix_Init(&kf->Q, kf->xhatSize, kf->xhatSize, (float *)kf->Q_data);
|
||||
|
||||
// measurement noise covariance matrix R
|
||||
kf->R_data = (float *)user_malloc(sizeof_float * zSize * zSize);
|
||||
memset(kf->R_data, 0, sizeof_float * zSize * zSize);
|
||||
Matrix_Init(&kf->R, kf->zSize, kf->zSize, (float *)kf->R_data);
|
||||
|
||||
// kalman gain K
|
||||
kf->K_data = (float *)user_malloc(sizeof_float * xhatSize * zSize);
|
||||
memset(kf->K_data, 0, sizeof_float * xhatSize * zSize);
|
||||
Matrix_Init(&kf->K, kf->xhatSize, kf->zSize, (float *)kf->K_data);
|
||||
|
||||
kf->S_data = (float *)user_malloc(sizeof_float * kf->xhatSize * kf->xhatSize);
|
||||
kf->temp_matrix_data = (float *)user_malloc(sizeof_float * kf->xhatSize * kf->xhatSize);
|
||||
kf->temp_matrix_data1 = (float *)user_malloc(sizeof_float * kf->xhatSize * kf->xhatSize);
|
||||
kf->temp_vector_data = (float *)user_malloc(sizeof_float * kf->xhatSize);
|
||||
kf->temp_vector_data1 = (float *)user_malloc(sizeof_float * kf->xhatSize);
|
||||
Matrix_Init(&kf->S, kf->xhatSize, kf->xhatSize, (float *)kf->S_data);
|
||||
Matrix_Init(&kf->temp_matrix, kf->xhatSize, kf->xhatSize, (float *)kf->temp_matrix_data);
|
||||
Matrix_Init(&kf->temp_matrix1, kf->xhatSize, kf->xhatSize, (float *)kf->temp_matrix_data1);
|
||||
Matrix_Init(&kf->temp_vector, kf->xhatSize, 1, (float *)kf->temp_vector_data);
|
||||
Matrix_Init(&kf->temp_vector1, kf->xhatSize, 1, (float *)kf->temp_vector_data1);
|
||||
|
||||
kf->SkipEq1 = 0;
|
||||
kf->SkipEq2 = 0;
|
||||
kf->SkipEq3 = 0;
|
||||
kf->SkipEq4 = 0;
|
||||
kf->SkipEq5 = 0;
|
||||
}
|
||||
|
||||
void Kalman_Filter_Measure(KalmanFilter_t *kf)
|
||||
{
|
||||
// 矩阵H K R根据量测情况自动调整
|
||||
// matrix H K R auto adjustment
|
||||
if (kf->UseAutoAdjustment != 0)
|
||||
H_K_R_Adjustment(kf);
|
||||
else
|
||||
{
|
||||
memcpy(kf->z_data, kf->MeasuredVector, sizeof_float * kf->zSize);
|
||||
memset(kf->MeasuredVector, 0, sizeof_float * kf->zSize);
|
||||
}
|
||||
|
||||
memcpy(kf->u_data, kf->ControlVector, sizeof_float * kf->uSize);
|
||||
}
|
||||
|
||||
void Kalman_Filter_xhatMinusUpdate(KalmanFilter_t *kf)
|
||||
{
|
||||
if (!kf->SkipEq1)
|
||||
{
|
||||
if (kf->uSize > 0)
|
||||
{
|
||||
kf->temp_vector.numRows = kf->xhatSize;
|
||||
kf->temp_vector.numCols = 1;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->F, &kf->xhat, &kf->temp_vector);
|
||||
kf->temp_vector1.numRows = kf->xhatSize;
|
||||
kf->temp_vector1.numCols = 1;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->B, &kf->u, &kf->temp_vector1);
|
||||
kf->MatStatus = Matrix_Add(&kf->temp_vector, &kf->temp_vector1, &kf->xhatminus);
|
||||
}
|
||||
else
|
||||
{
|
||||
kf->MatStatus = Matrix_Multiply(&kf->F, &kf->xhat, &kf->xhatminus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Kalman_Filter_PminusUpdate(KalmanFilter_t *kf)
|
||||
{
|
||||
if (!kf->SkipEq2)
|
||||
{
|
||||
kf->MatStatus = Matrix_Transpose(&kf->F, &kf->FT);
|
||||
kf->MatStatus = Matrix_Multiply(&kf->F, &kf->P, &kf->Pminus);
|
||||
kf->temp_matrix.numRows = kf->Pminus.numRows;
|
||||
kf->temp_matrix.numCols = kf->FT.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->Pminus, &kf->FT, &kf->temp_matrix); // temp_matrix = F P(k-1) FT
|
||||
kf->MatStatus = Matrix_Add(&kf->temp_matrix, &kf->Q, &kf->Pminus);
|
||||
}
|
||||
}
|
||||
void Kalman_Filter_SetK(KalmanFilter_t *kf)
|
||||
{
|
||||
if (!kf->SkipEq3)
|
||||
{
|
||||
kf->MatStatus = Matrix_Transpose(&kf->H, &kf->HT); // z|x => x|z
|
||||
kf->temp_matrix.numRows = kf->H.numRows;
|
||||
kf->temp_matrix.numCols = kf->Pminus.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->H, &kf->Pminus, &kf->temp_matrix); // temp_matrix = H·P'(k)
|
||||
kf->temp_matrix1.numRows = kf->temp_matrix.numRows;
|
||||
kf->temp_matrix1.numCols = kf->HT.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_matrix, &kf->HT, &kf->temp_matrix1); // temp_matrix1 = H·P'(k)·HT
|
||||
kf->S.numRows = kf->R.numRows;
|
||||
kf->S.numCols = kf->R.numCols;
|
||||
kf->MatStatus = Matrix_Add(&kf->temp_matrix1, &kf->R, &kf->S); // S = H P'(k) HT + R
|
||||
kf->MatStatus = Matrix_Inverse(&kf->S, &kf->temp_matrix1); // temp_matrix1 = inv(H·P'(k)·HT + R)
|
||||
kf->temp_matrix.numRows = kf->Pminus.numRows;
|
||||
kf->temp_matrix.numCols = kf->HT.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->Pminus, &kf->HT, &kf->temp_matrix); // temp_matrix = P'(k)·HT
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_matrix, &kf->temp_matrix1, &kf->K);
|
||||
}
|
||||
}
|
||||
void Kalman_Filter_xhatUpdate(KalmanFilter_t *kf)
|
||||
{
|
||||
if (!kf->SkipEq4)
|
||||
{
|
||||
kf->temp_vector.numRows = kf->H.numRows;
|
||||
kf->temp_vector.numCols = 1;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->H, &kf->xhatminus, &kf->temp_vector); // temp_vector = H xhat'(k)
|
||||
kf->temp_vector1.numRows = kf->z.numRows;
|
||||
kf->temp_vector1.numCols = 1;
|
||||
kf->MatStatus = Matrix_Subtract(&kf->z, &kf->temp_vector, &kf->temp_vector1); // temp_vector1 = z(k) - H·xhat'(k)
|
||||
kf->temp_vector.numRows = kf->K.numRows;
|
||||
kf->temp_vector.numCols = 1;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->K, &kf->temp_vector1, &kf->temp_vector); // temp_vector = K(k)·(z(k) - H·xhat'(k))
|
||||
kf->MatStatus = Matrix_Add(&kf->xhatminus, &kf->temp_vector, &kf->xhat);
|
||||
}
|
||||
}
|
||||
void Kalman_Filter_P_Update(KalmanFilter_t *kf)
|
||||
{
|
||||
if (!kf->SkipEq5)
|
||||
{
|
||||
kf->temp_matrix.numRows = kf->K.numRows;
|
||||
kf->temp_matrix.numCols = kf->H.numCols;
|
||||
kf->temp_matrix1.numRows = kf->temp_matrix.numRows;
|
||||
kf->temp_matrix1.numCols = kf->Pminus.numCols;
|
||||
kf->MatStatus = Matrix_Multiply(&kf->K, &kf->H, &kf->temp_matrix); // temp_matrix = K(k)·H
|
||||
kf->MatStatus = Matrix_Multiply(&kf->temp_matrix, &kf->Pminus, &kf->temp_matrix1); // temp_matrix1 = K(k)·H·P'(k)
|
||||
kf->MatStatus = Matrix_Subtract(&kf->Pminus, &kf->temp_matrix1, &kf->P);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行卡尔曼滤波黄金五式,提供了用户定义函数,可以替代五个中的任意一个环节,方便自行扩展为EKF/UKF/ESKF/AUKF等
|
||||
*
|
||||
* @param kf kf类型定义
|
||||
* @return float* 返回滤波值
|
||||
*/
|
||||
float *Kalman_Filter_Update(KalmanFilter_t *kf)
|
||||
{
|
||||
// 0. 获取量测信息
|
||||
Kalman_Filter_Measure(kf);
|
||||
if (kf->User_Func0_f != NULL)
|
||||
kf->User_Func0_f(kf);
|
||||
|
||||
// 先验估计
|
||||
// 1. xhat'(k)= A·xhat(k-1) + B·u
|
||||
Kalman_Filter_xhatMinusUpdate(kf);
|
||||
if (kf->User_Func1_f != NULL)
|
||||
kf->User_Func1_f(kf);
|
||||
|
||||
// 预测更新
|
||||
// 2. P'(k) = A·P(k-1)·AT + Q
|
||||
Kalman_Filter_PminusUpdate(kf);
|
||||
if (kf->User_Func2_f != NULL)
|
||||
kf->User_Func2_f(kf);
|
||||
|
||||
if (kf->MeasurementValidNum != 0 || kf->UseAutoAdjustment == 0)
|
||||
{
|
||||
// 量测更新
|
||||
// 3. K(k) = P'(k)·HT / (H·P'(k)·HT + R)
|
||||
Kalman_Filter_SetK(kf);
|
||||
|
||||
if (kf->User_Func3_f != NULL)
|
||||
kf->User_Func3_f(kf);
|
||||
|
||||
// 融合
|
||||
// 4. xhat(k) = xhat'(k) + K(k)·(z(k) - H·xhat'(k))
|
||||
Kalman_Filter_xhatUpdate(kf);
|
||||
|
||||
if (kf->User_Func4_f != NULL)
|
||||
kf->User_Func4_f(kf);
|
||||
|
||||
// 修正方差
|
||||
// 5. P(k) = (1-K(k)·H)·P'(k) ==> P(k) = P'(k)-K(k)·H·P'(k)
|
||||
Kalman_Filter_P_Update(kf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 无有效量测,仅预测
|
||||
// xhat(k) = xhat'(k)
|
||||
// P(k) = P'(k)
|
||||
memcpy(kf->xhat_data, kf->xhatminus_data, sizeof_float * kf->xhatSize);
|
||||
memcpy(kf->P_data, kf->Pminus_data, sizeof_float * kf->xhatSize * kf->xhatSize);
|
||||
}
|
||||
|
||||
// 自定义函数,可以提供后处理等
|
||||
if (kf->User_Func5_f != NULL)
|
||||
kf->User_Func5_f(kf);
|
||||
|
||||
// 避免滤波器过度收敛
|
||||
// suppress filter excessive convergence
|
||||
for (uint8_t i = 0; i < kf->xhatSize; i++)
|
||||
{
|
||||
if (kf->P_data[i * kf->xhatSize + i] < kf->StateMinVariance[i])
|
||||
kf->P_data[i * kf->xhatSize + i] = kf->StateMinVariance[i];
|
||||
}
|
||||
|
||||
memcpy(kf->FilteredValue, kf->xhat_data, sizeof_float * kf->xhatSize);
|
||||
|
||||
if (kf->User_Func6_f != NULL)
|
||||
kf->User_Func6_f(kf);
|
||||
|
||||
return kf->FilteredValue;
|
||||
}
|
||||
|
||||
static void H_K_R_Adjustment(KalmanFilter_t *kf)
|
||||
{
|
||||
kf->MeasurementValidNum = 0;
|
||||
|
||||
memcpy(kf->z_data, kf->MeasuredVector, sizeof_float * kf->zSize);
|
||||
memset(kf->MeasuredVector, 0, sizeof_float * kf->zSize);
|
||||
|
||||
// 识别量测数据有效性并调整矩阵H R K
|
||||
// recognize measurement validity and adjust matrices H R K
|
||||
memset(kf->R_data, 0, sizeof_float * kf->zSize * kf->zSize);
|
||||
memset(kf->H_data, 0, sizeof_float * kf->xhatSize * kf->zSize);
|
||||
for (uint8_t i = 0; i < kf->zSize; i++)
|
||||
{
|
||||
if (kf->z_data[i] != 0)
|
||||
{
|
||||
// 重构向量z
|
||||
// rebuild vector z
|
||||
kf->z_data[kf->MeasurementValidNum] = kf->z_data[i];
|
||||
kf->temp[kf->MeasurementValidNum] = i;
|
||||
// 重构矩阵H
|
||||
// rebuild matrix H
|
||||
kf->H_data[kf->xhatSize * kf->MeasurementValidNum + kf->MeasurementMap[i] - 1] = kf->MeasurementDegree[i];
|
||||
kf->MeasurementValidNum++;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < kf->MeasurementValidNum; i++)
|
||||
{
|
||||
// 重构矩阵R
|
||||
// rebuild matrix R
|
||||
kf->R_data[i * kf->MeasurementValidNum + i] = kf->MatR_DiagonalElements[kf->temp[i]];
|
||||
}
|
||||
|
||||
// 调整矩阵维数
|
||||
// adjust the dimensions of system matrices
|
||||
kf->H.numRows = kf->MeasurementValidNum;
|
||||
kf->H.numCols = kf->xhatSize;
|
||||
kf->HT.numRows = kf->xhatSize;
|
||||
kf->HT.numCols = kf->MeasurementValidNum;
|
||||
kf->R.numRows = kf->MeasurementValidNum;
|
||||
kf->R.numCols = kf->MeasurementValidNum;
|
||||
kf->K.numRows = kf->xhatSize;
|
||||
kf->K.numCols = kf->MeasurementValidNum;
|
||||
kf->z.numRows = kf->MeasurementValidNum;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file kalman filter.h
|
||||
* @author Wang Hongxi
|
||||
* @version V1.2.2
|
||||
* @date 2022/1/8
|
||||
* @brief
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef __KALMAN_FILTER_H
|
||||
#define __KALMAN_FILTER_H
|
||||
|
||||
// cortex-m4 DSP lib
|
||||
/*
|
||||
#define __CC_ARM // Keil
|
||||
#define ARM_MATH_CM4
|
||||
#define ARM_MATH_MATRIX_CHECK
|
||||
#define ARM_MATH_ROUNDING
|
||||
#define ARM_MATH_DSP // define in arm_math.h
|
||||
*/
|
||||
|
||||
#include "stm32f407xx.h"
|
||||
#include "arm_math.h"
|
||||
//#include "dsp/matrix_functions.h"
|
||||
#include "math.h"
|
||||
#include "stdint.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
#ifndef user_malloc
|
||||
#ifdef _CMSIS_OS_H
|
||||
#define user_malloc pvPortMalloc
|
||||
#else
|
||||
#define user_malloc malloc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define mat arm_matrix_instance_f32
|
||||
#define Matrix_Init arm_mat_init_f32
|
||||
#define Matrix_Add arm_mat_add_f32
|
||||
#define Matrix_Subtract arm_mat_sub_f32
|
||||
#define Matrix_Multiply arm_mat_mult_f32
|
||||
#define Matrix_Transpose arm_mat_trans_f32
|
||||
#define Matrix_Inverse arm_mat_inverse_f32
|
||||
|
||||
typedef struct kf_t
|
||||
{
|
||||
float *FilteredValue;
|
||||
float *MeasuredVector;
|
||||
float *ControlVector;
|
||||
|
||||
uint8_t xhatSize;
|
||||
uint8_t uSize;
|
||||
uint8_t zSize;
|
||||
|
||||
uint8_t UseAutoAdjustment;
|
||||
uint8_t MeasurementValidNum;
|
||||
|
||||
uint8_t *MeasurementMap; // 量测与状态的关系 how measurement relates to the state
|
||||
float *MeasurementDegree; // 测量值对应H矩阵元素值 elements of each measurement in H
|
||||
float *MatR_DiagonalElements; // 量测方差 variance for each measurement
|
||||
float *StateMinVariance; // 最小方差 避免方差过度收敛 suppress filter excessive convergence
|
||||
uint8_t *temp;
|
||||
|
||||
// 配合用户定义函数使用,作为标志位用于判断是否要跳过标准KF中五个环节中的任意一个
|
||||
uint8_t SkipEq1, SkipEq2, SkipEq3, SkipEq4, SkipEq5;
|
||||
|
||||
// definiion of struct mat: rows & cols & pointer to vars
|
||||
mat xhat; // x(k|k)
|
||||
mat xhatminus; // x(k|k-1)
|
||||
mat u; // control vector u
|
||||
mat z; // measurement vector z
|
||||
mat P; // covariance matrix P(k|k)
|
||||
mat Pminus; // covariance matrix P(k|k-1)
|
||||
mat F, FT; // state transition matrix F FT
|
||||
mat B; // control matrix B
|
||||
mat H, HT; // measurement matrix H
|
||||
mat Q; // process noise covariance matrix Q
|
||||
mat R; // measurement noise covariance matrix R
|
||||
mat K; // kalman gain K
|
||||
mat S, temp_matrix, temp_matrix1, temp_vector, temp_vector1;
|
||||
|
||||
int8_t MatStatus;
|
||||
|
||||
// 用户定义函数,可以替换或扩展基准KF的功能
|
||||
void (*User_Func0_f)(struct kf_t *kf);
|
||||
void (*User_Func1_f)(struct kf_t *kf);
|
||||
void (*User_Func2_f)(struct kf_t *kf);
|
||||
void (*User_Func3_f)(struct kf_t *kf);
|
||||
void (*User_Func4_f)(struct kf_t *kf);
|
||||
void (*User_Func5_f)(struct kf_t *kf);
|
||||
void (*User_Func6_f)(struct kf_t *kf);
|
||||
|
||||
// 矩阵存储空间指针
|
||||
float *xhat_data, *xhatminus_data;
|
||||
float *u_data;
|
||||
float *z_data;
|
||||
float *P_data, *Pminus_data;
|
||||
float *F_data, *FT_data;
|
||||
float *B_data;
|
||||
float *H_data, *HT_data;
|
||||
float *Q_data;
|
||||
float *R_data;
|
||||
float *K_data;
|
||||
float *S_data, *temp_matrix_data, *temp_matrix_data1, *temp_vector_data, *temp_vector_data1;
|
||||
} KalmanFilter_t;
|
||||
|
||||
extern uint16_t sizeof_float, sizeof_double;
|
||||
|
||||
void Kalman_Filter_Init(KalmanFilter_t *kf, uint8_t xhatSize, uint8_t uSize, uint8_t zSize);
|
||||
void Kalman_Filter_Measure(KalmanFilter_t *kf);
|
||||
void Kalman_Filter_xhatMinusUpdate(KalmanFilter_t *kf);
|
||||
void Kalman_Filter_PminusUpdate(KalmanFilter_t *kf);
|
||||
void Kalman_Filter_SetK(KalmanFilter_t *kf);
|
||||
void Kalman_Filter_xhatUpdate(KalmanFilter_t *kf);
|
||||
void Kalman_Filter_P_Update(KalmanFilter_t *kf);
|
||||
float *Kalman_Filter_Update(KalmanFilter_t *kf);
|
||||
|
||||
#endif //__KALMAN_FILTER_H
|
||||
Reference in New Issue
Block a user