blob: 5802d989b59ca1b40b70446c2af92a01e302d89c [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2008-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -08003/* 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(); });
Austin Schuh1e69f942020-11-14 15:06:14 -0800232 builder.AddDoubleProperty(
233 "p", [=]() { return GetP(); }, [=](double value) { SetP(value); });
234 builder.AddDoubleProperty(
235 "i", [=]() { return GetI(); }, [=](double value) { SetI(value); });
236 builder.AddDoubleProperty(
237 "d", [=]() { return GetD(); }, [=](double value) { SetD(value); });
238 builder.AddDoubleProperty(
239 "f", [=]() { return GetF(); }, [=](double value) { SetF(value); });
240 builder.AddDoubleProperty(
241 "setpoint", [=]() { return GetSetpoint(); },
242 [=](double value) { SetSetpoint(value); });
Brian Silverman8fce7482020-01-05 13:18:21 -0800243}
244
245void PIDBase::Calculate() {
246 if (m_pidInput == nullptr || m_pidOutput == nullptr) return;
247
248 bool enabled;
249 {
250 std::scoped_lock lock(m_thisMutex);
251 enabled = m_enabled;
252 }
253
254 if (enabled) {
255 double input;
256
257 // Storage for function inputs
258 PIDSourceType pidSourceType;
259 double P;
260 double I;
261 double D;
262 double feedForward = CalculateFeedForward();
263 double minimumOutput;
264 double maximumOutput;
265
266 // Storage for function input-outputs
267 double prevError;
268 double error;
269 double totalError;
270
271 {
272 std::scoped_lock lock(m_thisMutex);
273
274 input = m_filter.Calculate(m_pidInput->PIDGet());
275
276 pidSourceType = m_pidInput->GetPIDSourceType();
277 P = m_P;
278 I = m_I;
279 D = m_D;
280 minimumOutput = m_minimumOutput;
281 maximumOutput = m_maximumOutput;
282
283 prevError = m_prevError;
284 error = GetContinuousError(m_setpoint - input);
285 totalError = m_totalError;
286 }
287
288 // Storage for function outputs
289 double result;
290
291 if (pidSourceType == PIDSourceType::kRate) {
292 if (P != 0) {
293 totalError =
294 clamp(totalError + error, minimumOutput / P, maximumOutput / P);
295 }
296
297 result = D * error + P * totalError + feedForward;
298 } else {
299 if (I != 0) {
300 totalError =
301 clamp(totalError + error, minimumOutput / I, maximumOutput / I);
302 }
303
304 result =
305 P * error + I * totalError + D * (error - prevError) + feedForward;
306 }
307
308 result = clamp(result, minimumOutput, maximumOutput);
309
310 {
311 // Ensures m_enabled check and PIDWrite() call occur atomically
312 std::scoped_lock pidWriteLock(m_pidWriteMutex);
313 std::unique_lock mainLock(m_thisMutex);
314 if (m_enabled) {
315 // Don't block other PIDBase operations on PIDWrite()
316 mainLock.unlock();
317
318 m_pidOutput->PIDWrite(result);
319 }
320 }
321
322 std::scoped_lock lock(m_thisMutex);
323 m_prevError = m_error;
324 m_error = error;
325 m_totalError = totalError;
326 m_result = result;
327 }
328}
329
330double PIDBase::CalculateFeedForward() {
331 if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
332 return m_F * GetSetpoint();
333 } else {
334 double temp = m_F * GetDeltaSetpoint();
335 m_prevSetpoint = m_setpoint;
336 m_setpointTimer.Reset();
337 return temp;
338 }
339}
340
341double PIDBase::GetContinuousError(double error) const {
342 if (m_continuous && m_inputRange != 0) {
343 error = std::fmod(error, m_inputRange);
344 if (std::fabs(error) > m_inputRange / 2) {
345 if (error > 0) {
346 return error - m_inputRange;
347 } else {
348 return error + m_inputRange;
349 }
350 }
351 }
352
353 return error;
354}