CTRE Phoenix 6 C++ 26.70.0-alpha-2
Loading...
Searching...
No Matches
PhoenixPIDController.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) Cross The Road Electronics.  All rights reserved.
3 * License information can be found in CTRE_LICENSE.txt
4 * For support and suggestions contact support@ctr-electronics.com or file
5 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
6 */
7#pragma once
8
10#include <algorithm>
11#include <gcem.hpp>
12#include <limits>
13#include <string>
14#include <wpi/units/time.hpp>
15
16#if __has_include(<wpi/telemetry/TelemetryLoggable.hpp>) && __has_include(<wpi/tunables/ComplexTunable.hpp>)
17#include <wpi/math/util/MathShared.hpp>
18#include <wpi/telemetry/TelemetryLoggable.hpp>
19#include <wpi/tunables/ComplexTunable.hpp>
20#include <wpi/tunables/Tunable.hpp>
21#include <wpi/util/UsageReporting.hpp>
22#define CTRE_PHOENIX6_USE_WPILIB
23#endif
24
25namespace ctre {
26namespace phoenix6 {
27namespace swerve {
28
29/**
30 * \brief Phoenix-centric PID controller taken from WPI's wpi#math#PIDController class.
31 *
32 * This class differs from the WPI implementation by using explicit timestamps for
33 * integral/derivative calculations. Ideally, these timestamps come from the StatusSignal.
34 */
36#if defined(CTRE_PHOENIX6_USE_WPILIB) || defined(_CTRE_DOCS_)
37 : public wpi::telemetry::TelemetryLoggable, public wpi::tunables::ComplexTunable
38#endif
39{
40public:
41 /**
42 * Allocates a PhoenixPIDController with the given constants for Kp, Ki, and Kd.
43 *
44 * \param Kp The proportional coefficient.
45 * \param Ki The integral coefficient.
46 * \param Kd The derivative coefficient.
47 */
48 constexpr PhoenixPIDController(double Kp, double Ki, double Kd) :
49 m_Kp{Kp}, m_Ki{Ki}, m_Kd{Kd}
50 {
51#ifdef CTRE_PHOENIX6_USE_WPILIB
52 if !consteval {
53 ++instances;
54 wpi::util::ReportUsage("PhoenixPIDController", std::to_string(instances));
55 }
56#endif
57 }
58
59#if defined(CTRE_PHOENIX6_USE_WPILIB) || defined(_CTRE_DOCS_)
60 constexpr ~PhoenixPIDController() override = default;
61
62 constexpr PhoenixPIDController(PhoenixPIDController const &) = default;
66#endif
67
68 /**
69 * Sets the PID Controller gain parameters.
70 *
71 * Sets the proportional, integral, and differential coefficients.
72 *
73 * \param Kp The proportional coefficient.
74 * \param Ki The integral coefficient.
75 * \param Kd The differential coefficient.
76 */
77 constexpr void SetPID(double Kp, double Ki, double Kd)
78 {
79 m_Kp = Kp;
80 m_Ki = Ki;
81 m_Kd = Kd;
82 }
83
84 /**
85 * Sets the proportional coefficient of the PID controller gain.
86 *
87 * This setter is intended for online tuning.
88 *
89 * \param Kp The proportional coefficient.
90 */
91 constexpr void SetP(double Kp) { m_Kp = Kp; }
92
93 /**
94 * Sets the integral coefficient of the PID controller gain.
95 *
96 * This setter is intended for online tuning.
97 *
98 * \param Ki The integral coefficient.
99 */
100 constexpr void SetI(double Ki) { m_Ki = Ki; }
101
102 /**
103 * Sets the differential coefficient of the PID controller gain.
104 *
105 * This setter is intended for online tuning.
106 *
107 * \param Kd The differential coefficient.
108 */
109 constexpr void SetD(double Kd) { m_Kd = Kd; }
110
111 /**
112 * Sets the IZone range. When the absolute value of the position error is
113 * greater than IZone, the total accumulated error will reset to zero,
114 * disabling integral gain until the absolute value of the position error is
115 * less than IZone. This is used to prevent integral windup. Must be
116 * non-negative. Passing a value of zero will effectively disable integral
117 * gain. Passing a value of infinity disables IZone functionality.
118 *
119 * \param iZone Maximum magnitude of error to allow integral control. Must be
120 * >= 0.
121 */
122 constexpr void SetIZone(double iZone)
123 {
124 if (!std::is_constant_evaluated() && iZone < 0) {
125 std::string const error = "IZone must be a non-negative number, got " + std::to_string(iZone) + "!";
126 networking::Wrappers::ReportError(true, 0, error.c_str(), "PhoenixPIDController");
127 }
128 m_iZone = iZone;
129 }
130
131 /**
132 * Gets the proportional coefficient.
133 *
134 * \returns proportional coefficient
135 */
136 constexpr double GetP() const { return m_Kp; }
137
138 /**
139 * Gets the integral coefficient.
140 *
141 * \returns integral coefficient
142 */
143 constexpr double GetI() const { return m_Ki; }
144
145 /**
146 * Gets the differential coefficient.
147 *
148 * \returns differential coefficient
149 */
150 constexpr double GetD() const { return m_Kd; }
151
152 /**
153 * Get the IZone range.
154 *
155 * \returns Maximum magnitude of error to allow integral control.
156 */
157 constexpr double GetIZone() const { return m_iZone; }
158
159 /**
160 * Gets the error tolerance of this controller. Defaults to 0.05.
161 *
162 * \returns The error tolerance of the controller.
163 */
164 constexpr double GetErrorTolerance() const { return m_errorTolerance; }
165
166 /**
167 * Gets the error derivative tolerance of this controller. Defaults to ∞.
168 *
169 * \returns The error derivative tolerance of the controller.
170 */
171 constexpr double GetErrorDerivativeTolerance() const { return m_errorDerivativeTolerance; }
172
173 /**
174 * Gets the accumulated error used in the integral calculation of this
175 * controller.
176 *
177 * \returns The accumulated error of this controller.
178 */
179 constexpr double GetAccumulatedError() const { return m_totalError; }
180
181 /**
182 * Sets the setpoint for the PIDController.
183 *
184 * \param setpoint The desired setpoint.
185 */
186 constexpr void SetSetpoint(double setpoint)
187 {
188 m_setpoint = setpoint;
189 m_haveSetpoint = true;
190
191 if (m_continuous) {
192 double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
193 m_error = InputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
194 } else {
195 m_error = m_setpoint - m_measurement;
196 }
197
198 if (m_period > 0_s) {
199 m_errorDerivative = (m_error - m_prevError) / m_period.value();
200 } else {
201 m_errorDerivative = 0.0;
202 }
203 }
204
205 /**
206 * Returns the current setpoint of the PIDController.
207 *
208 * \returns The current setpoint.
209 */
210 constexpr double GetSetpoint() const { return m_setpoint; }
211
212 /**
213 * Returns true if the error is within the tolerance of the setpoint.
214 * The error tolerance defaults to 0.05, and the error derivative tolerance
215 * defaults to ∞.
216 *
217 * This will return false until at least one input value has been computed.
218 */
219 constexpr bool AtSetpoint() const
220 {
221 return m_haveMeasurement && m_haveSetpoint &&
222 gcem::abs(m_error) < m_errorTolerance &&
223 gcem::abs(m_errorDerivative) < m_errorDerivativeTolerance;
224 }
225
226 /**
227 * Enables continuous input.
228 *
229 * Rather than using the max and min input range as constraints, it considers
230 * them to be the same point and automatically calculates the shortest route
231 * to the setpoint.
232 *
233 * \param minimumInput The minimum value expected from the input.
234 * \param maximumInput The maximum value expected from the input.
235 */
236 constexpr void EnableContinuousInput(double minimumInput, double maximumInput)
237 {
238 m_continuous = true;
239 m_minimumInput = minimumInput;
240 m_maximumInput = maximumInput;
241 }
242
243 /**
244 * Disables continuous input.
245 */
246 constexpr void DisableContinuousInput() { m_continuous = false; }
247
248 /**
249 * Returns true if continuous input is enabled.
250 */
251 constexpr bool IsContinuousInputEnabled() const { return m_continuous; }
252
253 /**
254 * Sets the minimum and maximum contributions of the integral term.
255 *
256 * The internal integrator is clamped so that the integral term's contribution
257 * to the output stays between minimumIntegral and maximumIntegral. This
258 * prevents integral windup.
259 *
260 * \param minimumIntegral The minimum contribution of the integral term.
261 * \param maximumIntegral The maximum contribution of the integral term.
262 */
263 constexpr void SetIntegratorRange(double minimumIntegral, double maximumIntegral)
264 {
265 m_minimumIntegral = minimumIntegral;
266 m_maximumIntegral = maximumIntegral;
267 }
268
269 /**
270 * Sets the error which is considered tolerable for use with AtSetpoint().
271 *
272 * \param errorTolerance error which is tolerable.
273 * \param errorDerivativeTolerance error derivative which is tolerable.
274 */
275 constexpr void SetTolerance(
276 double errorTolerance,
277 double errorDerivativeTolerance = std::numeric_limits<double>::infinity()
278 ) {
279 m_errorTolerance = errorTolerance;
280 m_errorDerivativeTolerance = errorDerivativeTolerance;
281 }
282
283 /**
284 * Returns the difference between the setpoint and the measurement.
285 */
286 constexpr double GetError() const { return m_error; }
287
288 /**
289 * Returns the error derivative.
290 */
291 constexpr double GetErrorDerivative() const { return m_errorDerivative; }
292
293 /**
294 * Returns the next output of the PID controller.
295 *
296 * \param measurement The current measurement of the process variable.
297 * \param timestamp The current timestamp to use for calculating integral/derivative error.
298 */
299 constexpr double Calculate(double measurement, wpi::units::second_t timestamp)
300 {
301 m_measurement = measurement;
302 m_prevError = m_error;
303 m_haveMeasurement = true;
304
305 m_period = timestamp - m_lastTimestamp;
306 m_lastTimestamp = timestamp;
307
308 if (m_continuous) {
309 double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
310 m_error = InputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
311 } else {
312 m_error = m_setpoint - m_measurement;
313 }
314
315 m_errorDerivative = (m_error - m_prevError) / m_period.value();
316
317 // If the absolute value of the position error is outside of IZone, reset
318 // the total error
319 if (gcem::abs(m_error) > m_iZone) {
320 m_totalError = 0;
321 } else if (m_Ki != 0) {
322 m_totalError = std::clamp(
323 m_totalError + m_error * m_period.value(),
324 m_minimumIntegral / m_Ki, m_maximumIntegral / m_Ki
325 );
326 }
327
328 m_lastAppliedOutput = m_Kp * m_error + m_Ki * m_totalError + m_Kd * m_errorDerivative;
329 return m_lastAppliedOutput;
330 }
331
332 /**
333 * Returns the next output of the PID controller.
334 *
335 * \param measurement The current measurement of the process variable.
336 * \param setpoint The new setpoint of the controller.
337 * \param timestamp The current timestamp to use for calculating integral/derivative error
338 */
339 constexpr double Calculate(double measurement, double setpoint, wpi::units::second_t timestamp)
340 {
341 m_setpoint = setpoint;
342 m_haveSetpoint = true;
343 return Calculate(measurement, timestamp);
344 }
345
346 /**
347 * Returns the last applied output from this PID controller.
348 */
349 constexpr double GetLastAppliedOutput() const { return m_lastAppliedOutput; }
350
351 /**
352 * Reset the previous error and the integral term.
353 */
354 constexpr void Reset()
355 {
356 m_error = 0;
357 m_prevError = 0;
358 m_totalError = 0;
359 m_errorDerivative = 0;
360 m_haveMeasurement = false;
361 }
362
363#if defined(CTRE_PHOENIX6_USE_WPILIB) || defined(_CTRE_DOCS_)
364 /**
365 * Logs the object to a wpi::telemetry::TelemetryTable.
366 *
367 * \param table telemetry table
368 */
369 void LogTo(wpi::telemetry::TelemetryTable &table) const override;
370 /**
371 * Gets the telemetry table type.
372 *
373 * \returns Table type
374 */
375 constexpr std::string_view GetTelemetryType() const override { return "PIDController"; }
376
377 /**
378 * Publishes the tunable to the given table.
379 *
380 * This function may be called when the tunable is initially published or
381 * republished during backend migration, and it must not throw. If an
382 * exception escapes, registry and backend state is not guaranteed to be
383 * restored.
384 *
385 * \param table table
386 */
387 void PublishTunable(wpi::tunables::TunableTable &table) override;
388 /**
389 * Gets the tunable table type.
390 *
391 * \returns Table type
392 */
393 constexpr std::string_view GetTunableType() const override { return "PIDController"; }
394#endif
395
396private:
397 /**
398 * Returns modulus of input.
399 *
400 * \param input Input value to wrap.
401 * \param minimumInput The minimum value expected from the input.
402 * \param maximumInput The maximum value expected from the input.
403 */
404 template <typename T>
405 static constexpr T InputModulus(T input, T minimumInput, T maximumInput)
406 {
407 T const modulus = maximumInput - minimumInput;
408
409 // Wrap input if it's above the maximum input
410 int const numMax = (input - minimumInput) / modulus;
411 input -= numMax * modulus;
412
413 // Wrap input if it's below the minimum input
414 int const numMin = (input - maximumInput) / modulus;
415 input -= numMin * modulus;
416
417 return input;
418 }
419
420#if defined(CTRE_PHOENIX6_USE_WPILIB) || defined(_CTRE_DOCS_)
421 template <typename T, typename... I>
422 using Tunable = wpi::tunables::Tunable<T, I...>;
423#else
424 template <typename T, typename... I>
425 using Tunable = T;
426#endif
427
428 // Factor for "proportional" control
429 Tunable<double> m_Kp;
430
431 // Factor for "integral" control
432 Tunable<double> m_Ki;
433
434 // Factor for "derivative" control
435 Tunable<double> m_Kd;
436
437 // The error range where "integral" control applies
438 Tunable<double> m_iZone{std::numeric_limits<double>::infinity()};
439
440 double m_maximumIntegral = 1.0;
441
442 double m_minimumIntegral = -1.0;
443
444 double m_maximumInput = 0;
445
446 double m_minimumInput = 0;
447
448 // Do the endpoints wrap around? eg. Absolute encoder
449 bool m_continuous = false;
450
451 // The error at the time of the most recent call to Calculate()
452 double m_error = 0;
453 double m_errorDerivative = 0;
454
455 // The error at the time of the second-most-recent call to Calculate() (used
456 // to compute velocity)
457 double m_prevError = 0;
458
459 // The sum of the errors for use in the integral calc
460 double m_totalError = 0;
461
462 // The error that is considered at setpoint.
463 double m_errorTolerance = 0.05;
464 double m_errorDerivativeTolerance = std::numeric_limits<double>::infinity();
465
466 double m_setpoint = 0;
467 double m_measurement = 0;
468
469 bool m_haveSetpoint = false;
470 bool m_haveMeasurement = false;
471
472 double m_lastAppliedOutput;
473
474 // The last timestamp acquired when performing a calculation
475 wpi::units::second_t m_lastTimestamp;
476 // The period (in seconds) of the current loop calling the controller
477 wpi::units::second_t m_period;
478
479 // Usage reporting instances
480 inline static int instances = 0;
481};
482
483}
484}
485}
486
487#ifdef CTRE_PHOENIX6_USE_WPILIB
488#undef CTRE_PHOENIX6_USE_WPILIB
489#endif
static void ReportError(bool isError, int32_t errorCode, char const *details, char const *location, bool getStackTrace=true)
Reports an error.
void PublishTunable(wpi::tunables::TunableTable &table) override
Publishes the tunable to the given table.
constexpr double GetErrorDerivative() const
Returns the error derivative.
Definition PhoenixPIDController.hpp:291
constexpr double Calculate(double measurement, double setpoint, wpi::units::second_t timestamp)
Returns the next output of the PID controller.
Definition PhoenixPIDController.hpp:339
constexpr double GetLastAppliedOutput() const
Returns the last applied output from this PID controller.
Definition PhoenixPIDController.hpp:349
constexpr PhoenixPIDController & operator=(PhoenixPIDController &&)=default
constexpr double GetIZone() const
Get the IZone range.
Definition PhoenixPIDController.hpp:157
constexpr double GetErrorDerivativeTolerance() const
Gets the error derivative tolerance of this controller.
Definition PhoenixPIDController.hpp:171
constexpr void DisableContinuousInput()
Disables continuous input.
Definition PhoenixPIDController.hpp:246
constexpr void Reset()
Reset the previous error and the integral term.
Definition PhoenixPIDController.hpp:354
constexpr PhoenixPIDController & operator=(PhoenixPIDController const &)=default
constexpr std::string_view GetTunableType() const override
Gets the tunable table type.
Definition PhoenixPIDController.hpp:393
constexpr double GetAccumulatedError() const
Gets the accumulated error used in the integral calculation of this controller.
Definition PhoenixPIDController.hpp:179
constexpr void EnableContinuousInput(double minimumInput, double maximumInput)
Enables continuous input.
Definition PhoenixPIDController.hpp:236
constexpr double GetErrorTolerance() const
Gets the error tolerance of this controller.
Definition PhoenixPIDController.hpp:164
constexpr void SetIZone(double iZone)
Sets the IZone range.
Definition PhoenixPIDController.hpp:122
constexpr double GetP() const
Gets the proportional coefficient.
Definition PhoenixPIDController.hpp:136
void LogTo(wpi::telemetry::TelemetryTable &table) const override
Logs the object to a wpi::telemetry::TelemetryTable.
constexpr std::string_view GetTelemetryType() const override
Gets the telemetry table type.
Definition PhoenixPIDController.hpp:375
constexpr PhoenixPIDController(double Kp, double Ki, double Kd)
Allocates a PhoenixPIDController with the given constants for Kp, Ki, and Kd.
Definition PhoenixPIDController.hpp:48
constexpr ~PhoenixPIDController() override=default
constexpr double Calculate(double measurement, wpi::units::second_t timestamp)
Returns the next output of the PID controller.
Definition PhoenixPIDController.hpp:299
constexpr double GetI() const
Gets the integral coefficient.
Definition PhoenixPIDController.hpp:143
constexpr void SetPID(double Kp, double Ki, double Kd)
Sets the PID Controller gain parameters.
Definition PhoenixPIDController.hpp:77
constexpr void SetP(double Kp)
Sets the proportional coefficient of the PID controller gain.
Definition PhoenixPIDController.hpp:91
constexpr bool AtSetpoint() const
Returns true if the error is within the tolerance of the setpoint.
Definition PhoenixPIDController.hpp:219
constexpr void SetTolerance(double errorTolerance, double errorDerivativeTolerance=std::numeric_limits< double >::infinity())
Sets the error which is considered tolerable for use with AtSetpoint().
Definition PhoenixPIDController.hpp:275
constexpr void SetSetpoint(double setpoint)
Sets the setpoint for the PIDController.
Definition PhoenixPIDController.hpp:186
constexpr PhoenixPIDController(PhoenixPIDController &&)=default
constexpr double GetD() const
Gets the differential coefficient.
Definition PhoenixPIDController.hpp:150
constexpr bool IsContinuousInputEnabled() const
Returns true if continuous input is enabled.
Definition PhoenixPIDController.hpp:251
constexpr double GetError() const
Returns the difference between the setpoint and the measurement.
Definition PhoenixPIDController.hpp:286
constexpr void SetIntegratorRange(double minimumIntegral, double maximumIntegral)
Sets the minimum and maximum contributions of the integral term.
Definition PhoenixPIDController.hpp:263
constexpr void SetI(double Ki)
Sets the integral coefficient of the PID controller gain.
Definition PhoenixPIDController.hpp:100
constexpr void SetD(double Kd)
Sets the differential coefficient of the PID controller gain.
Definition PhoenixPIDController.hpp:109
constexpr double GetSetpoint() const
Returns the current setpoint of the PIDController.
Definition PhoenixPIDController.hpp:210
constexpr PhoenixPIDController(PhoenixPIDController const &)=default
Definition SwerveModule.hpp:28
Definition ExternalFeedbackConfigs.hpp:16
Definition motor_constants.h:14