blob: b6acb513d3b38eb4e9af105fb5ec95dcff109b42 [file] [log] [blame]
Patrick Ventured8012182018-03-08 08:21:38 -08001/**
2 * Copyright 2017 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "pid.hpp"
18
Josh Lehande745422020-11-07 02:14:09 -080019#include "logging.hpp"
20
Ed Tanousf8b6e552025-06-27 13:27:50 -070021#include <chrono>
22#include <string>
23
Patrick Venturea0764872020-08-08 07:48:43 -070024namespace pid_control
25{
Patrick Ventured8012182018-03-08 08:21:38 -080026namespace ec
27{
28
29/********************************
30 * clamp
31 *
32 */
Patrick Venture5f59c0f2018-11-11 12:55:14 -080033static double clamp(double x, double min, double max)
Patrick Ventured8012182018-03-08 08:21:38 -080034{
35 if (x < min)
36 {
37 return min;
38 }
Ed Tanousd2768c52025-06-26 11:42:57 -070039 if (x > max)
Patrick Ventured8012182018-03-08 08:21:38 -080040 {
41 return max;
42 }
43 return x;
44}
45
46/********************************
47 * pid code
48 * Note: Codes assumes the ts field is non-zero
49 */
Josh Lehande745422020-11-07 02:14:09 -080050double pid(pid_info_t* pidinfoptr, double input, double setpoint,
51 const std::string* nameptr)
Patrick Ventured8012182018-03-08 08:21:38 -080052{
Josh Lehande745422020-11-07 02:14:09 -080053 if (nameptr)
54 {
55 if (!(pidinfoptr->initialized))
56 {
57 LogInit(*nameptr, pidinfoptr);
58 }
59 }
60
61 auto logPtr = nameptr ? LogPeek(*nameptr) : nullptr;
62
63 PidCoreContext coreContext;
64 std::chrono::milliseconds msNow;
65
66 if (logPtr)
67 {
68 msNow = LogTimestamp();
69 }
70
71 coreContext.input = input;
72 coreContext.setpoint = setpoint;
73
Patrick Venture5f59c0f2018-11-11 12:55:14 -080074 double error;
Patrick Ventured8012182018-03-08 08:21:38 -080075
Patrick Venturea23468e2019-02-11 09:25:56 -080076 double proportionalTerm;
77 double integralTerm = 0.0f;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080078 double derivativeTerm = 0.0f;
Patrick Venturea23468e2019-02-11 09:25:56 -080079 double feedFwdTerm = 0.0f;
Patrick Ventured8012182018-03-08 08:21:38 -080080
Patrick Venture5f59c0f2018-11-11 12:55:14 -080081 double output;
Patrick Ventured8012182018-03-08 08:21:38 -080082
83 // calculate P, I, D, FF
84
85 // Pid
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070086 error = setpoint - input;
Patrick Venturea23468e2019-02-11 09:25:56 -080087 proportionalTerm = pidinfoptr->proportionalCoeff * error;
Patrick Ventured8012182018-03-08 08:21:38 -080088
Josh Lehande745422020-11-07 02:14:09 -080089 coreContext.error = error;
90 coreContext.proportionalTerm = proportionalTerm;
91 coreContext.integralTerm1 = 0.0;
92
Patrick Ventured8012182018-03-08 08:21:38 -080093 // pId
Patrick Venture4b0df322019-02-11 09:04:57 -080094 if (0.0f != pidinfoptr->integralCoeff)
Patrick Ventured8012182018-03-08 08:21:38 -080095 {
Patrick Venturea23468e2019-02-11 09:25:56 -080096 integralTerm = pidinfoptr->integral;
97 integralTerm += error * pidinfoptr->integralCoeff * pidinfoptr->ts;
Josh Lehande745422020-11-07 02:14:09 -080098
99 coreContext.integralTerm1 = integralTerm;
100
Patrick Venturea23468e2019-02-11 09:25:56 -0800101 integralTerm = clamp(integralTerm, pidinfoptr->integralLimit.min,
102 pidinfoptr->integralLimit.max);
Patrick Ventured8012182018-03-08 08:21:38 -0800103 }
104
Josh Lehande745422020-11-07 02:14:09 -0800105 coreContext.integralTerm2 = integralTerm;
106
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800107 // piD
108 derivativeTerm = pidinfoptr->derivativeCoeff *
109 ((error - pidinfoptr->lastError) / pidinfoptr->ts);
110
Josh Lehande745422020-11-07 02:14:09 -0800111 coreContext.derivativeTerm = derivativeTerm;
112
Patrick Ventured8012182018-03-08 08:21:38 -0800113 // FF
Patrick Williams8c051122023-05-10 07:50:59 -0500114 feedFwdTerm = (setpoint + pidinfoptr->feedFwdOffset) *
115 pidinfoptr->feedFwdGain;
Patrick Ventured8012182018-03-08 08:21:38 -0800116
Josh Lehande745422020-11-07 02:14:09 -0800117 coreContext.feedFwdTerm = feedFwdTerm;
118
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800119 output = proportionalTerm + integralTerm + derivativeTerm + feedFwdTerm;
Josh Lehande745422020-11-07 02:14:09 -0800120
121 coreContext.output1 = output;
122
Patrick Venture4b0df322019-02-11 09:04:57 -0800123 output = clamp(output, pidinfoptr->outLim.min, pidinfoptr->outLim.max);
Patrick Ventured8012182018-03-08 08:21:38 -0800124
Josh Lehande745422020-11-07 02:14:09 -0800125 coreContext.output2 = output;
126
127 coreContext.minOut = 0.0;
128 coreContext.maxOut = 0.0;
129
Patrick Ventured8012182018-03-08 08:21:38 -0800130 // slew rate
131 // TODO(aarena) - Simplify logic as Andy suggested by creating dynamic
Patrick Venture7442c372019-02-11 10:21:05 -0800132 // outLim_min/max that are affected by slew rate control and just clamping
Patrick Ventured8012182018-03-08 08:21:38 -0800133 // to those instead of effectively clamping twice.
134 if (pidinfoptr->initialized)
135 {
Patrick Venture4b0df322019-02-11 09:04:57 -0800136 if (pidinfoptr->slewNeg != 0.0f)
Patrick Ventured8012182018-03-08 08:21:38 -0800137 {
138 // Don't decrease too fast
Patrick Williams8c051122023-05-10 07:50:59 -0500139 double minOut = pidinfoptr->lastOutput +
140 pidinfoptr->slewNeg * pidinfoptr->ts;
Josh Lehande745422020-11-07 02:14:09 -0800141
142 coreContext.minOut = minOut;
143
Patrick Venturea23468e2019-02-11 09:25:56 -0800144 if (output < minOut)
Patrick Ventured8012182018-03-08 08:21:38 -0800145 {
Patrick Venturea23468e2019-02-11 09:25:56 -0800146 output = minOut;
Patrick Ventured8012182018-03-08 08:21:38 -0800147 }
148 }
Patrick Venture4b0df322019-02-11 09:04:57 -0800149 if (pidinfoptr->slewPos != 0.0f)
Patrick Ventured8012182018-03-08 08:21:38 -0800150 {
151 // Don't increase too fast
Patrick Williams8c051122023-05-10 07:50:59 -0500152 double maxOut = pidinfoptr->lastOutput +
153 pidinfoptr->slewPos * pidinfoptr->ts;
Josh Lehande745422020-11-07 02:14:09 -0800154
155 coreContext.maxOut = maxOut;
156
Patrick Venturea23468e2019-02-11 09:25:56 -0800157 if (output > maxOut)
Patrick Ventured8012182018-03-08 08:21:38 -0800158 {
Patrick Venturea23468e2019-02-11 09:25:56 -0800159 output = maxOut;
Patrick Ventured8012182018-03-08 08:21:38 -0800160 }
161 }
162
Patrick Venture4b0df322019-02-11 09:04:57 -0800163 if (pidinfoptr->slewNeg != 0.0f || pidinfoptr->slewPos != 0.0f)
Patrick Ventured8012182018-03-08 08:21:38 -0800164 {
165 // Back calculate integral term for the cases where we limited the
166 // output
Patrick Venturea23468e2019-02-11 09:25:56 -0800167 integralTerm = output - proportionalTerm;
Patrick Ventured8012182018-03-08 08:21:38 -0800168 }
169 }
170
Josh Lehande745422020-11-07 02:14:09 -0800171 coreContext.output3 = output;
172 coreContext.integralTerm3 = integralTerm;
173
Patrick Ventured8012182018-03-08 08:21:38 -0800174 // Clamp again because having limited the output may result in a
175 // larger integral term
Patrick Venturea23468e2019-02-11 09:25:56 -0800176 integralTerm = clamp(integralTerm, pidinfoptr->integralLimit.min,
177 pidinfoptr->integralLimit.max);
178 pidinfoptr->integral = integralTerm;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700179 pidinfoptr->initialized = true;
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800180 pidinfoptr->lastError = error;
Patrick Venture4b0df322019-02-11 09:04:57 -0800181 pidinfoptr->lastOutput = output;
Patrick Ventured8012182018-03-08 08:21:38 -0800182
Josh Lehande745422020-11-07 02:14:09 -0800183 coreContext.integralTerm = pidinfoptr->integral;
184 coreContext.output = pidinfoptr->lastOutput;
185
186 if (logPtr)
187 {
188 LogContext(*logPtr, msNow, coreContext);
189 }
190
Patrick Ventured8012182018-03-08 08:21:38 -0800191 return output;
192}
193
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700194} // namespace ec
Patrick Venturea0764872020-08-08 07:48:43 -0700195} // namespace pid_control