ros2_control - rolling
Loading...
Searching...
No Matches
pid.hpp
1// Copyright (c) 2008, Willow Garage, Inc.
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8//
9// * Redistributions in binary form must reproduce the above copyright
10// notice, this list of conditions and the following disclaimer in the
11// documentation and/or other materials provided with the distribution.
12//
13// * Neither the name of the Willow Garage nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28
29#ifndef CONTROL_TOOLBOX__PID_HPP_
30#define CONTROL_TOOLBOX__PID_HPP_
31
32#include <chrono>
33#include <cmath>
34#include <iostream>
35#include <limits>
36#include <string>
37
38#include "fmt/format.h"
39#include "rclcpp/duration.hpp"
40#include "realtime_tools/realtime_thread_safe_box.hpp"
41
42namespace control_toolbox
43{
63{
64public:
65 enum Value : int8_t
66 {
67 UNDEFINED = -1,
68 NONE,
69 BACK_CALCULATION,
70 CONDITIONAL_INTEGRATION
71 };
72
74 : type(NONE),
75 i_max(std::numeric_limits<double>::infinity()),
76 i_min(-std::numeric_limits<double>::infinity()),
78 error_deadband(std::numeric_limits<double>::epsilon())
79 {
80 }
81
82 void set_type(const std::string & s)
83 {
84 if (s == "back_calculation")
85 {
86 type = BACK_CALCULATION;
87 }
88 else if (s == "conditional_integration")
89 {
90 type = CONDITIONAL_INTEGRATION;
91 }
92 else if (s == "none")
93 {
94 type = NONE;
95 }
96 else
97 {
98 type = UNDEFINED;
99 throw std::invalid_argument(
100 "AntiWindupStrategy: Unknown antiwindup strategy : '" + s +
101 "'. Valid strategies are: 'back_calculation', 'conditional_integration', "
102 "'none'.");
103 }
104 }
105
106 void validate() const
107 {
108 if (type == UNDEFINED)
109 {
110 throw std::invalid_argument("AntiWindupStrategy is UNDEFINED. Please set a valid type");
111 }
112 if (
113 type == BACK_CALCULATION &&
114 (tracking_time_constant < 0.0 || !std::isfinite(tracking_time_constant)))
115 {
116 throw std::invalid_argument(
117 "AntiWindupStrategy 'back_calculation' requires a valid positive tracking time constant "
118 "(tracking_time_constant)");
119 }
120 if (i_min > 0)
121 {
122 throw std::invalid_argument(
123 fmt::format("PID requires i_min to be smaller or equal to 0 (i_min: {})", i_min));
124 }
125 if (i_max < 0)
126 {
127 throw std::invalid_argument(
128 fmt::format("PID requires i_max to be greater or equal to 0 (i_max: {})", i_max));
129 }
130 if (
131 type != NONE && type != UNDEFINED && type != BACK_CALCULATION &&
132 type != CONDITIONAL_INTEGRATION)
133 {
134 throw std::invalid_argument("AntiWindupStrategy has an invalid type");
135 }
136 }
137
138 void print() const { std::cout << string() << std::endl; }
139
140 std::string string() const
141 {
142 return fmt::format(
143 "antiwindup_strat: {}\ti_max: {}\ti_min: {}\ttracking_time_constant: {}\terror_deadband: {}",
145 }
146
147 operator std::string() const { return to_string(); }
148
149 constexpr bool operator==(Value other) const { return type == other; }
150 constexpr bool operator!=(Value other) const { return type != other; }
151
152 std::string to_string() const
153 {
154 switch (type)
155 {
156 case BACK_CALCULATION:
157 return "back_calculation";
158 case CONDITIONAL_INTEGRATION:
159 return "conditional_integration";
160 case NONE:
161 return "none";
162 case UNDEFINED:
163 default:
164 return "UNDEFINED";
165 }
166 }
167
168 Value type = UNDEFINED;
169 double i_max = std::numeric_limits<double>::infinity();
170 double i_min = -std::numeric_limits<double>::infinity();
172 // tracking_time_constant Specifies the tracking time constant for the 'back_calculation'
173 // strategy. If set to 0.0 a recommended default value will be applied.
177 std::numeric_limits<double>::epsilon();
178};
179
180template <typename T>
181inline bool is_zero(T value, T tolerance = std::numeric_limits<T>::epsilon())
182{
183 return std::abs(value) <= tolerance;
184}
185
186/***************************************************/
261/***************************************************/
262
263class Pid
264{
265public:
269 struct Gains
270 {
285 double p, double i, double d, double u_max, double u_min,
286 const AntiWindupStrategy & antiwindup_strat)
287 : p_gain_(p),
288 i_gain_(i),
289 d_gain_(d),
290 u_max_(u_max),
291 u_min_(u_min),
292 antiwindup_strat_(antiwindup_strat)
293 {
294 }
295
296 bool validate(std::string & error_msg) const
297 {
298 if (u_min_ >= u_max_) // is false if any value is nan
299 {
300 error_msg = fmt::format("Gains: u_min ({}) must be less than u_max ({})", u_min_, u_max_);
301 return false;
302 }
303 else if (std::isnan(u_min_) || std::isnan(u_max_))
304 {
305 error_msg = "Gains: u_min and u_max must not be NaN";
306 return false;
307 }
308 try
309 {
310 antiwindup_strat_.validate();
311 }
312 catch (const std::exception & e)
313 {
314 error_msg = e.what();
315 return false;
316 }
317 return true;
318 }
319
320 void print() const { std::cout << string() << std::endl; }
321
322 std::string string() const
323 {
324 return fmt::format(
325 "Gains(p: {}, i: {}, d: {}, u_max: {}, u_min: {}, antiwindup_strat: '{}')", p_gain_,
327 }
328
329 double p_gain_ = 0.0;
330 double i_gain_ = 0.0;
331 double d_gain_ = 0.0;
332 double u_max_ = std::numeric_limits<double>::infinity();
333 double u_min_ = -std::numeric_limits<double>::infinity();
335 };
336
351 Pid(
352 double p = 0.0, double i = 0.0, double d = 0.0,
353 double u_max = std::numeric_limits<double>::infinity(),
354 double u_min = -std::numeric_limits<double>::infinity(),
355 const AntiWindupStrategy & antiwindup_strat = AntiWindupStrategy());
356
361 Pid(const Pid & source);
362
366 ~Pid();
367
383 bool initialize(
384 double p, double i, double d, double u_max, double u_min,
385 const AntiWindupStrategy & antiwindup_strat);
386
391 void reset();
392
398 void reset(bool save_i_term);
399
403 void clear_saved_iterm();
404
418 void get_gains(
419 double & p, double & i, double & d, double & u_max, double & u_min,
420 AntiWindupStrategy & antiwindup_strat);
421
429
436 Gains get_gains_rt() { return gains_; }
437
454 bool set_gains(
455 double p, double i, double d, double u_max, double u_min,
456 const AntiWindupStrategy & antiwindup_strat);
457
466 bool set_gains(const Gains & gains);
467
478 [[nodiscard]] double compute_command(double error, const double & dt_s);
479
490 [[nodiscard]] double compute_command(double error, const rcl_duration_value_t & dt_ns);
491
502 [[nodiscard]] double compute_command(double error, const rclcpp::Duration & dt);
503
514 [[nodiscard]] double compute_command(double error, const std::chrono::nanoseconds & dt_ns);
515
527 [[nodiscard]] double compute_command(double error, double error_dot, const double & dt_s);
528
540 [[nodiscard]] double compute_command(
541 double error, double error_dot, const rcl_duration_value_t & dt_ns);
542
554 [[nodiscard]] double compute_command(double error, double error_dot, const rclcpp::Duration & dt);
555
567 [[nodiscard]] double compute_command(
568 double error, double error_dot, const std::chrono::nanoseconds & dt_ns);
569
573 void set_current_cmd(double cmd);
574
578 double get_current_cmd();
579
586 void get_current_pid_errors(double & pe, double & ie, double & de);
587
592 Pid & operator=(const Pid & source)
593 {
594 if (this == &source)
595 {
596 return *this;
597 }
598
599 // Copy the realtime box to then new PID class
600 gains_box_ = source.gains_box_;
601
602 // Reset the state of this PID controller
603 reset();
604
605 return *this;
606 }
607
608protected:
609 // local copy of the gains for the RT loop
610 Gains gains_{
611 0.0,
612 0.0,
613 0.0,
614 std::numeric_limits<double>::infinity(),
615 -std::numeric_limits<double>::infinity(),
617 // Store the PID gains in a realtime box to allow dynamic reconfigure to update it without
618 // blocking the realtime update loop
620
621 double p_error_last_ = 0;
622 double p_error_ = 0;
623 double d_error_ = 0;
624 double i_term_ = 0;
625 double cmd_ = 0;
626 double cmd_unsat_ = 0;
627};
628
629} // namespace control_toolbox
630
631#endif // CONTROL_TOOLBOX__PID_HPP_
Generic Proportional–Integral–Derivative (PID) controller.
Definition pid.hpp:264
void clear_saved_iterm()
Clear the saved integrator output of this controller.
Definition pid.cpp:106
Pid & operator=(const Pid &source)
Custom assignment operator Does not initialize dynamic reconfigure for PID gains.
Definition pid.hpp:592
double cmd_unsat_
Definition pid.hpp:626
Gains get_gains()
Get PID gains for the controller.
Definition pid.cpp:121
bool initialize(double p, double i, double d, double u_max, double u_min, const AntiWindupStrategy &antiwindup_strat)
Initialize Pid-gains and term limits.
Definition pid.cpp:75
~Pid()
Destructor of Pid class.
Definition pid.cpp:73
void set_current_cmd(double cmd)
Set current command for this PID controller.
Definition pid.cpp:329
double p_error_
Definition pid.hpp:622
void reset()
Reset the state of this PID controller.
Definition pid.cpp:87
void get_current_pid_errors(double &pe, double &ie, double &de)
Return PID error terms for the controller.
Definition pid.cpp:333
double d_error_
Definition pid.hpp:623
Gains get_gains_rt()
Get PID gains for the controller.
Definition pid.hpp:436
double get_current_cmd()
Return current command for this PID controller.
Definition pid.cpp:331
double compute_command(double error, const double &dt_s)
Set the PID error and compute the PID command with nonuniform time step size. The derivative error is...
Definition pid.cpp:182
double cmd_
Definition pid.hpp:625
double i_term_
Definition pid.hpp:624
bool set_gains(double p, double i, double d, double u_max, double u_min, const AntiWindupStrategy &antiwindup_strat)
Set PID gains for the controller.
Definition pid.cpp:127
Definition realtime_thread_safe_box.hpp:68
Definition dither.hpp:42
Antiwindup strategy for PID controllers.
Definition pid.hpp:63
double error_deadband
Definition pid.hpp:176
double tracking_time_constant
Definition pid.hpp:174
double i_max
Definition pid.hpp:169
double i_min
Definition pid.hpp:170
Store gains in a struct to allow easier realtime box usage.
Definition pid.hpp:270
double d_gain_
Definition pid.hpp:331
double i_gain_
Definition pid.hpp:330
AntiWindupStrategy antiwindup_strat_
Definition pid.hpp:334
Gains(double p, double i, double d, double u_max, double u_min, const AntiWindupStrategy &antiwindup_strat)
Constructor for passing in values.
Definition pid.hpp:284
double u_min_
Definition pid.hpp:333
double u_max_
Definition pid.hpp:332
double p_gain_
Definition pid.hpp:329