WPILibC++ 2025.0.0-alpha-1
ProfiledPIDController.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <algorithm>
8#include <cmath>
9#include <functional>
10#include <limits>
11
12#include <wpi/SymbolExports.h>
16
17#include "frc/MathUtil.h"
20#include "units/time.h"
21
22namespace frc {
23namespace detail {
26} // namespace detail
27
28/**
29 * Implements a PID control loop whose setpoint is constrained by a trapezoid
30 * profile.
31 */
32template <class Distance>
34 : public wpi::Sendable,
35 public wpi::SendableHelper<ProfiledPIDController<Distance>> {
36 public:
38 using Velocity =
46
47 /**
48 * Allocates a ProfiledPIDController with the given constants for Kp, Ki, and
49 * Kd. Users should call reset() when they first start running the controller
50 * to avoid unwanted behavior.
51 *
52 * @param Kp The proportional coefficient. Must be >= 0.
53 * @param Ki The integral coefficient. Must be >= 0.
54 * @param Kd The derivative coefficient. Must be >= 0.
55 * @param constraints Velocity and acceleration constraints for goal.
56 * @param period The period between controller updates in seconds. The
57 * default is 20 milliseconds. Must be positive.
58 */
59 ProfiledPIDController(double Kp, double Ki, double Kd,
60 Constraints constraints, units::second_t period = 20_ms)
61 : m_controller{Kp, Ki, Kd, period},
62 m_constraints{constraints},
63 m_profile{m_constraints} {
67 wpi::SendableRegistry::Add(this, "ProfiledPIDController", instances);
68 }
69
70 ~ProfiledPIDController() override = default;
71
76
77 /**
78 * Sets the PID Controller gain parameters.
79 *
80 * Sets the proportional, integral, and differential coefficients.
81 *
82 * @param Kp The proportional coefficient. Must be >= 0.
83 * @param Ki The integral coefficient. Must be >= 0.
84 * @param Kd The differential coefficient. Must be >= 0.
85 */
86 void SetPID(double Kp, double Ki, double Kd) {
87 m_controller.SetPID(Kp, Ki, Kd);
88 }
89
90 /**
91 * Sets the proportional coefficient of the PID controller gain.
92 *
93 * @param Kp The proportional coefficient. Must be >= 0.
94 */
95 void SetP(double Kp) { m_controller.SetP(Kp); }
96
97 /**
98 * Sets the integral coefficient of the PID controller gain.
99 *
100 * @param Ki The integral coefficient. Must be >= 0.
101 */
102 void SetI(double Ki) { m_controller.SetI(Ki); }
103
104 /**
105 * Sets the differential coefficient of the PID controller gain.
106 *
107 * @param Kd The differential coefficient. Must be >= 0.
108 */
109 void SetD(double Kd) { m_controller.SetD(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 void SetIZone(double iZone) { m_controller.SetIZone(iZone); }
123
124 /**
125 * Gets the proportional coefficient.
126 *
127 * @return proportional coefficient
128 */
129 double GetP() const { return m_controller.GetP(); }
130
131 /**
132 * Gets the integral coefficient.
133 *
134 * @return integral coefficient
135 */
136 double GetI() const { return m_controller.GetI(); }
137
138 /**
139 * Gets the differential coefficient.
140 *
141 * @return differential coefficient
142 */
143 double GetD() const { return m_controller.GetD(); }
144
145 /**
146 * Get the IZone range.
147 *
148 * @return Maximum magnitude of error to allow integral control.
149 */
150 double GetIZone() const { return m_controller.GetIZone(); }
151
152 /**
153 * Gets the period of this controller.
154 *
155 * @return The period of the controller.
156 */
157 units::second_t GetPeriod() const { return m_controller.GetPeriod(); }
158
159 /**
160 * Gets the position tolerance of this controller.
161 *
162 * @return The position tolerance of the controller.
163 */
164 double GetPositionTolerance() const {
165 return m_controller.GetPositionTolerance();
166 }
167
168 /**
169 * Gets the velocity tolerance of this controller.
170 *
171 * @return The velocity tolerance of the controller.
172 */
173 double GetVelocityTolerance() const {
174 return m_controller.GetVelocityTolerance();
175 }
176
177 /**
178 * Sets the goal for the ProfiledPIDController.
179 *
180 * @param goal The desired unprofiled setpoint.
181 */
182 void SetGoal(State goal) { m_goal = goal; }
183
184 /**
185 * Sets the goal for the ProfiledPIDController.
186 *
187 * @param goal The desired unprofiled setpoint.
188 */
189 void SetGoal(Distance_t goal) { m_goal = {goal, Velocity_t{0}}; }
190
191 /**
192 * Gets the goal for the ProfiledPIDController.
193 */
194 State GetGoal() const { return m_goal; }
195
196 /**
197 * Returns true if the error is within the tolerance of the error.
198 *
199 * This will return false until at least one input value has been computed.
200 */
201 bool AtGoal() const { return AtSetpoint() && m_goal == m_setpoint; }
202
203 /**
204 * Set velocity and acceleration constraints for goal.
205 *
206 * @param constraints Velocity and acceleration constraints for goal.
207 */
208 void SetConstraints(Constraints constraints) {
209 m_constraints = constraints;
210 m_profile = TrapezoidProfile<Distance>{m_constraints};
211 }
212
213 /**
214 * Get the velocity and acceleration constraints for this controller.
215 * @return Velocity and acceleration constraints.
216 */
217 Constraints GetConstraints() { return m_constraints; }
218
219 /**
220 * Returns the current setpoint of the ProfiledPIDController.
221 *
222 * @return The current setpoint.
223 */
224 State GetSetpoint() const { return m_setpoint; }
225
226 /**
227 * Returns true if the error is within the tolerance of the error.
228 *
229 * Currently this just reports on target as the actual value passes through
230 * the setpoint. Ideally it should be based on being within the tolerance for
231 * some period of time.
232 *
233 * This will return false until at least one input value has been computed.
234 */
235 bool AtSetpoint() const { return m_controller.AtSetpoint(); }
236
237 /**
238 * Enables continuous input.
239 *
240 * Rather then using the max and min input range as constraints, it considers
241 * them to be the same point and automatically calculates the shortest route
242 * to the setpoint.
243 *
244 * @param minimumInput The minimum value expected from the input.
245 * @param maximumInput The maximum value expected from the input.
246 */
247 void EnableContinuousInput(Distance_t minimumInput, Distance_t maximumInput) {
248 m_controller.EnableContinuousInput(minimumInput.value(),
249 maximumInput.value());
250 m_minimumInput = minimumInput;
251 m_maximumInput = maximumInput;
252 }
253
254 /**
255 * Disables continuous input.
256 */
258
259 /**
260 * Sets the minimum and maximum contributions of the integral term.
261 *
262 * The internal integrator is clamped so that the integral term's contribution
263 * to the output stays between minimumIntegral and maximumIntegral. This
264 * prevents integral windup.
265 *
266 * @param minimumIntegral The minimum contribution of the integral term.
267 * @param maximumIntegral The maximum contribution of the integral term.
268 */
269 void SetIntegratorRange(double minimumIntegral, double maximumIntegral) {
270 m_controller.SetIntegratorRange(minimumIntegral, maximumIntegral);
271 }
272
273 /**
274 * Sets the error which is considered tolerable for use with
275 * AtSetpoint().
276 *
277 * @param positionTolerance Position error which is tolerable.
278 * @param velocityTolerance Velocity error which is tolerable.
279 */
280 void SetTolerance(Distance_t positionTolerance,
281 Velocity_t velocityTolerance = Velocity_t{
282 std::numeric_limits<double>::infinity()}) {
283 m_controller.SetTolerance(positionTolerance.value(),
284 velocityTolerance.value());
285 }
286
287 /**
288 * Returns the difference between the setpoint and the measurement.
289 *
290 * @return The error.
291 */
293 return Distance_t{m_controller.GetPositionError()};
294 }
295
296 /**
297 * Returns the change in error per second.
298 */
300 return Velocity_t{m_controller.GetVelocityError()};
301 }
302
303 /**
304 * Returns the next output of the PID controller.
305 *
306 * @param measurement The current measurement of the process variable.
307 */
308 double Calculate(Distance_t measurement) {
309 if (m_controller.IsContinuousInputEnabled()) {
310 // Get error which is smallest distance between goal and measurement
311 auto errorBound = (m_maximumInput - m_minimumInput) / 2.0;
312 auto goalMinDistance = frc::InputModulus<Distance_t>(
313 m_goal.position - measurement, -errorBound, errorBound);
314 auto setpointMinDistance = frc::InputModulus<Distance_t>(
315 m_setpoint.position - measurement, -errorBound, errorBound);
316
317 // Recompute the profile goal with the smallest error, thus giving the
318 // shortest path. The goal may be outside the input range after this
319 // operation, but that's OK because the controller will still go there and
320 // report an error of zero. In other words, the setpoint only needs to be
321 // offset from the measurement by the input range modulus; they don't need
322 // to be equal.
323 m_goal.position = goalMinDistance + measurement;
324 m_setpoint.position = setpointMinDistance + measurement;
325 }
326
327 m_setpoint = m_profile.Calculate(GetPeriod(), m_setpoint, m_goal);
328 return m_controller.Calculate(measurement.value(),
329 m_setpoint.position.value());
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 goal The new goal of the controller.
337 */
338 double Calculate(Distance_t measurement, State goal) {
339 SetGoal(goal);
340 return Calculate(measurement);
341 }
342 /**
343 * Returns the next output of the PID controller.
344 *
345 * @param measurement The current measurement of the process variable.
346 * @param goal The new goal of the controller.
347 */
348 double Calculate(Distance_t measurement, Distance_t goal) {
349 SetGoal(goal);
350 return Calculate(measurement);
351 }
352
353 /**
354 * Returns the next output of the PID controller.
355 *
356 * @param measurement The current measurement of the process variable.
357 * @param goal The new goal of the controller.
358 * @param constraints Velocity and acceleration constraints for goal.
359 */
360 double Calculate(
361 Distance_t measurement, Distance_t goal,
363 SetConstraints(constraints);
364 return Calculate(measurement, goal);
365 }
366
367 /**
368 * Reset the previous error and the integral term.
369 *
370 * @param measurement The current measured State of the system.
371 */
372 void Reset(const State& measurement) {
373 m_controller.Reset();
374 m_setpoint = measurement;
375 }
376
377 /**
378 * Reset the previous error and the integral term.
379 *
380 * @param measuredPosition The current measured position of the system.
381 * @param measuredVelocity The current measured velocity of the system.
382 */
383 void Reset(Distance_t measuredPosition, Velocity_t measuredVelocity) {
384 Reset(State{measuredPosition, measuredVelocity});
385 }
386
387 /**
388 * Reset the previous error and the integral term.
389 *
390 * @param measuredPosition The current measured position of the system. The
391 * velocity is assumed to be zero.
392 */
393 void Reset(Distance_t measuredPosition) {
394 Reset(measuredPosition, Velocity_t{0});
395 }
396
397 void InitSendable(wpi::SendableBuilder& builder) override {
398 builder.SetSmartDashboardType("ProfiledPIDController");
399 builder.AddDoubleProperty(
400 "p", [this] { return GetP(); }, [this](double value) { SetP(value); });
401 builder.AddDoubleProperty(
402 "i", [this] { return GetI(); }, [this](double value) { SetI(value); });
403 builder.AddDoubleProperty(
404 "d", [this] { return GetD(); }, [this](double value) { SetD(value); });
405 builder.AddDoubleProperty(
406 "izone", [this] { return GetIZone(); },
407 [this](double value) { SetIZone(value); });
408 builder.AddDoubleProperty(
409 "maxVelocity", [this] { return GetConstraints().maxVelocity.value(); },
410 [this](double value) {
412 Constraints{Velocity_t{value}, GetConstraints().maxAcceleration});
413 });
414 builder.AddDoubleProperty(
415 "maxAcceleration",
416 [this] { return GetConstraints().maxAcceleration.value(); },
417 [this](double value) {
419 Constraints{GetConstraints().maxVelocity, Acceleration_t{value}});
420 });
421 builder.AddDoubleProperty(
422 "goal", [this] { return GetGoal().position.value(); },
423 [this](double value) { SetGoal(Distance_t{value}); });
424 }
425
426 private:
427 PIDController m_controller;
428 Distance_t m_minimumInput{0};
429 Distance_t m_maximumInput{0};
430
432 TrapezoidProfile<Distance> m_profile;
434 typename frc::TrapezoidProfile<Distance>::State m_setpoint;
435};
436
437} // namespace frc
#define WPILIB_DLLEXPORT
Definition: SymbolExports.h:36
Implements a PID control loop.
Definition: PIDController.h:23
double GetPositionError() const
Returns the difference between the setpoint and the measurement.
double GetVelocityTolerance() const
Gets the velocity tolerance of this controller.
void SetPID(double Kp, double Ki, double Kd)
Sets the PID Controller gain parameters.
bool IsContinuousInputEnabled() const
Returns true if continuous input is enabled.
units::second_t GetPeriod() const
Gets the period of this controller.
double GetIZone() const
Get the IZone range.
double GetP() const
Gets the proportional coefficient.
void DisableContinuousInput()
Disables continuous input.
double GetD() const
Gets the differential coefficient.
void SetI(double Ki)
Sets the integral coefficient of the PID controller gain.
void SetIZone(double iZone)
Sets the IZone range.
void SetP(double Kp)
Sets the proportional coefficient of the PID controller gain.
double Calculate(double measurement)
Returns the next output of the PID controller.
void Reset()
Reset the previous error, the integral term, and disable the controller.
double GetPositionTolerance() const
Gets the position tolerance of this controller.
double GetI() const
Gets the integral coefficient.
void SetTolerance(double positionTolerance, double velocityTolerance=std::numeric_limits< double >::infinity())
Sets the error which is considered tolerable for use with AtSetpoint().
bool AtSetpoint() const
Returns true if the error is within the tolerance of the setpoint.
void SetD(double Kd)
Sets the differential coefficient of the PID controller gain.
void SetIntegratorRange(double minimumIntegral, double maximumIntegral)
Sets the minimum and maximum contributions of the integral term.
void EnableContinuousInput(double minimumInput, double maximumInput)
Enables continuous input.
double GetVelocityError() const
Returns the velocity error.
Implements a PID control loop whose setpoint is constrained by a trapezoid profile.
Definition: ProfiledPIDController.h:35
ProfiledPIDController(double Kp, double Ki, double Kd, Constraints constraints, units::second_t period=20_ms)
Allocates a ProfiledPIDController with the given constants for Kp, Ki, and Kd.
Definition: ProfiledPIDController.h:59
double GetP() const
Gets the proportional coefficient.
Definition: ProfiledPIDController.h:129
State GetSetpoint() const
Returns the current setpoint of the ProfiledPIDController.
Definition: ProfiledPIDController.h:224
double Calculate(Distance_t measurement, Distance_t goal)
Returns the next output of the PID controller.
Definition: ProfiledPIDController.h:348
ProfiledPIDController & operator=(ProfiledPIDController &&)=default
void Reset(Distance_t measuredPosition)
Reset the previous error and the integral term.
Definition: ProfiledPIDController.h:393
State GetGoal() const
Gets the goal for the ProfiledPIDController.
Definition: ProfiledPIDController.h:194
bool AtSetpoint() const
Returns true if the error is within the tolerance of the error.
Definition: ProfiledPIDController.h:235
double Calculate(Distance_t measurement, State goal)
Returns the next output of the PID controller.
Definition: ProfiledPIDController.h:338
units::unit_t< Distance > Distance_t
Definition: ProfiledPIDController.h:37
typename TrapezoidProfile< Distance >::Constraints Constraints
Definition: ProfiledPIDController.h:45
Distance_t GetPositionError() const
Returns the difference between the setpoint and the measurement.
Definition: ProfiledPIDController.h:292
void SetTolerance(Distance_t positionTolerance, Velocity_t velocityTolerance=Velocity_t{ std::numeric_limits< double >::infinity()})
Sets the error which is considered tolerable for use with AtSetpoint().
Definition: ProfiledPIDController.h:280
void SetIntegratorRange(double minimumIntegral, double maximumIntegral)
Sets the minimum and maximum contributions of the integral term.
Definition: ProfiledPIDController.h:269
void SetI(double Ki)
Sets the integral coefficient of the PID controller gain.
Definition: ProfiledPIDController.h:102
ProfiledPIDController(const ProfiledPIDController &)=default
void SetGoal(Distance_t goal)
Sets the goal for the ProfiledPIDController.
Definition: ProfiledPIDController.h:189
void DisableContinuousInput()
Disables continuous input.
Definition: ProfiledPIDController.h:257
void SetD(double Kd)
Sets the differential coefficient of the PID controller gain.
Definition: ProfiledPIDController.h:109
units::second_t GetPeriod() const
Gets the period of this controller.
Definition: ProfiledPIDController.h:157
Velocity_t GetVelocityError() const
Returns the change in error per second.
Definition: ProfiledPIDController.h:299
double GetD() const
Gets the differential coefficient.
Definition: ProfiledPIDController.h:143
void SetP(double Kp)
Sets the proportional coefficient of the PID controller gain.
Definition: ProfiledPIDController.h:95
double GetPositionTolerance() const
Gets the position tolerance of this controller.
Definition: ProfiledPIDController.h:164
double GetIZone() const
Get the IZone range.
Definition: ProfiledPIDController.h:150
double Calculate(Distance_t measurement, Distance_t goal, typename frc::TrapezoidProfile< Distance >::Constraints constraints)
Returns the next output of the PID controller.
Definition: ProfiledPIDController.h:360
void EnableContinuousInput(Distance_t minimumInput, Distance_t maximumInput)
Enables continuous input.
Definition: ProfiledPIDController.h:247
ProfiledPIDController(ProfiledPIDController &&)=default
double GetVelocityTolerance() const
Gets the velocity tolerance of this controller.
Definition: ProfiledPIDController.h:173
void InitSendable(wpi::SendableBuilder &builder) override
Initializes this Sendable object.
Definition: ProfiledPIDController.h:397
ProfiledPIDController & operator=(const ProfiledPIDController &)=default
units::compound_unit< Velocity, units::inverse< units::seconds > > Acceleration
Definition: ProfiledPIDController.h:42
typename TrapezoidProfile< Distance >::State State
Definition: ProfiledPIDController.h:44
void SetPID(double Kp, double Ki, double Kd)
Sets the PID Controller gain parameters.
Definition: ProfiledPIDController.h:86
void Reset(const State &measurement)
Reset the previous error and the integral term.
Definition: ProfiledPIDController.h:372
units::compound_unit< Distance, units::inverse< units::seconds > > Velocity
Definition: ProfiledPIDController.h:39
double Calculate(Distance_t measurement)
Returns the next output of the PID controller.
Definition: ProfiledPIDController.h:308
void Reset(Distance_t measuredPosition, Velocity_t measuredVelocity)
Reset the previous error and the integral term.
Definition: ProfiledPIDController.h:383
void SetGoal(State goal)
Sets the goal for the ProfiledPIDController.
Definition: ProfiledPIDController.h:182
double GetI() const
Gets the integral coefficient.
Definition: ProfiledPIDController.h:136
Constraints GetConstraints()
Get the velocity and acceleration constraints for this controller.
Definition: ProfiledPIDController.h:217
~ProfiledPIDController() override=default
void SetConstraints(Constraints constraints)
Set velocity and acceleration constraints for goal.
Definition: ProfiledPIDController.h:208
bool AtGoal() const
Returns true if the error is within the tolerance of the error.
Definition: ProfiledPIDController.h:201
void SetIZone(double iZone)
Sets the IZone range.
Definition: ProfiledPIDController.h:122
Profile constraints.
Definition: TrapezoidProfile.h:56
Profile state.
Definition: TrapezoidProfile.h:88
A trapezoid-shaped velocity profile.
Definition: TrapezoidProfile.h:43
constexpr underlying_type value() const noexcept
unit value
Definition: base.h:2109
Helper class for building Sendable dashboard representations.
Definition: SendableBuilder.h:21
virtual void AddDoubleProperty(std::string_view key, std::function< double()> getter, std::function< void(double)> setter)=0
Add a double property.
virtual void SetSmartDashboardType(std::string_view type)=0
Set the string representation of the named data type that will be used by the smart dashboard for thi...
A helper class for use with objects that add themselves to SendableRegistry.
Definition: SendableHelper.h:19
Interface for Sendable objects.
Definition: Sendable.h:16
static void Add(Sendable *sendable, std::string_view name)
Adds an object to the registry.
static void ReportUsage(MathUsageId id, int count)
Definition: MathShared.h:73
typename units::detail::compound_impl< U, Us... >::type compound_unit
Represents a unit type made up from other units.
Definition: base.h:1436
detail namespace with internal helper functions
Definition: chrono.h:325
WPILIB_DLLEXPORT int IncrementAndGetProfiledPIDControllerInstances()
Definition: AprilTagDetector_cv.h:11