ros2_control - rolling
Loading...
Searching...
No Matches
pid.hpp
1// Copyright (c) 2008, Willow Garage, Inc.
2// All rights reserved.
3//
4// Software License Agreement (BSD License 2.0)
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions
8// are met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following
14// disclaimer in the documentation and/or other materials provided
15// with the distribution.
16// * Neither the name of the Willow Garage nor the names of its
17// contributors may be used to endorse or promote products derived
18// from this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31// POSSIBILITY OF SUCH DAMAGE.
32
33#ifndef CONTROL_TOOLBOX__PID_HPP_
34#define CONTROL_TOOLBOX__PID_HPP_
35
36#include <chrono>
37#include <cmath>
38#include <iostream>
39#include <limits>
40#include <string>
41
42#include "fmt/format.h"
43#include "rclcpp/duration.hpp"
44#include "realtime_tools/realtime_thread_safe_box.hpp"
45
46namespace control_toolbox
47{
67{
68public:
69 enum Value : int8_t
70 {
71 UNDEFINED = -1,
72 NONE,
73 BACK_CALCULATION,
74 CONDITIONAL_INTEGRATION
75 };
76
78 : type(NONE),
79 i_max(std::numeric_limits<double>::infinity()),
80 i_min(-std::numeric_limits<double>::infinity()),
82 error_deadband(std::numeric_limits<double>::epsilon())
83 {
84 }
85
86 void set_type(const std::string & s)
87 {
88 if (s == "back_calculation")
89 {
90 type = BACK_CALCULATION;
91 }
92 else if (s == "conditional_integration")
93 {
94 type = CONDITIONAL_INTEGRATION;
95 }
96 else if (s == "none")
97 {
98 type = NONE;
99 }
100 else
101 {
102 type = UNDEFINED;
103 throw std::invalid_argument(
104 "AntiWindupStrategy: Unknown antiwindup strategy : '" + s +
105 "'. Valid strategies are: 'back_calculation', 'conditional_integration', "
106 "'none'.");
107 }
108 }
109
110 void validate() const
111 {
112 if (type == UNDEFINED)
113 {
114 throw std::invalid_argument("AntiWindupStrategy is UNDEFINED. Please set a valid type");
115 }
116 if (
117 type == BACK_CALCULATION &&
118 (tracking_time_constant < 0.0 || !std::isfinite(tracking_time_constant)))
119 {
120 throw std::invalid_argument(
121 "AntiWindupStrategy 'back_calculation' requires a valid positive tracking time constant "
122 "(tracking_time_constant)");
123 }
124 if (i_min > 0)
125 {
126 throw std::invalid_argument(
127 fmt::format("PID requires i_min to be smaller or equal to 0 (i_min: {})", i_min));
128 }
129 if (i_max < 0)
130 {
131 throw std::invalid_argument(
132 fmt::format("PID requires i_max to be greater or equal to 0 (i_max: {})", i_max));
133 }
134 if (
135 type != NONE && type != UNDEFINED && type != BACK_CALCULATION &&
136 type != CONDITIONAL_INTEGRATION)
137 {
138 throw std::invalid_argument("AntiWindupStrategy has an invalid type");
139 }
140 }
141
142 void print() const
143 {
144 std::cout << "antiwindup_strat: " << to_string() << "\ti_max: " << i_max << ", i_min: " << i_min
145 << "\ttracking_time_constant: " << tracking_time_constant
146 << "\terror_deadband: " << error_deadband << std::endl;
147 }
148
149 operator std::string() const { return to_string(); }
150
151 constexpr bool operator==(Value other) const { return type == other; }
152 constexpr bool operator!=(Value other) const { return type != other; }
153
154 std::string to_string() const
155 {
156 switch (type)
157 {
158 case BACK_CALCULATION:
159 return "back_calculation";
160 case CONDITIONAL_INTEGRATION:
161 return "conditional_integration";
162 case NONE:
163 return "none";
164 case UNDEFINED:
165 default:
166 return "UNDEFINED";
167 }
168 }
169
170 Value type = UNDEFINED;
171 double i_max = std::numeric_limits<double>::infinity();
172 double i_min = -std::numeric_limits<double>::infinity();
174 // tracking_time_constant Specifies the tracking time constant for the 'back_calculation'
175 // strategy. If set to 0.0 a recommended default value will be applied.
179 std::numeric_limits<double>::epsilon();
180};
181
182template <typename T>
183inline bool is_zero(T value, T tolerance = std::numeric_limits<T>::epsilon())
184{
185 return std::abs(value) <= tolerance;
186}
187
188/***************************************************/
263/***************************************************/
264
265class Pid
266{
267public:
271 struct Gains
272 {
287 double p, double i, double d, double u_max, double u_min,
288 const AntiWindupStrategy & antiwindup_strat)
289 : p_gain_(p),
290 i_gain_(i),
291 d_gain_(d),
292 u_max_(u_max),
293 u_min_(u_min),
294 antiwindup_strat_(antiwindup_strat)
295 {
296 }
297
298 bool validate(std::string & error_msg) const
299 {
300 if (u_min_ >= u_max_) // is false if any value is nan
301 {
302 error_msg = fmt::format("Gains: u_min ({}) must be less than u_max ({})", u_min_, u_max_);
303 return false;
304 }
305 else if (std::isnan(u_min_) || std::isnan(u_max_))
306 {
307 error_msg = "Gains: u_min and u_max must not be NaN";
308 return false;
309 }
310 try
311 {
312 antiwindup_strat_.validate();
313 }
314 catch (const std::exception & e)
315 {
316 error_msg = e.what();
317 return false;
318 }
319 return true;
320 }
321
322 void print() const
323 {
324 std::cout << "Gains: p: " << p_gain_ << ", i: " << i_gain_ << ", d: " << d_gain_
325 << ", u_max: " << u_max_ << ", u_min: " << u_min_ << std::endl;
326 antiwindup_strat_.print();
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:266
void clear_saved_iterm()
Clear the saved integrator output of this controller.
Definition pid.cpp:110
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:125
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:79
~Pid()
Destructor of Pid class.
Definition pid.cpp:77
void set_current_cmd(double cmd)
Set current command for this PID controller.
Definition pid.cpp:333
double p_error_
Definition pid.hpp:622
void reset()
Reset the state of this PID controller.
Definition pid.cpp:91
void get_current_pid_errors(double &pe, double &ie, double &de)
Return PID error terms for the controller.
Definition pid.cpp:337
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:335
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:186
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:131
Definition realtime_thread_safe_box.hpp:68
Definition dither.hpp:46
Antiwindup strategy for PID controllers.
Definition pid.hpp:67
double error_deadband
Definition pid.hpp:178
double tracking_time_constant
Definition pid.hpp:176
double i_max
Definition pid.hpp:171
double i_min
Definition pid.hpp:172
Store gains in a struct to allow easier realtime box usage.
Definition pid.hpp:272
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:286
double u_min_
Definition pid.hpp:333
double u_max_
Definition pid.hpp:332
double p_gain_
Definition pid.hpp:329