1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-23 16:25:26 +03:00

Gyro Kalman filter from EmuFlight (#5519)

* Direct copy from EmuFlight, compiles, does not wrk

* Fix Kalman computation

* Catchup on Emu implementation

* Make Q, W and Sharpness configurable

* Settings for Kalman Q, W and Sharpness

* Make it possible to enable/disable Kalman filter

* Change scaling to make initial values simpler

* Change Kalman constrains

* Compute real variance

* Drop unused function parameter

* Improve EKF processing for gyro
This commit is contained in:
Paweł Spychalski 2020-06-11 13:46:04 +02:00 committed by GitHub
parent 839a877397
commit 706da4aef2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 230 additions and 2 deletions

View file

@ -104,6 +104,7 @@ COMMON_SRC = \
flight/gyroanalyse.c \ flight/gyroanalyse.c \
flight/rpm_filter.c \ flight/rpm_filter.c \
flight/dynamic_gyro_notch.c \ flight/dynamic_gyro_notch.c \
flight/kalman.c \
io/beeper.c \ io/beeper.c \
io/esc_serialshot.c \ io/esc_serialshot.c \
io/servo_sbus.c \ io/servo_sbus.c \

View file

@ -72,5 +72,6 @@ typedef enum {
DEBUG_DYNAMIC_FILTER_FREQUENCY, DEBUG_DYNAMIC_FILTER_FREQUENCY,
DEBUG_IRLOCK, DEBUG_IRLOCK,
DEBUG_CD, DEBUG_CD,
DEBUG_KALMAN,
DEBUG_COUNT DEBUG_COUNT
} debugType_e; } debugType_e;

View file

@ -84,7 +84,7 @@ tables:
"FLOW", "SBUS", "FPORT", "ALWAYS", "SAG_COMP_VOLTAGE", "FLOW", "SBUS", "FPORT", "ALWAYS", "SAG_COMP_VOLTAGE",
"VIBE", "CRUISE", "REM_FLIGHT_TIME", "SMARTAUDIO", "ACC", "ITERM_RELAX", "VIBE", "CRUISE", "REM_FLIGHT_TIME", "SMARTAUDIO", "ACC", "ITERM_RELAX",
"ERPM", "RPM_FILTER", "RPM_FREQ", "NAV_YAW", "DYNAMIC_FILTER", "DYNAMIC_FILTER_FREQUENCY", "ERPM", "RPM_FILTER", "RPM_FREQ", "NAV_YAW", "DYNAMIC_FILTER", "DYNAMIC_FILTER_FREQUENCY",
"IRLOCK", "CD"] "IRLOCK", "CD", "KALMAN"]
- name: async_mode - name: async_mode
values: ["NONE", "GYRO", "ALL"] values: ["NONE", "GYRO", "ALL"]
- name: aux_operator - name: aux_operator
@ -203,6 +203,25 @@ groups:
condition: USE_DYNAMIC_FILTERS condition: USE_DYNAMIC_FILTERS
min: 30 min: 30
max: 1000 max: 1000
- name: gyro_kalman_enabled
condition: USE_GYRO_KALMAN
field: kalmanEnabled
type: bool
- name: gyro_kalman_q
field: kalman_q
condition: USE_GYRO_KALMAN
min: 1
max: 16000
- name: gyro_kalman_w
field: kalman_w
condition: USE_GYRO_KALMAN
min: 1
max: 40
- name: gyro_kalman_sharpness
field: kalman_sharpness
condition: USE_GYRO_KALMAN
min: 1
max: 16000
- name: gyro_to_use - name: gyro_to_use
condition: USE_DUAL_GYRO condition: USE_DUAL_GYRO
min: 0 min: 0

126
src/main/flight/kalman.c Executable file
View file

@ -0,0 +1,126 @@
/*
* This file is part of Cleanflight and Betaflight.
*
* Cleanflight and Betaflight are free software. You can redistribute
* this software and/or modify this software under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Cleanflight and Betaflight are distributed in the hope that they
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform.h"
#ifdef USE_GYRO_KALMAN
FILE_COMPILE_FOR_SPEED
#include <string.h>
#include "arm_math.h"
#include "kalman.h"
#include "build/debug.h"
kalman_t kalmanFilterStateRate[XYZ_AXIS_COUNT];
float setPoint[XYZ_AXIS_COUNT];
static void gyroKalmanInitAxis(kalman_t *filter)
{
memset(filter, 0, sizeof(kalman_t));
filter->q = gyroConfig()->kalman_q * 0.03f; //add multiplier to make tuning easier
filter->r = 88.0f; //seeding R at 88.0f
filter->p = 30.0f; //seeding P at 30.0f
filter->e = 1.0f;
filter->s = gyroConfig()->kalman_sharpness / 10.0f;
filter->w = gyroConfig()->kalman_w * 8;
filter->inverseN = 1.0f / (float)(filter->w);
}
void gyroKalmanSetSetpoint(uint8_t axis, float rate)
{
setPoint[axis] = rate;
}
void gyroKalmanInitialize(void)
{
gyroKalmanInitAxis(&kalmanFilterStateRate[X]);
gyroKalmanInitAxis(&kalmanFilterStateRate[Y]);
gyroKalmanInitAxis(&kalmanFilterStateRate[Z]);
}
float kalman_process(kalman_t *kalmanState, float input, float target)
{
float targetAbs = fabsf(target);
//project the state ahead using acceleration
kalmanState->x += (kalmanState->x - kalmanState->lastX);
//figure out how much to boost or reduce our error in the estimate based on setpoint target.
//this should be close to 0 as we approach the sepoint and really high the futher away we are from the setpoint.
//update last state
kalmanState->lastX = kalmanState->x;
if (kalmanState->lastX != 0.0f)
{
// calculate the error and add multiply sharpness boost
float errorMultiplier = fabsf(target - kalmanState->x) * kalmanState->s;
// give a boost to the setpoint, used to caluclate the kalman q, based on the error and setpoint/gyrodata
errorMultiplier = constrainf(errorMultiplier * fabsf(1.0f - (target / kalmanState->lastX)) + 1.0f, 1.0f, 50.0f);
kalmanState->e = fabsf(1.0f - (((targetAbs + 1.0f) * errorMultiplier) / fabsf(kalmanState->lastX)));
}
//prediction update
kalmanState->p = kalmanState->p + (kalmanState->q * kalmanState->e);
//measurement update
kalmanState->k = kalmanState->p / (kalmanState->p + kalmanState->r);
kalmanState->x += kalmanState->k * (input - kalmanState->x);
kalmanState->p = (1.0f - kalmanState->k) * kalmanState->p;
return kalmanState->x;
}
static void updateAxisVariance(kalman_t *kalmanState, float rate)
{
kalmanState->axisWindow[kalmanState->windex] = rate;
kalmanState->axisSumMean += kalmanState->axisWindow[kalmanState->windex];
float varianceElement = kalmanState->axisWindow[kalmanState->windex] - kalmanState->axisMean;
varianceElement = varianceElement * varianceElement;
kalmanState->axisSumVar += varianceElement;
kalmanState->varianceWindow[kalmanState->windex] = varianceElement;
kalmanState->windex++;
if (kalmanState->windex >= kalmanState->w) {
kalmanState->windex = 0;
}
kalmanState->axisSumMean -= kalmanState->axisWindow[kalmanState->windex];
kalmanState->axisSumVar -= kalmanState->varianceWindow[kalmanState->windex];
//New mean
kalmanState->axisMean = kalmanState->axisSumMean * kalmanState->inverseN;
kalmanState->axisVar = kalmanState->axisSumVar * kalmanState->inverseN;
float squirt;
arm_sqrt_f32(kalmanState->axisVar, &squirt);
kalmanState->r = squirt * VARIANCE_SCALE;
}
float gyroKalmanUpdate(uint8_t axis, float input)
{
updateAxisVariance(&kalmanFilterStateRate[axis], input);
DEBUG_SET(DEBUG_KALMAN, axis, kalmanFilterStateRate[axis].k * 1000.0f); //Kalman gain
return kalman_process(&kalmanFilterStateRate[axis], input, setPoint[axis]);
}
#endif

54
src/main/flight/kalman.h Executable file
View file

@ -0,0 +1,54 @@
/*
* This file is part of Cleanflight and Betaflight.
*
* Cleanflight and Betaflight are free software. You can redistribute
* this software and/or modify this software under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Cleanflight and Betaflight are distributed in the hope that they
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "sensors/gyro.h"
#include "common/filter.h"
#define MAX_KALMAN_WINDOW_SIZE 512
#define VARIANCE_SCALE 0.67f
typedef struct kalman
{
float q; //process noise covariance
float r; //measurement noise covariance
float p; //estimation error covariance matrix
float k; //kalman gain
float x; //state
float lastX; //previous state
float e;
float s;
float axisVar;
uint16_t windex;
float axisWindow[MAX_KALMAN_WINDOW_SIZE];
float varianceWindow[MAX_KALMAN_WINDOW_SIZE];
float axisSumMean;
float axisMean;
float axisSumVar;
float inverseN;
uint16_t w;
} kalman_t;
void gyroKalmanInitialize(void);
float gyroKalmanUpdate(uint8_t axis, float input);
void gyroKalmanSetSetpoint(uint8_t axis, float rate);

View file

@ -44,6 +44,7 @@ FILE_COMPILE_FOR_SPEED
#include "flight/imu.h" #include "flight/imu.h"
#include "flight/mixer.h" #include "flight/mixer.h"
#include "flight/rpm_filter.h" #include "flight/rpm_filter.h"
#include "flight/kalman.h"
#include "io/gps.h" #include "io/gps.h"
@ -966,6 +967,9 @@ void FAST_CODE pidController(float dT)
// Limit desired rate to something gyro can measure reliably // Limit desired rate to something gyro can measure reliably
pidState[axis].rateTarget = constrainf(rateTarget, -GYRO_SATURATION_LIMIT, +GYRO_SATURATION_LIMIT); pidState[axis].rateTarget = constrainf(rateTarget, -GYRO_SATURATION_LIMIT, +GYRO_SATURATION_LIMIT);
#ifdef USE_GYRO_KALMAN
gyroKalmanSetSetpoint(axis, pidState[axis].rateTarget);
#endif
} }
// Step 3: Run control for ANGLE_MODE, HORIZON_MODE, and HEADING_LOCK // Step 3: Run control for ANGLE_MODE, HORIZON_MODE, and HEADING_LOCK

View file

@ -72,6 +72,7 @@ FILE_COMPILE_FOR_SPEED
#include "flight/gyroanalyse.h" #include "flight/gyroanalyse.h"
#include "flight/rpm_filter.h" #include "flight/rpm_filter.h"
#include "flight/dynamic_gyro_notch.h" #include "flight/dynamic_gyro_notch.h"
#include "flight/kalman.h"
#ifdef USE_HARDWARE_REVISION_DETECTION #ifdef USE_HARDWARE_REVISION_DETECTION
#include "hardware_revision.h" #include "hardware_revision.h"
@ -119,7 +120,11 @@ PG_RESET_TEMPLATE(gyroConfig_t, gyroConfig,
.dynamicGyroNotchRange = DYN_NOTCH_RANGE_MEDIUM, .dynamicGyroNotchRange = DYN_NOTCH_RANGE_MEDIUM,
.dynamicGyroNotchQ = 120, .dynamicGyroNotchQ = 120,
.dynamicGyroNotchMinHz = 150, .dynamicGyroNotchMinHz = 150,
.dynamicGyroNotchEnabled = 0 .dynamicGyroNotchEnabled = 0,
.kalman_q = 100,
.kalman_w = 4,
.kalman_sharpness = 100,
.kalmanEnabled = 0,
); );
STATIC_UNIT_TESTED gyroSensor_e gyroDetect(gyroDev_t *dev, gyroSensor_e gyroHardware) STATIC_UNIT_TESTED gyroSensor_e gyroDetect(gyroDev_t *dev, gyroSensor_e gyroHardware)
@ -307,6 +312,11 @@ bool gyroInit(void)
} }
gyroInitFilters(); gyroInitFilters();
#ifdef USE_GYRO_KALMAN
if (gyroConfig()->kalmanEnabled) {
gyroKalmanInitialize();
}
#endif
#ifdef USE_DYNAMIC_FILTERS #ifdef USE_DYNAMIC_FILTERS
dynamicGyroNotchFiltersInit(&dynamicGyroNotchState); dynamicGyroNotchFiltersInit(&dynamicGyroNotchState);
gyroDataAnalyseStateInit( gyroDataAnalyseStateInit(
@ -450,6 +460,14 @@ void FAST_CODE NOINLINE gyroUpdate()
gyro.gyroADCf[axis] = gyroADCf; gyro.gyroADCf[axis] = gyroADCf;
} }
#ifdef USE_GYRO_KALMAN
if (gyroConfig()->kalmanEnabled) {
gyro.gyroADCf[X] = gyroKalmanUpdate(X, gyro.gyroADCf[X]);
gyro.gyroADCf[Y] = gyroKalmanUpdate(Y, gyro.gyroADCf[Y]);
gyro.gyroADCf[Z] = gyroKalmanUpdate(Z, gyro.gyroADCf[Z]);
}
#endif
#ifdef USE_DYNAMIC_FILTERS #ifdef USE_DYNAMIC_FILTERS
if (dynamicGyroNotchState.enabled) { if (dynamicGyroNotchState.enabled) {
gyroDataAnalyse(&gyroAnalyseState); gyroDataAnalyse(&gyroAnalyseState);

View file

@ -74,6 +74,10 @@ typedef struct gyroConfig_s {
uint16_t dynamicGyroNotchQ; uint16_t dynamicGyroNotchQ;
uint16_t dynamicGyroNotchMinHz; uint16_t dynamicGyroNotchMinHz;
uint8_t dynamicGyroNotchEnabled; uint8_t dynamicGyroNotchEnabled;
uint16_t kalman_q;
uint16_t kalman_w;
uint16_t kalman_sharpness;
uint8_t kalmanEnabled;
} gyroConfig_t; } gyroConfig_t;
PG_DECLARE(gyroConfig_t, gyroConfig); PG_DECLARE(gyroConfig_t, gyroConfig);

View file

@ -72,6 +72,7 @@
#define USE_PITOT_VIRTUAL #define USE_PITOT_VIRTUAL
#define USE_DYNAMIC_FILTERS #define USE_DYNAMIC_FILTERS
#define USE_GYRO_KALMAN
#define USE_EXTENDED_CMS_MENUS #define USE_EXTENDED_CMS_MENUS
#define USE_UAV_INTERCONNECT #define USE_UAV_INTERCONNECT
#define USE_RX_UIB #define USE_RX_UIB