blob: d3c18b519623b05357bb2a6fd91e794eb43488c2 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
Brian Silverman1a675112016-02-20 20:42:49 -05002/* Copyright (c) FIRST 2008-2016. All Rights Reserved. */
Brian Silverman26e4e522015-12-17 01:56:40 -05003/* Open Source Software - may be modified and shared by FRC teams. The code */
Brian Silverman1a675112016-02-20 20:42:49 -05004/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
Brian Silverman26e4e522015-12-17 01:56:40 -05006/*----------------------------------------------------------------------------*/
7
8#include "PIDController.h"
9#include "Notifier.h"
10#include "PIDSource.h"
11#include "PIDOutput.h"
12#include <math.h>
13
14static const std::string kP = "p";
15static const std::string kI = "i";
16static const std::string kD = "d";
17static const std::string kF = "f";
18static const std::string kSetpoint = "setpoint";
19static const std::string kEnabled = "enabled";
20
21
22/**
23 * Allocate a PID object with the given constants for P, I, D
24 * @param Kp the proportional coefficient
25 * @param Ki the integral coefficient
26 * @param Kd the derivative coefficient
27 * @param source The PIDSource object that is used to get values
28 * @param output The PIDOutput object that is set to the output value
29 * @param period the loop time for doing calculations. This particularly effects calculations of the
30 * integral and differental terms. The default is 50ms.
31 */
32PIDController::PIDController(float Kp, float Ki, float Kd,
33 PIDSource *source, PIDOutput *output,
34 float period)
35{
36 Initialize(Kp, Ki, Kd, 0.0f, source, output, period);
37}
38
39/**
40 * Allocate a PID object with the given constants for P, I, D
41 * @param Kp the proportional coefficient
42 * @param Ki the integral coefficient
43 * @param Kd the derivative coefficient
44 * @param source The PIDSource object that is used to get values
45 * @param output The PIDOutput object that is set to the output value
46 * @param period the loop time for doing calculations. This particularly effects calculations of the
47 * integral and differental terms. The default is 50ms.
48 */
49PIDController::PIDController(float Kp, float Ki, float Kd, float Kf,
50 PIDSource *source, PIDOutput *output,
51 float period)
52{
53 Initialize(Kp, Ki, Kd, Kf, source, output, period);
54}
55
56
57void PIDController::Initialize(float Kp, float Ki, float Kd, float Kf,
58 PIDSource *source, PIDOutput *output,
59 float period)
60{
61 m_table = nullptr;
62
63 m_P = Kp;
64 m_I = Ki;
65 m_D = Kd;
66 m_F = Kf;
67
68 m_maximumOutput = 1.0;
69 m_minimumOutput = -1.0;
70
71 m_maximumInput = 0;
72 m_minimumInput = 0;
73
74 m_continuous = false;
75 m_enabled = false;
76 m_setpoint = 0;
77
Brian Silverman1a675112016-02-20 20:42:49 -050078 m_prevError = 0;
Brian Silverman26e4e522015-12-17 01:56:40 -050079 m_totalError = 0;
80 m_tolerance = .05;
81
82 m_result = 0;
83
84 m_pidInput = source;
85 m_pidOutput = output;
86 m_period = period;
87
Brian Silverman1a675112016-02-20 20:42:49 -050088 m_controlLoop = std::make_unique<Notifier>(&PIDController::Calculate, this);
Brian Silverman26e4e522015-12-17 01:56:40 -050089 m_controlLoop->StartPeriodic(m_period);
90
91 static int32_t instances = 0;
92 instances++;
93
94 m_toleranceType = kNoTolerance;
95}
96
97PIDController::~PIDController() {
98 if (m_table != nullptr) m_table->RemoveTableListener(this);
99}
100
101/**
Brian Silverman1a675112016-02-20 20:42:49 -0500102 * Read the input, calculate the output accordingly, and write to the output.
103 * This should only be called by the Notifier.
Brian Silverman26e4e522015-12-17 01:56:40 -0500104 */
Brian Silverman26e4e522015-12-17 01:56:40 -0500105void PIDController::Calculate()
106{
107 bool enabled;
108 PIDSource *pidInput;
109
110 {
Brian Silverman1a675112016-02-20 20:42:49 -0500111 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500112 if (m_pidInput == 0) return;
113 if (m_pidOutput == 0) return;
114 enabled = m_enabled;
115 pidInput = m_pidInput;
116 }
117
118 if (enabled)
119 {
120 float input = pidInput->PIDGet();
121 float result;
122 PIDOutput *pidOutput;
123
124 {
Brian Silverman1a675112016-02-20 20:42:49 -0500125 std::lock_guard<priority_recursive_mutex> sync(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500126 m_error = m_setpoint - input;
127 if (m_continuous)
128 {
129 if (fabs(m_error) > (m_maximumInput - m_minimumInput) / 2)
130 {
131 if (m_error > 0)
132 {
133 m_error = m_error - m_maximumInput + m_minimumInput;
134 }
135 else
136 {
137 m_error = m_error + m_maximumInput - m_minimumInput;
138 }
139 }
140 }
141
142 if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
143 if (m_P != 0) {
144 double potentialPGain = (m_totalError + m_error) * m_P;
145 if (potentialPGain < m_maximumOutput) {
146 if (potentialPGain > m_minimumOutput) {
147 m_totalError += m_error;
148 }
149 else {
150 m_totalError = m_minimumOutput / m_P;
151 }
152 }
153 else {
154 m_totalError = m_maximumOutput / m_P;
155 }
156 }
157
Brian Silverman1a675112016-02-20 20:42:49 -0500158 m_result = m_D * m_error + m_P * m_totalError +
159 CalculateFeedForward();
Brian Silverman26e4e522015-12-17 01:56:40 -0500160 }
161 else {
162 if (m_I != 0) {
163 double potentialIGain = (m_totalError + m_error) * m_I;
164 if (potentialIGain < m_maximumOutput) {
165 if (potentialIGain > m_minimumOutput) {
166 m_totalError += m_error;
167 }
168 else {
169 m_totalError = m_minimumOutput / m_I;
170 }
171 }
172 else {
173 m_totalError = m_maximumOutput / m_I;
174 }
175 }
176
Brian Silverman1a675112016-02-20 20:42:49 -0500177 m_result = m_P * m_error + m_I * m_totalError +
178 m_D * (m_error - m_prevError) + CalculateFeedForward();
Brian Silverman26e4e522015-12-17 01:56:40 -0500179 }
Brian Silverman1a675112016-02-20 20:42:49 -0500180 m_prevError = m_error;
Brian Silverman26e4e522015-12-17 01:56:40 -0500181
182 if (m_result > m_maximumOutput) m_result = m_maximumOutput;
183 else if (m_result < m_minimumOutput) m_result = m_minimumOutput;
184
185 pidOutput = m_pidOutput;
186 result = m_result;
187 }
188
189 pidOutput->PIDWrite(result);
190 }
191}
192
193/**
Brian Silverman1a675112016-02-20 20:42:49 -0500194 * Calculate the feed forward term
195 *
196 * Both of the provided feed forward calculations are velocity feed forwards.
197 * If a different feed forward calculation is desired, the user can override
198 * this function and provide his or her own. This function does no
199 * synchronization because the PIDController class only calls it in synchronized
200 * code, so be careful if calling it oneself.
201 *
202 * If a velocity PID controller is being used, the F term should be set to 1
203 * over the maximum setpoint for the output. If a position PID controller is
204 * being used, the F term should be set to 1 over the maximum speed for the
205 * output measured in setpoint units per this controller's update period (see
206 * the default period in this class's constructor).
207 */
208double PIDController::CalculateFeedForward() {
209 if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
210 return m_F * GetSetpoint();
211 }
212 else {
213 double temp = m_F * GetDeltaSetpoint();
214 m_prevSetpoint = m_setpoint;
215 m_setpointTimer.Reset();
216 return temp;
217 }
218}
219
220/**
Brian Silverman26e4e522015-12-17 01:56:40 -0500221 * Set the PID Controller gain parameters.
222 * Set the proportional, integral, and differential coefficients.
223 * @param p Proportional coefficient
224 * @param i Integral coefficient
225 * @param d Differential coefficient
226 */
227void PIDController::SetPID(double p, double i, double d)
228{
229 {
Brian Silverman1a675112016-02-20 20:42:49 -0500230 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500231 m_P = p;
232 m_I = i;
233 m_D = d;
234 }
235
236 if (m_table != nullptr) {
237 m_table->PutNumber("p", m_P);
238 m_table->PutNumber("i", m_I);
239 m_table->PutNumber("d", m_D);
240 }
241}
242
243/**
244 * Set the PID Controller gain parameters.
245 * Set the proportional, integral, and differential coefficients.
246 * @param p Proportional coefficient
247 * @param i Integral coefficient
248 * @param d Differential coefficient
249 * @param f Feed forward coefficient
250 */
251void PIDController::SetPID(double p, double i, double d, double f)
252{
253 {
Brian Silverman1a675112016-02-20 20:42:49 -0500254 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500255 m_P = p;
256 m_I = i;
257 m_D = d;
258 m_F = f;
259 }
260
261 if (m_table != nullptr) {
262 m_table->PutNumber("p", m_P);
263 m_table->PutNumber("i", m_I);
264 m_table->PutNumber("d", m_D);
265 m_table->PutNumber("f", m_F);
266 }
267}
268
269/**
270 * Get the Proportional coefficient
271 * @return proportional coefficient
272 */
273double PIDController::GetP() const
274{
Brian Silverman1a675112016-02-20 20:42:49 -0500275 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500276 return m_P;
277}
278
279/**
280 * Get the Integral coefficient
281 * @return integral coefficient
282 */
283double PIDController::GetI() const
284{
Brian Silverman1a675112016-02-20 20:42:49 -0500285 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500286 return m_I;
287}
288
289/**
290 * Get the Differential coefficient
291 * @return differential coefficient
292 */
293double PIDController::GetD() const
294{
Brian Silverman1a675112016-02-20 20:42:49 -0500295 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500296 return m_D;
297}
298
299/**
300 * Get the Feed forward coefficient
301 * @return Feed forward coefficient
302 */
303double PIDController::GetF() const
304{
Brian Silverman1a675112016-02-20 20:42:49 -0500305 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500306 return m_F;
307}
308
309/**
310 * Return the current PID result
311 * This is always centered on zero and constrained the the max and min outs
312 * @return the latest calculated output
313 */
314float PIDController::Get() const
315{
Brian Silverman1a675112016-02-20 20:42:49 -0500316 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500317 return m_result;
318}
319
320/**
321 * Set the PID controller to consider the input to be continuous,
322 * Rather then using the max and min in as constraints, it considers them to
323 * be the same point and automatically calculates the shortest route to
324 * the setpoint.
325 * @param continuous Set to true turns on continuous, false turns off continuous
326 */
327void PIDController::SetContinuous(bool continuous)
328{
Brian Silverman1a675112016-02-20 20:42:49 -0500329 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500330 m_continuous = continuous;
331}
332
333/**
334 * Sets the maximum and minimum values expected from the input.
335 *
336 * @param minimumInput the minimum value expected from the input
337 * @param maximumInput the maximum value expected from the output
338 */
339void PIDController::SetInputRange(float minimumInput, float maximumInput)
340{
341 {
Brian Silverman1a675112016-02-20 20:42:49 -0500342 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500343 m_minimumInput = minimumInput;
344 m_maximumInput = maximumInput;
345 }
346
347 SetSetpoint(m_setpoint);
348}
349
350/**
351 * Sets the minimum and maximum values to write.
352 *
353 * @param minimumOutput the minimum value to write to the output
354 * @param maximumOutput the maximum value to write to the output
355 */
356void PIDController::SetOutputRange(float minimumOutput, float maximumOutput)
357{
Brian Silverman1a675112016-02-20 20:42:49 -0500358 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500359 m_minimumOutput = minimumOutput;
360 m_maximumOutput = maximumOutput;
361}
362
363/**
364 * Set the setpoint for the PIDController
365 * @param setpoint the desired setpoint
366 */
367void PIDController::SetSetpoint(float setpoint)
368{
369 {
Brian Silverman1a675112016-02-20 20:42:49 -0500370 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
371
Brian Silverman26e4e522015-12-17 01:56:40 -0500372 if (m_maximumInput > m_minimumInput)
373 {
374 if (setpoint > m_maximumInput)
375 m_setpoint = m_maximumInput;
376 else if (setpoint < m_minimumInput)
377 m_setpoint = m_minimumInput;
378 else
379 m_setpoint = setpoint;
380 }
381 else
382 {
383 m_setpoint = setpoint;
384 }
385 }
386
387 if (m_table != nullptr) {
388 m_table->PutNumber("setpoint", m_setpoint);
389 }
390}
391
392/**
393 * Returns the current setpoint of the PIDController
394 * @return the current setpoint
395 */
396double PIDController::GetSetpoint() const
397{
Brian Silverman1a675112016-02-20 20:42:49 -0500398 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500399 return m_setpoint;
400}
401
402/**
Brian Silverman1a675112016-02-20 20:42:49 -0500403 * Returns the change in setpoint over time of the PIDController
404 * @return the change in setpoint over time
405 */
406double PIDController::GetDeltaSetpoint() const
407{
408 std::lock_guard<priority_recursive_mutex> sync(m_mutex);
409 return (m_setpoint - m_prevSetpoint) / m_setpointTimer.Get();
410}
411
412/**
Brian Silverman26e4e522015-12-17 01:56:40 -0500413 * Retruns the current difference of the input from the setpoint
414 * @return the current error
415 */
416float PIDController::GetError() const
417{
418 double pidInput;
419 {
Brian Silverman1a675112016-02-20 20:42:49 -0500420 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500421 pidInput = m_pidInput->PIDGet();
422 }
423 return GetSetpoint() - pidInput;
424}
425
426/**
427 * Sets what type of input the PID controller will use
428 */
429void PIDController::SetPIDSourceType(PIDSourceType pidSource) {
430 m_pidInput->SetPIDSourceType(pidSource);
431}
432
433/**
434 * Returns the type of input the PID controller is using
435 * @return the PID controller input type
436 */
437PIDSourceType PIDController::GetPIDSourceType() const {
438 return m_pidInput->GetPIDSourceType();
439}
440
441/**
442 * Returns the current average of the error over the past few iterations.
443 * You can specify the number of iterations to average with SetToleranceBuffer()
444 * (defaults to 1). This is the same value that is used for OnTarget().
445 * @return the average error
446 */
447float PIDController::GetAvgError() const {
448 float avgError = 0;
449 {
Brian Silverman1a675112016-02-20 20:42:49 -0500450 std::lock_guard<priority_recursive_mutex> sync(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500451 // Don't divide by zero.
452 if (m_buf.size()) avgError = m_bufTotal / m_buf.size();
453 }
454 return avgError;
455}
456
457/*
458 * Set the percentage error which is considered tolerable for use with
459 * OnTarget.
460 * @param percentage error which is tolerable
461 */
462void PIDController::SetTolerance(float percent)
463{
Brian Silverman1a675112016-02-20 20:42:49 -0500464 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500465 m_toleranceType = kPercentTolerance;
466 m_tolerance = percent;
467}
468
469/*
470 * Set the percentage error which is considered tolerable for use with
471 * OnTarget.
472 * @param percentage error which is tolerable
473 */
474void PIDController::SetPercentTolerance(float percent)
475{
Brian Silverman1a675112016-02-20 20:42:49 -0500476 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500477 m_toleranceType = kPercentTolerance;
478 m_tolerance = percent;
479}
480
481/*
482 * Set the absolute error which is considered tolerable for use with
483 * OnTarget.
484 * @param percentage error which is tolerable
485 */
486void PIDController::SetAbsoluteTolerance(float absTolerance)
487{
Brian Silverman1a675112016-02-20 20:42:49 -0500488 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500489 m_toleranceType = kAbsoluteTolerance;
490 m_tolerance = absTolerance;
491}
492
493/*
494 * Set the number of previous error samples to average for tolerancing. When
495 * determining whether a mechanism is on target, the user may want to use a
496 * rolling average of previous measurements instead of a precise position or
497 * velocity. This is useful for noisy sensors which return a few erroneous
498 * measurements when the mechanism is on target. However, the mechanism will
499 * not register as on target for at least the specified bufLength cycles.
500 * @param bufLength Number of previous cycles to average. Defaults to 1.
501 */
502void PIDController::SetToleranceBuffer(unsigned bufLength) {
Brian Silverman1a675112016-02-20 20:42:49 -0500503 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500504 m_bufLength = bufLength;
505
506 // Cut the buffer down to size if needed.
507 while (m_buf.size() > bufLength) {
508 m_bufTotal -= m_buf.front();
509 m_buf.pop();
510 }
511}
512
513/*
514 * Return true if the error is within the percentage of the total input range,
515 * determined by SetTolerance. This asssumes that the maximum and minimum input
516 * were set using SetInput.
517 * Currently this just reports on target as the actual value passes through the setpoint.
518 * Ideally it should be based on being within the tolerance for some period of time.
519 */
520bool PIDController::OnTarget() const
521{
Brian Silverman1a675112016-02-20 20:42:49 -0500522 std::lock_guard<priority_recursive_mutex> sync(m_mutex);
523 if (m_buf.size() == 0) return false;
Brian Silverman26e4e522015-12-17 01:56:40 -0500524 double error = GetError();
Brian Silverman26e4e522015-12-17 01:56:40 -0500525 switch (m_toleranceType) {
526 case kPercentTolerance:
527 return fabs(error) < m_tolerance / 100 * (m_maximumInput - m_minimumInput);
528 break;
529 case kAbsoluteTolerance:
530 return fabs(error) < m_tolerance;
531 break;
532 case kNoTolerance: // TODO: this case needs an error
533 return false;
534 }
535 return false;
536}
537
538/**
539 * Begin running the PIDController
540 */
541void PIDController::Enable()
542{
543 {
Brian Silverman1a675112016-02-20 20:42:49 -0500544 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500545 m_enabled = true;
546 }
547
548 if (m_table != nullptr) {
549 m_table->PutBoolean("enabled", true);
550 }
551}
552
553/**
554 * Stop running the PIDController, this sets the output to zero before stopping.
555 */
556void PIDController::Disable()
557{
558 {
Brian Silverman1a675112016-02-20 20:42:49 -0500559 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500560 m_pidOutput->PIDWrite(0);
561 m_enabled = false;
562 }
563
564 if (m_table != nullptr) {
565 m_table->PutBoolean("enabled", false);
566 }
567}
568
569/**
570 * Return true if PIDController is enabled.
571 */
572bool PIDController::IsEnabled() const
573{
Brian Silverman1a675112016-02-20 20:42:49 -0500574 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
Brian Silverman26e4e522015-12-17 01:56:40 -0500575 return m_enabled;
576}
577
578/**
579 * Reset the previous error,, the integral term, and disable the controller.
580 */
581void PIDController::Reset()
582{
583 Disable();
584
Brian Silverman1a675112016-02-20 20:42:49 -0500585 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
586 m_prevError = 0;
Brian Silverman26e4e522015-12-17 01:56:40 -0500587 m_totalError = 0;
588 m_result = 0;
589}
590
591std::string PIDController::GetSmartDashboardType() const {
592 return "PIDController";
593}
594
595void PIDController::InitTable(std::shared_ptr<ITable> table){
596 if(m_table!=nullptr)
597 m_table->RemoveTableListener(this);
598 m_table = table;
599 if(m_table!=nullptr){
600 m_table->PutNumber(kP, GetP());
601 m_table->PutNumber(kI, GetI());
602 m_table->PutNumber(kD, GetD());
603 m_table->PutNumber(kF, GetF());
604 m_table->PutNumber(kSetpoint, GetSetpoint());
605 m_table->PutBoolean(kEnabled, IsEnabled());
606 m_table->AddTableListener(this, false);
607 }
608}
609
610std::shared_ptr<ITable> PIDController::GetTable() const {
611 return m_table;
612}
613
614void PIDController::ValueChanged(ITable* source, llvm::StringRef key,
615 std::shared_ptr<nt::Value> value, bool isNew) {
616 if (key == kP || key == kI || key == kD || key == kF) {
617 if (m_P != m_table->GetNumber(kP, 0.0) ||
618 m_I != m_table->GetNumber(kI, 0.0) ||
619 m_D != m_table->GetNumber(kD, 0.0) ||
620 m_F != m_table->GetNumber(kF, 0.0)) {
621 SetPID(m_table->GetNumber(kP, 0.0), m_table->GetNumber(kI, 0.0),
622 m_table->GetNumber(kD, 0.0), m_table->GetNumber(kF, 0.0));
623 }
624 } else if (key == kSetpoint && value->IsDouble() &&
625 m_setpoint != value->GetDouble()) {
626 SetSetpoint(value->GetDouble());
627 } else if (key == kEnabled && value->IsBoolean() &&
628 m_enabled != value->GetBoolean()) {
629 if (value->GetBoolean()) {
630 Enable();
631 } else {
632 Disable();
633 }
634 }
635}
636
637void PIDController::UpdateTable() {
638
639}
640
641void PIDController::StartLiveWindowMode() {
642 Disable();
643}
644
645void PIDController::StopLiveWindowMode() {
646
647}