blob: c8f6fed3d5d6a394591091f7146fbbb51590ea3b [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "frc/PIDBase.h"
9
10#include <algorithm>
11#include <cmath>
12
13#include <hal/FRCUsageReporting.h>
14
15#include "frc/PIDOutput.h"
16#include "frc/smartdashboard/SendableBuilder.h"
17#include "frc/smartdashboard/SendableRegistry.h"
18
19using namespace frc;
20
21template <class T>
22constexpr const T& clamp(const T& value, const T& low, const T& high) {
23 return std::max(low, std::min(value, high));
24}
25
26PIDBase::PIDBase(double Kp, double Ki, double Kd, PIDSource& source,
27 PIDOutput& output)
28 : PIDBase(Kp, Ki, Kd, 0.0, source, output) {}
29
30PIDBase::PIDBase(double Kp, double Ki, double Kd, double Kf, PIDSource& source,
31 PIDOutput& output) {
32 m_P = Kp;
33 m_I = Ki;
34 m_D = Kd;
35 m_F = Kf;
36
37 m_pidInput = &source;
38 m_filter = LinearFilter<double>::MovingAverage(1);
39
40 m_pidOutput = &output;
41
42 m_setpointTimer.Start();
43
44 static int instances = 0;
45 instances++;
46 HAL_Report(HALUsageReporting::kResourceType_PIDController, instances);
47 SendableRegistry::GetInstance().Add(this, "PIDController", instances);
48}
49
50double PIDBase::Get() const {
51 std::scoped_lock lock(m_thisMutex);
52 return m_result;
53}
54
55void PIDBase::SetContinuous(bool continuous) {
56 std::scoped_lock lock(m_thisMutex);
57 m_continuous = continuous;
58}
59
60void PIDBase::SetInputRange(double minimumInput, double maximumInput) {
61 {
62 std::scoped_lock lock(m_thisMutex);
63 m_minimumInput = minimumInput;
64 m_maximumInput = maximumInput;
65 m_inputRange = maximumInput - minimumInput;
66 }
67
68 SetSetpoint(m_setpoint);
69}
70
71void PIDBase::SetOutputRange(double minimumOutput, double maximumOutput) {
72 std::scoped_lock lock(m_thisMutex);
73 m_minimumOutput = minimumOutput;
74 m_maximumOutput = maximumOutput;
75}
76
77void PIDBase::SetPID(double p, double i, double d) {
78 {
79 std::scoped_lock lock(m_thisMutex);
80 m_P = p;
81 m_I = i;
82 m_D = d;
83 }
84}
85
86void PIDBase::SetPID(double p, double i, double d, double f) {
87 std::scoped_lock lock(m_thisMutex);
88 m_P = p;
89 m_I = i;
90 m_D = d;
91 m_F = f;
92}
93
94void PIDBase::SetP(double p) {
95 std::scoped_lock lock(m_thisMutex);
96 m_P = p;
97}
98
99void PIDBase::SetI(double i) {
100 std::scoped_lock lock(m_thisMutex);
101 m_I = i;
102}
103
104void PIDBase::SetD(double d) {
105 std::scoped_lock lock(m_thisMutex);
106 m_D = d;
107}
108
109void PIDBase::SetF(double f) {
110 std::scoped_lock lock(m_thisMutex);
111 m_F = f;
112}
113
114double PIDBase::GetP() const {
115 std::scoped_lock lock(m_thisMutex);
116 return m_P;
117}
118
119double PIDBase::GetI() const {
120 std::scoped_lock lock(m_thisMutex);
121 return m_I;
122}
123
124double PIDBase::GetD() const {
125 std::scoped_lock lock(m_thisMutex);
126 return m_D;
127}
128
129double PIDBase::GetF() const {
130 std::scoped_lock lock(m_thisMutex);
131 return m_F;
132}
133
134void PIDBase::SetSetpoint(double setpoint) {
135 {
136 std::scoped_lock lock(m_thisMutex);
137
138 if (m_maximumInput > m_minimumInput) {
139 if (setpoint > m_maximumInput)
140 m_setpoint = m_maximumInput;
141 else if (setpoint < m_minimumInput)
142 m_setpoint = m_minimumInput;
143 else
144 m_setpoint = setpoint;
145 } else {
146 m_setpoint = setpoint;
147 }
148 }
149}
150
151double PIDBase::GetSetpoint() const {
152 std::scoped_lock lock(m_thisMutex);
153 return m_setpoint;
154}
155
156double PIDBase::GetDeltaSetpoint() const {
157 std::scoped_lock lock(m_thisMutex);
158 return (m_setpoint - m_prevSetpoint) / m_setpointTimer.Get();
159}
160
161double PIDBase::GetError() const {
162 double setpoint = GetSetpoint();
163 {
164 std::scoped_lock lock(m_thisMutex);
165 return GetContinuousError(setpoint - m_pidInput->PIDGet());
166 }
167}
168
169double PIDBase::GetAvgError() const { return GetError(); }
170
171void PIDBase::SetPIDSourceType(PIDSourceType pidSource) {
172 m_pidInput->SetPIDSourceType(pidSource);
173}
174
175PIDSourceType PIDBase::GetPIDSourceType() const {
176 return m_pidInput->GetPIDSourceType();
177}
178
179void PIDBase::SetTolerance(double percent) {
180 std::scoped_lock lock(m_thisMutex);
181 m_toleranceType = kPercentTolerance;
182 m_tolerance = percent;
183}
184
185void PIDBase::SetAbsoluteTolerance(double absTolerance) {
186 std::scoped_lock lock(m_thisMutex);
187 m_toleranceType = kAbsoluteTolerance;
188 m_tolerance = absTolerance;
189}
190
191void PIDBase::SetPercentTolerance(double percent) {
192 std::scoped_lock lock(m_thisMutex);
193 m_toleranceType = kPercentTolerance;
194 m_tolerance = percent;
195}
196
197void PIDBase::SetToleranceBuffer(int bufLength) {
198 std::scoped_lock lock(m_thisMutex);
199 m_filter = LinearFilter<double>::MovingAverage(bufLength);
200}
201
202bool PIDBase::OnTarget() const {
203 double error = GetError();
204
205 std::scoped_lock lock(m_thisMutex);
206 switch (m_toleranceType) {
207 case kPercentTolerance:
208 return std::fabs(error) < m_tolerance / 100 * m_inputRange;
209 break;
210 case kAbsoluteTolerance:
211 return std::fabs(error) < m_tolerance;
212 break;
213 case kNoTolerance:
214 // TODO: this case needs an error
215 return false;
216 }
217 return false;
218}
219
220void PIDBase::Reset() {
221 std::scoped_lock lock(m_thisMutex);
222 m_prevError = 0;
223 m_totalError = 0;
224 m_result = 0;
225}
226
227void PIDBase::PIDWrite(double output) { SetSetpoint(output); }
228
229void PIDBase::InitSendable(SendableBuilder& builder) {
230 builder.SetSmartDashboardType("PIDController");
231 builder.SetSafeState([=]() { Reset(); });
232 builder.AddDoubleProperty("p", [=]() { return GetP(); },
233 [=](double value) { SetP(value); });
234 builder.AddDoubleProperty("i", [=]() { return GetI(); },
235 [=](double value) { SetI(value); });
236 builder.AddDoubleProperty("d", [=]() { return GetD(); },
237 [=](double value) { SetD(value); });
238 builder.AddDoubleProperty("f", [=]() { return GetF(); },
239 [=](double value) { SetF(value); });
240 builder.AddDoubleProperty("setpoint", [=]() { return GetSetpoint(); },
241 [=](double value) { SetSetpoint(value); });
242}
243
244void PIDBase::Calculate() {
245 if (m_pidInput == nullptr || m_pidOutput == nullptr) return;
246
247 bool enabled;
248 {
249 std::scoped_lock lock(m_thisMutex);
250 enabled = m_enabled;
251 }
252
253 if (enabled) {
254 double input;
255
256 // Storage for function inputs
257 PIDSourceType pidSourceType;
258 double P;
259 double I;
260 double D;
261 double feedForward = CalculateFeedForward();
262 double minimumOutput;
263 double maximumOutput;
264
265 // Storage for function input-outputs
266 double prevError;
267 double error;
268 double totalError;
269
270 {
271 std::scoped_lock lock(m_thisMutex);
272
273 input = m_filter.Calculate(m_pidInput->PIDGet());
274
275 pidSourceType = m_pidInput->GetPIDSourceType();
276 P = m_P;
277 I = m_I;
278 D = m_D;
279 minimumOutput = m_minimumOutput;
280 maximumOutput = m_maximumOutput;
281
282 prevError = m_prevError;
283 error = GetContinuousError(m_setpoint - input);
284 totalError = m_totalError;
285 }
286
287 // Storage for function outputs
288 double result;
289
290 if (pidSourceType == PIDSourceType::kRate) {
291 if (P != 0) {
292 totalError =
293 clamp(totalError + error, minimumOutput / P, maximumOutput / P);
294 }
295
296 result = D * error + P * totalError + feedForward;
297 } else {
298 if (I != 0) {
299 totalError =
300 clamp(totalError + error, minimumOutput / I, maximumOutput / I);
301 }
302
303 result =
304 P * error + I * totalError + D * (error - prevError) + feedForward;
305 }
306
307 result = clamp(result, minimumOutput, maximumOutput);
308
309 {
310 // Ensures m_enabled check and PIDWrite() call occur atomically
311 std::scoped_lock pidWriteLock(m_pidWriteMutex);
312 std::unique_lock mainLock(m_thisMutex);
313 if (m_enabled) {
314 // Don't block other PIDBase operations on PIDWrite()
315 mainLock.unlock();
316
317 m_pidOutput->PIDWrite(result);
318 }
319 }
320
321 std::scoped_lock lock(m_thisMutex);
322 m_prevError = m_error;
323 m_error = error;
324 m_totalError = totalError;
325 m_result = result;
326 }
327}
328
329double PIDBase::CalculateFeedForward() {
330 if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
331 return m_F * GetSetpoint();
332 } else {
333 double temp = m_F * GetDeltaSetpoint();
334 m_prevSetpoint = m_setpoint;
335 m_setpointTimer.Reset();
336 return temp;
337 }
338}
339
340double PIDBase::GetContinuousError(double error) const {
341 if (m_continuous && m_inputRange != 0) {
342 error = std::fmod(error, m_inputRange);
343 if (std::fabs(error) > m_inputRange / 2) {
344 if (error > 0) {
345 return error - m_inputRange;
346 } else {
347 return error + m_inputRange;
348 }
349 }
350 }
351
352 return error;
353}