1d8012181SPatrick Venture /**
2d8012181SPatrick Venture * Copyright 2017 Google Inc.
3d8012181SPatrick Venture *
4d8012181SPatrick Venture * Licensed under the Apache License, Version 2.0 (the "License");
5d8012181SPatrick Venture * you may not use this file except in compliance with the License.
6d8012181SPatrick Venture * You may obtain a copy of the License at
7d8012181SPatrick Venture *
8d8012181SPatrick Venture * http://www.apache.org/licenses/LICENSE-2.0
9d8012181SPatrick Venture *
10d8012181SPatrick Venture * Unless required by applicable law or agreed to in writing, software
11d8012181SPatrick Venture * distributed under the License is distributed on an "AS IS" BASIS,
12d8012181SPatrick Venture * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d8012181SPatrick Venture * See the License for the specific language governing permissions and
14d8012181SPatrick Venture * limitations under the License.
15d8012181SPatrick Venture */
16d8012181SPatrick Venture
17d8012181SPatrick Venture #include "pid.hpp"
18d8012181SPatrick Venture
19de74542cSJosh Lehan #include "../tuning.hpp"
20de74542cSJosh Lehan #include "logging.hpp"
21de74542cSJosh Lehan
22a076487aSPatrick Venture namespace pid_control
23a076487aSPatrick Venture {
24d8012181SPatrick Venture namespace ec
25d8012181SPatrick Venture {
26d8012181SPatrick Venture
27d8012181SPatrick Venture /********************************
28d8012181SPatrick Venture * clamp
29d8012181SPatrick Venture *
30d8012181SPatrick Venture */
clamp(double x,double min,double max)315f59c0fdSPatrick Venture static double clamp(double x, double min, double max)
32d8012181SPatrick Venture {
33d8012181SPatrick Venture if (x < min)
34d8012181SPatrick Venture {
35d8012181SPatrick Venture return min;
36d8012181SPatrick Venture }
37d8012181SPatrick Venture else if (x > max)
38d8012181SPatrick Venture {
39d8012181SPatrick Venture return max;
40d8012181SPatrick Venture }
41d8012181SPatrick Venture return x;
42d8012181SPatrick Venture }
43d8012181SPatrick Venture
44d8012181SPatrick Venture /********************************
45d8012181SPatrick Venture * pid code
46d8012181SPatrick Venture * Note: Codes assumes the ts field is non-zero
47d8012181SPatrick Venture */
pid(pid_info_t * pidinfoptr,double input,double setpoint,const std::string * nameptr)48de74542cSJosh Lehan double pid(pid_info_t* pidinfoptr, double input, double setpoint,
49de74542cSJosh Lehan const std::string* nameptr)
50d8012181SPatrick Venture {
51de74542cSJosh Lehan if (nameptr)
52de74542cSJosh Lehan {
53de74542cSJosh Lehan if (!(pidinfoptr->initialized))
54de74542cSJosh Lehan {
55de74542cSJosh Lehan LogInit(*nameptr, pidinfoptr);
56de74542cSJosh Lehan }
57de74542cSJosh Lehan }
58de74542cSJosh Lehan
59de74542cSJosh Lehan auto logPtr = nameptr ? LogPeek(*nameptr) : nullptr;
60de74542cSJosh Lehan
61de74542cSJosh Lehan PidCoreContext coreContext;
62de74542cSJosh Lehan std::chrono::milliseconds msNow;
63de74542cSJosh Lehan
64de74542cSJosh Lehan if (logPtr)
65de74542cSJosh Lehan {
66de74542cSJosh Lehan msNow = LogTimestamp();
67de74542cSJosh Lehan }
68de74542cSJosh Lehan
69de74542cSJosh Lehan coreContext.input = input;
70de74542cSJosh Lehan coreContext.setpoint = setpoint;
71de74542cSJosh Lehan
725f59c0fdSPatrick Venture double error;
73d8012181SPatrick Venture
74a23468efSPatrick Venture double proportionalTerm;
75a23468efSPatrick Venture double integralTerm = 0.0f;
760e8fc398SBonnie Lo double derivativeTerm = 0.0f;
77a23468efSPatrick Venture double feedFwdTerm = 0.0f;
78d8012181SPatrick Venture
795f59c0fdSPatrick Venture double output;
80d8012181SPatrick Venture
81d8012181SPatrick Venture // calculate P, I, D, FF
82d8012181SPatrick Venture
83d8012181SPatrick Venture // Pid
84d8012181SPatrick Venture error = setpoint - input;
85a23468efSPatrick Venture proportionalTerm = pidinfoptr->proportionalCoeff * error;
86d8012181SPatrick Venture
87de74542cSJosh Lehan coreContext.error = error;
88de74542cSJosh Lehan coreContext.proportionalTerm = proportionalTerm;
89de74542cSJosh Lehan coreContext.integralTerm1 = 0.0;
90de74542cSJosh Lehan
91d8012181SPatrick Venture // pId
924b0df320SPatrick Venture if (0.0f != pidinfoptr->integralCoeff)
93d8012181SPatrick Venture {
94a23468efSPatrick Venture integralTerm = pidinfoptr->integral;
95a23468efSPatrick Venture integralTerm += error * pidinfoptr->integralCoeff * pidinfoptr->ts;
96de74542cSJosh Lehan
97de74542cSJosh Lehan coreContext.integralTerm1 = integralTerm;
98de74542cSJosh Lehan
99a23468efSPatrick Venture integralTerm = clamp(integralTerm, pidinfoptr->integralLimit.min,
1004b0df320SPatrick Venture pidinfoptr->integralLimit.max);
101d8012181SPatrick Venture }
102d8012181SPatrick Venture
103de74542cSJosh Lehan coreContext.integralTerm2 = integralTerm;
104de74542cSJosh Lehan
1050e8fc398SBonnie Lo // piD
1060e8fc398SBonnie Lo derivativeTerm = pidinfoptr->derivativeCoeff *
1070e8fc398SBonnie Lo ((error - pidinfoptr->lastError) / pidinfoptr->ts);
1080e8fc398SBonnie Lo
109de74542cSJosh Lehan coreContext.derivativeTerm = derivativeTerm;
110de74542cSJosh Lehan
111d8012181SPatrick Venture // FF
112*8c051121SPatrick Williams feedFwdTerm = (setpoint + pidinfoptr->feedFwdOffset) *
113*8c051121SPatrick Williams pidinfoptr->feedFwdGain;
114d8012181SPatrick Venture
115de74542cSJosh Lehan coreContext.feedFwdTerm = feedFwdTerm;
116de74542cSJosh Lehan
1170e8fc398SBonnie Lo output = proportionalTerm + integralTerm + derivativeTerm + feedFwdTerm;
118de74542cSJosh Lehan
119de74542cSJosh Lehan coreContext.output1 = output;
120de74542cSJosh Lehan
1214b0df320SPatrick Venture output = clamp(output, pidinfoptr->outLim.min, pidinfoptr->outLim.max);
122d8012181SPatrick Venture
123de74542cSJosh Lehan coreContext.output2 = output;
124de74542cSJosh Lehan
125de74542cSJosh Lehan coreContext.minOut = 0.0;
126de74542cSJosh Lehan coreContext.maxOut = 0.0;
127de74542cSJosh Lehan
128d8012181SPatrick Venture // slew rate
129d8012181SPatrick Venture // TODO(aarena) - Simplify logic as Andy suggested by creating dynamic
1307442c37aSPatrick Venture // outLim_min/max that are affected by slew rate control and just clamping
131d8012181SPatrick Venture // to those instead of effectively clamping twice.
132d8012181SPatrick Venture if (pidinfoptr->initialized)
133d8012181SPatrick Venture {
1344b0df320SPatrick Venture if (pidinfoptr->slewNeg != 0.0f)
135d8012181SPatrick Venture {
136d8012181SPatrick Venture // Don't decrease too fast
137*8c051121SPatrick Williams double minOut = pidinfoptr->lastOutput +
138*8c051121SPatrick Williams pidinfoptr->slewNeg * pidinfoptr->ts;
139de74542cSJosh Lehan
140de74542cSJosh Lehan coreContext.minOut = minOut;
141de74542cSJosh Lehan
142a23468efSPatrick Venture if (output < minOut)
143d8012181SPatrick Venture {
144a23468efSPatrick Venture output = minOut;
145d8012181SPatrick Venture }
146d8012181SPatrick Venture }
1474b0df320SPatrick Venture if (pidinfoptr->slewPos != 0.0f)
148d8012181SPatrick Venture {
149d8012181SPatrick Venture // Don't increase too fast
150*8c051121SPatrick Williams double maxOut = pidinfoptr->lastOutput +
151*8c051121SPatrick Williams pidinfoptr->slewPos * pidinfoptr->ts;
152de74542cSJosh Lehan
153de74542cSJosh Lehan coreContext.maxOut = maxOut;
154de74542cSJosh Lehan
155a23468efSPatrick Venture if (output > maxOut)
156d8012181SPatrick Venture {
157a23468efSPatrick Venture output = maxOut;
158d8012181SPatrick Venture }
159d8012181SPatrick Venture }
160d8012181SPatrick Venture
1614b0df320SPatrick Venture if (pidinfoptr->slewNeg != 0.0f || pidinfoptr->slewPos != 0.0f)
162d8012181SPatrick Venture {
163d8012181SPatrick Venture // Back calculate integral term for the cases where we limited the
164d8012181SPatrick Venture // output
165a23468efSPatrick Venture integralTerm = output - proportionalTerm;
166d8012181SPatrick Venture }
167d8012181SPatrick Venture }
168d8012181SPatrick Venture
169de74542cSJosh Lehan coreContext.output3 = output;
170de74542cSJosh Lehan coreContext.integralTerm3 = integralTerm;
171de74542cSJosh Lehan
172d8012181SPatrick Venture // Clamp again because having limited the output may result in a
173d8012181SPatrick Venture // larger integral term
174a23468efSPatrick Venture integralTerm = clamp(integralTerm, pidinfoptr->integralLimit.min,
1754b0df320SPatrick Venture pidinfoptr->integralLimit.max);
176a23468efSPatrick Venture pidinfoptr->integral = integralTerm;
177d8012181SPatrick Venture pidinfoptr->initialized = true;
1780e8fc398SBonnie Lo pidinfoptr->lastError = error;
1794b0df320SPatrick Venture pidinfoptr->lastOutput = output;
180d8012181SPatrick Venture
181de74542cSJosh Lehan coreContext.integralTerm = pidinfoptr->integral;
182de74542cSJosh Lehan coreContext.output = pidinfoptr->lastOutput;
183de74542cSJosh Lehan
184de74542cSJosh Lehan if (logPtr)
185de74542cSJosh Lehan {
186de74542cSJosh Lehan LogContext(*logPtr, msNow, coreContext);
187de74542cSJosh Lehan }
188de74542cSJosh Lehan
189d8012181SPatrick Venture return output;
190d8012181SPatrick Venture }
191d8012181SPatrick Venture
192da4a5dd1SPatrick Venture } // namespace ec
193a076487aSPatrick Venture } // namespace pid_control
194