2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
23 #include "unittest_macros.h"
24 #include "gtest/gtest.h"
25 #include "build/debug.h"
27 bool simulatedThrottleRaised
= true;
28 float simulatedSetpointRate
[3] = { 0,0,0 };
29 float simulatedPrevSetpointRate
[3] = { 0,0,0 };
30 float simulatedRcDeflection
[3] = { 0,0,0 };
31 float simulatedMaxRcDeflectionAbs
= 0;
32 float simulatedMixerGetRcThrottle
= 0;
33 float simulatedRcCommandDelta
[3] = { 1,1,1 };
34 float simulatedRawSetpoint
[3] = { 0,0,0 };
35 float simulatedMaxRate
[3] = { 670,670,670 };
36 float simulatedFeedforward
[3] = { 0,0,0 };
37 float simulatedMotorMixRange
= 0.0f
;
39 int16_t debug
[DEBUG16_VALUE_COUNT
];
45 #include "build/debug.h"
47 #include "common/axis.h"
48 #include "common/maths.h"
49 #include "common/filter.h"
51 #include "config/config.h"
52 #include "config/config_reset.h"
54 #include "drivers/sound_beeper.h"
55 #include "drivers/time.h"
57 #include "fc/controlrate_profile.h"
61 #include "fc/rc_controls.h"
62 #include "fc/runtime_config.h"
64 #include "flight/imu.h"
65 #include "flight/mixer.h"
66 #include "flight/pid.h"
67 #include "flight/pid_init.h"
68 #include "flight/position.h"
73 #include "pg/pg_ids.h"
78 #include "sensors/gyro.h"
79 #include "sensors/acceleration.h"
83 attitudeEulerAngles_t attitude
;
85 rxRuntimeState_t rxRuntimeState
= {};
87 PG_REGISTER(accelerometerConfig_t
, accelerometerConfig
, PG_ACCELEROMETER_CONFIG
, 0);
88 PG_REGISTER(systemConfig_t
, systemConfig
, PG_SYSTEM_CONFIG
, 2);
89 PG_REGISTER(positionConfig_t
, positionConfig
, PG_SYSTEM_CONFIG
, 4);
91 bool unitLaunchControlActive
= false;
92 launchControlMode_e unitLaunchControlMode
= LAUNCH_CONTROL_MODE_NORMAL
;
94 float getMotorMixRange(void) { return simulatedMotorMixRange
; }
95 float getSetpointRate(int axis
) { return simulatedSetpointRate
[axis
]; }
96 bool wasThrottleRaised(void) { return simulatedThrottleRaised
; }
97 float getRcDeflectionAbs(int axis
) { return fabsf(simulatedRcDeflection
[axis
]); }
99 // used by auto-disarm code
100 float getMaxRcDeflectionAbs() { return fabsf(simulatedMaxRcDeflectionAbs
); }
101 float mixerGetRcThrottle() { return fabsf(simulatedMixerGetRcThrottle
); }
104 bool isBelowLandingAltitude(void) { return false; }
106 void systemBeep(bool) { }
107 bool gyroOverflowDetected(void) { return false; }
108 float getRcDeflection(int axis
) { return simulatedRcDeflection
[axis
]; }
109 float getRcDeflectionRaw(int axis
) { return simulatedRcDeflection
[axis
]; }
110 float getRawSetpoint(int axis
) { return simulatedRawSetpoint
[axis
]; }
111 float getFeedforward(int axis
) {
112 return simulatedSetpointRate
[axis
] - simulatedPrevSetpointRate
[axis
];
114 void beeperConfirmationBeeps(uint8_t) { }
115 bool isLaunchControlActive(void) {return unitLaunchControlActive
; }
116 void disarm(flightLogDisarmReason_e
) { }
117 float getMaxRcRate(int axis
)
120 float maxRate
= simulatedMaxRate
[axis
];
123 void initRcProcessing(void) { }
126 pidProfile_t
*pidProfile
;
130 // Always use same defaults for testing in future releases even when defaults change
131 void setDefaultTestSettings(void)
134 pidProfile
= pidProfilesMutable(1);
135 pidProfile
->pid
[PID_ROLL
] = { 40, 40, 30, 65, 0 };
136 pidProfile
->pid
[PID_PITCH
] = { 58, 50, 35, 60, 0 };
137 pidProfile
->pid
[PID_YAW
] = { 70, 45, 20, 60, 0 };
138 pidProfile
->pid
[PID_LEVEL
] = { 50, 50, 75, 50, 0 };
140 // Compensate for the upscaling done without 'use_integrated_yaw'
141 pidProfile
->pid
[PID_YAW
].I
= pidProfile
->pid
[PID_YAW
].I
/ 2.5f
;
143 pidProfile
->pidSumLimit
= PIDSUM_LIMIT
; // 500
144 pidProfile
->pidSumLimitYaw
= PIDSUM_LIMIT_YAW
; // 400
145 pidProfile
->yaw_lowpass_hz
= 0;
146 pidProfile
->dterm_lpf1_static_hz
= 100;
147 pidProfile
->dterm_lpf2_static_hz
= 0;
148 pidProfile
->dterm_notch_hz
= 260;
149 pidProfile
->dterm_notch_cutoff
= 160;
150 pidProfile
->dterm_lpf1_type
= FILTER_BIQUAD
;
151 pidProfile
->itermWindup
= 80;
152 pidProfile
->pidAtMinThrottle
= PID_STABILISATION_ON
;
153 pidProfile
->angle_limit
= 60;
154 pidProfile
->feedforward_transition
= 100;
155 pidProfile
->yawRateAccelLimit
= 100;
156 pidProfile
->rateAccelLimit
= 0;
157 pidProfile
->anti_gravity_gain
= 10;
158 pidProfile
->crash_time
= 500;
159 pidProfile
->crash_delay
= 0;
160 pidProfile
->crash_recovery_angle
= 10;
161 pidProfile
->crash_recovery_rate
= 100;
162 pidProfile
->crash_dthreshold
= 50;
163 pidProfile
->crash_gthreshold
= 400;
164 pidProfile
->crash_setpoint_threshold
= 350;
165 pidProfile
->crash_recovery
= PID_CRASH_RECOVERY_OFF
;
166 pidProfile
->horizon_limit_degrees
= 135;
167 pidProfile
->horizon_ignore_sticks
= false;
168 pidProfile
->crash_limit_yaw
= 200;
169 pidProfile
->itermLimit
= 150;
170 pidProfile
->throttle_boost
= 0;
171 pidProfile
->throttle_boost_cutoff
= 15;
172 pidProfile
->iterm_rotation
= false;
173 pidProfile
->iterm_relax
= ITERM_RELAX_OFF
,
174 pidProfile
->iterm_relax_cutoff
= 11,
175 pidProfile
->iterm_relax_type
= ITERM_RELAX_SETPOINT
,
176 pidProfile
->abs_control_gain
= 0,
177 pidProfile
->launchControlMode
= LAUNCH_CONTROL_MODE_NORMAL
,
178 pidProfile
->launchControlGain
= 40,
179 pidProfile
->level_race_mode
= false,
181 gyro
.targetLooptime
= 8000;
184 timeUs_t
currentTestTime(void)
186 return targetPidLooptime
* loopIter
++;
192 pidRuntime
.tpaFactor
= 1.0f
;
193 simulatedMotorMixRange
= 0.0f
;
195 pidStabilisationState(PID_STABILISATION_OFF
);
196 DISABLE_ARMING_FLAG(ARMED
);
198 setDefaultTestSettings();
199 for (int axis
= FD_ROLL
; axis
<= FD_YAW
; axis
++) {
205 pidData
[axis
].Sum
= 0;
206 simulatedSetpointRate
[axis
] = 0;
207 simulatedRcDeflection
[axis
] = 0;
208 simulatedRawSetpoint
[axis
] = 0;
209 gyro
.gyroADCf
[axis
] = 0;
211 attitude
.values
.roll
= 0;
212 attitude
.values
.pitch
= 0;
213 attitude
.values
.yaw
= 0;
216 unitLaunchControlActive
= false;
217 pidProfile
->launchControlMode
= unitLaunchControlMode
;
219 loadControlRateProfile();
221 // Run pidloop for a while after reset
222 for (int loop
= 0; loop
< 20; loop
++) {
223 pidController(pidProfile
, currentTestTime());
227 void setStickPosition(int axis
, float stickRatio
)
229 simulatedPrevSetpointRate
[axis
] = simulatedSetpointRate
[axis
];
230 simulatedSetpointRate
[axis
] = 1998.0f
* stickRatio
;
231 simulatedRcDeflection
[axis
] = stickRatio
;
234 // All calculations will have 10% tolerance
235 float calculateTolerance(float input
)
237 return fabsf(input
* 0.1f
);
240 TEST(pidControllerTest
, testInitialisation
)
244 // In initial state PIDsums should be 0
245 for (int axis
= 0; axis
<= FD_YAW
; axis
++) {
246 EXPECT_FLOAT_EQ(0, pidData
[axis
].P
);
247 EXPECT_FLOAT_EQ(0, pidData
[axis
].I
);
248 EXPECT_FLOAT_EQ(0, pidData
[axis
].D
);
252 TEST(pidControllerTest
, testStabilisationDisabled
)
254 ENABLE_ARMING_FLAG(ARMED
);
255 // Run few loops to make sure there is no error building up when stabilisation disabled
257 for (int loop
= 0; loop
< 10; loop
++) {
258 pidController(pidProfile
, currentTestTime());
260 // PID controller should not do anything, while stabilisation disabled
261 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].P
);
262 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].P
);
263 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
264 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].I
);
265 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].I
);
266 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
267 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
268 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
269 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
273 TEST(pidControllerTest
, testPidLoop
)
275 // Make sure to start with fresh values
277 ENABLE_ARMING_FLAG(ARMED
);
278 pidStabilisationState(PID_STABILISATION_ON
);
280 pidController(pidProfile
, currentTestTime());
282 // Loop 1 - Expecting zero since there is no error
283 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].P
);
284 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].P
);
285 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
286 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].I
);
287 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].I
);
288 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
289 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
290 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
291 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
293 // Add some rotation on ROLL to generate error
294 gyro
.gyroADCf
[FD_ROLL
] = 100;
295 pidController(pidProfile
, currentTestTime());
297 // Loop 2 - Expect PID loop reaction to ROLL error
298 EXPECT_NEAR(-128.1, pidData
[FD_ROLL
].P
, calculateTolerance(-128.1));
299 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].P
);
300 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
301 EXPECT_NEAR(-7.8, pidData
[FD_ROLL
].I
, calculateTolerance(-7.8));
302 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].I
);
303 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
304 EXPECT_NEAR(-198.4, pidData
[FD_ROLL
].D
, calculateTolerance(-198.4));
305 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
306 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
308 // Add some rotation on PITCH to generate error
309 gyro
.gyroADCf
[FD_PITCH
] = -100;
310 pidController(pidProfile
, currentTestTime());
312 // Loop 3 - Expect PID loop reaction to PITCH error, ROLL is still in error
313 EXPECT_NEAR(-128.1, pidData
[FD_ROLL
].P
, calculateTolerance(-128.1));
314 EXPECT_NEAR(185.8, pidData
[FD_PITCH
].P
, calculateTolerance(185.8));
315 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
316 EXPECT_NEAR(-15.6, pidData
[FD_ROLL
].I
, calculateTolerance(-15.6));
317 EXPECT_NEAR(9.8, pidData
[FD_PITCH
].I
, calculateTolerance(9.8));
318 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
319 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
320 EXPECT_NEAR(231.4, pidData
[FD_PITCH
].D
, calculateTolerance(231.4));
321 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
323 // Add some rotation on YAW to generate error, but not enough to trigger pidSumLimitYaw
324 gyro
.gyroADCf
[FD_YAW
] = 10;
325 pidController(pidProfile
, currentTestTime());
327 // Loop 4 - Expect PID loop reaction to PITCH error, ROLL and PITCH are still in error
328 EXPECT_NEAR(-128.1, pidData
[FD_ROLL
].P
, calculateTolerance(-128.1));
329 EXPECT_NEAR(185.8, pidData
[FD_PITCH
].P
, calculateTolerance(185.8));
330 EXPECT_NEAR(-22.4, pidData
[FD_YAW
].P
, calculateTolerance(-22.4));
331 EXPECT_NEAR(-23.5, pidData
[FD_ROLL
].I
, calculateTolerance(-23.5));
332 EXPECT_NEAR(19.6, pidData
[FD_PITCH
].I
, calculateTolerance(19.6));
333 EXPECT_NEAR(-0.87, pidData
[FD_YAW
].I
, calculateTolerance(-0.87));
334 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
335 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
337 // Simulate Iterm growth if not saturated
338 pidController(pidProfile
, currentTestTime());
339 EXPECT_NEAR(-31.3, pidData
[FD_ROLL
].I
, calculateTolerance(-31.3));
340 EXPECT_NEAR(29.3, pidData
[FD_PITCH
].I
, calculateTolerance(29.3));
341 EXPECT_NEAR(-1.76, pidData
[FD_YAW
].I
, calculateTolerance(-1.76));
342 EXPECT_NEAR(-24.2, pidData
[FD_YAW
].Sum
, calculateTolerance(-24.2));
344 // Match the stick to gyro to stop error
345 simulatedSetpointRate
[FD_ROLL
] = 100;
346 simulatedSetpointRate
[FD_PITCH
] = -100;
347 simulatedSetpointRate
[FD_YAW
] = 10; // error
349 pidController(pidProfile
, currentTestTime());
350 // Iterm is stalled as it is not accumulating anymore
351 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].P
);
352 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].P
);
353 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
354 EXPECT_NEAR(-31.3, pidData
[FD_ROLL
].I
, calculateTolerance(-31.3));
355 EXPECT_NEAR(29.3, pidData
[FD_PITCH
].I
, calculateTolerance(29.3));
356 EXPECT_NEAR(-1.76, pidData
[FD_YAW
].I
, calculateTolerance(-1.76));
357 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
358 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
359 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
362 for(int loop
= 0; loop
< 5; loop
++) {
363 pidController(pidProfile
, currentTestTime());
365 // Iterm is stalled as it is not accumulating anymore
366 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].P
);
367 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].P
);
368 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
369 EXPECT_NEAR(-31.3, pidData
[FD_ROLL
].I
, calculateTolerance(-31.3));
370 EXPECT_NEAR(29.3, pidData
[FD_PITCH
].I
, calculateTolerance(29.3));
371 EXPECT_NEAR(-1.76, pidData
[FD_YAW
].I
, calculateTolerance(-1.76));
372 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
373 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
374 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
376 // Now disable Stabilisation
377 pidStabilisationState(PID_STABILISATION_OFF
);
378 pidController(pidProfile
, currentTestTime());
380 // Should all be zero again
381 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].P
);
382 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].P
);
383 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
384 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].I
);
385 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].I
);
386 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
387 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
388 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
389 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
392 TEST(pidControllerTest
, testPidLevel
)
394 // Make sure to start with fresh values
396 ENABLE_ARMING_FLAG(ARMED
);
397 pidStabilisationState(PID_STABILISATION_ON
);
399 // Test Angle mode response
400 enableFlightMode(ANGLE_MODE
);
401 float currentPidSetpointRoll
= 0;
402 float currentPidSetpointPitch
= 0;
403 float calculatedAngleSetpoint
= 0;
404 rollAndPitchTrims_t angleTrim
= { { 0, 0 } };
406 calculatedAngleSetpoint
= pidLevel(FD_ROLL
, pidProfile
, &angleTrim
, currentPidSetpointRoll
, calcHorizonLevelStrength());
407 EXPECT_FLOAT_EQ(0, calculatedAngleSetpoint
);
408 calculatedAngleSetpoint
= pidLevel(FD_PITCH
, pidProfile
, &angleTrim
, currentPidSetpointPitch
, calcHorizonLevelStrength());
409 EXPECT_FLOAT_EQ(0, calculatedAngleSetpoint
);
411 currentPidSetpointRoll
= 200;
412 calculatedAngleSetpoint
= pidLevel(FD_ROLL
, pidProfile
, &angleTrim
, currentPidSetpointRoll
, calcHorizonLevelStrength());
413 EXPECT_FLOAT_EQ(51.456356, calculatedAngleSetpoint
);
414 currentPidSetpointPitch
= -200;
415 calculatedAngleSetpoint
= pidLevel(FD_PITCH
, pidProfile
, &angleTrim
, currentPidSetpointPitch
, calcHorizonLevelStrength());
416 EXPECT_FLOAT_EQ(-51.456356, calculatedAngleSetpoint
);
418 currentPidSetpointRoll
= 400;
419 calculatedAngleSetpoint
= pidLevel(FD_ROLL
, pidProfile
, &angleTrim
, currentPidSetpointRoll
, calcHorizonLevelStrength());
420 EXPECT_FLOAT_EQ(128.94597, calculatedAngleSetpoint
);
421 currentPidSetpointPitch
= -400;
422 calculatedAngleSetpoint
= pidLevel(FD_PITCH
, pidProfile
, &angleTrim
, currentPidSetpointPitch
, calcHorizonLevelStrength());
423 EXPECT_FLOAT_EQ(-128.94597, calculatedAngleSetpoint
);
425 // Test attitude response
426 attitude
.values
.roll
= -275;
427 attitude
.values
.pitch
= 275;
428 calculatedAngleSetpoint
= pidLevel(FD_ROLL
, pidProfile
, &angleTrim
, currentPidSetpointRoll
, calcHorizonLevelStrength());
429 EXPECT_FLOAT_EQ(242.76686, calculatedAngleSetpoint
);
430 calculatedAngleSetpoint
= pidLevel(FD_PITCH
, pidProfile
, &angleTrim
, currentPidSetpointPitch
, calcHorizonLevelStrength());
431 EXPECT_FLOAT_EQ(-242.76686, calculatedAngleSetpoint
);
433 // Disable ANGLE_MODE
434 disableFlightMode(ANGLE_MODE
);
435 calculatedAngleSetpoint
= pidLevel(FD_ROLL
, pidProfile
, &angleTrim
, currentPidSetpointRoll
, calcHorizonLevelStrength());
436 EXPECT_FLOAT_EQ(393.44571, calculatedAngleSetpoint
);
437 calculatedAngleSetpoint
= pidLevel(FD_PITCH
, pidProfile
, &angleTrim
, currentPidSetpointPitch
, calcHorizonLevelStrength());
438 EXPECT_FLOAT_EQ(-392.88422, calculatedAngleSetpoint
);
440 // Test level mode expo
441 enableFlightMode(ANGLE_MODE
);
442 attitude
.values
.roll
= 0;
443 attitude
.values
.pitch
= 0;
444 currentPidSetpointRoll
= 400;
445 currentPidSetpointPitch
= -400;
446 // need to set some rates type and some expo here ??? HELP !!
447 calculatedAngleSetpoint
= pidLevel(FD_ROLL
, pidProfile
, &angleTrim
, currentPidSetpointRoll
, calcHorizonLevelStrength());
448 EXPECT_FLOAT_EQ(231.55479, calculatedAngleSetpoint
);
449 calculatedAngleSetpoint
= pidLevel(FD_PITCH
, pidProfile
, &angleTrim
, currentPidSetpointPitch
, calcHorizonLevelStrength());
450 EXPECT_FLOAT_EQ(-231.55479, calculatedAngleSetpoint
);
454 TEST(pidControllerTest
, testPidHorizon
)
457 ENABLE_ARMING_FLAG(ARMED
);
458 pidStabilisationState(PID_STABILISATION_ON
);
459 enableFlightMode(HORIZON_MODE
);
461 // Test stick response greater than default limit of 0.75
462 setStickPosition(FD_ROLL
, 0.76f
);
463 setStickPosition(FD_PITCH
, -0.76f
);
464 EXPECT_FLOAT_EQ(0.0f
, calcHorizonLevelStrength());
466 // Return sticks to center, should expect some levelling, but will be delayed
467 setStickPosition(FD_ROLL
, 0);
468 setStickPosition(FD_PITCH
, 0);
469 EXPECT_FLOAT_EQ(0.0078740157, calcHorizonLevelStrength());
471 // Test small stick response when flat, considering delay
472 setStickPosition(FD_ROLL
, 0.1f
);
473 setStickPosition(FD_PITCH
, -0.1f
);
474 EXPECT_NEAR(0.01457f
, calcHorizonLevelStrength(), calculateTolerance(0.01457));
476 // Test larger stick response when flat
477 setStickPosition(FD_ROLL
, 0.5f
);
478 setStickPosition(FD_PITCH
, -0.5f
);
479 EXPECT_NEAR(0.0166, calcHorizonLevelStrength(), calculateTolerance(0.0166));
481 // set attitude of craft to 90 degrees
482 attitude
.values
.roll
= 900;
483 attitude
.values
.pitch
= 900;
485 // Test centered sticks at 90 degrees
486 setStickPosition(FD_ROLL
, 0);
487 setStickPosition(FD_PITCH
, 0);
488 // with gain of 50, and max angle of 135 deg, strength = 0.5 * (135-90) / 90 ie 0.5 * 45/136 or 0.5 * 0.333 = 0.166
489 EXPECT_NEAR(0.0193f
, calcHorizonLevelStrength(), calculateTolerance(0.0193));
491 // Test small stick response at 90 degrees
492 setStickPosition(FD_ROLL
, 0.1f
);
493 setStickPosition(FD_PITCH
, -0.1f
);
494 EXPECT_NEAR(0.0213f
, calcHorizonLevelStrength(), calculateTolerance(0.0213));
496 // Test larger stick response at 90 degrees
497 setStickPosition(FD_ROLL
, 0.5f
);
498 setStickPosition(FD_PITCH
, -0.5f
);
499 EXPECT_NEAR(0.0218f
, calcHorizonLevelStrength(), calculateTolerance(0.0218));
501 // set attitude of craft to 120 degrees, inside limit of 135
502 attitude
.values
.roll
= 1200;
503 attitude
.values
.pitch
= 1200;
505 // Test centered sticks at 120 degrees
506 setStickPosition(FD_ROLL
, 0);
507 setStickPosition(FD_PITCH
, 0);
508 EXPECT_NEAR(0.0224f
, calcHorizonLevelStrength(), calculateTolerance(0.0224));
510 // Test small stick response at 120 degrees
511 setStickPosition(FD_ROLL
, 0.1f
);
512 setStickPosition(FD_PITCH
, -0.1f
);
513 EXPECT_NEAR(0.0228f
, calcHorizonLevelStrength(), calculateTolerance(0.0228));
515 // Test larger stick response at 120 degrees
516 setStickPosition(FD_ROLL
, 0.5f
);
517 setStickPosition(FD_PITCH
, -0.5f
);
518 EXPECT_NEAR(0.018f
, calcHorizonLevelStrength(), calculateTolerance(0.018));
520 // set attitude of craft to 1500 degrees, outside limit of 135
521 attitude
.values
.roll
= 1500;
522 attitude
.values
.pitch
= 1500;
524 // Test centered sticks at 150 degrees - should be zero at any stick angle
525 setStickPosition(FD_ROLL
, 0);
526 setStickPosition(FD_PITCH
, 0);
527 EXPECT_NEAR(0.0f
, calcHorizonLevelStrength(), calculateTolerance(0.0));
529 setStickPosition(FD_ROLL
, 0.5f
);
530 setStickPosition(FD_PITCH
, -0.5f
);
531 EXPECT_NEAR(0.0f
, calcHorizonLevelStrength(), calculateTolerance(0.0));
539 TEST(pidControllerTest
, testMixerSaturation
)
543 ENABLE_ARMING_FLAG(ARMED
);
544 pidStabilisationState(PID_STABILISATION_ON
);
546 pidRuntime
.itermLimit
= 400;
547 pidRuntime
.itermLimitYaw
= 320;
549 // Test full stick response
550 setStickPosition(FD_ROLL
, 1.0f
);
551 setStickPosition(FD_PITCH
, -1.0f
);
552 setStickPosition(FD_YAW
, 1.0f
);
553 pidController(pidProfile
, currentTestTime());
555 // Expect iterm accumulation for all axes because at this point, pidSum is not at limit
556 EXPECT_NEAR(156.2f
, pidData
[FD_ROLL
].I
, calculateTolerance(156.2f
));
557 EXPECT_NEAR(-195.3f
, pidData
[FD_PITCH
].I
, calculateTolerance(-195.3f
));
558 EXPECT_NEAR(7.0f
, pidData
[FD_YAW
].I
, calculateTolerance(7.0f
));
560 // ????? why such slow yaw iTerm growth ?? this is not what I see in the real logs == strange
562 // Check for iterm growth, should not reach limits yet
563 pidController(pidProfile
, currentTestTime());
564 EXPECT_NEAR(312.4f
, pidData
[FD_ROLL
].I
, calculateTolerance(312.4f
));
565 EXPECT_NEAR(-390.6f
, pidData
[FD_PITCH
].I
, calculateTolerance(-390.6));
566 EXPECT_NEAR(21.1f
, pidData
[FD_YAW
].I
, calculateTolerance(21.1));
568 // Expect iterm roll + pitch to stop at limit of 400
569 // yaw is still growing,
570 pidController(pidProfile
, currentTestTime());
571 EXPECT_NEAR(400.0f
, pidData
[FD_ROLL
].I
, calculateTolerance(400.0f
));
572 EXPECT_NEAR(-400.0f
, pidData
[FD_PITCH
].I
, calculateTolerance(-400.0f
));
573 EXPECT_NEAR(42.2f
, pidData
[FD_YAW
].I
, calculateTolerance(42.2f
));
575 // run some more loops, check all iTerm values are at their limit
576 for (int loop
= 0; loop
< 7; loop
++) {
577 pidController(pidProfile
, currentTestTime());
579 EXPECT_NEAR(400, pidData
[FD_ROLL
].I
, calculateTolerance(400));
580 EXPECT_NEAR(-400, pidData
[FD_PITCH
].I
, calculateTolerance(-400));
581 EXPECT_NEAR(320, pidData
[FD_YAW
].I
, calculateTolerance(320));
583 // Test that the added i term gain from Anti Gravity
586 ENABLE_ARMING_FLAG(ARMED
);
587 pidStabilisationState(PID_STABILISATION_ON
);
588 pidRuntime
.itermLimit
= 400;
589 pidRuntime
.itermLimitYaw
= 320;
591 setStickPosition(FD_ROLL
, 1.0f
);
592 setStickPosition(FD_PITCH
, -1.0f
);
593 setStickPosition(FD_YAW
, 1.0f
);
594 const bool prevAgState
= pidRuntime
.antiGravityEnabled
;
595 const float prevAgTrhottleD
= pidRuntime
.antiGravityThrottleD
;
596 pidRuntime
.antiGravityEnabled
= true;
597 pidRuntime
.antiGravityThrottleD
= 1.0;
599 pidController(pidProfile
, currentTestTime());
601 // Expect more iterm accumulation than before on pitch and roll, no change on yaw
602 // without antigravity values were 156, 195, 7
603 EXPECT_NEAR(210.6, pidData
[FD_ROLL
].I
, calculateTolerance(210.6f
));
604 EXPECT_NEAR(-249.6f
, pidData
[FD_PITCH
].I
, calculateTolerance(-249.6f
));
605 EXPECT_NEAR(7.0f
, pidData
[FD_YAW
].I
, calculateTolerance(7.0f
));
607 // run again and should expect to hit the limit on pitch and roll but yaw unaffected
608 pidController(pidProfile
, currentTestTime());
609 EXPECT_NEAR(400, pidData
[FD_ROLL
].I
, calculateTolerance(400));
610 EXPECT_NEAR(-400, pidData
[FD_PITCH
].I
, calculateTolerance(-400));
611 EXPECT_NEAR(21.0f
, pidData
[FD_YAW
].I
, calculateTolerance(21.0f
));
613 pidRuntime
.antiGravityEnabled
= prevAgState
;
614 pidRuntime
.antiGravityThrottleD
= prevAgTrhottleD
;
616 // Test that i term is limited on yaw at 320 when only yaw is saturated
618 ENABLE_ARMING_FLAG(ARMED
);
619 pidRuntime
.itermLimit
= 400;
620 pidRuntime
.itermLimitYaw
= 320;
622 pidStabilisationState(PID_STABILISATION_ON
);
623 setStickPosition(FD_ROLL
, 0.0f
);
624 setStickPosition(FD_PITCH
, 0.0f
);
625 setStickPosition(FD_YAW
, 0.5f
);
627 for (int loop
= 0; loop
< 7; loop
++) {
628 pidController(pidProfile
, currentTestTime());
631 EXPECT_NEAR(0, pidData
[FD_ROLL
].I
, calculateTolerance(0));
632 EXPECT_NEAR(0, pidData
[FD_PITCH
].I
, calculateTolerance(0));
633 EXPECT_NEAR(197, pidData
[FD_YAW
].I
, calculateTolerance(197));
635 pidController(pidProfile
, currentTestTime());
636 EXPECT_NEAR(253, pidData
[FD_YAW
].I
, calculateTolerance(320));
638 pidController(pidProfile
, currentTestTime());
639 EXPECT_NEAR(320, pidData
[FD_YAW
].I
, calculateTolerance(320));
641 pidController(pidProfile
, currentTestTime());
642 EXPECT_NEAR(0, pidData
[FD_ROLL
].I
, calculateTolerance(0));
643 EXPECT_NEAR(0, pidData
[FD_PITCH
].I
, calculateTolerance(0));
644 EXPECT_NEAR(320, pidData
[FD_YAW
].I
, calculateTolerance(320));
647 TEST(pidControllerTest
, testiTermWindup
)
650 ENABLE_ARMING_FLAG(ARMED
);
651 // simulate the outcome with iterm_windup of 50
652 pidRuntime
.itermLimit
= 200;
653 pidRuntime
.itermLimitYaw
= 160;
655 pidStabilisationState(PID_STABILISATION_ON
);
656 setStickPosition(FD_ROLL
, 0.12f
);
657 setStickPosition(FD_PITCH
, 0.12f
);
658 setStickPosition(FD_YAW
, 0.12f
);
660 for (int loop
= 0; loop
< 7; loop
++) {
661 pidController(pidProfile
, currentTestTime());
664 EXPECT_NEAR(131, pidData
[FD_ROLL
].I
, calculateTolerance(131));
665 EXPECT_NEAR(164, pidData
[FD_PITCH
].I
, calculateTolerance(164));
666 EXPECT_NEAR(126, pidData
[FD_YAW
].I
, calculateTolerance(126));
668 pidController(pidProfile
, currentTestTime());
669 EXPECT_NEAR(150, pidData
[FD_ROLL
].I
, calculateTolerance(150));
670 EXPECT_NEAR(200, pidData
[FD_PITCH
].I
, calculateTolerance(200));
671 EXPECT_NEAR(137, pidData
[FD_YAW
].I
, calculateTolerance(137));
673 pidController(pidProfile
, currentTestTime());
674 EXPECT_NEAR(169, pidData
[FD_ROLL
].I
, calculateTolerance(200));
675 EXPECT_NEAR(200, pidData
[FD_PITCH
].I
, calculateTolerance(200));
676 EXPECT_NEAR(160, pidData
[FD_YAW
].I
, calculateTolerance(160));
678 pidController(pidProfile
, currentTestTime());
679 EXPECT_NEAR(200, pidData
[FD_ROLL
].I
, calculateTolerance(200));
680 EXPECT_NEAR(200, pidData
[FD_PITCH
].I
, calculateTolerance(200));
681 EXPECT_NEAR(160, pidData
[FD_YAW
].I
, calculateTolerance(320));
684 // TODO - Add more scenarios
685 TEST(pidControllerTest
, testCrashRecoveryMode
)
688 pidProfile
->crash_recovery
= PID_CRASH_RECOVERY_ON
;
690 ENABLE_ARMING_FLAG(ARMED
);
691 pidStabilisationState(PID_STABILISATION_ON
);
692 sensorsSet(SENSOR_ACC
);
694 EXPECT_FALSE(crashRecoveryModeActive());
696 int loopsToCrashTime
= (int)((pidProfile
->crash_time
* 1000) / targetPidLooptime
) + 1;
698 // generate crash detection for roll axis
699 gyro
.gyroADCf
[FD_ROLL
] = 800;
700 simulatedMotorMixRange
= 1.2f
;
701 for (int loop
=0; loop
<= loopsToCrashTime
; loop
++) {
702 gyro
.gyroADCf
[FD_ROLL
] += gyro
.gyroADCf
[FD_ROLL
];
703 // advance the time to avoid initialized state prevention of crash recovery
704 pidController(pidProfile
, currentTestTime() + 2000000);
707 EXPECT_TRUE(crashRecoveryModeActive());
708 // Add additional verifications
711 TEST(pidControllerTest
, testFeedForward
)
712 // NOTE: THIS DOES NOT TEST THE FEEDFORWARD CALCULATIONS, which are now in rc.c, and return setpointDelta
713 // This test only validates the feedforward value calculated in pid.c for a given simulated setpointDelta
716 ENABLE_ARMING_FLAG(ARMED
);
717 pidStabilisationState(PID_STABILISATION_ON
);
719 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].F
);
720 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].F
);
721 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].F
);
723 // Move the sticks fully
724 setStickPosition(FD_ROLL
, 1.0f
);
725 setStickPosition(FD_PITCH
, -1.0f
);
726 setStickPosition(FD_YAW
, -1.0f
);
728 pidController(pidProfile
, currentTestTime());
730 EXPECT_NEAR(17.86, pidData
[FD_ROLL
].F
, calculateTolerance(17.86));
731 EXPECT_NEAR(-16.49, pidData
[FD_PITCH
].F
, calculateTolerance(-16.49));
732 EXPECT_NEAR(-16.49, pidData
[FD_YAW
].F
, calculateTolerance(-16.49));
734 // Bring sticks back to half way
735 setStickPosition(FD_ROLL
, 0.5f
);
736 setStickPosition(FD_PITCH
, -0.5f
);
737 setStickPosition(FD_YAW
, -0.5f
);
739 pidController(pidProfile
, currentTestTime());
741 EXPECT_NEAR(-8.93, pidData
[FD_ROLL
].F
, calculateTolerance(-8.93));
742 EXPECT_NEAR(8.24, pidData
[FD_PITCH
].F
, calculateTolerance(8.24));
743 EXPECT_NEAR(8.24, pidData
[FD_YAW
].F
, calculateTolerance(8.24));
745 // Bring sticks back to two tenths out
746 setStickPosition(FD_ROLL
, 0.2f
);
747 setStickPosition(FD_PITCH
, -0.2f
);
748 setStickPosition(FD_YAW
, -0.2f
);
750 pidController(pidProfile
, currentTestTime());
752 EXPECT_NEAR(-5.36, pidData
[FD_ROLL
].F
, calculateTolerance(-5.36));
753 EXPECT_NEAR(4.95, pidData
[FD_PITCH
].F
, calculateTolerance(4.95));
754 EXPECT_NEAR(4.95, pidData
[FD_YAW
].F
, calculateTolerance(4.95));
756 // Bring sticks back to one tenth out, to give a difference of 0.1
757 setStickPosition(FD_ROLL
, 0.1f
);
758 setStickPosition(FD_PITCH
, -0.1f
);
759 setStickPosition(FD_YAW
, -0.1f
);
761 pidController(pidProfile
, currentTestTime());
763 EXPECT_NEAR(-1.79, pidData
[FD_ROLL
].F
, calculateTolerance(-1.79));
764 EXPECT_NEAR(1.65, pidData
[FD_PITCH
].F
, calculateTolerance(1.65));
765 EXPECT_NEAR(1.65, pidData
[FD_YAW
].F
, calculateTolerance(1.65));
767 // Center the sticks, giving the same delta value as before, should return the same feedforward
768 setStickPosition(FD_ROLL
, 0.0f
);
769 setStickPosition(FD_PITCH
, 0.0f
);
770 setStickPosition(FD_YAW
, 0.0f
);
772 pidController(pidProfile
, currentTestTime());
774 EXPECT_NEAR(-1.79, pidData
[FD_ROLL
].F
, calculateTolerance(-1.79));
775 EXPECT_NEAR(1.65, pidData
[FD_PITCH
].F
, calculateTolerance(1.65));
776 EXPECT_NEAR(1.65, pidData
[FD_YAW
].F
, calculateTolerance(1.65));
779 setStickPosition(FD_ROLL
, 0.0f
);
780 setStickPosition(FD_PITCH
, 0.0f
);
781 setStickPosition(FD_YAW
, 0.0f
);
783 pidController(pidProfile
, currentTestTime());
785 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].F
);
786 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].F
);
787 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].F
);
790 TEST(pidControllerTest
, testItermRelax
)
793 pidProfile
->iterm_relax
= ITERM_RELAX_RP
;
794 ENABLE_ARMING_FLAG(ARMED
);
795 pidStabilisationState(PID_STABILISATION_ON
);
797 pidProfile
->iterm_relax_type
= ITERM_RELAX_SETPOINT
;
800 float itermErrorRate
= 0;
801 float currentPidSetpoint
= 0;
804 applyItermRelax(FD_PITCH
, 0, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
805 EXPECT_FLOAT_EQ(itermErrorRate
, 0);
806 itermErrorRate
= -10;
807 currentPidSetpoint
= 10;
808 pidData
[FD_PITCH
].I
= 10;
810 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
812 EXPECT_NEAR(-8.16, itermErrorRate
, calculateTolerance(-8.16));
813 currentPidSetpoint
+= ITERM_RELAX_SETPOINT_THRESHOLD
;
814 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
815 EXPECT_NEAR(0, itermErrorRate
, calculateTolerance(0));
816 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
817 EXPECT_NEAR(0, itermErrorRate
, calculateTolerance(0));
819 pidProfile
->iterm_relax_type
= ITERM_RELAX_GYRO
;
822 currentPidSetpoint
= 100;
823 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
824 EXPECT_FLOAT_EQ(itermErrorRate
, 0);
826 itermErrorRate
= -10;
827 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
828 EXPECT_NEAR(7, itermErrorRate
, calculateTolerance(7));
830 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
831 EXPECT_NEAR(-10, itermErrorRate
, calculateTolerance(-10));
833 pidProfile
->iterm_relax
= ITERM_RELAX_RP_INC
;
836 itermErrorRate
= -10;
837 pidData
[FD_PITCH
].I
= 10;
838 currentPidSetpoint
= 10;
839 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
840 EXPECT_FLOAT_EQ(itermErrorRate
, -10);
842 pidData
[FD_PITCH
].I
= -10;
843 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
844 EXPECT_FLOAT_EQ(itermErrorRate
, 10);
845 itermErrorRate
= -10;
846 currentPidSetpoint
= 10;
847 applyItermRelax(FD_PITCH
, pidData
[FD_PITCH
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
848 EXPECT_FLOAT_EQ(itermErrorRate
, -100);
850 pidProfile
->iterm_relax_type
= ITERM_RELAX_SETPOINT
;
853 itermErrorRate
= -10;
854 currentPidSetpoint
= ITERM_RELAX_SETPOINT_THRESHOLD
;
855 applyItermRelax(FD_YAW
, pidData
[FD_YAW
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
856 EXPECT_FLOAT_EQ(itermErrorRate
, -10);
858 pidProfile
->iterm_relax
= ITERM_RELAX_RPY
;
860 applyItermRelax(FD_YAW
, pidData
[FD_YAW
].I
, gyroRate
, &itermErrorRate
, ¤tPidSetpoint
);
861 EXPECT_NEAR(-3.6, itermErrorRate
, calculateTolerance(-3.6));
864 // TODO - Add more tests
865 TEST(pidControllerTest
, testAbsoluteControl
)
868 pidProfile
->abs_control_gain
= 10;
870 ENABLE_ARMING_FLAG(ARMED
);
871 pidStabilisationState(PID_STABILISATION_ON
);
875 float itermErrorRate
= 10;
876 float currentPidSetpoint
= 10;
878 applyAbsoluteControl(FD_PITCH
, gyroRate
, ¤tPidSetpoint
, &itermErrorRate
);
880 EXPECT_NEAR(10.8, itermErrorRate
, calculateTolerance(10.8));
881 EXPECT_NEAR(10.8, currentPidSetpoint
, calculateTolerance(10.8));
883 applyAbsoluteControl(FD_PITCH
, gyroRate
, ¤tPidSetpoint
, &itermErrorRate
);
884 EXPECT_NEAR(10.8, itermErrorRate
, calculateTolerance(10.8));
885 EXPECT_NEAR(10.8, currentPidSetpoint
, calculateTolerance(10.8));
888 axisError
[FD_PITCH
] = -60;
889 applyAbsoluteControl(FD_PITCH
, gyroRate
, ¤tPidSetpoint
, &itermErrorRate
);
890 EXPECT_NEAR(-79.2, itermErrorRate
, calculateTolerance(-79.2));
891 EXPECT_NEAR(-79.2, currentPidSetpoint
, calculateTolerance(-79.2));
894 TEST(pidControllerTest
, testDtermFiltering
)
899 TEST(pidControllerTest
, testItermRotationHandling
)
904 rotateItermAndAxisError();
905 EXPECT_FLOAT_EQ(pidData
[FD_ROLL
].I
, 0);
906 EXPECT_FLOAT_EQ(pidData
[FD_PITCH
].I
, 0);
907 EXPECT_FLOAT_EQ(pidData
[FD_YAW
].I
, 0);
909 pidProfile
->iterm_rotation
= true;
912 rotateItermAndAxisError();
913 EXPECT_FLOAT_EQ(pidData
[FD_ROLL
].I
, 0);
914 EXPECT_FLOAT_EQ(pidData
[FD_PITCH
].I
, 0);
915 EXPECT_FLOAT_EQ(pidData
[FD_YAW
].I
, 0);
917 pidData
[FD_ROLL
].I
= 10;
918 pidData
[FD_PITCH
].I
= 1000;
919 pidData
[FD_YAW
].I
= 1000;
920 gyro
.gyroADCf
[FD_ROLL
] = -1000;
921 rotateItermAndAxisError();
922 EXPECT_FLOAT_EQ(pidData
[FD_ROLL
].I
, 10);
923 EXPECT_NEAR(860.37, pidData
[FD_PITCH
].I
, calculateTolerance(860.37));
924 EXPECT_NEAR(1139.6, pidData
[FD_YAW
].I
, calculateTolerance(1139.6));
926 pidProfile
->abs_control_gain
= 10;
928 pidData
[FD_ROLL
].I
= 10;
929 pidData
[FD_PITCH
].I
= 1000;
930 pidData
[FD_YAW
].I
= 1000;
932 gyro
.gyroADCf
[FD_ROLL
] = -1000;
933 // FIXME - axisError changes don't affect the system. This is a potential bug or intendend behaviour?
934 axisError
[FD_PITCH
] = 1000;
935 axisError
[FD_YAW
] = 1000;
936 rotateItermAndAxisError();
937 EXPECT_FLOAT_EQ(pidData
[FD_ROLL
].I
, 10);
938 EXPECT_NEAR(860.37, pidData
[FD_PITCH
].I
, calculateTolerance(860.37));
939 EXPECT_NEAR(1139.6, pidData
[FD_YAW
].I
, calculateTolerance(1139.6));
942 TEST(pidControllerTest
, testLaunchControl
)
944 // The launchControlGain is indirectly tested since when launch control is active the
945 // the gain overrides the PID settings. If the logic to use launchControlGain wasn't
946 // working then any I calculations would be incorrect.
949 unitLaunchControlActive
= true;
950 ENABLE_ARMING_FLAG(ARMED
);
951 pidStabilisationState(PID_STABILISATION_ON
);
953 // test that feedforward and D are disabled (always zero) when launch control is active
955 pidController(pidProfile
, currentTestTime());
957 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].F
);
958 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].F
);
959 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].F
);
960 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
961 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
962 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
964 // Move the sticks to induce feedforward
965 setStickPosition(FD_ROLL
, 0.5f
);
966 setStickPosition(FD_PITCH
, -0.5f
);
967 setStickPosition(FD_YAW
, -0.5f
);
969 // add gyro activity to induce D
970 gyro
.gyroADCf
[FD_ROLL
] = -1000;
971 gyro
.gyroADCf
[FD_PITCH
] = 1000;
972 gyro
.gyroADCf
[FD_YAW
] = -1000;
974 pidController(pidProfile
, currentTestTime());
976 // validate that feedforwad is still 0
977 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].F
);
978 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].F
);
979 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].F
);
981 // validate that D is still 0
982 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].D
);
983 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].D
);
984 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].D
);
986 // test NORMAL mode - expect P/I on roll and pitch, P on yaw but I == 0
987 unitLaunchControlMode
= LAUNCH_CONTROL_MODE_NORMAL
;
989 unitLaunchControlActive
= true;
990 ENABLE_ARMING_FLAG(ARMED
);
991 pidStabilisationState(PID_STABILISATION_ON
);
993 pidController(pidProfile
, currentTestTime());
995 gyro
.gyroADCf
[FD_ROLL
] = -20;
996 gyro
.gyroADCf
[FD_PITCH
] = 20;
997 gyro
.gyroADCf
[FD_YAW
] = -20;
998 pidController(pidProfile
, currentTestTime());
1000 EXPECT_NEAR(25.62, pidData
[FD_ROLL
].P
, calculateTolerance(25.62));
1001 EXPECT_NEAR(1.56, pidData
[FD_ROLL
].I
, calculateTolerance(1.56));
1002 EXPECT_NEAR(-37.15, pidData
[FD_PITCH
].P
, calculateTolerance(-37.15));
1003 EXPECT_NEAR(-1.56, pidData
[FD_PITCH
].I
, calculateTolerance(-1.56));
1004 EXPECT_NEAR(44.84, pidData
[FD_YAW
].P
, calculateTolerance(44.84));
1005 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
1007 // test PITCHONLY mode - expect P/I only on pitch; I cannot go negative
1008 unitLaunchControlMode
= LAUNCH_CONTROL_MODE_PITCHONLY
;
1010 unitLaunchControlActive
= true;
1011 ENABLE_ARMING_FLAG(ARMED
);
1012 pidStabilisationState(PID_STABILISATION_ON
);
1014 pidController(pidProfile
, currentTestTime());
1016 // first test that pitch I is prevented from going negative
1017 gyro
.gyroADCf
[FD_ROLL
] = 0;
1018 gyro
.gyroADCf
[FD_PITCH
] = 20;
1019 gyro
.gyroADCf
[FD_YAW
] = 0;
1020 pidController(pidProfile
, currentTestTime());
1022 EXPECT_FLOAT_EQ(0, pidData
[FD_PITCH
].I
);
1024 gyro
.gyroADCf
[FD_ROLL
] = 20;
1025 gyro
.gyroADCf
[FD_PITCH
] = -20;
1026 gyro
.gyroADCf
[FD_YAW
] = 20;
1027 pidController(pidProfile
, currentTestTime());
1029 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].P
);
1030 EXPECT_FLOAT_EQ(0, pidData
[FD_ROLL
].I
);
1031 EXPECT_NEAR(37.15, pidData
[FD_PITCH
].P
, calculateTolerance(37.15));
1032 EXPECT_NEAR(1.56, pidData
[FD_PITCH
].I
, calculateTolerance(1.56));
1033 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].P
);
1034 EXPECT_FLOAT_EQ(0, pidData
[FD_YAW
].I
);
1036 // test FULL mode - expect P/I on all axes
1037 unitLaunchControlMode
= LAUNCH_CONTROL_MODE_FULL
;
1039 unitLaunchControlActive
= true;
1040 ENABLE_ARMING_FLAG(ARMED
);
1041 pidStabilisationState(PID_STABILISATION_ON
);
1043 pidController(pidProfile
, currentTestTime());
1045 gyro
.gyroADCf
[FD_ROLL
] = -20;
1046 gyro
.gyroADCf
[FD_PITCH
] = 20;
1047 gyro
.gyroADCf
[FD_YAW
] = -20;
1048 pidController(pidProfile
, currentTestTime());
1050 EXPECT_NEAR(25.62, pidData
[FD_ROLL
].P
, calculateTolerance(25.62));
1051 EXPECT_NEAR(1.56, pidData
[FD_ROLL
].I
, calculateTolerance(1.56));
1052 EXPECT_NEAR(-37.15, pidData
[FD_PITCH
].P
, calculateTolerance(-37.15));
1053 EXPECT_NEAR(-1.56, pidData
[FD_PITCH
].I
, calculateTolerance(-1.56));
1054 EXPECT_NEAR(44.84, pidData
[FD_YAW
].P
, calculateTolerance(44.84));
1055 EXPECT_NEAR(1.56, pidData
[FD_YAW
].I
, calculateTolerance(1.56));
1058 TEST(pidControllerTest
, testTpaClassic
)
1062 pidProfile
->tpa_curve_type
= TPA_CURVE_CLASSIC
;
1063 pidProfile
->tpa_rate
= 30;
1064 pidProfile
->tpa_breakpoint
= 1600;
1065 pidProfile
->tpa_low_rate
= -50;
1066 pidProfile
->tpa_low_breakpoint
= 1200;
1067 pidProfile
->tpa_low_always
= 1;
1069 pidInit(pidProfile
);
1071 pidUpdateTpaFactor(0.0f
);
1072 EXPECT_FLOAT_EQ(1.5f
, pidRuntime
.tpaFactor
);
1074 pidUpdateTpaFactor(0.1f
);
1075 EXPECT_FLOAT_EQ(1.25f
, pidRuntime
.tpaFactor
);
1077 pidUpdateTpaFactor(0.2f
);
1078 EXPECT_FLOAT_EQ(1.0f
, pidRuntime
.tpaFactor
);
1080 pidUpdateTpaFactor(0.6f
);
1081 EXPECT_FLOAT_EQ(1.0f
, pidRuntime
.tpaFactor
);
1083 pidUpdateTpaFactor(0.8f
);
1084 EXPECT_FLOAT_EQ(0.85f
, pidRuntime
.tpaFactor
);
1086 pidUpdateTpaFactor(1.0f
);
1087 EXPECT_FLOAT_EQ(0.7f
, pidRuntime
.tpaFactor
);
1090 pidProfile
->tpa_curve_type
= TPA_CURVE_CLASSIC
;
1091 pidProfile
->tpa_rate
= 30;
1092 pidProfile
->tpa_breakpoint
= 1600;
1093 pidProfile
->tpa_low_rate
= -50;
1094 pidProfile
->tpa_low_breakpoint
= 1000;
1095 pidProfile
->tpa_low_always
= 1;
1097 pidInit(pidProfile
);
1099 pidUpdateTpaFactor(0.0f
);
1100 EXPECT_FLOAT_EQ(1.0f
, pidRuntime
.tpaFactor
);
1102 pidUpdateTpaFactor(0.1f
);
1103 EXPECT_FLOAT_EQ(1.0f
, pidRuntime
.tpaFactor
);
1105 pidUpdateTpaFactor(0.2f
);
1106 EXPECT_FLOAT_EQ(1.0f
, pidRuntime
.tpaFactor
);
1108 pidUpdateTpaFactor(0.6f
);
1109 EXPECT_FLOAT_EQ(1.0f
, pidRuntime
.tpaFactor
);
1111 pidUpdateTpaFactor(0.8f
);
1112 EXPECT_FLOAT_EQ(0.85f
, pidRuntime
.tpaFactor
);
1114 pidUpdateTpaFactor(1.0f
);
1115 EXPECT_FLOAT_EQ(0.7f
, pidRuntime
.tpaFactor
);
1118 TEST(pidControllerTest
, testTpaHyperbolic
)
1122 // curve sligly down - edge case where internal expo -> inf
1123 pidProfile
->tpa_curve_type
= TPA_CURVE_HYPERBOLIC
;
1124 pidProfile
->tpa_curve_pid_thr100
= 50;
1125 pidProfile
->tpa_curve_pid_thr0
= 500;
1126 pidProfile
->tpa_curve_expo
= 10;
1127 pidProfile
->tpa_curve_stall_throttle
= 30;
1129 pidInit(pidProfile
);
1131 pidUpdateTpaFactor(0.0f
);
1132 EXPECT_FLOAT_EQ(5.0f
, pidRuntime
.tpaFactor
);
1134 pidUpdateTpaFactor(0.15f
);
1135 EXPECT_FLOAT_EQ(5.0f
, pidRuntime
.tpaFactor
);
1137 pidUpdateTpaFactor(0.5);
1138 EXPECT_NEAR(2.588f
, pidRuntime
.tpaFactor
, 0.01f
);
1140 pidUpdateTpaFactor(0.9);
1141 EXPECT_NEAR(0.693f
, pidRuntime
.tpaFactor
, 0.01f
);
1143 pidUpdateTpaFactor(1.0);
1144 EXPECT_NEAR(0.5f
, pidRuntime
.tpaFactor
, 0.01f
);
1147 pidProfile
->tpa_curve_type
= TPA_CURVE_HYPERBOLIC
;
1148 pidProfile
->tpa_curve_pid_thr100
= 10;
1149 pidProfile
->tpa_curve_pid_thr0
= 300;
1150 pidProfile
->tpa_curve_expo
= 0;
1151 pidProfile
->tpa_curve_stall_throttle
= 0;
1153 pidInit(pidProfile
);
1155 pidUpdateTpaFactor(0.0f
);
1156 EXPECT_FLOAT_EQ(3.0f
, pidRuntime
.tpaFactor
);
1158 pidUpdateTpaFactor(0.15f
);
1159 EXPECT_NEAR(2.565f
, pidRuntime
.tpaFactor
, 0.01f
);
1161 pidUpdateTpaFactor(0.5);
1162 EXPECT_NEAR(1.550f
, pidRuntime
.tpaFactor
, 0.01f
);
1164 pidUpdateTpaFactor(0.9);
1165 EXPECT_NEAR(0.390f
, pidRuntime
.tpaFactor
, 0.01f
);
1167 pidUpdateTpaFactor(1.0);
1168 EXPECT_NEAR(0.1f
, pidRuntime
.tpaFactor
, 0.01f
);
1171 pidProfile
->tpa_curve_type
= TPA_CURVE_HYPERBOLIC
;
1172 pidProfile
->tpa_curve_pid_thr100
= 60;
1173 pidProfile
->tpa_curve_pid_thr0
= 1000;
1174 pidProfile
->tpa_curve_expo
= -50;
1175 pidProfile
->tpa_curve_stall_throttle
= 40;
1177 pidInit(pidProfile
);
1179 pidUpdateTpaFactor(0.0f
);
1180 EXPECT_FLOAT_EQ(10.0f
, pidRuntime
.tpaFactor
);
1182 pidUpdateTpaFactor(0.15f
);
1183 EXPECT_NEAR(10.0f
, pidRuntime
.tpaFactor
, 0.01f
);
1185 pidUpdateTpaFactor(0.5);
1186 EXPECT_NEAR(9.700f
, pidRuntime
.tpaFactor
, 0.01f
);
1188 pidUpdateTpaFactor(0.9);
1189 EXPECT_NEAR(7.364f
, pidRuntime
.tpaFactor
, 0.01f
);
1191 pidUpdateTpaFactor(1.0);
1192 EXPECT_NEAR(0.625f
, pidRuntime
.tpaFactor
, 0.01f
);
1195 pidProfile
->tpa_curve_type
= TPA_CURVE_HYPERBOLIC
;
1196 pidProfile
->tpa_curve_pid_thr100
= 90;
1197 pidProfile
->tpa_curve_pid_thr0
= 250;
1198 pidProfile
->tpa_curve_expo
= 60;
1199 pidProfile
->tpa_curve_stall_throttle
= 60;
1201 pidInit(pidProfile
);
1203 pidUpdateTpaFactor(0.0f
);
1204 EXPECT_FLOAT_EQ(2.5f
, pidRuntime
.tpaFactor
);
1206 pidUpdateTpaFactor(0.15f
);
1207 EXPECT_NEAR(2.5f
, pidRuntime
.tpaFactor
, 0.01f
);
1209 pidUpdateTpaFactor(0.5);
1210 EXPECT_NEAR(2.5f
, pidRuntime
.tpaFactor
, 0.01f
);
1212 pidUpdateTpaFactor(0.9);
1213 EXPECT_NEAR(0.954f
, pidRuntime
.tpaFactor
, 0.01f
);
1215 pidUpdateTpaFactor(1.0);
1216 EXPECT_NEAR(0.9f
, pidRuntime
.tpaFactor
, 0.01f
);