1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-24 16:55:36 +03:00

update sensor fusion

This commit is contained in:
ahmedsalah52 2024-11-18 20:01:32 +01:00
parent f959083383
commit 6dd63aa5cf
6 changed files with 422 additions and 245 deletions

View file

@ -169,12 +169,23 @@ float filterGetNotchQ(float centerFreq, float cutoffFreq)
return centerFreq * cutoffFreq / (centerFreq * centerFreq - cutoffFreq * cutoffFreq);
}
float filterGetBandQ(float lowerFreq, float higherFreq)
{
float centerFreq = sqrtf(lowerFreq * higherFreq);
return centerFreq / (higherFreq - lowerFreq);
}
/* sets up a biquad filter as a 2nd order butterworth LPF */
void biquadFilterInitLPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate)
{
biquadFilterInit(filter, filterFreq, refreshRate, BIQUAD_Q, FILTER_LPF, 1.0f);
}
void biquadFilterInitHPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate)
{
biquadFilterInit(filter, filterFreq, refreshRate, BIQUAD_Q, FILTER_HPF, 1.0f);
}
void biquadFilterInit(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType, float weight)
{
biquadFilterUpdate(filter, filterFreq, refreshRate, Q, filterType, weight);
@ -202,6 +213,13 @@ FAST_CODE void biquadFilterUpdate(biquadFilter_t *filter, float filterFreq, uint
filter->a1 = -2 * cs;
filter->a2 = 1 - alpha;
break;
case FILTER_HPF:
filter->b0 = 0.5f * (1 + cs);
filter->b1 = -1 * (1 + cs); // the negative sign here is not in the TI document
filter->b2 = filter->b0;
filter->a1 = -2 * cs;
filter->a2 = 1 - alpha;
break;
case FILTER_NOTCH:
filter->b0 = 1;
filter->b1 = -2 * cs;

View file

@ -38,6 +38,7 @@ typedef enum {
FILTER_LPF, // 2nd order Butterworth section
FILTER_NOTCH,
FILTER_BPF,
FILTER_HPF,
} biquadFilterType_e;
typedef struct pt1Filter_s {
@ -116,8 +117,10 @@ void pt3FilterUpdateCutoff(pt3Filter_t *filter, float k);
float pt3FilterApply(pt3Filter_t *filter, float input);
float filterGetNotchQ(float centerFreq, float cutoffFreq);
float filterGetBandQ(float lowerFreq, float higherFreq);
void biquadFilterInitLPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate);
void biquadFilterInitHPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate);
void biquadFilterInit(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType, float weight);
void biquadFilterUpdate(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType, float weight);
void biquadFilterUpdateLPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate);

View file

@ -59,12 +59,12 @@
#include "flight/pid.h"
#include "flight/position.h"
#include "flight/alt_hold.h"
#include "flight/altitude.h"
#include "io/asyncfatfs/asyncfatfs.h"
#include "io/beeper.h"
#include "io/dashboard.h"
#include "io/flashfs.h"
#include "io/gimbal_control.h"
#include "io/gps.h"
#include "io/ledstrip.h"
#include "io/piniobox.h"
@ -157,6 +157,7 @@ static void taskBatteryAlerts(timeUs_t currentTimeUs)
static void taskUpdateAccelerometer(timeUs_t currentTimeUs)
{
accUpdate(currentTimeUs);
updateAccItegralCallback(currentTimeUs);
}
#endif
@ -453,9 +454,6 @@ task_attribute_t task_attributes[TASK_COUNT] = {
[TASK_RC_STATS] = DEFINE_TASK("RC_STATS", NULL, NULL, rcStatsUpdate, TASK_PERIOD_HZ(100), TASK_PRIORITY_LOW),
#endif
#ifdef USE_GIMBAL
[TASK_GIMBAL] = DEFINE_TASK("GIMBAL", NULL, NULL, gimbalUpdate, TASK_PERIOD_HZ(100), TASK_PRIORITY_MEDIUM),
#endif
};
task_t *getTask(unsigned taskId)
@ -635,8 +633,4 @@ void tasksInit(void)
#ifdef USE_RC_STATS
setTaskEnabled(TASK_RC_STATS, true);
#endif
#ifdef USE_GIMBAL
setTaskEnabled(TASK_GIMBAL, true);
#endif
}

View file

@ -37,6 +37,7 @@
#include "sensors/sensors.h"
#include "sensors/barometer.h"
#include "sensors/acceleration.h"
#include "sensors/rangefinder.h"
@ -50,67 +51,26 @@
#include "flight/altitude.h"
#define ACC_VAR_COEFF (1.0f)
#define ACC_CONST_VAR (10.0f)
#define SENSOR_VEL_ERROR_THRESH (1000.0f) // the error above this value excludes the sensor from the fusion and changes the offset
#define SENSOR_VEL_MAX_ERROR (10000.0f)
#define SENSOR_MAX_PENALITY_ITERS (100)
#define SENSOR_MAX_OFFSET_ERROR (1000.0f)
#define BARO_VAR_ALT_COEFF (0.05f)
#define BARO_VAR_VEL_COEFF (0.01f)
#define BARO_VAR_TEMP_COEFF (0.5f)
#define BARO_VAR_ALT_COEFF (0.01f)
#define BARO_VAR_VEL_COEFF (0.01f)
#define BARO_VAR_TEMP_COEFF (0.01f)
#define BARO_VAR_VEL_ERROR_COEFF (1.00f)
#define GPS_VAR_VDOP_COEFF (1.0f)
#define RANGEFINDER_ACC_ERROR_THRESH (50.0f)
#define RANGEFINDER_RAPID_ERR_THRESH (100.0f)
#define RANGEFINDER_ACC_ERROR_THRESH (50.0f)
#define RANGEFINDER_RAPID_ERR_THRESH (100.0f)
#define RANGEFINDER_VAR_VEL_ERROR_COEFF (2.0f)
#define RANGEFINDER_CONST_VAR (10.0f)
typedef struct sensorState_s {
float currentAltReadingCm;
float previousAltReadingCm;
float zeroAltOffsetCm;
float filteredAltReadingCm;
float variance;
timeMs_t deltaTimeMs;
float VelocityCmSec;
bool isValid;
void (*updateReading)(struct sensorState_s *sensor);
void (*updateVariance)(struct sensorState_s *sensor);
void (*updateOffset)(struct sensorState_s *sensor);
} sensorState_t;
typedef struct velocity3D_s {
uint16_t value;
bool isValid;
} velocity3D_t;
// #ifdef USE_ACC
// void updateAccReading(sensorState_t *sensor, bool ARMING_FLAG(ARMED));
// void updateAccVariance(sensorState_t *sensor, bool ARMING_FLAG(ARMED));
// float updateAccFilter(float value);
// void updateAccItegralCallback(timeUs_t currentTimeUs);
// typedef struct accIntegral_s {
// float vel[3];
// timeDelta_t deltaTimeUs;
// } accIntegral_t;
// accIntegral_t accIntegral;
// #endif
#ifdef USE_BARO
void updateBaroReading(sensorState_t *sensor);
void updateBaroVariance(sensorState_t *sensor);
#endif
#ifdef USE_GPS
void updateGpsReading(sensorState_t *sensor);
void updateGpsVariance(sensorState_t *sensor);
#endif
#ifdef USE_RANGEFINDER
void updateRangefinderReading(sensorState_t *sensor);
void updateRangefinderVariance(sensorState_t *sensor);
void updateRangefinderOffset(sensorState_t *sensor);
#endif
void updateSensorOffset(sensorState_t *sensor);
#define GPS_VAR_DOP_COEFF (1.0f)
#define GPS_VAR_VEL_ERROR_COEFF (2.0f)
#define GPS_RAPID_ERR_THRESH (100.0f)
#define GPS_ACC_ERROR_THRESH (200.0f)
typedef enum {
#ifdef USE_BARO
@ -120,165 +80,289 @@ typedef enum {
SF_GPS,
#endif
// the only must for the order of the sensors is that the local sensors like the rangefinder should be last after the global sensors like the GPS and Baro
#ifdef USE_ACC
SF_ACC,
#endif
#ifdef USE_RANGEFINDER
SF_RANGEFINDER,
#endif
SENSOR_COUNT
} altSensor_e;
typedef struct sensorState_s {
float currentAltReadingCm;
float zeroAltOffsetCm;
float velocityCmS;
float variance;
float offsetError;
uint32_t velError;
altSensor_e type;
uint8_t penalityIters;
bool isValid;
bool toFuse;
void (*updateReading)(struct sensorState_s *sensor);
void (*updateVariance)(struct sensorState_s *sensor);
void (*updateOffset)(struct sensorState_s *sensor);
} sensorState_t;
typedef struct velocity3D_s {
float value;
bool isValid;
} velocity3D_t;
#ifdef USE_ACC
void updateAccReading(sensorState_t *sensor);
void applyAccVelFilter(float *velocity);
void updateAccItegralCallback(timeUs_t currentTimeUs);
typedef struct accIntegral_s {
float vel[XYZ_AXIS_COUNT + 1]; // 3 axis + 1 for the 3D magnitude
timeDelta_t deltaTimeUs;
} accIntegral_t;
accIntegral_t accIntegral;
#endif
#ifdef USE_BARO
void updateBaroReading(sensorState_t *sensor);
void updateBaroVariance(sensorState_t *sensor);
void applyBaroFilter(float *value);
#endif
#ifdef USE_GPS
void updateGpsReading(sensorState_t *sensor);
void updateGpsVariance(sensorState_t *sensor);
void updateGpsOffset(sensorState_t *sensor);
void applyGpsFilter(float *value);
#endif
#ifdef USE_RANGEFINDER
void updateRangefinderReading(sensorState_t *sensor);
void updateRangefinderVariance(sensorState_t *sensor);
void updateRangefinderOffset(sensorState_t *sensor);
void applyRangefinderFilter(float *value);
#endif
void updateSensorOffset(sensorState_t *sensor);
void updateVelError(sensorState_t *sensor);
void updateSensorFusability(sensorState_t *sensor);
void doNothing(sensorState_t *sensor);
sensorState_t altSenFusSources[SENSOR_COUNT];
altitudeState_t altitudeState;
velocity3D_t velocityDm3D; // for Decimeter/sec 0.1m/s, coming from the GPS if available
velocity3D_t velocityCm3D;
static KalmanFilter kf;
void altSensorFusionInit(void) {
#ifdef USE_ACC
altSenFusSources[SF_ACC].updateReading = updateAccReading;
altSenFusSources[SF_ACC].updateVariance = doNothing;
altSenFusSources[SF_ACC].updateOffset = doNothing;
altSenFusSources[SF_ACC].type = SF_ACC;
altSenFusSources[SF_ACC].isValid = false;
altSenFusSources[SF_ACC].toFuse = false;
velocityCm3D.isValid = true;
#endif
#ifdef USE_BARO
altSenFusSources[SF_BARO].updateReading = updateBaroReading;
altSenFusSources[SF_BARO].updateVariance = updateBaroVariance;
altSenFusSources[SF_BARO].updateOffset = updateSensorOffset;
altSenFusSources[SF_BARO].currentAltReadingCm = 0;
altSenFusSources[SF_BARO].variance = 0.0f;
altSenFusSources[SF_BARO].zeroAltOffsetCm = 0.0f;
altSenFusSources[SF_BARO].type = SF_BARO;
altSenFusSources[SF_BARO].isValid = isBaroReady();
altSenFusSources[SF_BARO].toFuse = true;
#endif
#ifdef USE_GPS
altSenFusSources[SF_GPS].updateReading = updateGpsReading;
altSenFusSources[SF_GPS].updateVariance = updateGpsVariance;
altSenFusSources[SF_GPS].updateOffset = updateSensorOffset;
altSenFusSources[SF_GPS].currentAltReadingCm = 0;
altSenFusSources[SF_GPS].variance = 0.0f;
altSenFusSources[SF_GPS].zeroAltOffsetCm = 0.0f;
altSenFusSources[SF_GPS].type = SF_GPS;
altSenFusSources[SF_GPS].isValid = false;
altSenFusSources[SF_GPS].toFuse = true;
#endif
#ifdef USE_RANGEFINDER
altSenFusSources[SF_RANGEFINDER].updateReading = updateRangefinderReading;
altSenFusSources[SF_RANGEFINDER].updateVariance = updateRangefinderVariance;
altSenFusSources[SF_RANGEFINDER].updateOffset = updateRangefinderOffset;
altSenFusSources[SF_RANGEFINDER].currentAltReadingCm = 0;
altSenFusSources[SF_RANGEFINDER].variance = 0.0f;
altSenFusSources[SF_RANGEFINDER].zeroAltOffsetCm = 0.0f;
altSenFusSources[SF_RANGEFINDER].updateOffset = updateSensorOffset;
altSenFusSources[SF_RANGEFINDER].type = SF_RANGEFINDER;
altSenFusSources[SF_RANGEFINDER].isValid = false;
altSenFusSources[SF_RANGEFINDER].toFuse = true;
#endif
kf_init(&kf, 0.0f, 1.0f, 1.0f);
velocityDm3D.value = 0;
velocityDm3D.isValid = false;
velocityCm3D.value = 0;
velocityCm3D.isValid = false;
}
void altSensorFusionUpdate(void) {
kf_update_variance(&kf);
SensorMeasurement tempSensorMeas;
float previousAltitude = altitudeState.value;
for (altSensor_e sensor = 0; sensor < SENSOR_COUNT; sensor++) {
altSenFusSources[sensor].updateReading(&altSenFusSources[sensor]);
altSenFusSources[sensor].updateVariance(&altSenFusSources[sensor]);
altSenFusSources[sensor].updateOffset(&altSenFusSources[sensor]);
float previousAltitude = altitudeState.distCm;
for (sensorState_t * sensor = altSenFusSources; sensor < altSenFusSources + SENSOR_COUNT; sensor++) {
sensor->updateReading(sensor);
updateVelError(sensor);
updateSensorFusability(sensor); // this should handle the case of sudden jumps in the sensor readings compared to the accelerometer velocity estimation
sensor->updateVariance(sensor);
sensor->updateOffset(sensor);
tempSensorMeas.value = altSenFusSources[sensor].currentAltReadingCm - altSenFusSources[sensor].zeroAltOffsetCm;
tempSensorMeas.variance = altSenFusSources[sensor].variance;
kf_update(&kf, tempSensorMeas);
altitudeState.value = kf.estimatedValue; // update the altitude state every loop since it maybe used in the variance estimate
if (sensor->isValid && sensor->toFuse) {
tempSensorMeas.value = sensor->currentAltReadingCm - sensor->zeroAltOffsetCm;
tempSensorMeas.variance = sensor->variance;
kf_update(&kf, tempSensorMeas);
}
}
altitudeState.variance = kf.estimatedVariance;
altitudeState.velocity = (altitudeState.value - previousAltitude) * HZ_TO_INTERVAL(TASK_ALTITUDE_RATE_HZ);
altitudeState.distCm = kf.estimatedValue;
altitudeState.variance = kf.estimatedVariance;
altitudeState.velocityCm = (altitudeState.distCm - previousAltitude) * TASK_ALTITUDE_RATE_HZ;
previousAltitude = altitudeState.distCm;
DEBUG_SET(DEBUG_ALTITUDE, 4, (int32_t)(altitudeState.value));
DEBUG_SET(DEBUG_ALTITUDE, 5, (int32_t)(altSenFusSources[SF_BARO].currentAltReadingCm));
DEBUG_SET(DEBUG_ALTITUDE, 6, (int32_t)(altSenFusSources[SF_GPS].currentAltReadingCm));
DEBUG_SET(DEBUG_ALTITUDE, 7, (int32_t)(altSenFusSources[SF_RANGEFINDER].currentAltReadingCm));
DEBUG_SET(DEBUG_ALTITUDE, 0, altSenFusSources[SF_BARO].isValid ? lrintf(altSenFusSources[SF_BARO].currentAltReadingCm) : -1);
DEBUG_SET(DEBUG_ALTITUDE, 1, lrintf(altSenFusSources[SF_GPS].currentAltReadingCm));
DEBUG_SET(DEBUG_ALTITUDE, 2, altSenFusSources[SF_RANGEFINDER].isValid ? lrintf(altSenFusSources[SF_RANGEFINDER].currentAltReadingCm) : -1);
DEBUG_SET(DEBUG_ALTITUDE, 3, lrintf(altitudeState.distCm));
DEBUG_SET(DEBUG_ALTITUDE, 4, lrintf(altSenFusSources[SF_BARO].variance));
DEBUG_SET(DEBUG_ALTITUDE, 5, lrintf(altSenFusSources[SF_GPS].variance));
DEBUG_SET(DEBUG_ALTITUDE, 6, lrintf(altSenFusSources[SF_RANGEFINDER].variance));
DEBUG_SET(DEBUG_ALTITUDE, 7, lrintf(altitudeState.variance));
}
// void updateAccItegralCallback(timeUs_t currentTimeUs) {
// static bool firstRun = true;
// static timeUs_t prevTimeUs = 0;
// if (firstRun) {
// prevTimeUs = currentTimeUs;
// firstRun = false;
// return;
// }
// timeDelta_t deltaTimeUs = cmpTimeUs(currentTimeUs, prevTimeUs);
// for (int i = 0; i < XYZ_AXIS_COUNT; i++) {
// accIntegral.vel[i] += acc.accADC.v[i] * (float)deltaTimeUs / 1e6f;
// }
// accIntegral.deltaTimeUs += deltaTimeUs;
// prevTimeUs = currentTimeUs;
// }
void updateSensorOffset(sensorState_t *sensor) {
if (!sensor->isValid) {
return;
}
// void updateAccReading(sensorState_t *sensor, bool ARMING_FLAG(ARMED)) {
// static float velOffset = 0;
// static float velAccumlator = 0;
// static float velocityDecay = 0.99f;
// sensor->previousAltReadingCm = sensor->currentAltReadingCm;
// // given the attiude roll and pitch angles of the drone, and the integrated acceleration in x,y and z
// // calculate the integrated acceleration in the z direction in the world frame
// float roll = DEGREES_TO_RADIANS((float)attitude.values.roll / 10.0f); // integer devision to reduce noise
// float pitch = DEGREES_TO_RADIANS((float)attitude.values.pitch / 10.0f);
if (!ARMING_FLAG(ARMED)) {// default offset update when not armed
sensor->zeroAltOffsetCm = 0.2f * sensor->currentAltReadingCm + 0.8f * sensor->zeroAltOffsetCm;
} else { // when armed the offset should be updated according to the velocity error value
// float estAlt = sensor->currentAltReadingCm - sensor->zeroAltOffsetCm;
float newOffset = sensor->currentAltReadingCm - kf.estimatedValue;
if (sensor->velError > SENSOR_VEL_ERROR_THRESH) {
sensor->zeroAltOffsetCm = newOffset;
sensor->velError = 0;
} else { // update the offset smoothly using the error value, if the error is 0 then no update is done
// float correctnessRatio = (float)(SENSOR_VEL_MAX_ERROR - sensor->velError) / SENSOR_VEL_MAX_ERROR;
// sensor->zeroAltOffsetCm = (correctnessRatio * sensor->zeroAltOffsetCm)
// + ((1.0f - correctnessRatio) * newOffset);
sensor->offsetError += newOffset - sensor->zeroAltOffsetCm;
if (fabsf(sensor->offsetError) > SENSOR_MAX_OFFSET_ERROR) {
sensor->zeroAltOffsetCm = 0.01f * newOffset + 0.99f * sensor->zeroAltOffsetCm;
// decaying the error
sensor->offsetError = 0.99f * sensor->offsetError;
}
}
}
}
void updateVelError(sensorState_t *sensor) {
if (!sensor->isValid || sensor->type == SF_ACC) {
return;
}
sensor->velError = 0.1 * (sq(altSenFusSources[SF_ACC].velocityCmS - sensor->velocityCmS) / (100.f))
+ 0.9 * sensor->velError;
sensor->velError = constrain(sensor->velError, 0, SENSOR_VEL_MAX_ERROR);
}
void updateSensorFusability(sensorState_t *sensor) {
if (!sensor->isValid || sensor->type == SF_ACC) {
return;
}
// float cosPitch = cosf(pitch);
// float velWorldZ = - accIntegral.vel[0] * sinf(pitch)
// + accIntegral.vel[1] * cosPitch * sinf(roll)
// + accIntegral.vel[2] * cosPitch * cosf(roll);
// sensor->deltaTimeMs = (float)accIntegral.deltaTimeUs / 1e3f;
// float gravityVel = (float)accIntegral.deltaTimeUs / 1e6f; // g/Us to g/S
if (sensor->velError > SENSOR_VEL_ERROR_THRESH) {
sensor->penalityIters = SENSOR_MAX_PENALITY_ITERS;
} else if (sensor->penalityIters > 0) {
sensor->penalityIters--;
}
// float velCmSec = ((velWorldZ * acc.dev.acc_1G_rec) - gravityVel) * 981.0f; // g to cm/s
sensor->toFuse = (sensor->penalityIters == 0);
}
// velCmSec -= velOffset;
void doNothing(sensorState_t *sensor) {
UNUSED(sensor);
}
// velCmSec = updateAccFilter(velCmSec);
// ======================================================================================================
// ==================================== Sensor specific functions =======================================
// ======================================================================================================
void updateAccItegralCallback(timeUs_t currentTimeUs) {
static bool firstRun = true;
static timeUs_t prevTimeUs = 0;
if (firstRun) {
prevTimeUs = currentTimeUs;
firstRun = false;
return;
}
// if (!ARMING_FLAG(ARMED)) {
// velOffset = 0.99f * velOffset + 0.01f * velCmSec;
// sensor->currentAltReadingCm = 0;
// velAccumlator = 0;
// }
timeDelta_t deltaTimeUs = cmpTimeUs(currentTimeUs, prevTimeUs);
for (int i = 0; i < XYZ_AXIS_COUNT; i++) {
accIntegral.vel[i] += acc.accADC.v[i] * (float)deltaTimeUs / 1e6f;
}
accIntegral.vel[XYZ_AXIS_COUNT] += (acc.accMagnitude - 1.0f) * (float)deltaTimeUs / 1e6f;
accIntegral.deltaTimeUs += deltaTimeUs;
prevTimeUs = currentTimeUs;
}
// // use the average velocity and cut the decimals after the first decimal
// sensor->VelocityCmSec = (int)((velCmSec) * 100 / 2.0f) / 100.0f;
// velAccumlator += sensor->VelocityCmSec;
void updateAccReading(sensorState_t *sensor) {
static float velDriftZ = 0;
// sensor->currentAltReadingCm += velAccumlator * ((float)accIntegral.deltaTimeUs/1e6f);
// DEBUG_SET(DEBUG_ALTITUDE, 4, (int32_t)(accIntegral.vel[2] * 1000));
// DEBUG_SET(DEBUG_ALTITUDE, 5, (int32_t)(sensor->VelocityCmSec * 1000));
// DEBUG_SET(DEBUG_ALTITUDE, 6, (int32_t)(velAccumlator * 1000));
// DEBUG_SET(DEBUG_ALTITUDE, 7, (int32_t)(sensor->currentAltReadingCm * 1000));
// accIntegral.vel[0] = 0;
// accIntegral.vel[1] = 0;
// accIntegral.vel[2] = 0;
// accIntegral.deltaTimeUs = 0;
// }
// void updateAccVariance(sensorState_t *sensor, bool ARMING_FLAG(ARMED)) { // maybe we don't need this since the altitude quality is not good anyway
// static float unVar = 0;
// if (!armed) {
// float newVar = ACC_VAR_COEFF * sq(sensor->currentAltReadingCm - sensor->previousAltReadingCm);
// unarmedVar = (unarmedVar + newVar)/2;
// }
// sensor->variance = unarmedVar + ACC_CONST_VAR;
// }
// float updateAccFilter(float value) {
// static pt3Filter_t velFilter;
// static bool firstRun = true;
// if (firstRun) {
// pt3FilterInit(&velFilter, 0.5);
// firstRun = false;
// }
static float accVelZ = 0;
// given the attiude roll and pitch angles of the drone, and the integrated acceleration in x,y and z
// calculate the integrated acceleration in the z direction in the world frame
float roll = DEGREES_TO_RADIANS((float)attitude.values.roll / 10.0f); // integer devision to reduce noise
float pitch = DEGREES_TO_RADIANS((float)attitude.values.pitch / 10.0f);
// return pt3FilterApply(&velFilter, value);
// }
float cosPitch = cosf(pitch);
float velWorldZ = - accIntegral.vel[0] * sinf(pitch)
+ accIntegral.vel[1] * cosPitch * sinf(roll)
+ accIntegral.vel[2] * cosPitch * cosf(roll);
// sensor->deltaTimeMs = (float)accIntegral.deltaTimeUs / 1e3f;
float gravityVel = (float)accIntegral.deltaTimeUs / 1e6f; // g/Us to g/S
float velCmSecZ = ((velWorldZ * acc.dev.acc_1G_rec) - gravityVel) * 981.0f; // g to cm/s
velCmSecZ = (int)(velCmSecZ * 10) / 10.0f;
accVelZ += velCmSecZ;
velDriftZ = 0.005 * accVelZ + (1.0f - 0.005) * velDriftZ;
sensor->velocityCmS = accVelZ - velDriftZ;
velocityCm3D.value = fabsf(accIntegral.vel[XYZ_AXIS_COUNT] * 981.0f);
// applyAccVelFilter(&sensor->velocityCmS);
// sensor->currentAltReadingCm += sensor->velocityCmS * ((float)accIntegral.deltaTimeUs/1e6f);
DEBUG_SET(DEBUG_ALTITUDE, 0, lrintf(accIntegral.vel[2] * 981.0f));
DEBUG_SET(DEBUG_ALTITUDE, 1, lrintf(accVelZ));
DEBUG_SET(DEBUG_ALTITUDE, 2, lrintf(velDriftZ));
DEBUG_SET(DEBUG_ALTITUDE, 3, lrintf(sensor->velocityCmS));
DEBUG_SET(DEBUG_ALTITUDE, 4, lrintf(accIntegral.vel[XYZ_AXIS_COUNT] * 981.0f));
DEBUG_SET(DEBUG_ALTITUDE, 7, lrintf(velocityCm3D.value));
for (int i = 0; i <= XYZ_AXIS_COUNT; i++) {
accIntegral.vel[i] = 0;
}
accIntegral.deltaTimeUs = 0;
}
void applyAccVelFilter(float *velocity) {
static pt2Filter_t velFilter;
static bool firstRun = true;
if (firstRun) {
pt2FilterInit(&velFilter, 0.5);
firstRun = false;
}
*velocity = pt2FilterApply(&velFilter, *velocity);
}
#ifdef USE_BARO
@ -288,80 +372,153 @@ void updateBaroReading(sensorState_t *sensor) {
return;
}
static pt2Filter_t baroLpfFilter;
static bool firstRun = true;
static timeMs_t prevTime = 0;
static float previousAltitude = 0.0f;
if (firstRun) { // init
const float sampleTimeS = HZ_TO_INTERVAL(TASK_ALTITUDE_RATE_HZ);
const float altitudeCutoffHz = positionConfig()->altitude_lpf / 100.0f;
const float altitudeGain = pt2FilterGain(altitudeCutoffHz, sampleTimeS);
pt2FilterInit(&baroLpfFilter, altitudeGain);
prevTime = millis();
previousAltitude = getBaroAltitude();
sensor->zeroAltOffsetCm = previousAltitude; // init the offset with the first reading
firstRun = false;
}
sensor->previousAltReadingCm = sensor->currentAltReadingCm;
sensor->currentAltReadingCm = pt2FilterApply(&baroLpfFilter, getBaroAltitude());
sensor->deltaTimeMs = millis() - prevTime;
prevTime = millis();
sensor->currentAltReadingCm = getBaroAltitude();
applyBaroFilter(&sensor->currentAltReadingCm);
sensor->velocityCmS = (sensor->currentAltReadingCm - previousAltitude) * TASK_ALTITUDE_RATE_HZ;
previousAltitude = sensor->currentAltReadingCm;
}
void updateBaroVariance(sensorState_t *sensor) {
// the baro variance can be calculated as initial variance while the drone is stationary (not armed)
// and while armed as a funtion of the altitude, velocity and the baro temprature
if (!sensor->isValid) {
return;
}
static float contantVariance = 0;
static float stationaryVariance = 0;
static float stationaryMean = 0;
static uint16_t n = 0;
float velocity = 0;
if (!ARMING_FLAG(ARMED)) {
contantVariance = (sensor->variance * n + sq(sensor->currentAltReadingCm)) / (n + 1);
n++;
if (!ARMING_FLAG(ARMED)) { // approximating the mean and variance of the baro readings during the stationary phase
stationaryMean += (sensor->currentAltReadingCm - stationaryMean) / (n + 1);
stationaryVariance += (sq(sensor->currentAltReadingCm - stationaryMean) - stationaryVariance) / (n + 1);
n++;
velocity = 0;
} else {
sensor->variance = contantVariance
+ BARO_VAR_VEL_COEFF * fabsf(altitudeState.velocity) //todo: use the global velocity, but from where?
+ BARO_VAR_TEMP_COEFF * fabsf((float)getBaroTemperature())
+ BARO_VAR_ALT_COEFF * fabsf(sensor->currentAltReadingCm);
velocity = velocityCm3D.isValid ? (float)velocityCm3D.value : (altitudeState.velocityCm);
}
float newVariance = stationaryVariance
+ BARO_VAR_VEL_ERROR_COEFF * (float)sensor->velError
+ BARO_VAR_VEL_COEFF * fabsf(velocity)
+ BARO_VAR_TEMP_COEFF * fabsf((float)getBaroTemperature())
+ BARO_VAR_ALT_COEFF * fabsf(sensor->currentAltReadingCm);
sensor->variance = 0.9f * sensor->variance + 0.1f * newVariance;
}
void applyBaroFilter(float *value) {
static pt2Filter_t baroLpfFilter;
static bool firstRun = true;
if (firstRun) {
const float altitudeCutoffHz = positionConfig()->altitude_lpf / 100.0f;
const float altitudeGain = pt2FilterGain(altitudeCutoffHz, HZ_TO_INTERVAL(TASK_ALTITUDE_RATE_HZ));
pt2FilterInit(&baroLpfFilter, altitudeGain);
firstRun = false;
}
*value = pt2FilterApply(&baroLpfFilter, *value);
}
#endif // USE_BARO
#ifdef USE_GPS
void updateGpsReading(sensorState_t *sensor) {
static timeMs_t prevTime = 0;
sensor->isValid = gpsIsHealthy() && sensors(SENSOR_GPS) && STATE(GPS_FIX);
static uint32_t prevTimeStamp = 0;
static bool firstRun = true;
static float previousAltitude = 0.0f;
bool hasNewData = gpsSol.time != prevTimeStamp;
sensor->isValid = gpsIsHealthy() && sensors(SENSOR_GPS) && STATE(GPS_FIX) && hasNewData;
if (!sensor->isValid) {
velocityDm3D.isValid = false;
#ifndef USE_ACC
velocityCm3D.isValid = false;
#endif
return;
}
sensor->previousAltReadingCm = sensor->currentAltReadingCm;
sensor->currentAltReadingCm = gpsSol.llh.altCm;
velocityDm3D.value = gpsSol.speed3d;
velocityDm3D.isValid = true;
if (firstRun) {
previousAltitude = gpsSol.llh.altCm;
sensor->zeroAltOffsetCm = previousAltitude;
prevTimeStamp = gpsSol.time;
firstRun = false;
}
sensor->deltaTimeMs = gpsSol.time - prevTime;
prevTime = gpsSol.time;
sensor->currentAltReadingCm = gpsSol.llh.altCm;
applyGpsFilter(&sensor->currentAltReadingCm);
#ifndef USE_ACC
velocityCm3D.value = gpsSol.speed3d * 10;
velocityCm3D.isValid = true;
#endif
timeDelta_t deltaTime = cmpTimeUs(gpsSol.time, prevTimeStamp);
sensor->velocityCmS = ((sensor->currentAltReadingCm - previousAltitude) * deltaTime) / 1000.0f;
previousAltitude = sensor->currentAltReadingCm;
prevTimeStamp = gpsSol.time;
}
void updateGpsVariance(sensorState_t *sensor) {
if (!sensor->isValid) {
return;
}
float newVariance = GPS_VAR_VEL_ERROR_COEFF * sensor->velError;
if (gpsSol.dop.vdop != 0) {
sensor->variance = GPS_VAR_VDOP_COEFF * gpsSol.dop.vdop;
newVariance += GPS_VAR_DOP_COEFF * gpsSol.dop.vdop;
} else if (gpsSol.dop.pdop != 0) {
newVariance += GPS_VAR_DOP_COEFF * gpsSol.dop.pdop;
} else {
sensor->variance = 1000.0f;
}
newVariance += 10000.0f;
}
sensor->variance = 0.9f * sensor->variance + 0.1f * newVariance;
}
void updateGpsOffset(sensorState_t *sensor) {
if (!sensor->isValid) {
return;
}
if (!ARMING_FLAG(ARMED)) { // default offset update when not armed
updateSensorOffset(sensor);
} else {
// the offset calculated when not armed is not accurate anymore, in case of getting more satalites, the offset should be updated.
static float accError = 0;
float estAlt = sensor->currentAltReadingCm - sensor->zeroAltOffsetCm;
float error = (estAlt - kf.estimatedValue);
if (fabsf(error) > GPS_RAPID_ERR_THRESH) { // if the error is too high, update the offset directly
sensor->zeroAltOffsetCm = sensor->zeroAltOffsetCm + error;
return;
} else { // otherwise, add the error to the accumlated error and smoothly update the offset if the error is high enough
accError += error;
if (accError > GPS_ACC_ERROR_THRESH) {
sensor->zeroAltOffsetCm = 0.99f * sensor->zeroAltOffsetCm + 0.01f * (estAlt - kf.estimatedValue);
}
}
}
}
void applyGpsFilter(float *value) {
static pt2Filter_t filter;
static bool firstRun = true;
if (firstRun) {
pt2FilterInit(&filter, 0.5);
firstRun = false;
}
*value = pt2FilterApply(&filter, *value);
}
#endif // USE_GPS
@ -369,10 +526,10 @@ void updateGpsVariance(sensorState_t *sensor) {
#ifdef USE_RANGEFINDER
void updateRangefinderReading(sensorState_t *sensor) {
static timeMs_t prevTime = 0;
static bool firstRun = true;
static float previousAltitude = 0.0f;
int32_t rfAlt = rangefinderGetLatestAltitude();
sensor->isValid = rangefinderIsHealthy() && sensors(SENSOR_RANGEFINDER) && rfAlt >= 0;
if (!sensor->isValid) {
@ -380,14 +537,15 @@ void updateRangefinderReading(sensorState_t *sensor) {
}
if (firstRun) {
prevTime = millis();
previousAltitude = rfAlt;
sensor->zeroAltOffsetCm = rfAlt;
firstRun = false;
}
sensor->previousAltReadingCm = sensor->currentAltReadingCm;
sensor->currentAltReadingCm = rfAlt;
sensor->deltaTimeMs = millis() - prevTime;
prevTime = millis();
sensor->currentAltReadingCm = rfAlt;
// applyRangefinderFilter(&sensor->currentAltReadingCm);
sensor->velocityCmS = (sensor->currentAltReadingCm - previousAltitude) * TASK_ALTITUDE_RATE_HZ;
previousAltitude = sensor->currentAltReadingCm;
}
void updateRangefinderVariance(sensorState_t *sensor) {
@ -395,7 +553,8 @@ void updateRangefinderVariance(sensorState_t *sensor) {
return;
}
sensor->variance = 1.0f; // is there a better way to get the variance of the rangefinder?
float newVariance = RANGEFINDER_VAR_VEL_ERROR_COEFF * sensor->velError + RANGEFINDER_CONST_VAR; // is there a better way to get the variance of the rangefinder?
sensor->variance = 0.9f * sensor->variance + 0.1f * newVariance;
}
void updateRangefinderOffset(sensorState_t *sensor) {
@ -404,30 +563,33 @@ void updateRangefinderOffset(sensorState_t *sensor) {
}
if (!ARMING_FLAG(ARMED)) { // default offset update when not armed
updateSensorOffset(sensor);
} else { // when armed the offset should be updated using the altitude state
static float accError = 0;
float zeroedAlt = sensor->currentAltReadingCm - sensor->zeroAltOffsetCm;
float error = (zeroedAlt - altitudeState.value);
sensor->zeroAltOffsetCm = 0.2f * sensor->currentAltReadingCm + 0.8f * sensor->zeroAltOffsetCm;
} else { // when armed the offset should be updated according to the velocity error value
float estAlt = sensor->currentAltReadingCm - sensor->zeroAltOffsetCm;
float error = (estAlt - kf.estimatedValue);
if (sensor->velError > SENSOR_VEL_ERROR_THRESH) {
sensor->zeroAltOffsetCm += error;
} else { // update the offset smoothly using the error value, if the error is 0 then no update is done
// this is not effective, we should find a way to fix the position drift of the rangefinder or gps
if (fabsf(error) > RANGEFINDER_RAPID_ERR_THRESH) { // if the error is too high, update the offset directly
sensor->zeroAltOffsetCm = sensor->zeroAltOffsetCm + error;
return;
} else { // otherwise, add the error to the accumlated error and smoothly update the offset if the error is high enough
accError += error;
if (accError > RANGEFINDER_ACC_ERROR_THRESH) {
sensor->zeroAltOffsetCm = 0.99f * sensor->zeroAltOffsetCm + 0.01f * (sensor->currentAltReadingCm - altitudeState.value);
}
float correctnessRatio = (float)(SENSOR_VEL_MAX_ERROR - sensor->velError) / SENSOR_VEL_MAX_ERROR;
sensor->zeroAltOffsetCm = (correctnessRatio * sensor->zeroAltOffsetCm)
+ ((1.0f - correctnessRatio) * (estAlt - kf.estimatedValue));
}
}
}
void applyRangefinderFilter(float *value) {
static pt2Filter_t filter;
static bool firstRun = true;
if (firstRun) {
pt2FilterInit(&filter, 0.5);
firstRun = false;
}
*value = pt2FilterApply(&filter, *value);
}
#endif // USE_RANGEFINDER
void updateSensorOffset(sensorState_t *sensor) {
if (!sensor->isValid || ARMING_FLAG(ARMED)) {
return;
}
sensor->zeroAltOffsetCm = 0.2f * sensor->currentAltReadingCm + 0.8f * sensor->zeroAltOffsetCm;
}
#endif // USE_BARO || USE_GPS || USE_RANGEFINDER

View file

@ -18,9 +18,9 @@
#pragma once
typedef struct altitudeState_s {
float value;
float distCm;
float variance;
float velocity;
float velocityCm;
} altitudeState_t;
#ifdef USE_ACC

View file

@ -138,7 +138,7 @@ void calculateEstimatedAltitude(void) {
#endif
if(!kfInitDone) { //first kf init
kf_init(&kf, 0.0f, 0.5, 0.5); //to do: init variance
kf_init(&kf, 1.0f, 0.5, 0.5); //to do: init variance
kfInitDone = true;
}
@ -161,7 +161,7 @@ void calculateEstimatedAltitude(void) {
}
zeroedGpsAltitudeCm = 0.0f; // always hold relativeAltitude at zero while disarmed
DEBUG_SET(DEBUG_ALTITUDE, 2, gpsAltCm / 100.0f); // Absolute altitude ASL in metres, max 32,767m
// DEBUG_SET(DEBUG_ALTITUDE, 2, gpsAltCm / 100.0f); // Absolute altitude ASL in metres, max 32,767m
// *** ARMED ***
} else {
if (!wasArmed) { // things to run once, on arming, after being disarmed
@ -183,7 +183,7 @@ void calculateEstimatedAltitude(void) {
gpsTrust = 0.0f;
// TO DO - smoothly reduce GPS trust, rather than immediately dropping to zero for what could be only a very brief loss of 3D fix
}
DEBUG_SET(DEBUG_ALTITUDE, 2, lrintf(zeroedGpsAltitudeCm / 10.0f)); // Relative altitude above takeoff, to 0.1m, rolls over at 3,276.7m
// DEBUG_SET(DEBUG_ALTITUDE, 2, lrintf(zeroedGpsAltitudeCm / 10.0f)); // Relative altitude above takeoff, to 0.1m, rolls over at 3,276.7m
// apply Kalman filter on available sensors
kf_update_variance(&kf);
@ -236,12 +236,12 @@ void calculateEstimatedAltitude(void) {
#endif
// *** set debugs
DEBUG_SET(DEBUG_ALTITUDE, 0, (int32_t)(100 * gpsTrust));
DEBUG_SET(DEBUG_ALTITUDE, 1, lrintf(baroAltCm / 10.0f)); // Relative altitude above takeoff, to 0.1m, rolls over at 3,276.7m
// DEBUG_SET(DEBUG_ALTITUDE, 0, (int32_t)(100 * gpsTrust));
// DEBUG_SET(DEBUG_ALTITUDE, 1, lrintf(baroAltCm / 10.0f)); // Relative altitude above takeoff, to 0.1m, rolls over at 3,276.7m
#ifdef USE_VARIO
DEBUG_SET(DEBUG_ALTITUDE, 3, estimatedVario);
// DEBUG_SET(DEBUG_ALTITUDE, 3, estimatedVario);
#endif
DEBUG_SET(DEBUG_RTH, 1, lrintf(displayAltitudeCm / 10.0f));
// DEBUG_SET(DEBUG_RTH, 1, lrintf(displayAltitudeCm / 10.0f));
DEBUG_SET(DEBUG_AUTOPILOT_ALTITUDE, 2, lrintf(zeroedFusedAltitudeCm));
altitudeAvailable = haveGpsAlt || haveBaroAlt || haveRangefinderAlt;