blob: a2efe0f23caf1d1e9ad9bd06b24c2beebaca383e [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -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/HAL.h>
14
15#include "frc/PIDOutput.h"
16#include "frc/smartdashboard/SendableBuilder.h"
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080017#include "frc/smartdashboard/SendableRegistry.h"
Brian Silverman41cdd3e2019-01-19 19:48:58 -080018
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,
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080031 PIDOutput& output) {
Brian Silverman41cdd3e2019-01-19 19:48:58 -080032 m_P = Kp;
33 m_I = Ki;
34 m_D = Kd;
35 m_F = Kf;
36
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080037 m_pidInput = &source;
38 m_filter = LinearFilter::MovingAverage(1);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080039
40 m_pidOutput = &output;
41
42 m_setpointTimer.Start();
43
44 static int instances = 0;
45 instances++;
46 HAL_Report(HALUsageReporting::kResourceType_PIDController, instances);
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080047 SendableRegistry::GetInstance().Add(this, "PIDController", instances);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080048}
49
50double PIDBase::Get() const {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080051 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080052 return m_result;
53}
54
55void PIDBase::SetContinuous(bool continuous) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080056 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080057 m_continuous = continuous;
58}
59
60void PIDBase::SetInputRange(double minimumInput, double maximumInput) {
61 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080062 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080063 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) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080072 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080073 m_minimumOutput = minimumOutput;
74 m_maximumOutput = maximumOutput;
75}
76
77void PIDBase::SetPID(double p, double i, double d) {
78 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080079 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080080 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) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080087 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080088 m_P = p;
89 m_I = i;
90 m_D = d;
91 m_F = f;
92}
93
94void PIDBase::SetP(double p) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080095 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080096 m_P = p;
97}
98
99void PIDBase::SetI(double i) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800100 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800101 m_I = i;
102}
103
104void PIDBase::SetD(double d) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800105 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800106 m_D = d;
107}
108
109void PIDBase::SetF(double f) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800110 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800111 m_F = f;
112}
113
114double PIDBase::GetP() const {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800115 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800116 return m_P;
117}
118
119double PIDBase::GetI() const {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800120 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800121 return m_I;
122}
123
124double PIDBase::GetD() const {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800125 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800126 return m_D;
127}
128
129double PIDBase::GetF() const {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800130 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800131 return m_F;
132}
133
134void PIDBase::SetSetpoint(double setpoint) {
135 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800136 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800137
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 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800152 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800153 return m_setpoint;
154}
155
156double PIDBase::GetDeltaSetpoint() const {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800157 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800158 return (m_setpoint - m_prevSetpoint) / m_setpointTimer.Get();
159}
160
161double PIDBase::GetError() const {
162 double setpoint = GetSetpoint();
163 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800164 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800165 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) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800180 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800181 m_toleranceType = kPercentTolerance;
182 m_tolerance = percent;
183}
184
185void PIDBase::SetAbsoluteTolerance(double absTolerance) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800186 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800187 m_toleranceType = kAbsoluteTolerance;
188 m_tolerance = absTolerance;
189}
190
191void PIDBase::SetPercentTolerance(double percent) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800192 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800193 m_toleranceType = kPercentTolerance;
194 m_tolerance = percent;
195}
196
197void PIDBase::SetToleranceBuffer(int bufLength) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800198 std::scoped_lock lock(m_thisMutex);
199 m_filter = LinearFilter::MovingAverage(bufLength);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800200}
201
202bool PIDBase::OnTarget() const {
203 double error = GetError();
204
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800205 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800206 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() {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800221 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800222 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() {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800245 if (m_pidInput == nullptr || m_pidOutput == nullptr) return;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800246
247 bool enabled;
248 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800249 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800250 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 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800271 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800272
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800273 input = m_filter.Calculate(m_pidInput->PIDGet());
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800274
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
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800311 std::scoped_lock pidWriteLock(m_pidWriteMutex);
312 std::unique_lock mainLock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800313 if (m_enabled) {
314 // Don't block other PIDBase operations on PIDWrite()
315 mainLock.unlock();
316
317 m_pidOutput->PIDWrite(result);
318 }
319 }
320
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800321 std::scoped_lock lock(m_thisMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800322 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}