blob: 03beb5c119fc1be83089a38debdffdacb3146f31 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. 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 $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include "PIDController.h"
8#include "Notifier.h"
9#include "PIDSource.h"
10#include "PIDOutput.h"
11#include <math.h>
12
13static const std::string kP = "p";
14static const std::string kI = "i";
15static const std::string kD = "d";
16static const std::string kF = "f";
17static const std::string kSetpoint = "setpoint";
18static const std::string kEnabled = "enabled";
19
20
21/**
22 * Allocate a PID object with the given constants for P, I, D
23 * @param Kp the proportional coefficient
24 * @param Ki the integral coefficient
25 * @param Kd the derivative coefficient
26 * @param source The PIDSource object that is used to get values
27 * @param output The PIDOutput object that is set to the output value
28 * @param period the loop time for doing calculations. This particularly effects calculations of the
29 * integral and differental terms. The default is 50ms.
30 */
31PIDController::PIDController(float Kp, float Ki, float Kd,
32 PIDSource *source, PIDOutput *output,
33 float period)
34{
35 Initialize(Kp, Ki, Kd, 0.0f, source, output, period);
36}
37
38/**
39 * Allocate a PID object with the given constants for P, I, D
40 * @param Kp the proportional coefficient
41 * @param Ki the integral coefficient
42 * @param Kd the derivative coefficient
43 * @param source The PIDSource object that is used to get values
44 * @param output The PIDOutput object that is set to the output value
45 * @param period the loop time for doing calculations. This particularly effects calculations of the
46 * integral and differental terms. The default is 50ms.
47 */
48PIDController::PIDController(float Kp, float Ki, float Kd, float Kf,
49 PIDSource *source, PIDOutput *output,
50 float period)
51{
52 Initialize(Kp, Ki, Kd, Kf, source, output, period);
53}
54
55
56void PIDController::Initialize(float Kp, float Ki, float Kd, float Kf,
57 PIDSource *source, PIDOutput *output,
58 float period)
59{
60 m_table = nullptr;
61
62 m_P = Kp;
63 m_I = Ki;
64 m_D = Kd;
65 m_F = Kf;
66
67 m_maximumOutput = 1.0;
68 m_minimumOutput = -1.0;
69
70 m_maximumInput = 0;
71 m_minimumInput = 0;
72
73 m_continuous = false;
74 m_enabled = false;
75 m_setpoint = 0;
76
77 m_prevInput = 0;
78 m_totalError = 0;
79 m_tolerance = .05;
80
81 m_result = 0;
82
83 m_pidInput = source;
84 m_pidOutput = output;
85 m_period = period;
86
87 m_controlLoop = std::make_unique<Notifier>(PIDController::CallCalculate, this);
88 m_controlLoop->StartPeriodic(m_period);
89
90 static int32_t instances = 0;
91 instances++;
92
93 m_toleranceType = kNoTolerance;
94}
95
96PIDController::~PIDController() {
97 if (m_table != nullptr) m_table->RemoveTableListener(this);
98}
99
100/**
101 * Call the Calculate method as a non-static method. This avoids having to prepend
102 * all local variables in that method with the class pointer. This way the "this"
103 * pointer will be set up and class variables can be called more easily.
104 * This method is static and called by the Notifier class.
105 * @param controller the address of the PID controller object to use in the background loop
106 */
107void PIDController::CallCalculate(void *controller)
108{
109 PIDController *control = (PIDController*) controller;
110 control->Calculate();
111}
112
113 /**
114 * Read the input, calculate the output accordingly, and write to the output.
115 * This should only be called by the Notifier indirectly through CallCalculate
116 * and is created during initialization.
117 */
118void PIDController::Calculate()
119{
120 bool enabled;
121 PIDSource *pidInput;
122
123 {
124 std::lock_guard<priority_mutex> lock(m_mutex);
125 if (m_pidInput == 0) return;
126 if (m_pidOutput == 0) return;
127 enabled = m_enabled;
128 pidInput = m_pidInput;
129 }
130
131 if (enabled)
132 {
133 float input = pidInput->PIDGet();
134 float result;
135 PIDOutput *pidOutput;
136
137 {
138 std::lock_guard<priority_mutex> sync(m_mutex);
139 m_error = m_setpoint - input;
140 if (m_continuous)
141 {
142 if (fabs(m_error) > (m_maximumInput - m_minimumInput) / 2)
143 {
144 if (m_error > 0)
145 {
146 m_error = m_error - m_maximumInput + m_minimumInput;
147 }
148 else
149 {
150 m_error = m_error + m_maximumInput - m_minimumInput;
151 }
152 }
153 }
154
155 if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
156 if (m_P != 0) {
157 double potentialPGain = (m_totalError + m_error) * m_P;
158 if (potentialPGain < m_maximumOutput) {
159 if (potentialPGain > m_minimumOutput) {
160 m_totalError += m_error;
161 }
162 else {
163 m_totalError = m_minimumOutput / m_P;
164 }
165 }
166 else {
167 m_totalError = m_maximumOutput / m_P;
168 }
169 }
170
171 m_result = m_D * m_error + m_P * m_totalError + m_setpoint * m_F;
172 }
173 else {
174 if (m_I != 0) {
175 double potentialIGain = (m_totalError + m_error) * m_I;
176 if (potentialIGain < m_maximumOutput) {
177 if (potentialIGain > m_minimumOutput) {
178 m_totalError += m_error;
179 }
180 else {
181 m_totalError = m_minimumOutput / m_I;
182 }
183 }
184 else {
185 m_totalError = m_maximumOutput / m_I;
186 }
187 }
188
189 m_result = m_P * m_error + m_I * m_totalError + m_D * (m_prevInput - input) + m_setpoint * m_F;
190 }
191 m_prevInput = input;
192
193 if (m_result > m_maximumOutput) m_result = m_maximumOutput;
194 else if (m_result < m_minimumOutput) m_result = m_minimumOutput;
195
196 pidOutput = m_pidOutput;
197 result = m_result;
198 }
199
200 pidOutput->PIDWrite(result);
201 }
202}
203
204/**
205 * Set the PID Controller gain parameters.
206 * Set the proportional, integral, and differential coefficients.
207 * @param p Proportional coefficient
208 * @param i Integral coefficient
209 * @param d Differential coefficient
210 */
211void PIDController::SetPID(double p, double i, double d)
212{
213 {
214 std::lock_guard<priority_mutex> lock(m_mutex);
215 m_P = p;
216 m_I = i;
217 m_D = d;
218 }
219
220 if (m_table != nullptr) {
221 m_table->PutNumber("p", m_P);
222 m_table->PutNumber("i", m_I);
223 m_table->PutNumber("d", m_D);
224 }
225}
226
227/**
228 * Set the PID Controller gain parameters.
229 * Set the proportional, integral, and differential coefficients.
230 * @param p Proportional coefficient
231 * @param i Integral coefficient
232 * @param d Differential coefficient
233 * @param f Feed forward coefficient
234 */
235void PIDController::SetPID(double p, double i, double d, double f)
236{
237 {
238 std::lock_guard<priority_mutex> lock(m_mutex);
239 m_P = p;
240 m_I = i;
241 m_D = d;
242 m_F = f;
243 }
244
245 if (m_table != nullptr) {
246 m_table->PutNumber("p", m_P);
247 m_table->PutNumber("i", m_I);
248 m_table->PutNumber("d", m_D);
249 m_table->PutNumber("f", m_F);
250 }
251}
252
253/**
254 * Get the Proportional coefficient
255 * @return proportional coefficient
256 */
257double PIDController::GetP() const
258{
259 std::lock_guard<priority_mutex> lock(m_mutex);
260 return m_P;
261}
262
263/**
264 * Get the Integral coefficient
265 * @return integral coefficient
266 */
267double PIDController::GetI() const
268{
269 std::lock_guard<priority_mutex> lock(m_mutex);
270 return m_I;
271}
272
273/**
274 * Get the Differential coefficient
275 * @return differential coefficient
276 */
277double PIDController::GetD() const
278{
279 std::lock_guard<priority_mutex> lock(m_mutex);
280 return m_D;
281}
282
283/**
284 * Get the Feed forward coefficient
285 * @return Feed forward coefficient
286 */
287double PIDController::GetF() const
288{
289 std::lock_guard<priority_mutex> lock(m_mutex);
290 return m_F;
291}
292
293/**
294 * Return the current PID result
295 * This is always centered on zero and constrained the the max and min outs
296 * @return the latest calculated output
297 */
298float PIDController::Get() const
299{
300 std::lock_guard<priority_mutex> lock(m_mutex);
301 return m_result;
302}
303
304/**
305 * Set the PID controller to consider the input to be continuous,
306 * Rather then using the max and min in as constraints, it considers them to
307 * be the same point and automatically calculates the shortest route to
308 * the setpoint.
309 * @param continuous Set to true turns on continuous, false turns off continuous
310 */
311void PIDController::SetContinuous(bool continuous)
312{
313 std::lock_guard<priority_mutex> lock(m_mutex);
314 m_continuous = continuous;
315}
316
317/**
318 * Sets the maximum and minimum values expected from the input.
319 *
320 * @param minimumInput the minimum value expected from the input
321 * @param maximumInput the maximum value expected from the output
322 */
323void PIDController::SetInputRange(float minimumInput, float maximumInput)
324{
325 {
326 std::lock_guard<priority_mutex> lock(m_mutex);
327 m_minimumInput = minimumInput;
328 m_maximumInput = maximumInput;
329 }
330
331 SetSetpoint(m_setpoint);
332}
333
334/**
335 * Sets the minimum and maximum values to write.
336 *
337 * @param minimumOutput the minimum value to write to the output
338 * @param maximumOutput the maximum value to write to the output
339 */
340void PIDController::SetOutputRange(float minimumOutput, float maximumOutput)
341{
342 std::lock_guard<priority_mutex> lock(m_mutex);
343 m_minimumOutput = minimumOutput;
344 m_maximumOutput = maximumOutput;
345}
346
347/**
348 * Set the setpoint for the PIDController
349 * @param setpoint the desired setpoint
350 */
351void PIDController::SetSetpoint(float setpoint)
352{
353 {
354 std::lock_guard<priority_mutex> lock(m_mutex);
355 if (m_maximumInput > m_minimumInput)
356 {
357 if (setpoint > m_maximumInput)
358 m_setpoint = m_maximumInput;
359 else if (setpoint < m_minimumInput)
360 m_setpoint = m_minimumInput;
361 else
362 m_setpoint = setpoint;
363 }
364 else
365 {
366 m_setpoint = setpoint;
367 }
368 }
369
370 if (m_table != nullptr) {
371 m_table->PutNumber("setpoint", m_setpoint);
372 }
373}
374
375/**
376 * Returns the current setpoint of the PIDController
377 * @return the current setpoint
378 */
379double PIDController::GetSetpoint() const
380{
381 std::lock_guard<priority_mutex> lock(m_mutex);
382 return m_setpoint;
383}
384
385/**
386 * Retruns the current difference of the input from the setpoint
387 * @return the current error
388 */
389float PIDController::GetError() const
390{
391 double pidInput;
392 {
393 std::lock_guard<priority_mutex> lock(m_mutex);
394 pidInput = m_pidInput->PIDGet();
395 }
396 return GetSetpoint() - pidInput;
397}
398
399/**
400 * Sets what type of input the PID controller will use
401 */
402void PIDController::SetPIDSourceType(PIDSourceType pidSource) {
403 m_pidInput->SetPIDSourceType(pidSource);
404}
405
406/**
407 * Returns the type of input the PID controller is using
408 * @return the PID controller input type
409 */
410PIDSourceType PIDController::GetPIDSourceType() const {
411 return m_pidInput->GetPIDSourceType();
412}
413
414/**
415 * Returns the current average of the error over the past few iterations.
416 * You can specify the number of iterations to average with SetToleranceBuffer()
417 * (defaults to 1). This is the same value that is used for OnTarget().
418 * @return the average error
419 */
420float PIDController::GetAvgError() const {
421 float avgError = 0;
422 {
423 std::lock_guard<priority_mutex> sync(m_mutex);
424 // Don't divide by zero.
425 if (m_buf.size()) avgError = m_bufTotal / m_buf.size();
426 }
427 return avgError;
428}
429
430/*
431 * Set the percentage error which is considered tolerable for use with
432 * OnTarget.
433 * @param percentage error which is tolerable
434 */
435void PIDController::SetTolerance(float percent)
436{
437 std::lock_guard<priority_mutex> lock(m_mutex);
438 m_toleranceType = kPercentTolerance;
439 m_tolerance = percent;
440}
441
442/*
443 * Set the percentage error which is considered tolerable for use with
444 * OnTarget.
445 * @param percentage error which is tolerable
446 */
447void PIDController::SetPercentTolerance(float percent)
448{
449 std::lock_guard<priority_mutex> lock(m_mutex);
450 m_toleranceType = kPercentTolerance;
451 m_tolerance = percent;
452}
453
454/*
455 * Set the absolute error which is considered tolerable for use with
456 * OnTarget.
457 * @param percentage error which is tolerable
458 */
459void PIDController::SetAbsoluteTolerance(float absTolerance)
460{
461 std::lock_guard<priority_mutex> lock(m_mutex);
462 m_toleranceType = kAbsoluteTolerance;
463 m_tolerance = absTolerance;
464}
465
466/*
467 * Set the number of previous error samples to average for tolerancing. When
468 * determining whether a mechanism is on target, the user may want to use a
469 * rolling average of previous measurements instead of a precise position or
470 * velocity. This is useful for noisy sensors which return a few erroneous
471 * measurements when the mechanism is on target. However, the mechanism will
472 * not register as on target for at least the specified bufLength cycles.
473 * @param bufLength Number of previous cycles to average. Defaults to 1.
474 */
475void PIDController::SetToleranceBuffer(unsigned bufLength) {
476 m_bufLength = bufLength;
477
478 // Cut the buffer down to size if needed.
479 while (m_buf.size() > bufLength) {
480 m_bufTotal -= m_buf.front();
481 m_buf.pop();
482 }
483}
484
485/*
486 * Return true if the error is within the percentage of the total input range,
487 * determined by SetTolerance. This asssumes that the maximum and minimum input
488 * were set using SetInput.
489 * Currently this just reports on target as the actual value passes through the setpoint.
490 * Ideally it should be based on being within the tolerance for some period of time.
491 */
492bool PIDController::OnTarget() const
493{
494 double error = GetError();
495
496 std::lock_guard<priority_mutex> sync(m_mutex);
497 switch (m_toleranceType) {
498 case kPercentTolerance:
499 return fabs(error) < m_tolerance / 100 * (m_maximumInput - m_minimumInput);
500 break;
501 case kAbsoluteTolerance:
502 return fabs(error) < m_tolerance;
503 break;
504 case kNoTolerance: // TODO: this case needs an error
505 return false;
506 }
507 return false;
508}
509
510/**
511 * Begin running the PIDController
512 */
513void PIDController::Enable()
514{
515 {
516 std::lock_guard<priority_mutex> lock(m_mutex);
517 m_enabled = true;
518 }
519
520 if (m_table != nullptr) {
521 m_table->PutBoolean("enabled", true);
522 }
523}
524
525/**
526 * Stop running the PIDController, this sets the output to zero before stopping.
527 */
528void PIDController::Disable()
529{
530 {
531 std::lock_guard<priority_mutex> lock(m_mutex);
532 m_pidOutput->PIDWrite(0);
533 m_enabled = false;
534 }
535
536 if (m_table != nullptr) {
537 m_table->PutBoolean("enabled", false);
538 }
539}
540
541/**
542 * Return true if PIDController is enabled.
543 */
544bool PIDController::IsEnabled() const
545{
546 std::lock_guard<priority_mutex> lock(m_mutex);
547 return m_enabled;
548}
549
550/**
551 * Reset the previous error,, the integral term, and disable the controller.
552 */
553void PIDController::Reset()
554{
555 Disable();
556
557 std::lock_guard<priority_mutex> lock(m_mutex);
558 m_prevInput = 0;
559 m_totalError = 0;
560 m_result = 0;
561}
562
563std::string PIDController::GetSmartDashboardType() const {
564 return "PIDController";
565}
566
567void PIDController::InitTable(std::shared_ptr<ITable> table){
568 if(m_table!=nullptr)
569 m_table->RemoveTableListener(this);
570 m_table = table;
571 if(m_table!=nullptr){
572 m_table->PutNumber(kP, GetP());
573 m_table->PutNumber(kI, GetI());
574 m_table->PutNumber(kD, GetD());
575 m_table->PutNumber(kF, GetF());
576 m_table->PutNumber(kSetpoint, GetSetpoint());
577 m_table->PutBoolean(kEnabled, IsEnabled());
578 m_table->AddTableListener(this, false);
579 }
580}
581
582std::shared_ptr<ITable> PIDController::GetTable() const {
583 return m_table;
584}
585
586void PIDController::ValueChanged(ITable* source, llvm::StringRef key,
587 std::shared_ptr<nt::Value> value, bool isNew) {
588 if (key == kP || key == kI || key == kD || key == kF) {
589 if (m_P != m_table->GetNumber(kP, 0.0) ||
590 m_I != m_table->GetNumber(kI, 0.0) ||
591 m_D != m_table->GetNumber(kD, 0.0) ||
592 m_F != m_table->GetNumber(kF, 0.0)) {
593 SetPID(m_table->GetNumber(kP, 0.0), m_table->GetNumber(kI, 0.0),
594 m_table->GetNumber(kD, 0.0), m_table->GetNumber(kF, 0.0));
595 }
596 } else if (key == kSetpoint && value->IsDouble() &&
597 m_setpoint != value->GetDouble()) {
598 SetSetpoint(value->GetDouble());
599 } else if (key == kEnabled && value->IsBoolean() &&
600 m_enabled != value->GetBoolean()) {
601 if (value->GetBoolean()) {
602 Enable();
603 } else {
604 Disable();
605 }
606 }
607}
608
609void PIDController::UpdateTable() {
610
611}
612
613void PIDController::StartLiveWindowMode() {
614 Disable();
615}
616
617void PIDController::StopLiveWindowMode() {
618
619}