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_MWREWRITE_C_
27 #include "build/build_config.h"
29 #ifdef USE_PID_MWREWRITE
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/rc_controls.h"
48 #include "fc/rate_profile.h"
49 #include "fc/runtime_config.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"
58 extern uint8_t PIDweight
[3];
59 extern int32_t lastITerm
[3], ITermLimit
[3];
61 extern pt1Filter_t deltaFilter
[3];
62 extern pt1Filter_t yawFilter
;
64 extern uint8_t motorCount
;
67 extern int32_t axisPID_P
[3], axisPID_I
[3], axisPID_D
[3];
70 STATIC_UNIT_TESTED
int16_t pidMultiWiiRewriteCore(int axis
, const pidProfile_t
*pidProfile
, int32_t gyroRate
, int32_t angleRate
)
72 static int32_t lastRateForDelta
[3];
74 SET_PID_MULTI_WII_REWRITE_CORE_LOCALS(axis
);
76 const int32_t rateError
= angleRate
- gyroRate
;
78 // -----calculate P component
79 int32_t PTerm
= (rateError
* pidProfile
->P8
[axis
] * PIDweight
[axis
] / 100) >> 7;
80 // Constrain YAW by yaw_p_limit value if not servo driven, in that case servolimits apply
82 if (pidProfile
->yaw_lpf_hz
) {
83 PTerm
= pt1FilterApply4(&yawFilter
, PTerm
, pidProfile
->yaw_lpf_hz
, getdT());
85 if (pidProfile
->yaw_p_limit
&& motorCount
>= 4) {
86 PTerm
= constrain(PTerm
, -pidProfile
->yaw_p_limit
, pidProfile
->yaw_p_limit
);
90 // -----calculate I component
91 // There should be no division before accumulating the error to integrator, because the precision would be reduced.
92 // Precision is critical, as I prevents from long-time drift. Thus, 32 bits integrator (Q19.13 format) is used.
93 // Time correction (to avoid different I scaling for different builds based on average cycle time)
94 // is normalized to cycle time = 2048 (2^11).
95 int32_t ITerm
= lastITerm
[axis
] + ((rateError
* (uint16_t)targetPidLooptime
) >> 11) * pidProfile
->I8
[axis
];
96 // limit maximum integrator value to prevent WindUp - accumulating extreme values when system is saturated.
97 // I coefficient (I8) moved before integration to make limiting independent from PID settings
98 ITerm
= constrain(ITerm
, (int32_t)(-PID_MAX_I
<< 13), (int32_t)(PID_MAX_I
<< 13));
99 // Anti windup protection
100 if (rcModeIsActive(BOXAIRMODE
)) {
101 if (STATE(ANTI_WINDUP
) || motorLimitReached
) {
102 ITerm
= constrain(ITerm
, -ITermLimit
[axis
], ITermLimit
[axis
]);
104 ITermLimit
[axis
] = ABS(ITerm
);
107 lastITerm
[axis
] = ITerm
;
108 ITerm
= ITerm
>> 13; // take integer part of Q19.13 value
110 // -----calculate D component
112 if (pidProfile
->D8
[axis
] == 0) {
113 // optimisation for when D8 is zero, often used by YAW axis
117 if (pidProfile
->deltaMethod
== PID_DELTA_FROM_MEASUREMENT
) {
118 delta
= -(gyroRate
- lastRateForDelta
[axis
]);
119 lastRateForDelta
[axis
] = gyroRate
;
121 delta
= rateError
- lastRateForDelta
[axis
];
122 lastRateForDelta
[axis
] = rateError
;
124 // Divide delta by targetLooptime to get differential (ie dr/dt)
125 delta
= (delta
* ((uint16_t)0xFFFF / ((uint16_t)targetPidLooptime
>> 4))) >> 5;
126 if (pidProfile
->dterm_lpf_hz
) {
127 // DTerm delta low pass filter
128 delta
= lrintf(pt1FilterApply4(&deltaFilter
[axis
], (float)delta
, pidProfile
->dterm_lpf_hz
, getdT()));
130 DTerm
= (delta
* pidProfile
->D8
[axis
] * PIDweight
[axis
] / 100) >> 8;
131 DTerm
= constrain(DTerm
, -PID_MAX_D
, PID_MAX_D
);
135 axisPID_P
[axis
] = PTerm
;
136 axisPID_I
[axis
] = ITerm
;
137 axisPID_D
[axis
] = DTerm
;
139 GET_PID_MULTI_WII_REWRITE_CORE_LOCALS(axis
);
140 // -----calculate total PID output
141 return PTerm
+ ITerm
+ DTerm
;
144 void pidMultiWiiRewrite(const pidProfile_t
*pidProfile
, const controlRateConfig_t
*controlRateConfig
,
145 uint16_t max_angle_inclination
, const rollAndPitchTrims_t
*angleTrim
, const rxConfig_t
*rxConfig
)
148 pidInitFilters(pidProfile
);
150 int horizonLevelStrength
= 0;
151 if (FLIGHT_MODE(HORIZON_MODE
)) {
152 // Using Level D as a Sensitivity for Horizon. 0 more rate to 255 more level.
153 // For more rate mode decrease D and slower flips and rolls will be possible
154 horizonLevelStrength
= calcHorizonLevelStrength(rxConfig
->midrc
, pidProfile
->horizon_tilt_effect
,
155 pidProfile
->horizon_tilt_mode
, pidProfile
->D8
[PIDLEVEL
]);
158 // ----------PID controller----------
159 for (int axis
= 0; axis
< 3; axis
++) {
160 const uint8_t rate
= controlRateConfig
->rates
[axis
];
162 // -----Get the desired angle rate depending on flight mode
164 if (axis
== FD_YAW
) {
165 // YAW is always gyro-controlled (MAG correction is applied to rcCommand)
166 angleRate
= (((int32_t)(rate
+ 27) * rcCommand
[YAW
]) >> 5);
168 // control is GYRO based for ACRO and HORIZON - direct sticks control is applied to rate PID
169 angleRate
= ((int32_t)(rate
+ 27) * rcCommand
[axis
]) >> 4;
170 if (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
)) {
171 // calculate error angle and limit the angle to the max inclination
172 // multiplication of rcCommand corresponds to changing the sticks scaling here
174 const int32_t errorAngle
= constrain(2 * rcCommand
[axis
] + GPS_angle
[axis
], -((int)max_angle_inclination
), max_angle_inclination
)
175 - attitude
.raw
[axis
] + angleTrim
->raw
[axis
];
177 const int32_t errorAngle
= constrain(2 * rcCommand
[axis
], -((int)max_angle_inclination
), max_angle_inclination
)
178 - attitude
.raw
[axis
] + angleTrim
->raw
[axis
];
180 if (FLIGHT_MODE(ANGLE_MODE
)) {
182 angleRate
= (errorAngle
* pidProfile
->P8
[PIDLEVEL
]) >> 4;
185 // mix in errorAngle to desired angleRate to add a little auto-level feel.
186 // horizonLevelStrength has been scaled to the stick input
187 angleRate
+= (errorAngle
* pidProfile
->I8
[PIDLEVEL
] * horizonLevelStrength
/ 100) >> 4;
192 // --------low-level gyro-based PID. ----------
193 const int32_t gyroRate
= gyroADC
[axis
] / 4;
194 axisPID
[axis
] = pidMultiWiiRewriteCore(axis
, pidProfile
, gyroRate
, angleRate
);
197 if (FLIGHT_MODE(GTUNE_MODE
) && ARMING_FLAG(ARMED
)) {
198 calculate_Gtune(axis
);