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/>.
18 #define SRC_MAIN_FLIGHT_PID_LUXFLOAT_C_
27 #include "build/build_config.h"
29 #ifndef SKIP_PID_LUXFLOAT
31 #include "common/axis.h"
32 #include "common/maths.h"
33 #include "common/filter.h"
35 #include "config/parameter_group.h"
37 #include "drivers/sensor.h"
38 #include "drivers/accgyro.h"
39 #include "drivers/gyro_sync.h"
41 #include "sensors/sensors.h"
42 #include "sensors/gyro.h"
43 #include "sensors/acceleration.h"
47 #include "fc/runtime_config.h"
48 #include "fc/rc_controls.h"
49 #include "fc/rate_profile.h"
51 #include "flight/pid.h"
52 #include "config/config_unittest.h"
53 #include "flight/imu.h"
54 #include "flight/navigation.h"
55 #include "flight/gtune.h"
56 #include "flight/mixer.h"
59 extern uint8_t PIDweight
[3];
60 extern float lastITermf
[3], ITermLimitf
[3];
62 extern pt1Filter_t deltaFilter
[3];
63 extern pt1Filter_t yawFilter
;
65 extern bool dtermNotchInitialised
, dtermBiquadLpfInitialised
;
66 extern biquadFilter_t dtermFilterNotch
[3];
67 extern biquadFilter_t dtermFilterLpf
[3];
69 extern uint8_t motorCount
;
72 extern int32_t axisPID_P
[3], axisPID_I
[3], axisPID_D
[3];
75 // constants to scale pidLuxFloat so output is same as pidMultiWiiRewrite
76 static const float luxPTermScale
= 1.0f
/ 128;
77 static const float luxITermScale
= 1000000.0f
/ 0x1000000;
78 static const float luxDTermScale
= (0.000001f
* (float)0xFFFF) / 512;
79 static const float luxGyroScale
= 16.4f
/ 4; // the 16.4 is needed because mwrewrite does not scale according to the gyro model gyro.scale
81 STATIC_UNIT_TESTED
int16_t pidLuxFloatCore(int axis
, const pidProfile_t
*pidProfile
, float gyroRate
, float angleRate
)
83 static float lastRateForDelta
[3];
85 SET_PID_LUX_FLOAT_CORE_LOCALS(axis
);
87 pidInitFilters(pidProfile
);
89 const float rateError
= angleRate
- gyroRate
;
91 // -----calculate P component
92 float PTerm
= luxPTermScale
* rateError
* pidProfile
->P8
[axis
] * PIDweight
[axis
] / 100;
93 // Constrain YAW by yaw_p_limit value if not servo driven, in that case servolimits apply
95 if (pidProfile
->yaw_lpf_hz
) {
96 PTerm
= pt1FilterApply4(&yawFilter
, PTerm
, pidProfile
->yaw_lpf_hz
, getdT());
98 if (pidProfile
->yaw_p_limit
&& motorCount
>= 4) {
99 PTerm
= constrainf(PTerm
, -pidProfile
->yaw_p_limit
, pidProfile
->yaw_p_limit
);
103 // -----calculate I component
104 float ITerm
= lastITermf
[axis
] + luxITermScale
* rateError
* getdT() * pidProfile
->I8
[axis
];
105 // limit maximum integrator value to prevent WindUp - accumulating extreme values when system is saturated.
106 // I coefficient (I8) moved before integration to make limiting independent from PID settings
107 ITerm
= constrainf(ITerm
, -PID_MAX_I
, PID_MAX_I
);
108 // Anti windup protection
109 if (rcModeIsActive(BOXAIRMODE
)) {
110 if (STATE(ANTI_WINDUP
) || motorLimitReached
) {
111 ITerm
= constrainf(ITerm
, -ITermLimitf
[axis
], ITermLimitf
[axis
]);
113 ITermLimitf
[axis
] = ABS(ITerm
);
116 lastITermf
[axis
] = ITerm
;
118 // -----calculate D component
120 if (pidProfile
->D8
[axis
] == 0) {
121 // optimisation for when D8 is zero, often used by YAW axis
125 if (pidProfile
->deltaMethod
== PID_DELTA_FROM_MEASUREMENT
) {
126 delta
= -(gyroRate
- lastRateForDelta
[axis
]);
127 lastRateForDelta
[axis
] = gyroRate
;
129 delta
= rateError
- lastRateForDelta
[axis
];
130 lastRateForDelta
[axis
] = rateError
;
132 // Divide delta by targetLooptime to get differential (ie dr/dt)
133 delta
*= (1.0f
/ getdT());
136 if (dtermNotchInitialised
) delta
= biquadFilterApply(&dtermFilterNotch
[axis
], delta
);
138 if (pidProfile
->dterm_lpf_hz
) {
139 if (dtermBiquadLpfInitialised
) {
140 delta
= biquadFilterApply(&dtermFilterLpf
[axis
], delta
);
142 // DTerm delta low pass filter
143 delta
= pt1FilterApply4(&deltaFilter
[axis
], delta
, pidProfile
->dterm_lpf_hz
, getdT());
146 DTerm
= luxDTermScale
* delta
* pidProfile
->D8
[axis
] * PIDweight
[axis
] / 100;
147 DTerm
= constrainf(DTerm
, -PID_MAX_D
, PID_MAX_D
);
151 axisPID_P
[axis
] = PTerm
;
152 axisPID_I
[axis
] = ITerm
;
153 axisPID_D
[axis
] = DTerm
;
155 GET_PID_LUX_FLOAT_CORE_LOCALS(axis
);
156 // -----calculate total PID output
157 return lrintf(PTerm
+ ITerm
+ DTerm
);
160 void pidLuxFloat(const pidProfile_t
*pidProfile
, const controlRateConfig_t
*controlRateConfig
,
161 uint16_t max_angle_inclination
, const rollAndPitchTrims_t
*angleTrim
, const rxConfig_t
*rxConfig
)
163 float horizonLevelStrength
= 0.0f
;
164 if (FLIGHT_MODE(HORIZON_MODE
)) {
165 // (convert 0-100 range to 0.0-1.0 range)
166 horizonLevelStrength
= (float)calcHorizonLevelStrength(rxConfig
->midrc
, pidProfile
->horizon_tilt_effect
,
167 pidProfile
->horizon_tilt_mode
, pidProfile
->D8
[PIDLEVEL
]) / 100.0f
;
170 // ----------PID controller----------
171 for (int axis
= 0; axis
< 3; axis
++) {
172 const uint8_t rate
= controlRateConfig
->rates
[axis
];
174 // -----Get the desired angle rate depending on flight mode
176 if (axis
== FD_YAW
) {
177 // YAW is always gyro-controlled (MAG correction is applied to rcCommand) 100dps to 1100dps max yaw rate
178 angleRate
= (float)((rate
+ 27) * rcCommand
[YAW
]) / 32.0f
;
180 // control is GYRO based for ACRO and HORIZON - direct sticks control is applied to rate PID
181 angleRate
= (float)((rate
+ 27) * rcCommand
[axis
]) / 16.0f
; // 200dps to 1200dps max roll/pitch rate
182 if (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
)) {
183 // calculate error angle and limit the angle to the max inclination
184 // multiplication of rcCommand corresponds to changing the sticks scaling here
186 const float errorAngle
= constrain(2 * rcCommand
[axis
] + GPS_angle
[axis
], -((int)max_angle_inclination
), max_angle_inclination
)
187 - attitude
.raw
[axis
] + angleTrim
->raw
[axis
];
189 const float errorAngle
= constrain(2 * rcCommand
[axis
], -((int)max_angle_inclination
), max_angle_inclination
)
190 - attitude
.raw
[axis
] + angleTrim
->raw
[axis
];
192 if (FLIGHT_MODE(ANGLE_MODE
)) {
194 angleRate
= errorAngle
* pidProfile
->P8
[PIDLEVEL
] / 16.0f
;
197 // mix in errorAngle to desired angleRate to add a little auto-level feel.
198 // horizonLevelStrength has been scaled to the stick input
199 angleRate
+= errorAngle
* pidProfile
->I8
[PIDLEVEL
] * horizonLevelStrength
/ 16.0f
;
204 // --------low-level gyro-based PID. ----------
205 const float gyroRate
= luxGyroScale
* gyroADC
[axis
] * gyro
.scale
;
206 axisPID
[axis
] = pidLuxFloatCore(axis
, pidProfile
, gyroRate
, angleRate
);
207 //axisPID[axis] = constrain(axisPID[axis], -PID_LUX_FLOAT_MAX_PID, PID_LUX_FLOAT_MAX_PID);
209 if (FLIGHT_MODE(GTUNE_MODE
) && ARMING_FLAG(ARMED
)) {
210 calculate_Gtune(axis
);