Skip to content

Commit 75efa5b

Browse files
Merge pull request #11046 from shota3527/sh_mc_yaw
Better yaw estimation for mulitirotor(magless operation)
2 parents 0121d2c + 209e8a0 commit 75efa5b

File tree

5 files changed

+97
-54
lines changed

5 files changed

+97
-54
lines changed

docs/Settings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Total gyro rotation rate threshold [deg/s] before scaling to consider accelerome
198198

199199
| Default | Min | Max |
200200
| --- | --- | --- |
201-
| 15 | 0 | 30 |
201+
| 20 | 0 | 30 |
202202

203203
---
204204

src/main/fc/settings.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1544,7 +1544,7 @@ groups:
15441544
max: 180
15451545
- name: ahrs_acc_ignore_rate
15461546
description: "Total gyro rotation rate threshold [deg/s] before scaling to consider accelerometer trustworthy"
1547-
default_value: 15
1547+
default_value: 20
15481548
field: acc_ignore_rate
15491549
min: 0
15501550
max: 30

src/main/flight/imu.c

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ STATIC_FASTRAM pt1Filter_t rotRateFilterY;
103103
STATIC_FASTRAM pt1Filter_t rotRateFilterZ;
104104
FASTRAM fpVector3_t imuMeasuredRotationBFFiltered = {.v = {0.0f, 0.0f, 0.0f}};
105105

106+
STATIC_FASTRAM pt1Filter_t accelFilterX;
107+
STATIC_FASTRAM pt1Filter_t accelFilterY;
108+
STATIC_FASTRAM pt1Filter_t accelFilterZ;
109+
FASTRAM fpVector3_t imuMeasuredAccelBFFiltered = {.v = {0.0f, 0.0f, 0.0f}};
110+
106111
STATIC_FASTRAM pt1Filter_t HeadVecEFFilterX;
107112
STATIC_FASTRAM pt1Filter_t HeadVecEFFilterY;
108113
STATIC_FASTRAM pt1Filter_t HeadVecEFFilterZ;
@@ -196,6 +201,10 @@ void imuInit(void)
196201
pt1FilterReset(&rotRateFilterX, 0);
197202
pt1FilterReset(&rotRateFilterY, 0);
198203
pt1FilterReset(&rotRateFilterZ, 0);
204+
// Initialize accel filter
205+
pt1FilterReset(&accelFilterX, 0);
206+
pt1FilterReset(&accelFilterY, 0);
207+
pt1FilterReset(&accelFilterZ, 0);
199208
// Initialize Heading vector filter
200209
pt1FilterReset(&HeadVecEFFilterX, 0);
201210
pt1FilterReset(&HeadVecEFFilterY, 0);
@@ -339,17 +348,23 @@ bool isGPSTrustworthy(void)
339348

340349
static float imuCalculateMcCogWeight(void)
341350
{
342-
float wCoG = imuCalculateAccelerometerWeightNearness(&imuMeasuredAccelBF);
351+
float wCoG = imuCalculateAccelerometerWeightNearness(&imuMeasuredAccelBFFiltered);
343352
float rotRateMagnitude = fast_fsqrtf(vectorNormSquared(&imuMeasuredRotationBFFiltered));
344353
const float rateSlopeMax = DEGREES_TO_RADIANS((imuConfig()->acc_ignore_rate)) * 4.0f;
345354
wCoG *= scaleRangef(constrainf(rotRateMagnitude, 0.0f, rateSlopeMax), 0.0f, rateSlopeMax, 1.0f, 0.0f);
346355
return wCoG;
347356
}
357+
static float imuCalculateMcCogAccWeight(void)
358+
{
359+
fpVector3_t accBFNorm;
360+
vectorScale(&accBFNorm, &imuMeasuredAccelBFFiltered, 1.0f / GRAVITY_CMSS);
361+
float wCoGAcc = constrainf((accBFNorm.z - 1.0f)* 2, 0.0f, 1.0f); //z direction is verified via SITL
362+
return wCoGAcc;
363+
}
348364

349-
static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVector3_t * accBF, const fpVector3_t * magBF, bool useCOG, float courseOverGround, float accWScaler, float magWScaler)
365+
static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVector3_t * accBF, const fpVector3_t * magBF, const fpVector3_t * vCOG, const fpVector3_t * vCOGAcc, float accWScaler, float magWScaler)
350366
{
351367
STATIC_FASTRAM fpVector3_t vGyroDriftEstimate = { 0 };
352-
353368
fpQuaternion_t prevOrientation = orientation;
354369
fpVector3_t vRotation = *gyroBF;
355370

@@ -358,10 +373,10 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
358373

359374
/* Step 1: Yaw correction */
360375
// Use measured magnetic field vector
361-
if (magBF || useCOG) {
376+
if (magBF || vCOG || vCOGAcc) {
362377
float wMag = 1.0f;
363378
float wCoG = 1.0f;
364-
if(magBF){wCoG *= imuConfig()->gps_yaw_weight / 100.0f;}
379+
if (magBF) { wCoG *= imuConfig()->gps_yaw_weight / 100.0f; }
365380

366381
fpVector3_t vMagErr = { .v = { 0.0f, 0.0f, 0.0f } };
367382
fpVector3_t vCoGErr = { .v = { 0.0f, 0.0f, 0.0f } };
@@ -399,7 +414,8 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
399414
quaternionRotateVector(&vMagErr, &vMagErr, &orientation);
400415
}
401416
}
402-
if (useCOG) {
417+
if (vCOG || vCOGAcc) {
418+
fpVector3_t vCoGlocal = { .v = { 0.0f, 0.0f, 0.0f } };
403419
fpVector3_t vForward = { .v = { 0.0f, 0.0f, 0.0f } };
404420
//vForward as trust vector
405421
if (STATE(MULTIROTOR) && (!isMixerTransitionMixing)){
@@ -408,49 +424,58 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
408424
vForward.x = 1.0f;
409425
}
410426
fpVector3_t vHeadingEF;
411-
412-
// Use raw heading error (from GPS or whatever else)
413-
while (courseOverGround > M_PIf) courseOverGround -= (2.0f * M_PIf);
414-
while (courseOverGround < -M_PIf) courseOverGround += (2.0f * M_PIf);
415-
416-
// William Premerlani and Paul Bizard, Direction Cosine Matrix IMU - Eqn. 22-23
417-
// (Rxx; Ryx) - measured (estimated) heading vector (EF)
418-
// (-cos(COG), sin(COG)) - reference heading vector (EF)
419-
420-
float airSpeed = gpsSol.groundSpeed;
421-
// Compute heading vector in EF from scalar CoG,x axis of accelerometer is pointing backwards.
422-
fpVector3_t vCoG = { .v = { -cos_approx(courseOverGround), sin_approx(courseOverGround), 0.0f } };
423-
#if defined(USE_WIND_ESTIMATOR)
424-
// remove wind elements in vCoG for better heading estimation
425-
if (isEstimatedWindSpeedValid() && imuConfig()->gps_yaw_windcomp)
426-
{
427-
vectorScale(&vCoG, &vCoG, gpsSol.groundSpeed);
428-
vCoG.x += getEstimatedWindSpeed(X);
429-
vCoG.y -= getEstimatedWindSpeed(Y);
430-
airSpeed = fast_fsqrtf(vectorNormSquared(&vCoG));
431-
vectorNormalize(&vCoG, &vCoG);
432-
}
433-
#endif
434-
wCoG *= scaleRangef(constrainf((airSpeed+gpsSol.groundSpeed)/2, 400, 1000), 400, 1000, 0.0f, 1.0f);
435427
// Rotate Forward vector from BF to EF - will yield Heading vector in Earth frame
436428
quaternionRotateVectorInv(&vHeadingEF, &vForward, &orientation);
437-
429+
if (vCOG) {
430+
LOG_DEBUG(IMU, "vCOG=(%f,%f,%f)", (double)vCOG->x, (double)vCOG->y, (double)vCOG->z);
431+
vCoGlocal = *vCOG;
432+
float airSpeed = gpsSol.groundSpeed;
433+
#if defined(USE_WIND_ESTIMATOR)
434+
// remove wind elements in vCoGlocal for better heading estimation
435+
if (isEstimatedWindSpeedValid() && imuConfig()->gps_yaw_windcomp)
436+
{
437+
vectorScale(&vCoGlocal, &vCoGlocal, gpsSol.groundSpeed);
438+
vCoGlocal.x += getEstimatedWindSpeed(X);
439+
vCoGlocal.y -= getEstimatedWindSpeed(Y);
440+
airSpeed = fast_fsqrtf(vectorNormSquared(&vCoGlocal));
441+
}
442+
#endif
443+
wCoG *= scaleRangef(constrainf((airSpeed+gpsSol.groundSpeed)/2, 400, 1000), 400, 1000, 0.0f, 1.0f);
444+
} else { //vCOG is not avaliable and vCOGAcc is avaliable, set the weight of vCOG to zero
445+
wCoG = 0.0f;
446+
}
438447
if (STATE(MULTIROTOR)){
439448
//when multicopter`s orientation or speed is changing rapidly. less weight on gps heading
440449
wCoG *= imuCalculateMcCogWeight();
441-
//scale accroading to multirotor`s tilt angle
450+
//handle acc based vector
451+
if(vCOGAcc){
452+
float wCoGAcc = imuCalculateMcCogAccWeight();//stronger weight on acc if body frame z axis greate than 1G
453+
LOG_DEBUG(IMU, "accFiltZ=%f", (double)imuMeasuredAccelBFFiltered.z);
454+
LOG_DEBUG(IMU, "wCoGAcc=%f wCoG=%f", (double)wCoGAcc, (double)wCoG);
455+
LOG_DEBUG(IMU, "vHeadingEF=(%f,%f,%f)", (double)vHeadingEF.x, (double)vHeadingEF.y, (double)vHeadingEF.z);
456+
LOG_DEBUG(IMU, "vCOGAcc=(%f,%f,%f)", (double)vCOGAcc->x, (double)vCOGAcc->y, (double)vCOGAcc->z);
457+
if (wCoGAcc > wCoG){
458+
//when copter is accelerating use gps acc vector instead of gps speed vector
459+
wCoG = wCoGAcc;
460+
vCoGlocal = *vCOGAcc;
461+
}
462+
}
463+
//scale according to multirotor`s tilt angle
442464
wCoG *= scaleRangef(constrainf(vHeadingEF.z, COS20DEG, COS10DEG), COS20DEG, COS10DEG, 1.0f, 0.0f);
443-
//for inverted flying, wCoG is lowered by imuCalculateMcCogWeight no additional processing needed
465+
// Inverted flight relies on the existing tilt scaling(scaleRangef); there is no extra handling here
444466
}
467+
LOG_DEBUG(IMU, " wCoG=%f", (double)wCoG);
468+
LOG_DEBUG(IMU, "vCoGlocal=(%f,%f,%f)", (double)vCoGlocal.x, (double)vCoGlocal.y, (double)vCoGlocal.z);
445469
vHeadingEF.z = 0.0f;
446470

447471
// We zeroed out vHeadingEF.z - make sure the whole vector didn't go to zero
448-
if (vectorNormSquared(&vHeadingEF) > 0.01f) {
472+
if (vectorNormSquared(&vHeadingEF) > 0.01f && vectorNormSquared(&vCoGlocal) > 0.01f) {
449473
// Normalize to unit vector
450474
vectorNormalize(&vHeadingEF, &vHeadingEF);
475+
vectorNormalize(&vCoGlocal, &vCoGlocal);
451476

452477
// error is cross product between reference heading and estimated heading (calculated in EF)
453-
vectorCrossProduct(&vCoGErr, &vCoG, &vHeadingEF);
478+
vectorCrossProduct(&vCoGErr, &vCoGlocal, &vHeadingEF);
454479

455480
// Rotate error back into body frame
456481
quaternionRotateVector(&vCoGErr, &vCoGErr, &orientation);
@@ -637,6 +662,11 @@ static void imuCalculateFilters(float dT)
637662
imuMeasuredRotationBFFiltered.x = pt1FilterApply4(&rotRateFilterX, imuMeasuredRotationBF.x, IMU_ROTATION_LPF, dT);
638663
imuMeasuredRotationBFFiltered.y = pt1FilterApply4(&rotRateFilterY, imuMeasuredRotationBF.y, IMU_ROTATION_LPF, dT);
639664
imuMeasuredRotationBFFiltered.z = pt1FilterApply4(&rotRateFilterZ, imuMeasuredRotationBF.z, IMU_ROTATION_LPF, dT);
665+
666+
imuMeasuredAccelBFFiltered.x = pt1FilterApply4(&accelFilterX, imuMeasuredAccelBF.x, IMU_ROTATION_LPF, dT);
667+
imuMeasuredAccelBFFiltered.y = pt1FilterApply4(&accelFilterY, imuMeasuredAccelBF.y, IMU_ROTATION_LPF, dT);
668+
imuMeasuredAccelBFFiltered.z = pt1FilterApply4(&accelFilterZ, imuMeasuredAccelBF.z, IMU_ROTATION_LPF, dT);
669+
640670
HeadVecEFFiltered.x = pt1FilterApply4(&HeadVecEFFilterX, rMat[0][0], IMU_ROTATION_LPF, dT);
641671
HeadVecEFFiltered.y = pt1FilterApply4(&HeadVecEFFilterY, rMat[1][0], IMU_ROTATION_LPF, dT);
642672
HeadVecEFFiltered.z = pt1FilterApply4(&HeadVecEFFilterZ, rMat[2][0], IMU_ROTATION_LPF, dT);
@@ -646,7 +676,7 @@ static void imuCalculateFilters(float dT)
646676
GPS3DspeedFiltered = pt1FilterApply4(&GPS3DspeedFilter, GPS3Dspeed, IMU_ROTATION_LPF, dT);
647677
}
648678

649-
static void imuCalculateGPSacceleration(fpVector3_t *vEstcentrifugalAccelBF, float *acc_ignore_slope_multipiler)
679+
static void imuCalculateGPSacceleration(fpVector3_t *vEstAccelEF,fpVector3_t *vEstcentrifugalAccelBF, float *acc_ignore_slope_multipiler)
650680
{
651681
static rtcTime_t lastGPSNewDataTime = 0;
652682
static bool lastGPSHeartbeat;
@@ -660,17 +690,16 @@ static void imuCalculateGPSacceleration(fpVector3_t *vEstcentrifugalAccelBF, flo
660690
if (lastGPSHeartbeat != gpsSol.flags.gpsHeartbeat && time_delta_ms > 0)
661691
{
662692
// on new gps frame, update accEF and estimate centrifugal accleration
663-
fpVector3_t vGPSacc = {.v = {0.0f, 0.0f, 0.0f}};
664-
vGPSacc.x = -(currentGPSvel.x - lastGPSvel.x) / (MS2S(time_delta_ms)); // the x axis of accerometer is pointing backward
665-
vGPSacc.y = (currentGPSvel.y - lastGPSvel.y) / (MS2S(time_delta_ms));
666-
vGPSacc.z = (currentGPSvel.z - lastGPSvel.z) / (MS2S(time_delta_ms));
693+
vEstAccelEF->x = -(currentGPSvel.x - lastGPSvel.x) / (MS2S(time_delta_ms)); // the x axis of accerometer is pointing backward
694+
vEstAccelEF->y = (currentGPSvel.y - lastGPSvel.y) / (MS2S(time_delta_ms));
695+
vEstAccelEF->z = (currentGPSvel.z - lastGPSvel.z) / (MS2S(time_delta_ms));
667696
// Calculate estimated centrifugal accleration vector in body frame
668-
quaternionRotateVector(vEstcentrifugalAccelBF, &vGPSacc, &orientation); // EF -> BF
697+
quaternionRotateVector(vEstcentrifugalAccelBF, vEstAccelEF, &orientation); // EF -> BF
669698
lastGPSNewDataTime = currenttime;
670699
lastGPSvel = currentGPSvel;
671700
}
672701
lastGPSHeartbeat = gpsSol.flags.gpsHeartbeat;
673-
*acc_ignore_slope_multipiler = 4;
702+
*acc_ignore_slope_multipiler = 4.0f;
674703
}
675704

676705
static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF, float dT, float *acc_ignore_slope_multipiler)
@@ -684,14 +713,14 @@ static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF
684713
*acc_ignore_slope_multipiler = 4.0f;
685714
}
686715
#ifdef USE_PITOT
687-
if (sensors(SENSOR_PITOT) && pitotIsHealthy() && currentspeed < 0)
716+
else if (sensors(SENSOR_PITOT) && pitotIsHealthy())
688717
{
689718
// second choice is pitot
690719
currentspeed = getAirspeedEstimate();
691720
*acc_ignore_slope_multipiler = 2.0f;
692721
}
693722
#endif
694-
if (currentspeed < 0)
723+
else
695724
{
696725
//third choice is fixedWingReferenceAirspeed
697726
currentspeed = pidProfile()->fixedWingReferenceAirspeed;
@@ -738,10 +767,11 @@ static void imuCalculateEstimatedAttitude(float dT)
738767
#else
739768
const bool canUseMAG = false;
740769
#endif
741-
742-
float courseOverGround = 0;
770+
static fpVector3_t vCOG;
771+
static fpVector3_t vCOGAcc;
743772
bool useMag = false;
744773
bool useCOG = false;
774+
bool useCOGAcc = false;
745775
#if defined(USE_GPS)
746776
bool canUseCOG = isGPSHeadingValid();
747777

@@ -753,7 +783,16 @@ static void imuCalculateEstimatedAttitude(float dT)
753783
// Use GPS (if available)
754784
if (canUseCOG) {
755785
if (gpsHeadingInitialized) {
756-
courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse);
786+
float courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse);
787+
while (courseOverGround > M_PIf) courseOverGround -= (2.0f * M_PIf);
788+
while (courseOverGround < -M_PIf) courseOverGround += (2.0f * M_PIf);
789+
// William Premerlani and Paul Bizard, Direction Cosine Matrix IMU - Eqn. 22-23
790+
// (Rxx; Ryx) - measured (estimated) heading vector (EF)
791+
// (-cos(COG), sin(COG)) - reference heading vector (EF)
792+
vCOG.x = -cos_approx(courseOverGround); // the x axis of accerometer is pointing tail
793+
vCOG.y = sin_approx(courseOverGround);
794+
vCOG.z = 0;
795+
// LOG_DEBUG(IMU, "currentGPSvel=(%f,%f,%f)", -(double)gpsSol.velNED[X], (double)gpsSol.velNED[Y], (double)gpsSol.velNED[Z]);
757796
useCOG = true;
758797
}
759798
else if (!canUseMAG) {
@@ -775,7 +814,9 @@ static void imuCalculateEstimatedAttitude(float dT)
775814
float acc_ignore_slope_multipiler = 1.0f; // when using gps centrifugal_force_compensation, AccelerometerWeightRateIgnore slope will be multiplied by this value
776815
if (isGPSTrustworthy())
777816
{
778-
imuCalculateGPSacceleration(&vEstcentrifugalAccelBF_velned, &acc_ignore_slope_multipiler);
817+
LOG_DEBUG(IMU, "vCOG=(%f,%f,%f)", (double)vCOG.x, (double)vCOG.y, (double)vCOG.z);
818+
imuCalculateGPSacceleration(&vCOGAcc, &vEstcentrifugalAccelBF_velned, &acc_ignore_slope_multipiler);
819+
useCOGAcc = true; //currently only for multicopter
779820
}
780821
if (STATE(AIRPLANE))
781822
{
@@ -824,7 +865,8 @@ static void imuCalculateEstimatedAttitude(float dT)
824865
imuMahonyAHRSupdate(dT, &imuMeasuredRotationBF,
825866
useAcc ? &compansatedGravityBF : NULL,
826867
useMag ? &measuredMagBF : NULL,
827-
useCOG, courseOverGround,
868+
useCOG ? &vCOG : NULL,
869+
useCOGAcc ? &vCOGAcc : NULL,
828870
accWeight,
829871
magWeight);
830872
imuUpdateTailSitter();

src/main/flight/imu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "config/parameter_group.h"
2626

2727
extern fpVector3_t imuMeasuredAccelBF; // cm/s/s
28+
extern fpVector3_t imuMeasuredAccelBFFiltered; // cm/s/s
2829
extern fpVector3_t imuMeasuredRotationBF; // rad/s
2930
extern fpVector3_t imuMeasuredRotationBFFiltered; // rad/s
3031
extern fpVector3_t compansatedGravityBF; // cm/s/s

src/main/target/SITL/sim/realFlight.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,9 @@ static void exchangeData(void)
344344
altitude,
345345
(int16_t)roundf(rfValues.m_groundspeed_MPS * 100),
346346
course,
347-
0,//(int16_t)roundf(rfValues.m_velocityWorldV_MPS * 100), //not sure about the direction
348-
0,//(int16_t)roundf(-rfValues.m_velocityWorldU_MPS * 100),
349-
0,//(int16_t)roundf(rfValues.m_velocityWorldW_MPS * 100),
347+
(int16_t)roundf(rfValues.m_velocityWorldV_MPS * 100), //direction seems ok
348+
(int16_t)roundf(-rfValues.m_velocityWorldU_MPS * 100),//direction seems ok
349+
(int16_t)roundf(rfValues.m_velocityWorldW_MPS * 100),//direction not sure
350350
0
351351
);
352352

0 commit comments

Comments
 (0)