2 * This file is part of INAV Project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
28 #include "common/fp_pid.h"
30 /*-----------------------------------------------------------
31 * Float point PID-controller implementation
32 *-----------------------------------------------------------*/
33 // Implementation of PID with back-calculation I-term anti-windup
34 // Control System Design, Lecture Notes for ME 155A by Karl Johan Åström (p.228)
35 // http://www.cds.caltech.edu/~murray/courses/cds101/fa02/caltech/astrom-ch6.pdf
39 const float measurement
,
43 const pidControllerFlags_e pidFlags
,
44 const float gainScaler
,
45 const float dTermScaler
47 float newProportional
, newDerivative
, newFeedForward
;
50 if (pid
->errorLpfHz
> 0.0f
) {
51 error
= pt1FilterApply4(&pid
->error_filter_state
, setpoint
- measurement
, pid
->errorLpfHz
, dt
);
53 error
= setpoint
- measurement
;
57 newProportional
= error
* pid
->param
.kP
* gainScaler
;
61 pid
->last_input
= (pidFlags
& PID_DTERM_FROM_ERROR
) ? error
: measurement
;
65 if (pidFlags
& PID_DTERM_FROM_ERROR
) {
66 /* Error-tracking D-term */
67 newDerivative
= (error
- pid
->last_input
) / dt
;
68 pid
->last_input
= error
;
70 /* Measurement tracking D-term */
71 newDerivative
= -(measurement
- pid
->last_input
) / dt
;
72 pid
->last_input
= measurement
;
75 if (pid
->dTermLpfHz
> 0.0f
) {
76 newDerivative
= pid
->param
.kD
* pt1FilterApply4(&pid
->dterm_filter_state
, newDerivative
, pid
->dTermLpfHz
, dt
);
78 newDerivative
= pid
->param
.kD
* newDerivative
;
81 newDerivative
= newDerivative
* gainScaler
* dTermScaler
;
83 if (pidFlags
& PID_ZERO_INTEGRATOR
) {
84 pid
->integrator
= 0.0f
;
88 * Compute FeedForward parameter
90 newFeedForward
= setpoint
* pid
->param
.kFF
* gainScaler
;
92 /* Pre-calculate output and limit it if actuator is saturating */
93 const float outVal
= newProportional
+ (pid
->integrator
* gainScaler
) + newDerivative
+ newFeedForward
;
94 const float outValConstrained
= constrainf(outVal
, outMin
, outMax
);
95 float backCalc
= outValConstrained
- outVal
;//back-calculation anti-windup
96 if (SIGN(backCalc
) == SIGN(pid
->integrator
)) {
97 //back calculation anti-windup can only shrink integrator, will not push it to the opposite direction
101 pid
->proportional
= newProportional
;
102 pid
->integral
= pid
->integrator
;
103 pid
->derivative
= newDerivative
;
104 pid
->feedForward
= newFeedForward
;
105 pid
->output_constrained
= outValConstrained
;
109 !(pidFlags
& PID_ZERO_INTEGRATOR
) &&
110 !(pidFlags
& PID_FREEZE_INTEGRATOR
)
112 const float newIntegrator
= pid
->integrator
+ (error
* pid
->param
.kI
* gainScaler
* dt
) + (backCalc
* pid
->param
.kT
* dt
);
114 if (pidFlags
& PID_SHRINK_INTEGRATOR
) {
115 // Only allow integrator to shrink
116 if (fabsf(newIntegrator
) < fabsf(pid
->integrator
)) {
117 pid
->integrator
= newIntegrator
;
121 pid
->integrator
= newIntegrator
;
125 if (pidFlags
& PID_LIMIT_INTEGRATOR
) {
126 pid
->integrator
= constrainf(pid
->integrator
, outMin
, outMax
);
129 return outValConstrained
;
132 float navPidApply2(pidController_t
*pid
, const float setpoint
, const float measurement
, const float dt
, const float outMin
, const float outMax
, const pidControllerFlags_e pidFlags
)
134 return navPidApply3(pid
, setpoint
, measurement
, dt
, outMin
, outMax
, pidFlags
, 1.0f
, 1.0f
);
137 void navPidReset(pidController_t
*pid
)
140 pid
->proportional
= 0.0f
;
141 pid
->integral
= 0.0f
;
142 pid
->derivative
= 0.0f
;
143 pid
->integrator
= 0.0f
;
144 pid
->last_input
= 0.0f
;
145 pid
->feedForward
= 0.0f
;
146 pt1FilterReset(&pid
->dterm_filter_state
, 0.0f
);
147 pid
->output_constrained
= 0.0f
;
150 void navPidInit(pidController_t
*pid
, float _kP
, float _kI
, float _kD
, float _kFF
, float _dTermLpfHz
, float _errorLpfHz
)
155 pid
->param
.kFF
= _kFF
;
157 if (_kI
> 1e-6f
&& _kP
> 1e-6f
) {
158 float Ti
= _kP
/ _kI
;
159 float Td
= _kD
/ _kP
;
160 pid
->param
.kT
= 2.0f
/ (Ti
+ Td
);
162 else if (_kI
> 1e-6f
) {
164 pid
->param
.kT
= 0.0f
;
170 pid
->dTermLpfHz
= _dTermLpfHz
;
171 pid
->errorLpfHz
= _errorLpfHz
;