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/>.
25 #include "build/build_config.h"
29 #include "common/axis.h"
30 #include "common/maths.h"
31 #include "common/filter.h"
33 #include "config/parameter_group.h"
34 #include "config/config_unittest.h"
36 #include "drivers/sensor.h"
37 #include "drivers/accgyro.h"
38 #include "drivers/gyro_sync.h"
40 #include "sensors/sensors.h"
41 #include "sensors/gyro.h"
42 #include "sensors/acceleration.h"
46 #include "fc/rc_controls.h"
47 #include "fc/rate_profile.h"
48 #include "fc/runtime_config.h"
50 #include "flight/pid.h"
51 #include "flight/imu.h"
52 #include "flight/navigation.h"
53 #include "flight/gtune.h"
54 #include "flight/mixer.h"
56 static int32_t ITermAngle
[2];
58 uint8_t dynP8
[3], dynI8
[3], dynD8
[3];
61 extern uint8_t motorCount
;
64 extern int32_t axisPID_P
[3], axisPID_I
[3], axisPID_D
[3];
66 extern int32_t lastITerm
[3], ITermLimit
[3];
68 extern pt1Filter_t deltaFilter
[3];
71 void pidResetITermAngle(void)
73 ITermAngle
[AI_ROLL
] = 0;
74 ITermAngle
[AI_PITCH
] = 0;
77 void pidMultiWii23(const pidProfile_t
*pidProfile
, const controlRateConfig_t
*controlRateConfig
,
78 uint16_t max_angle_inclination
, const rollAndPitchTrims_t
*angleTrim
, const rxConfig_t
*rxConfig
)
82 pidInitFilters(pidProfile
);
85 int32_t rc
, error
, errorAngle
, delta
, gyroError
;
86 int32_t PTerm
, ITerm
, PTermACC
, ITermACC
, DTerm
;
87 static int16_t lastErrorForDelta
[2];
88 static int32_t delta1
[2], delta2
[2];
90 if (FLIGHT_MODE(HORIZON_MODE
)) {
91 prop
= MIN(MAX(ABS(rcCommand
[PITCH
]), ABS(rcCommand
[ROLL
])), 512);
95 for (axis
= 0; axis
< 2; axis
++) {
97 rc
= rcCommand
[axis
] << 1;
99 gyroError
= gyroADC
[axis
] / 4;
101 error
= rc
- gyroError
;
102 lastITerm
[axis
] = constrain(lastITerm
[axis
] + error
, -16000, +16000); // WindUp 16 bits is ok here
104 if (ABS(gyroADC
[axis
]) > (640 * 4)) {
108 // Anti windup protection
109 if (rcModeIsActive(BOXAIRMODE
)) {
110 if (STATE(ANTI_WINDUP
) || motorLimitReached
) {
111 lastITerm
[axis
] = constrain(lastITerm
[axis
], -ITermLimit
[axis
], ITermLimit
[axis
]);
113 ITermLimit
[axis
] = ABS(lastITerm
[axis
]);
117 ITerm
= (lastITerm
[axis
] >> 7) * pidProfile
->I8
[axis
] >> 6; // 16 bits is ok here 16000/125 = 128 ; 128*250 = 32000
119 PTerm
= (int32_t)rc
* pidProfile
->P8
[axis
] >> 6;
121 if (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
)) { // axis relying on ACC
122 // 50 degrees max inclination
124 errorAngle
= constrain(2 * rcCommand
[axis
] + GPS_angle
[axis
], -((int) max_angle_inclination
),
125 +max_angle_inclination
) - attitude
.raw
[axis
] + angleTrim
->raw
[axis
];
127 errorAngle
= constrain(2 * rcCommand
[axis
], -((int) max_angle_inclination
),
128 +max_angle_inclination
) - attitude
.raw
[axis
] + angleTrim
->raw
[axis
];
131 ITermAngle
[axis
] = constrain(ITermAngle
[axis
] + errorAngle
, -10000, +10000); // WindUp //16 bits is ok here
133 PTermACC
= ((int32_t)errorAngle
* pidProfile
->P8
[PIDLEVEL
]) >> 7; // 32 bits is needed for calculation: errorAngle*P8 could exceed 32768 16 bits is ok for result
135 int16_t limit
= pidProfile
->D8
[PIDLEVEL
] * 5;
136 PTermACC
= constrain(PTermACC
, -limit
, +limit
);
138 ITermACC
= ((int32_t)ITermAngle
[axis
] * pidProfile
->I8
[PIDLEVEL
]) >> 12; // 32 bits is needed for calculation:10000*I8 could exceed 32768 16 bits is ok for result
140 ITerm
= ITermACC
+ ((ITerm
- ITermACC
) * prop
>> 9);
141 PTerm
= PTermACC
+ ((PTerm
- PTermACC
) * prop
>> 9);
144 PTerm
-= ((int32_t)gyroError
* dynP8
[axis
]) >> 6; // 32 bits is needed for calculation
146 //-----calculate D-term based on the configured approach (delta from measurement or deltafromError)
147 // Delta from measurement
148 delta
= -(gyroError
- lastErrorForDelta
[axis
]);
149 lastErrorForDelta
[axis
] = gyroError
;
150 if (pidProfile
->dterm_lpf_hz
) {
151 // Dterm delta low pass
153 DTerm
= lrintf(pt1FilterApply4(&deltaFilter
[axis
], (float)DTerm
, pidProfile
->dterm_lpf_hz
, dT
)) * 3; // Keep same scaling as unfiltered DTerm
155 // When dterm filter disabled apply moving average to reduce noise
156 DTerm
= delta1
[axis
] + delta2
[axis
] + delta
;
157 delta2
[axis
] = delta1
[axis
];
158 delta1
[axis
] = delta
;
160 DTerm
= ((int32_t)DTerm
* dynD8
[axis
]) >> 5; // 32 bits is needed for calculation
162 axisPID
[axis
] = PTerm
+ ITerm
+ DTerm
;
165 if (FLIGHT_MODE(GTUNE_MODE
) && ARMING_FLAG(ARMED
)) {
166 calculate_Gtune(axis
);
171 axisPID_P
[axis
] = PTerm
;
172 axisPID_I
[axis
] = ITerm
;
173 axisPID_D
[axis
] = DTerm
;
178 rc
= (int32_t)rcCommand
[YAW
] * (2 * controlRateConfig
->rates
[YAW
] + 30) >> 5;
180 error
= rc
- gyroADC
[FD_YAW
];
182 error
= rc
- (gyroADC
[FD_YAW
] / 4);
184 lastITerm
[FD_YAW
] += (int32_t)error
* pidProfile
->I8
[FD_YAW
];
185 lastITerm
[FD_YAW
] = constrain(lastITerm
[FD_YAW
], 2 - ((int32_t)1 << 28), -2 + ((int32_t)1 << 28));
186 if (ABS(rc
) > 50) lastITerm
[FD_YAW
] = 0;
188 PTerm
= (int32_t)error
* pidProfile
->P8
[FD_YAW
] >> 6; // TODO: Bitwise shift on a signed integer is not recommended
190 // Constrain YAW by D value if not servo driven in that case servolimits apply
191 if(motorCount
>= 4 && pidProfile
->yaw_p_limit
< YAW_P_LIMIT_MAX
) {
192 PTerm
= constrain(PTerm
, -pidProfile
->yaw_p_limit
, pidProfile
->yaw_p_limit
);
195 ITerm
= constrain((int16_t)(lastITerm
[FD_YAW
] >> 13), -GYRO_I_MAX
, +GYRO_I_MAX
);
197 axisPID
[FD_YAW
] = PTerm
+ ITerm
;
200 if (FLIGHT_MODE(GTUNE_MODE
) && ARMING_FLAG(ARMED
)) {
201 calculate_Gtune(FD_YAW
);
206 axisPID_P
[FD_YAW
] = PTerm
;
207 axisPID_I
[FD_YAW
] = ITerm
;
208 axisPID_D
[FD_YAW
] = 0;