001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import java.util.TimerTask; 011import java.util.concurrent.locks.ReentrantLock; 012 013import edu.wpi.first.wpilibj.filters.LinearDigitalFilter; 014import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; 015import edu.wpi.first.wpilibj.util.BoundaryException; 016 017import static java.util.Objects.requireNonNull; 018 019/** 020 * Class implements a PID Control Loop. 021 * 022 * <p>Creates a separate thread which reads the given PIDSource and takes care of the integral 023 * calculations, as well as writing the given PIDOutput. 024 * 025 * <p>This feedback controller runs in discrete time, so time deltas are not used in the integral 026 * and derivative calculations. Therefore, the sample rate affects the controller's behavior for a 027 * given set of PID constants. 028 */ 029public class PIDController extends SendableBase implements PIDInterface, Sendable, Controller { 030 public static final double kDefaultPeriod = .05; 031 private static int instances = 0; 032 @SuppressWarnings("MemberName") 033 private double m_P; // factor for "proportional" control 034 @SuppressWarnings("MemberName") 035 private double m_I; // factor for "integral" control 036 @SuppressWarnings("MemberName") 037 private double m_D; // factor for "derivative" control 038 @SuppressWarnings("MemberName") 039 private double m_F; // factor for feedforward term 040 private double m_maximumOutput = 1.0; // |maximum output| 041 private double m_minimumOutput = -1.0; // |minimum output| 042 private double m_maximumInput = 0.0; // maximum input - limit setpoint to this 043 private double m_minimumInput = 0.0; // minimum input - limit setpoint to this 044 private double m_inputRange = 0.0; // input range - difference between maximum and minimum 045 // do the endpoints wrap around? eg. Absolute encoder 046 private boolean m_continuous = false; 047 private boolean m_enabled = false; // is the pid controller enabled 048 // the prior error (used to compute velocity) 049 private double m_prevError = 0.0; 050 // the sum of the errors for use in the integral calc 051 private double m_totalError = 0.0; 052 // the tolerance object used to check if on target 053 private Tolerance m_tolerance; 054 private double m_setpoint = 0.0; 055 private double m_prevSetpoint = 0.0; 056 @SuppressWarnings("PMD.UnusedPrivateField") 057 private double m_error = 0.0; 058 private double m_result = 0.0; 059 private double m_period = kDefaultPeriod; 060 061 PIDSource m_origSource; 062 LinearDigitalFilter m_filter; 063 064 ReentrantLock m_thisMutex = new ReentrantLock(); 065 066 // Ensures when disable() is called, pidWrite() won't run if calculate() 067 // is already running at that time. 068 ReentrantLock m_pidWriteMutex = new ReentrantLock(); 069 070 protected PIDSource m_pidInput; 071 protected PIDOutput m_pidOutput; 072 java.util.Timer m_controlLoop; 073 Timer m_setpointTimer; 074 075 /** 076 * Tolerance is the type of tolerance used to specify if the PID controller is on target. 077 * 078 * <p>The various implementations of this class such as PercentageTolerance and AbsoluteTolerance 079 * specify types of tolerance specifications to use. 080 */ 081 public interface Tolerance { 082 boolean onTarget(); 083 } 084 085 /** 086 * Used internally for when Tolerance hasn't been set. 087 */ 088 public class NullTolerance implements Tolerance { 089 @Override 090 public boolean onTarget() { 091 throw new RuntimeException("No tolerance value set when calling onTarget()."); 092 } 093 } 094 095 public class PercentageTolerance implements Tolerance { 096 private final double m_percentage; 097 098 PercentageTolerance(double value) { 099 m_percentage = value; 100 } 101 102 @Override 103 public boolean onTarget() { 104 return Math.abs(getError()) < m_percentage / 100 * m_inputRange; 105 } 106 } 107 108 public class AbsoluteTolerance implements Tolerance { 109 private final double m_value; 110 111 AbsoluteTolerance(double value) { 112 m_value = value; 113 } 114 115 @Override 116 public boolean onTarget() { 117 return Math.abs(getError()) < m_value; 118 } 119 } 120 121 private class PIDTask extends TimerTask { 122 private PIDController m_controller; 123 124 PIDTask(PIDController controller) { 125 requireNonNull(controller, "Given PIDController was null"); 126 127 m_controller = controller; 128 } 129 130 @Override 131 public void run() { 132 m_controller.calculate(); 133 } 134 } 135 136 /** 137 * Allocate a PID object with the given constants for P, I, D, and F. 138 * 139 * @param Kp the proportional coefficient 140 * @param Ki the integral coefficient 141 * @param Kd the derivative coefficient 142 * @param Kf the feed forward term 143 * @param source The PIDSource object that is used to get values 144 * @param output The PIDOutput object that is set to the output percentage 145 * @param period the loop time for doing calculations. This particularly effects calculations of 146 * the integral and differential terms. The default is 50ms. 147 */ 148 @SuppressWarnings("ParameterName") 149 public PIDController(double Kp, double Ki, double Kd, double Kf, PIDSource source, 150 PIDOutput output, double period) { 151 super(false); 152 requireNonNull(source, "Null PIDSource was given"); 153 requireNonNull(output, "Null PIDOutput was given"); 154 155 m_controlLoop = new java.util.Timer(); 156 m_setpointTimer = new Timer(); 157 m_setpointTimer.start(); 158 159 m_P = Kp; 160 m_I = Ki; 161 m_D = Kd; 162 m_F = Kf; 163 164 // Save original source 165 m_origSource = source; 166 167 // Create LinearDigitalFilter with original source as its source argument 168 m_filter = LinearDigitalFilter.movingAverage(m_origSource, 1); 169 m_pidInput = m_filter; 170 171 m_pidOutput = output; 172 m_period = period; 173 174 m_controlLoop.schedule(new PIDTask(this), 0L, (long) (m_period * 1000)); 175 176 instances++; 177 HLUsageReporting.reportPIDController(instances); 178 m_tolerance = new NullTolerance(); 179 setName("PIDController", instances); 180 } 181 182 /** 183 * Allocate a PID object with the given constants for P, I, D and period. 184 * 185 * @param Kp the proportional coefficient 186 * @param Ki the integral coefficient 187 * @param Kd the derivative coefficient 188 * @param source the PIDSource object that is used to get values 189 * @param output the PIDOutput object that is set to the output percentage 190 * @param period the loop time for doing calculations. This particularly effects calculations of 191 * the integral and differential terms. The default is 50ms. 192 */ 193 @SuppressWarnings("ParameterName") 194 public PIDController(double Kp, double Ki, double Kd, PIDSource source, PIDOutput output, 195 double period) { 196 this(Kp, Ki, Kd, 0.0, source, output, period); 197 } 198 199 /** 200 * Allocate a PID object with the given constants for P, I, D, using a 50ms period. 201 * 202 * @param Kp the proportional coefficient 203 * @param Ki the integral coefficient 204 * @param Kd the derivative coefficient 205 * @param source The PIDSource object that is used to get values 206 * @param output The PIDOutput object that is set to the output percentage 207 */ 208 @SuppressWarnings("ParameterName") 209 public PIDController(double Kp, double Ki, double Kd, PIDSource source, PIDOutput output) { 210 this(Kp, Ki, Kd, source, output, kDefaultPeriod); 211 } 212 213 /** 214 * Allocate a PID object with the given constants for P, I, D, using a 50ms period. 215 * 216 * @param Kp the proportional coefficient 217 * @param Ki the integral coefficient 218 * @param Kd the derivative coefficient 219 * @param Kf the feed forward term 220 * @param source The PIDSource object that is used to get values 221 * @param output The PIDOutput object that is set to the output percentage 222 */ 223 @SuppressWarnings("ParameterName") 224 public PIDController(double Kp, double Ki, double Kd, double Kf, PIDSource source, 225 PIDOutput output) { 226 this(Kp, Ki, Kd, Kf, source, output, kDefaultPeriod); 227 } 228 229 /** 230 * Free the PID object. 231 */ 232 @Override 233 public void free() { 234 super.free(); 235 m_controlLoop.cancel(); 236 m_thisMutex.lock(); 237 try { 238 m_pidOutput = null; 239 m_pidInput = null; 240 m_controlLoop = null; 241 } finally { 242 m_thisMutex.unlock(); 243 } 244 } 245 246 /** 247 * Read the input, calculate the output accordingly, and write to the output. This should only be 248 * called by the PIDTask and is created during initialization. 249 */ 250 @SuppressWarnings("LocalVariableName") 251 protected void calculate() { 252 if (m_origSource == null || m_pidOutput == null) { 253 return; 254 } 255 256 boolean enabled; 257 258 m_thisMutex.lock(); 259 try { 260 enabled = m_enabled; 261 } finally { 262 m_thisMutex.unlock(); 263 } 264 265 if (enabled) { 266 double input; 267 268 // Storage for function inputs 269 PIDSourceType pidSourceType; 270 double P; 271 double I; 272 double D; 273 double feedForward = calculateFeedForward(); 274 double minimumOutput; 275 double maximumOutput; 276 277 // Storage for function input-outputs 278 double prevError; 279 double error; 280 double totalError; 281 282 m_thisMutex.lock(); 283 try { 284 input = m_pidInput.pidGet(); 285 286 pidSourceType = m_pidInput.getPIDSourceType(); 287 P = m_P; 288 I = m_I; 289 D = m_D; 290 minimumOutput = m_minimumOutput; 291 maximumOutput = m_maximumOutput; 292 293 prevError = m_prevError; 294 error = getContinuousError(m_setpoint - input); 295 totalError = m_totalError; 296 } finally { 297 m_thisMutex.unlock(); 298 } 299 300 // Storage for function outputs 301 double result; 302 303 if (pidSourceType.equals(PIDSourceType.kRate)) { 304 if (P != 0) { 305 totalError = clamp(totalError + error, minimumOutput / P, 306 maximumOutput / P); 307 } 308 309 result = P * totalError + D * error + feedForward; 310 } else { 311 if (I != 0) { 312 totalError = clamp(totalError + error, minimumOutput / I, 313 maximumOutput / I); 314 } 315 316 result = P * error + I * totalError + D * (error - prevError) 317 + feedForward; 318 } 319 320 result = clamp(result, minimumOutput, maximumOutput); 321 322 // Ensures m_enabled check and pidWrite() call occur atomically 323 m_pidWriteMutex.lock(); 324 try { 325 m_thisMutex.lock(); 326 try { 327 if (m_enabled) { 328 // Don't block other PIDController operations on pidWrite() 329 m_thisMutex.unlock(); 330 331 m_pidOutput.pidWrite(result); 332 } 333 } finally { 334 if (m_thisMutex.isHeldByCurrentThread()) { 335 m_thisMutex.unlock(); 336 } 337 } 338 } finally { 339 m_pidWriteMutex.unlock(); 340 } 341 342 m_thisMutex.lock(); 343 try { 344 m_prevError = error; 345 m_error = error; 346 m_totalError = totalError; 347 m_result = result; 348 } finally { 349 m_thisMutex.unlock(); 350 } 351 } 352 } 353 354 /** 355 * Calculate the feed forward term. 356 * 357 * <p>Both of the provided feed forward calculations are velocity feed forwards. If a different 358 * feed forward calculation is desired, the user can override this function and provide his or 359 * her own. This function does no synchronization because the PIDController class only calls it 360 * in synchronized code, so be careful if calling it oneself. 361 * 362 * <p>If a velocity PID controller is being used, the F term should be set to 1 over the maximum 363 * setpoint for the output. If a position PID controller is being used, the F term should be set 364 * to 1 over the maximum speed for the output measured in setpoint units per this controller's 365 * update period (see the default period in this class's constructor). 366 */ 367 protected double calculateFeedForward() { 368 if (m_pidInput.getPIDSourceType().equals(PIDSourceType.kRate)) { 369 return m_F * getSetpoint(); 370 } else { 371 double temp = m_F * getDeltaSetpoint(); 372 m_prevSetpoint = m_setpoint; 373 m_setpointTimer.reset(); 374 return temp; 375 } 376 } 377 378 /** 379 * Set the PID Controller gain parameters. Set the proportional, integral, and differential 380 * coefficients. 381 * 382 * @param p Proportional coefficient 383 * @param i Integral coefficient 384 * @param d Differential coefficient 385 */ 386 @SuppressWarnings("ParameterName") 387 public void setPID(double p, double i, double d) { 388 m_thisMutex.lock(); 389 try { 390 m_P = p; 391 m_I = i; 392 m_D = d; 393 } finally { 394 m_thisMutex.unlock(); 395 } 396 } 397 398 /** 399 * Set the PID Controller gain parameters. Set the proportional, integral, and differential 400 * coefficients. 401 * 402 * @param p Proportional coefficient 403 * @param i Integral coefficient 404 * @param d Differential coefficient 405 * @param f Feed forward coefficient 406 */ 407 @SuppressWarnings("ParameterName") 408 public void setPID(double p, double i, double d, double f) { 409 m_thisMutex.lock(); 410 try { 411 m_P = p; 412 m_I = i; 413 m_D = d; 414 m_F = f; 415 } finally { 416 m_thisMutex.unlock(); 417 } 418 } 419 420 /** 421 * Set the Proportional coefficient of the PID controller gain. 422 * 423 * @param p Proportional coefficient 424 */ 425 @SuppressWarnings("ParameterName") 426 public void setP(double p) { 427 m_thisMutex.lock(); 428 try { 429 m_P = p; 430 } finally { 431 m_thisMutex.unlock(); 432 } 433 } 434 435 /** 436 * Set the Integral coefficient of the PID controller gain. 437 * 438 * @param i Integral coefficient 439 */ 440 @SuppressWarnings("ParameterName") 441 public void setI(double i) { 442 m_thisMutex.lock(); 443 try { 444 m_I = i; 445 } finally { 446 m_thisMutex.unlock(); 447 } 448 } 449 450 /** 451 * Set the Differential coefficient of the PID controller gain. 452 * 453 * @param d differential coefficient 454 */ 455 @SuppressWarnings("ParameterName") 456 public void setD(double d) { 457 m_thisMutex.lock(); 458 try { 459 m_D = d; 460 } finally { 461 m_thisMutex.unlock(); 462 } 463 } 464 465 /** 466 * Set the Feed forward coefficient of the PID controller gain. 467 * 468 * @param f feed forward coefficient 469 */ 470 @SuppressWarnings("ParameterName") 471 public void setF(double f) { 472 m_thisMutex.lock(); 473 try { 474 m_F = f; 475 } finally { 476 m_thisMutex.unlock(); 477 } 478 } 479 480 /** 481 * Get the Proportional coefficient. 482 * 483 * @return proportional coefficient 484 */ 485 public double getP() { 486 m_thisMutex.lock(); 487 try { 488 return m_P; 489 } finally { 490 m_thisMutex.unlock(); 491 } 492 } 493 494 /** 495 * Get the Integral coefficient. 496 * 497 * @return integral coefficient 498 */ 499 public double getI() { 500 m_thisMutex.lock(); 501 try { 502 return m_I; 503 } finally { 504 m_thisMutex.unlock(); 505 } 506 } 507 508 /** 509 * Get the Differential coefficient. 510 * 511 * @return differential coefficient 512 */ 513 public double getD() { 514 m_thisMutex.lock(); 515 try { 516 return m_D; 517 } finally { 518 m_thisMutex.unlock(); 519 } 520 } 521 522 /** 523 * Get the Feed forward coefficient. 524 * 525 * @return feed forward coefficient 526 */ 527 public double getF() { 528 m_thisMutex.lock(); 529 try { 530 return m_F; 531 } finally { 532 m_thisMutex.unlock(); 533 } 534 } 535 536 /** 537 * Return the current PID result This is always centered on zero and constrained the the max and 538 * min outs. 539 * 540 * @return the latest calculated output 541 */ 542 public double get() { 543 m_thisMutex.lock(); 544 try { 545 return m_result; 546 } finally { 547 m_thisMutex.unlock(); 548 } 549 } 550 551 /** 552 * Set the PID controller to consider the input to be continuous, Rather then using the max and 553 * min input range as constraints, it considers them to be the same point and automatically 554 * calculates the shortest route to the setpoint. 555 * 556 * @param continuous Set to true turns on continuous, false turns off continuous 557 */ 558 public void setContinuous(boolean continuous) { 559 if (continuous && m_inputRange <= 0) { 560 throw new RuntimeException("No input range set when calling setContinuous()."); 561 } 562 m_thisMutex.lock(); 563 try { 564 m_continuous = continuous; 565 } finally { 566 m_thisMutex.unlock(); 567 } 568 } 569 570 /** 571 * Set the PID controller to consider the input to be continuous, Rather then using the max and 572 * min input range as constraints, it considers them to be the same point and automatically 573 * calculates the shortest route to the setpoint. 574 */ 575 public void setContinuous() { 576 setContinuous(true); 577 } 578 579 /** 580 * Sets the maximum and minimum values expected from the input and setpoint. 581 * 582 * @param minimumInput the minimum value expected from the input 583 * @param maximumInput the maximum value expected from the input 584 */ 585 public void setInputRange(double minimumInput, double maximumInput) { 586 m_thisMutex.lock(); 587 try { 588 if (minimumInput > maximumInput) { 589 throw new BoundaryException("Lower bound is greater than upper bound"); 590 } 591 m_minimumInput = minimumInput; 592 m_maximumInput = maximumInput; 593 m_inputRange = maximumInput - minimumInput; 594 } finally { 595 m_thisMutex.unlock(); 596 } 597 598 setSetpoint(m_setpoint); 599 } 600 601 /** 602 * Sets the minimum and maximum values to write. 603 * 604 * @param minimumOutput the minimum percentage to write to the output 605 * @param maximumOutput the maximum percentage to write to the output 606 */ 607 public void setOutputRange(double minimumOutput, double maximumOutput) { 608 m_thisMutex.lock(); 609 try { 610 if (minimumOutput > maximumOutput) { 611 throw new BoundaryException("Lower bound is greater than upper bound"); 612 } 613 m_minimumOutput = minimumOutput; 614 m_maximumOutput = maximumOutput; 615 } finally { 616 m_thisMutex.unlock(); 617 } 618 } 619 620 /** 621 * Set the setpoint for the PIDController. 622 * 623 * @param setpoint the desired setpoint 624 */ 625 public void setSetpoint(double setpoint) { 626 m_thisMutex.lock(); 627 try { 628 if (m_maximumInput > m_minimumInput) { 629 if (setpoint > m_maximumInput) { 630 m_setpoint = m_maximumInput; 631 } else if (setpoint < m_minimumInput) { 632 m_setpoint = m_minimumInput; 633 } else { 634 m_setpoint = setpoint; 635 } 636 } else { 637 m_setpoint = setpoint; 638 } 639 } finally { 640 m_thisMutex.unlock(); 641 } 642 } 643 644 /** 645 * Returns the current setpoint of the PIDController. 646 * 647 * @return the current setpoint 648 */ 649 public double getSetpoint() { 650 m_thisMutex.lock(); 651 try { 652 return m_setpoint; 653 } finally { 654 m_thisMutex.unlock(); 655 } 656 } 657 658 /** 659 * Returns the change in setpoint over time of the PIDController. 660 * 661 * @return the change in setpoint over time 662 */ 663 public double getDeltaSetpoint() { 664 m_thisMutex.lock(); 665 try { 666 return (m_setpoint - m_prevSetpoint) / m_setpointTimer.get(); 667 } finally { 668 m_thisMutex.unlock(); 669 } 670 } 671 672 /** 673 * Returns the current difference of the input from the setpoint. 674 * 675 * @return the current error 676 */ 677 public double getError() { 678 m_thisMutex.lock(); 679 try { 680 return getContinuousError(getSetpoint() - m_pidInput.pidGet()); 681 } finally { 682 m_thisMutex.unlock(); 683 } 684 } 685 686 /** 687 * Returns the current difference of the error over the past few iterations. You can specify the 688 * number of iterations to average with setToleranceBuffer() (defaults to 1). getAvgError() is 689 * used for the onTarget() function. 690 * 691 * @deprecated Use getError(), which is now already filtered. 692 * @return the current average of the error 693 */ 694 @Deprecated 695 public double getAvgError() { 696 m_thisMutex.lock(); 697 try { 698 return getError(); 699 } finally { 700 m_thisMutex.unlock(); 701 } 702 } 703 704 /** 705 * Sets what type of input the PID controller will use. 706 * 707 * @param pidSource the type of input 708 */ 709 void setPIDSourceType(PIDSourceType pidSource) { 710 m_pidInput.setPIDSourceType(pidSource); 711 } 712 713 /** 714 * Returns the type of input the PID controller is using. 715 * 716 * @return the PID controller input type 717 */ 718 PIDSourceType getPIDSourceType() { 719 return m_pidInput.getPIDSourceType(); 720 } 721 722 /** 723 * Set the PID tolerance using a Tolerance object. Tolerance can be specified as a percentage of 724 * the range or as an absolute value. The Tolerance object encapsulates those options in an 725 * object. Use it by creating the type of tolerance that you want to use: setTolerance(new 726 * PIDController.AbsoluteTolerance(0.1)) 727 * 728 * @deprecated Use setPercentTolerance() instead. 729 * @param tolerance A tolerance object of the right type, e.g. PercentTolerance or 730 * AbsoluteTolerance 731 */ 732 @Deprecated 733 public void setTolerance(Tolerance tolerance) { 734 m_tolerance = tolerance; 735 } 736 737 /** 738 * Set the absolute error which is considered tolerable for use with OnTarget. 739 * 740 * @param absvalue absolute error which is tolerable in the units of the input object 741 */ 742 public void setAbsoluteTolerance(double absvalue) { 743 m_thisMutex.lock(); 744 try { 745 m_tolerance = new AbsoluteTolerance(absvalue); 746 } finally { 747 m_thisMutex.unlock(); 748 } 749 } 750 751 /** 752 * Set the percentage error which is considered tolerable for use with OnTarget. (Input of 15.0 = 753 * 15 percent) 754 * 755 * @param percentage percent error which is tolerable 756 */ 757 public void setPercentTolerance(double percentage) { 758 m_thisMutex.lock(); 759 try { 760 m_tolerance = new PercentageTolerance(percentage); 761 } finally { 762 m_thisMutex.unlock(); 763 } 764 } 765 766 /** 767 * Set the number of previous error samples to average for tolerancing. When determining whether a 768 * mechanism is on target, the user may want to use a rolling average of previous measurements 769 * instead of a precise position or velocity. This is useful for noisy sensors which return a few 770 * erroneous measurements when the mechanism is on target. However, the mechanism will not 771 * register as on target for at least the specified bufLength cycles. 772 * 773 * @deprecated Use a LinearDigitalFilter as the input. 774 * @param bufLength Number of previous cycles to average. 775 */ 776 @Deprecated 777 public void setToleranceBuffer(int bufLength) { 778 m_thisMutex.lock(); 779 try { 780 m_filter = LinearDigitalFilter.movingAverage(m_origSource, bufLength); 781 m_pidInput = m_filter; 782 } finally { 783 m_thisMutex.unlock(); 784 } 785 } 786 787 /** 788 * Return true if the error is within the percentage of the total input range, determined by 789 * setTolerance. This assumes that the maximum and minimum input were set using setInput. 790 * 791 * @return true if the error is less than the tolerance 792 */ 793 public boolean onTarget() { 794 m_thisMutex.lock(); 795 try { 796 return m_tolerance.onTarget(); 797 } finally { 798 m_thisMutex.unlock(); 799 } 800 } 801 802 /** 803 * Begin running the PIDController. 804 */ 805 @Override 806 public void enable() { 807 m_thisMutex.lock(); 808 try { 809 m_enabled = true; 810 } finally { 811 m_thisMutex.unlock(); 812 } 813 } 814 815 /** 816 * Stop running the PIDController, this sets the output to zero before stopping. 817 */ 818 @Override 819 public void disable() { 820 // Ensures m_enabled check and pidWrite() call occur atomically 821 m_pidWriteMutex.lock(); 822 try { 823 m_thisMutex.lock(); 824 try { 825 m_enabled = false; 826 } finally { 827 m_thisMutex.unlock(); 828 } 829 830 m_pidOutput.pidWrite(0); 831 } finally { 832 m_pidWriteMutex.unlock(); 833 } 834 } 835 836 /** 837 * Set the enabled state of the PIDController. 838 */ 839 public void setEnabled(boolean enable) { 840 if (enable) { 841 enable(); 842 } else { 843 disable(); 844 } 845 } 846 847 /** 848 * Return true if PIDController is enabled. 849 */ 850 @Override 851 public boolean isEnabled() { 852 m_thisMutex.lock(); 853 try { 854 return m_enabled; 855 } finally { 856 m_thisMutex.unlock(); 857 } 858 } 859 860 /** 861 * Reset the previous error,, the integral term, and disable the controller. 862 */ 863 @Override 864 public void reset() { 865 disable(); 866 867 m_thisMutex.lock(); 868 try { 869 m_prevError = 0; 870 m_totalError = 0; 871 m_result = 0; 872 } finally { 873 m_thisMutex.unlock(); 874 } 875 } 876 877 @Override 878 public void initSendable(SendableBuilder builder) { 879 builder.setSmartDashboardType("PIDController"); 880 builder.setSafeState(this::reset); 881 builder.addDoubleProperty("p", this::getP, this::setP); 882 builder.addDoubleProperty("i", this::getI, this::setI); 883 builder.addDoubleProperty("d", this::getD, this::setD); 884 builder.addDoubleProperty("f", this::getF, this::setF); 885 builder.addDoubleProperty("setpoint", this::getSetpoint, this::setSetpoint); 886 builder.addBooleanProperty("enabled", this::isEnabled, this::setEnabled); 887 } 888 889 /** 890 * Wraps error around for continuous inputs. The original error is returned if continuous mode is 891 * disabled. This is an unsynchronized function. 892 * 893 * @param error The current error of the PID controller. 894 * @return Error for continuous inputs. 895 */ 896 protected double getContinuousError(double error) { 897 if (m_continuous && m_inputRange > 0) { 898 error %= m_inputRange; 899 if (Math.abs(error) > m_inputRange / 2) { 900 if (error > 0) { 901 return error - m_inputRange; 902 } else { 903 return error + m_inputRange; 904 } 905 } 906 } 907 908 return error; 909 } 910 911 private static double clamp(double value, double low, double high) { 912 return Math.max(low, Math.min(value, high)); 913 } 914}