Skip to content

Commit b6af83d

Browse files
ViktorCVSmergify[bot]
authored andcommitted
Update documentation of PID class (#388)
(cherry picked from commit ff1f2d6) # Conflicts: # doc/migration.rst
1 parent ba3ac2c commit b6af83d

File tree

3 files changed

+158
-62
lines changed

3 files changed

+158
-62
lines changed

control_toolbox/include/control_toolbox/pid.hpp

Lines changed: 70 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -202,69 +202,78 @@ inline bool is_zero(T value, T tolerance = std::numeric_limits<T>::epsilon())
202202
}
203203

204204
/***************************************************/
205-
/*! \class Pid
206-
\brief A basic pid class.
207-
208-
This class implements a generic structure that
209-
can be used to create a wide range of pid
210-
controllers. It can function independently or
211-
be subclassed to provide more specific controls
212-
based on a particular control loop.
213-
214-
This class also allows for retention of integral
215-
term on reset. This is useful for control loops
216-
that are enabled/disabled with a constant steady-state
217-
external disturbance. Once the integrator cancels
218-
out the external disturbance, disabling/resetting/
219-
re-enabling closed-loop control does not require
220-
the integrator to wind up again.
221-
222-
In particular, this class implements the standard
223-
pid equation:
224-
225-
\f$command = p_{term} + i_{term} + d_{term} \f$
226-
227-
where: <br>
228-
<UL TYPE="none">
229-
<LI> \f$ p_{term} = p_{gain} * p_{error} \f$
230-
<LI> \f$ i_{term} = i_{term} + \int{i_{gain} * p_{error} * dt} \f$
231-
<LI> \f$ d_{term} = d_{gain} * d_{error} \f$
232-
<LI> \f$ d_{error} = (p_{error} - p_{error last}) / dt \f$
233-
</UL>
234-
235-
given:<br>
236-
<UL TYPE="none">
237-
<LI> \f$ p_{error} = p_{desired} - p_{state} \f$.
238-
</UL>
239-
240-
\param p Proportional gain
241-
242-
\param d Derivative gain
243-
244-
\param i Integral gain
245-
246-
\param i_clamp Minimum and maximum bounds for the integral windup, the clamp is applied to the \f$i_{term}\f$
247-
248-
\param u_clamp Minimum and maximum bounds for the controller output. The clamp is applied to the \f$command\f$.
249-
250-
\section Usage
251-
252-
To use the Pid class, you should first call some version of init()
253-
(in non-realtime) and then call updatePid() at every update step.
254-
For example:
255-
256-
\verbatim
205+
/*!
206+
\class Pid
207+
\brief Generic Proportional–Integral–Derivative (PID) controller.
208+
209+
\details
210+
The PID (Proportional–Integral–Derivative) controller is a widely used feedback
211+
controller. This class implements a generic structure that can be used to create
212+
a wide range of PID controllers. It can function independently or be subclassed
213+
to provide more specific control loops. Integral retention on reset is supported,
214+
which prevents re-winding the integrator after temporary disabling in presence
215+
of constant disturbances.
216+
217+
\section pid_equation PID Equation
218+
The standard PID equation is:
219+
\f[
220+
command = p\_term + i\_term + d\_term
221+
\f]
222+
where:
223+
- \f$ p\_term = p\_gain \times error \f$
224+
- \f$ i\_term \mathrel{+}= i\_gain \times error \times dt \f$
225+
- \f$ d\_term = d\_gain \times d\_error \f$
226+
and:
227+
- \f$ error = desired\_state - measured\_state \f$
228+
- \f$ d\_error = (error - error\_{last}) / dt \f$
229+
230+
\section parameters Parameters
231+
\param p Proportional gain. Reacts to current error.
232+
\param i Integral gain. Accumulates past error to eliminate steady-state error.
233+
\param d Derivative gain. Predicts future error to reduce overshoot and settling time.
234+
\param u\_min Minimum bound for the controller output.
235+
\param u\_max Maximum bound for the controller output.
236+
\param tracking\_time\_constant Tracking time constant for BACK_CALCULATION anti-windup.
237+
If zero, a default is chosen based on gains:
238+
- \f$ \sqrt{d\_gain / i\_gain} \f$ if \c d\_gain ≠ 0
239+
- \f$ p\_gain / i\_gain \f$ otherwise.
240+
\param antiwindup\_strat Anti-windup strategy:
241+
- NONE: no anti-windup (integral always accumulates).
242+
- BACK_CALCULATION: adjusts \c i\_term based on difference between saturated
243+
and unsaturated outputs using \c tracking\_time\_constant.
244+
- CONDITIONAL_INTEGRATION: only integrates when output is not saturated
245+
or error drives it away from saturation.
246+
247+
\section antiwindup Anti-Windup Strategies
248+
Without anti-windup, clamping causes integral windup, leading to overshoot and sluggish
249+
recovery. This class provides two strategies:
250+
251+
- **BACK_CALCULATION**
252+
\f[
253+
i\_term \mathrel{+}= dt \times \Bigl(i\_gain \times error + \frac{1}{trk\_tc}\,(command_{sat} - command)\Bigr)
254+
\f]
255+
Prevents excessive accumulation by correcting \c i\_term toward the saturation limit.
256+
257+
- **CONDITIONAL_INTEGRATION**
258+
Integrates only if
259+
\f[
260+
(command - command_{sat} = 0)\quad\lor\quad(error \times command \le 0)
261+
\f]
262+
Freezes integration when saturated and error drives further saturation.
263+
264+
\section usage Usage Example
265+
Initialize and compute at each control step:
266+
\code{.cpp}
257267
control_toolbox::Pid pid;
258-
pid.initialize(6.0, 1.0, 2.0, 0.3, -0.3);
259-
double position_desired = 0.5;
260-
...
261-
rclcpp::Time last_time = get_clock()->now();
262-
while (true) {
263-
rclcpp::Time time = get_clock()->now();
264-
double effort = pid.compute_command(position_desired - currentPosition(), time - last_time);
265-
last_time = time;
268+
pid.initialize(6.0, 1.0, 2.0, -5.0, 5.0,
269+
2.0, control_toolbox::AntiwindupStrategy::BACK_CALCULATION);
270+
rclcpp::Time last = get_clock()->now();
271+
while (running) {
272+
rclcpp::Time now = get_clock()->now();
273+
double effort = pid.compute_command(setpoint - current(), now - last);
274+
last = now;
266275
}
267-
\endverbatim
276+
\endcode
268277
269278
*/
270279
/***************************************************/

doc/control_toolbox.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,82 @@
11
# Base classes
22

3-
Tbd.
3+
4+
## PID
5+
6+
7+
## PID Controller
8+
9+
The PID (Proportional-Integral-Derivative) controller is a widely used feedback controller. This class implements a generic structure that can be used to create a wide range of PID controllers. It can function independently or be subclassed to provide more specific controls based on a particular control loop. Integral retention on reset is supported, which prevents re-winding the integrator after temporary disabling in presence of constant disturbances.
10+
11+
### PID Equation
12+
13+
The standard PID equation is given by:
14+
15+
command = p<sub>term</sub> + i<sub>term</sub> + d<sub>term</sub>
16+
17+
where:
18+
* p<sub>term</sub> = p<sub>gain</sub> * error
19+
* i<sub>term</sub> = i<sub>term</sub> + i<sub>gain</sub> * error * dt
20+
* d<sub>term</sub> = d<sub>gain</sub> * d<sub>error</sub>
21+
22+
and:
23+
* error = desired_state - measured_state
24+
* d<sub>error</sub> = (error - error<sub>last</sub>) / dt
25+
26+
### Parameters
27+
28+
* `p` (Proportional gain): This gain determines the reaction to the current error. A larger proportional gain results in a larger change in the controller output for a given change in the error.
29+
* `i` (Integral gain): This gain determines the reaction based on the sum of recent errors. The integral term accounts for past values of the error and integrates them over time to produce the `i_term`. This helps in eliminating steady-state errors.
30+
* `d` (Derivative gain): This gain determines the reaction based on the rate at which the error has been changing. The derivative term predicts future errors based on the rate of change of the current error. This helps in reducing overshoot, settling time, and other transient performance variables.
31+
* `u_clamp` (Minimum and maximum bounds for the controller output): These bounds are applied to the final command output of the controller, ensuring the output stays within acceptable physical limits.
32+
* `tracking_time_constant` (Tracking time constant): This parameter is specific to the 'back_calculation' anti-windup strategy. If set to 0.0 when this strategy is selected, a recommended default value will be applied.
33+
* `antiwindup_strat` (Anti-windup strategy): This parameter selects how the integrator is prevented from winding up when the controller output saturates. Available options are:
34+
* `NONE`: no anti-windup technique; the integral term accumulates without correction.
35+
* `BACK_CALCULATION`: adjusts the integral term based on the difference between the unsaturated and saturated outputs using the tracking time constant `tracking_time_constant`. Faster correction for smaller `tracking_time_constant`.
36+
* `CONDITIONAL_INTEGRATION`: only updates the integral term when the controller is not in saturation or when the error drives the output away from saturation, freezing integration otherwise.
37+
38+
### Anti-Windup Strategies
39+
40+
Anti-windup functionality is crucial for PID controllers, especially when the control output is subject to saturation (clamping). Without anti-windup, the integral term can accumulate excessively when the controller output is saturated, leading to large overshoots and sluggish response once the error changes direction. The `control_toolbox::Pid` class offers two anti-windup strategies:
41+
42+
* **`BACK_CALCULATION`**: This strategy adjusts the integral term based on the difference between the saturated and unsaturated controller output. When the controller output `command` exceeds the output limits (`u_max` or `u_min`), the integral term `i_term` is adjusted by subtracting a value proportional to the difference between the saturated output `command_sat` and the unsaturated output `command`. This prevents the integral term from accumulating beyond what is necessary to maintain the output at its saturation limit. The `tracking_time_constant` parameter is used to tune the speed of this adjustment. A smaller value results in faster anti-windup action.
43+
44+
The update rule for the integral term with back-calculation is:
45+
46+
i<sub>term</sub> += dt * (i<sub>gain</sub> * error + (1 / trk<sub>tc</sub>) * (command<sub>sat</sub> - command))
47+
48+
If `trk_tc`, i.e., `tracking_time_constant` parameter, is set to 0.0, a default value is calculated based on the proportional and derivative gains:
49+
* If `d_gain` is not zero: trk<sub>tc</sub> = &radic;(d<sub>gain</sub> / i<sub>gain</sub>)
50+
* If `d_gain` is zero: trk<sub>tc</sub> = p<sub>gain</sub> / i<sub>gain</sub>
51+
52+
* **`CONDITIONAL_INTEGRATION`**: In this strategy, the integral term is only updated when the controller is not in saturation or when the error has a sign that would lead the controller out of saturation. Specifically, the integral term is frozen (not updated) if the controller output is saturated and the error has the same sign as the saturated output. This prevents further accumulation of the integral term in the direction of saturation.
53+
54+
The integral term is updated only if the following condition is met:
55+
56+
(command - command<sub>sat</sub> = 0) &or; (error * command &le; 0)
57+
58+
This means the integral term `i_term` is updated as `i_term += dt * i_gain * error` only when the controller is not saturated, or when it is saturated but the error is driving the output away from the saturation limit.
59+
60+
### Usage Example
61+
62+
To use the `Pid` class, you should first call some version of `initialize()` and then call `compute_command()` at every update step. For example:
63+
64+
```cpp
65+
control_toolbox::Pid pid;
66+
pid.initialize(6.0, 1.0, 2.0, 5, -5,2,control_toolbox::AntiwindupStrategy::BACK_CALCULATION);
67+
double position_desired = 0.5;
68+
...
69+
rclcpp::Time last_time = get_clock()->now();
70+
while (true) {
71+
rclcpp::Time time = get_clock()->now();
72+
double effort = pid.compute_command(position_desired - currentPosition(), time - last_time);
73+
last_time = time;
74+
}
75+
```
76+
77+
### References
78+
79+
1. Visioli, A. _Practical PID Control_. London: Springer-Verlag London Limited, 2006. 476 p.
80+
2. Vrancic, D., Horowitz, R., & Hagiwara, T. “Antiwindup, Bumpless, and Conditioned Transfer Techniques for PID Controllers.” _IEEE Control Systems Magazine_, vol. 16, no. 4, 1996, pp. 48–57.
81+
3. Bohn, C.; Atherton, D. “An analysis package comparing PID anti-windup strategies.” _IEEE Control Systems Magazine_, 1995, pp. 34–40.
82+
4. Åström, K.; Hägglund, T. _PID Controllers: Theory, Design and Tuning_. Research Triangle Park, USA: ISA Press / Springer-Verlag London Limited, 1995. 343 p.

doc/migration.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22

33
Migration Guides: Humble to Jazzy
44
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
<<<<<<< HEAD
56
This list summarizes important changes between Humble (previous) and Jazzy (current) releases, where changes to user code might be necessary.
67

78
.. note::
89

910
This list was created in June 2025 (tag 4.4.0), earlier changes may not be included.
11+
=======
12+
This list summarizes important changes between Jazzy (previous) and Kilted (current) releases, where changes to user code might be necessary.
13+
14+
Pid/PidRos
15+
***********************************************************
16+
* The parameters :paramref:`antiwindup`, :paramref:`i_clamp_max`, and :paramref:`i_clamp_min` have been removed. The anti-windup behavior is now configured via the :paramref:`AntiWindupStrategy` enum. (`#298 <https://github.com/ros-controls/control_toolbox/pull/298>`_).
17+
>>>>>>> ff1f2d6 (Update documentation of PID class (#388))

0 commit comments

Comments
 (0)