xref: /openbmc/phosphor-pid-control/pid/ec/pid.cpp (revision 8c051121ade815a46e39d5c669fee77302df2b6d)
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