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