blob: 825d4ebf834648082eedc9be013aed46ed82757a [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include "frc/PIDBase.h"
6
7#include <algorithm>
8#include <cmath>
9
10#include <hal/FRCUsageReporting.h>
11#include <wpi/sendable/SendableBuilder.h>
12#include <wpi/sendable/SendableRegistry.h>
13
14#include "frc/PIDOutput.h"
15
16using namespace frc;
17
18template <class T>
19constexpr const T& clamp(const T& value, const T& low, const T& high) {
20 return std::max(low, std::min(value, high));
21}
22
23PIDBase::PIDBase(double Kp, double Ki, double Kd, PIDSource& source,
24 PIDOutput& output)
25 : PIDBase(Kp, Ki, Kd, 0.0, source, output) {}
26
27PIDBase::PIDBase(double Kp, double Ki, double Kd, double Kf, PIDSource& source,
28 PIDOutput& output) {
29 m_P = Kp;
30 m_I = Ki;
31 m_D = Kd;
32 m_F = Kf;
33
34 m_pidInput = &source;
35 m_filter = LinearFilter<double>::MovingAverage(1);
36
37 m_pidOutput = &output;
38
39 m_setpointTimer.Start();
40
41 static int instances = 0;
42 instances++;
43 HAL_Report(HALUsageReporting::kResourceType_PIDController, instances);
44 wpi::SendableRegistry::Add(this, "PIDController", instances);
45}
46
47double PIDBase::Get() const {
48 std::scoped_lock lock(m_thisMutex);
49 return m_result;
50}
51
52void PIDBase::SetContinuous(bool continuous) {
53 std::scoped_lock lock(m_thisMutex);
54 m_continuous = continuous;
55}
56
57void PIDBase::SetInputRange(double minimumInput, double maximumInput) {
58 {
59 std::scoped_lock lock(m_thisMutex);
60 m_minimumInput = minimumInput;
61 m_maximumInput = maximumInput;
62 m_inputRange = maximumInput - minimumInput;
63 }
64
65 SetSetpoint(m_setpoint);
66}
67
68void PIDBase::SetOutputRange(double minimumOutput, double maximumOutput) {
69 std::scoped_lock lock(m_thisMutex);
70 m_minimumOutput = minimumOutput;
71 m_maximumOutput = maximumOutput;
72}
73
74void PIDBase::SetPID(double p, double i, double d) {
75 {
76 std::scoped_lock lock(m_thisMutex);
77 m_P = p;
78 m_I = i;
79 m_D = d;
80 }
81}
82
83void PIDBase::SetPID(double p, double i, double d, double f) {
84 std::scoped_lock lock(m_thisMutex);
85 m_P = p;
86 m_I = i;
87 m_D = d;
88 m_F = f;
89}
90
91void PIDBase::SetP(double p) {
92 std::scoped_lock lock(m_thisMutex);
93 m_P = p;
94}
95
96void PIDBase::SetI(double i) {
97 std::scoped_lock lock(m_thisMutex);
98 m_I = i;
99}
100
101void PIDBase::SetD(double d) {
102 std::scoped_lock lock(m_thisMutex);
103 m_D = d;
104}
105
106void PIDBase::SetF(double f) {
107 std::scoped_lock lock(m_thisMutex);
108 m_F = f;
109}
110
111double PIDBase::GetP() const {
112 std::scoped_lock lock(m_thisMutex);
113 return m_P;
114}
115
116double PIDBase::GetI() const {
117 std::scoped_lock lock(m_thisMutex);
118 return m_I;
119}
120
121double PIDBase::GetD() const {
122 std::scoped_lock lock(m_thisMutex);
123 return m_D;
124}
125
126double PIDBase::GetF() const {
127 std::scoped_lock lock(m_thisMutex);
128 return m_F;
129}
130
131void PIDBase::SetSetpoint(double setpoint) {
132 {
133 std::scoped_lock lock(m_thisMutex);
134
135 if (m_maximumInput > m_minimumInput) {
136 if (setpoint > m_maximumInput) {
137 m_setpoint = m_maximumInput;
138 } else if (setpoint < m_minimumInput) {
139 m_setpoint = m_minimumInput;
140 } else {
141 m_setpoint = setpoint;
142 }
143 } else {
144 m_setpoint = setpoint;
145 }
146 }
147}
148
149double PIDBase::GetSetpoint() const {
150 std::scoped_lock lock(m_thisMutex);
151 return m_setpoint;
152}
153
154double PIDBase::GetDeltaSetpoint() const {
155 std::scoped_lock lock(m_thisMutex);
156 return (m_setpoint - m_prevSetpoint) / m_setpointTimer.Get().value();
157}
158
159double PIDBase::GetError() const {
160 double setpoint = GetSetpoint();
161 {
162 std::scoped_lock lock(m_thisMutex);
163 return GetContinuousError(setpoint - m_pidInput->PIDGet());
164 }
165}
166
167double PIDBase::GetAvgError() const {
168 return GetError();
169}
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) {
228 SetSetpoint(output);
229}
230
231void PIDBase::InitSendable(wpi::SendableBuilder& builder) {
232 builder.SetSmartDashboardType("PIDController");
233 builder.SetSafeState([=] { Reset(); });
234 builder.AddDoubleProperty(
235 "p", [=] { return GetP(); }, [=](double value) { SetP(value); });
236 builder.AddDoubleProperty(
237 "i", [=] { return GetI(); }, [=](double value) { SetI(value); });
238 builder.AddDoubleProperty(
239 "d", [=] { return GetD(); }, [=](double value) { SetD(value); });
240 builder.AddDoubleProperty(
241 "f", [=] { return GetF(); }, [=](double value) { SetF(value); });
242 builder.AddDoubleProperty(
243 "setpoint", [=] { return GetSetpoint(); },
244 [=](double value) { SetSetpoint(value); });
245}
246
247void PIDBase::Calculate() {
248 if (m_pidInput == nullptr || m_pidOutput == nullptr) {
249 return;
250 }
251
252 bool enabled;
253 {
254 std::scoped_lock lock(m_thisMutex);
255 enabled = m_enabled;
256 }
257
258 if (enabled) {
259 double input;
260
261 // Storage for function inputs
262 PIDSourceType pidSourceType;
263 double P;
264 double I;
265 double D;
266 double feedForward = CalculateFeedForward();
267 double minimumOutput;
268 double maximumOutput;
269
270 // Storage for function input-outputs
271 double prevError;
272 double error;
273 double totalError;
274
275 {
276 std::scoped_lock lock(m_thisMutex);
277
278 input = m_filter.Calculate(m_pidInput->PIDGet());
279
280 pidSourceType = m_pidInput->GetPIDSourceType();
281 P = m_P;
282 I = m_I;
283 D = m_D;
284 minimumOutput = m_minimumOutput;
285 maximumOutput = m_maximumOutput;
286
287 prevError = m_prevError;
288 error = GetContinuousError(m_setpoint - input);
289 totalError = m_totalError;
290 }
291
292 // Storage for function outputs
293 double result;
294
295 if (pidSourceType == PIDSourceType::kRate) {
296 if (P != 0) {
297 totalError =
298 clamp(totalError + error, minimumOutput / P, maximumOutput / P);
299 }
300
301 result = D * error + P * totalError + feedForward;
302 } else {
303 if (I != 0) {
304 totalError =
305 clamp(totalError + error, minimumOutput / I, maximumOutput / I);
306 }
307
308 result =
309 P * error + I * totalError + D * (error - prevError) + feedForward;
310 }
311
312 result = clamp(result, minimumOutput, maximumOutput);
313
314 {
315 // Ensures m_enabled check and PIDWrite() call occur atomically
316 std::scoped_lock pidWriteLock(m_pidWriteMutex);
317 std::unique_lock mainLock(m_thisMutex);
318 if (m_enabled) {
319 // Don't block other PIDBase operations on PIDWrite()
320 mainLock.unlock();
321
322 m_pidOutput->PIDWrite(result);
323 }
324 }
325
326 std::scoped_lock lock(m_thisMutex);
327 m_prevError = m_error;
328 m_error = error;
329 m_totalError = totalError;
330 m_result = result;
331 }
332}
333
334double PIDBase::CalculateFeedForward() {
335 if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
336 return m_F * GetSetpoint();
337 } else {
338 double temp = m_F * GetDeltaSetpoint();
339 m_prevSetpoint = m_setpoint;
340 m_setpointTimer.Reset();
341 return temp;
342 }
343}
344
345double PIDBase::GetContinuousError(double error) const {
346 if (m_continuous && m_inputRange != 0) {
347 error = std::fmod(error, m_inputRange);
348 if (std::fabs(error) > m_inputRange / 2) {
349 if (error > 0) {
350 return error - m_inputRange;
351 } else {
352 return error + m_inputRange;
353 }
354 }
355 }
356
357 return error;
358}