Skip to content

Commit a87537d

Browse files
Merge pull request #11358 from sensei-hacker/perf/imu-mahony-hot-path-opts
optimize imuMahonyAHRSupdate() hot path (5 micro-opts)
2 parents 8b1b8ec + 2ce3f0a commit a87537d

2 files changed

Lines changed: 54 additions & 7 deletions

File tree

src/main/flight/imu.c

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ void imuConfigure(void)
169169
imuRuntimeConfig.dcm_kp_mag = imuConfig()->dcm_kp_mag / 10000.0f;
170170
imuRuntimeConfig.dcm_ki_mag = imuConfig()->dcm_ki_mag / 10000.0f;
171171
imuRuntimeConfig.small_angle = imuConfig()->small_angle;
172+
/* Precompute the Mahony anti-windup clamp. The PID loop reads this value
173+
* 1000× per second; the kP gains only change when the user saves settings,
174+
* so computing it here (called once per save) avoids one add, one multiply,
175+
* and one divide on every PID cycle. */
176+
imuRuntimeConfig.dcm_i_limit = DEGREES_TO_RADIANS(2.0f)
177+
* (imuRuntimeConfig.dcm_kp_acc + imuRuntimeConfig.dcm_kp_mag) * 0.5f;
172178
}
173179

174180
void imuInit(void)
@@ -364,7 +370,19 @@ static float imuCalculateMcCogAccWeight(void)
364370
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)
365371
{
366372
STATIC_FASTRAM fpVector3_t vGyroDriftEstimate = { 0 };
367-
fpQuaternion_t prevOrientation = orientation;
373+
374+
/* Opt 5: snapshot prevOrientation every 100 PID cycles instead of every cycle.
375+
* The snapshot is only used by the fault-recovery path in
376+
* imuCheckAndResetOrientationQuaternion(), which should never fire in normal
377+
* flight. Copying 4 floats 1000×/s just to support a near-zero-probability
378+
* reset path is wasteful; 100 ms staleness is a safe recovery point. */
379+
static uint8_t prevOrientationSnapshotCount = 0;
380+
static fpQuaternion_t prevOrientation = { .q0 = 1.0f }; // identity quaternion safe default
381+
if (++prevOrientationSnapshotCount >= 100) {
382+
prevOrientationSnapshotCount = 0;
383+
prevOrientation = orientation;
384+
}
385+
368386
fpVector3_t vRotation = *gyroBF;
369387

370388
/* Calculate general spin rate (rad/s) */
@@ -497,11 +515,17 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
497515

498516
/* Step 2: Roll and pitch correction - use measured acceleration vector */
499517
if (accBF) {
500-
static const fpVector3_t vGravity = { .v = { 0.0f, 0.0f, 1.0f } };
501518
fpVector3_t vEstGravity, vAcc, vErr;
502519

503-
// Calculate estimated gravity vector in body frame
504-
quaternionRotateVector(&vEstGravity, &vGravity, &orientation); // EF -> BF
520+
/* Opt 1: imuComputeRotationMatrix() is called at the END of every
521+
* imuMahonyAHRSupdate() and keeps rMat in sync with orientation.
522+
* Rotating the constant unit-gravity vector {0,0,1} from EF to BF by
523+
* the current orientation quaternion yields exactly the third row of rMat
524+
* (rMat[2][0..2]). Reading those three floats replaces a quaternionRotateVector()
525+
* call that costs 2× quaternionMultiply = ~32 multiplies + 24 adds. */
526+
vEstGravity.x = rMat[2][0];
527+
vEstGravity.y = rMat[2][1];
528+
vEstGravity.z = rMat[2][2];
505529

506530
// Error is sum of cross product between estimated direction and measured direction of gravity
507531
vectorNormalize(&vAcc, accBF);
@@ -524,7 +548,9 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
524548
vectorAdd(&vRotation, &vRotation, &vErr);
525549
}
526550
// Anti wind-up
527-
float i_limit = DEGREES_TO_RADIANS(2.0f) * (imuRuntimeConfig.dcm_kp_acc + imuRuntimeConfig.dcm_kp_mag) / 2.0f;
551+
/* Opt 4: dcm_i_limit is computed once in imuConfigure() (called on settings save),
552+
* not recomputed each PID cycle. The kP gains do not change at runtime. */
553+
const float i_limit = imuRuntimeConfig.dcm_i_limit;
528554
vGyroDriftEstimate.x = constrainf(vGyroDriftEstimate.x, -i_limit, i_limit);
529555
vGyroDriftEstimate.y = constrainf(vGyroDriftEstimate.y, -i_limit, i_limit);
530556
vGyroDriftEstimate.z = constrainf(vGyroDriftEstimate.z, -i_limit, i_limit);
@@ -547,7 +573,10 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
547573
// Proper quaternion from axis/angle involves computing sin/cos, but the formula becomes numerically unstable as Theta approaches zero.
548574
// For near-zero cases we use the first 3 terms of the Taylor series expansion for sin/cos. We check if fourth term is less than machine precision -
549575
// then we can safely use the "low angle" approximated version without loss of accuracy.
550-
if (thetaMagnitudeSq < fast_fsqrtf(24.0f * 1e-6f)) {
576+
/* Opt 2: original condition was "thetaMagnitudeSq < sqrt(24e-6)".
577+
* Squaring both sides (both are non-negative) gives an equivalent
578+
* condition without a sqrt() call: thetaMagnitudeSq² < 24e-6. */
579+
if (thetaMagnitudeSq * thetaMagnitudeSq < 24.0e-6f) {
551580
quaternionScale(&deltaQ, &deltaQ, 1.0f - thetaMagnitudeSq / 6.0f);
552581
deltaQ.q0 = 1.0f - thetaMagnitudeSq / 2.0f;
553582
}
@@ -559,7 +588,20 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
559588

560589
// Calculate final orientation and renormalize
561590
quaternionMultiply(&orientation, &orientation, &deltaQ);
562-
quaternionNormalize(&orientation, &orientation);
591+
/* Opt 3: first-order Newton renormalization avoids sqrt() and 4 divides.
592+
* At 1 kHz the quaternion norm drifts by < 1e-6 per step, so normSq = 1 + ε
593+
* with |ε| ≪ 1. The identity 1/sqrt(x) ≈ (3-x)/2 is accurate to O(ε²) ≈ 1e-12
594+
* — well within float precision. imuCheckAndResetOrientationQuaternion() below
595+
* catches any catastrophic norm deviation that this approximation cannot correct. */
596+
{
597+
const float normSq = orientation.q0 * orientation.q0 + orientation.q1 * orientation.q1
598+
+ orientation.q2 * orientation.q2 + orientation.q3 * orientation.q3;
599+
const float scale = (3.0f - normSq) * 0.5f;
600+
orientation.q0 *= scale;
601+
orientation.q1 *= scale;
602+
orientation.q2 *= scale;
603+
orientation.q3 *= scale;
604+
}
563605
}
564606

565607
// Check for invalid quaternion and reset to previous known good one

src/main/flight/imu.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ typedef struct imuRuntimeConfig_s {
6565
float dcm_ki_acc;
6666
float dcm_kp_mag;
6767
float dcm_ki_mag;
68+
/* Precomputed anti-windup limit for imuMahonyAHRSupdate(): equals
69+
* DEGREES_TO_RADIANS(2) * (dcm_kp_acc + dcm_kp_mag) / 2.
70+
* Updated once by imuConfigure() whenever settings are saved, so the
71+
* hot PID path reads a single float instead of doing arithmetic. */
72+
float dcm_i_limit;
6873
uint8_t small_angle;
6974
} imuRuntimeConfig_t;
7075

0 commit comments

Comments
 (0)