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 >= i_max)
125 {
126 throw std::invalid_argument(
127 fmt::format(
128 "PID requires i_min < i_max if limits are finite (i_min: {}, i_max: {})", i_min, 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 operator std::string() const { return to_string(); }
139
140 constexpr bool operator==(Value other) const { return type == other; }
141 constexpr bool operator!=(Value other) const { return type != other; }
142
143 std::string to_string() const
144 {
145 switch (type)
146 {
147 case BACK_CALCULATION:
148 return "back_calculation";
149 case CONDITIONAL_INTEGRATION:
150 return "conditional_integration";
151 case NONE:
152 return "none";
153 case UNDEFINED:
154 default:
155 return "UNDEFINED";
156 }
157 }
158
159 Value type = UNDEFINED;
160 double i_max = std::numeric_limits<double>::infinity();
161 double i_min = -std::numeric_limits<double>::infinity();
163 // tracking_time_constant Specifies the tracking time constant for the 'back_calculation'
164 // strategy. If set to 0.0 a recommended default value will be applied.
168 std::numeric_limits<double>::epsilon();
169};
170
171template <typename T>
172inline bool is_zero(T value, T tolerance = std::numeric_limits<T>::epsilon())
173{
174 return std::abs(value) <= tolerance;
175}
176
177/***************************************************/
252/***************************************************/
253
254class Pid
255{
256public:
260 struct Gains
261 {
276 double p, double i, double d, double u_max, double u_min,
278 : p_gain_(p),
279 i_gain_(i),
280 d_gain_(d),
283 u_max_(u_max),
284 u_min_(u_min),
286 {
287 if (std::isnan(u_min) || std::isnan(u_max))
288 {
289 throw std::invalid_argument("Gains: u_min and u_max must not be NaN");
290 }
291 if (u_min > u_max)
292 {
293 std::cout << "Received invalid u_min and u_max values: " << "u_min: " << u_min
294 << ", u_max: " << u_max << ". Setting saturation to false." << std::endl;
295 u_max_ = std::numeric_limits<double>::infinity();
296 u_min_ = -std::numeric_limits<double>::infinity();
297 }
298 }
299
300 bool validate(std::string & error_msg) const
301 {
302 if (i_min_ > i_max_)
303 {
304 error_msg = fmt::format("Gains: i_min ({}) must be less than i_max ({})", i_min_, i_max_);
305 return false;
306 }
307 else if (u_min_ >= u_max_)
308 {
309 error_msg = fmt::format("Gains: u_min ({}) must be less than u_max ({})", u_min_, u_max_);
310 return false;
311 }
312 else if (std::isnan(u_min_) || std::isnan(u_max_))
313 {
314 error_msg = "Gains: u_min and u_max must not be NaN";
315 return false;
316 }
317 try
318 {
319 antiwindup_strat_.validate();
320 }
321 catch (const std::exception & e)
322 {
323 error_msg = e.what();
324 return false;
325 }
326 return true;
327 }
328
329 void print() const
330 {
331 std::cout << "Gains: p: " << p_gain_ << ", i: " << i_gain_ << ", d: " << d_gain_
332 << ", i_max: " << i_max_ << ", i_min: " << i_min_ << ", u_max: " << u_max_
333 << ", u_min: " << u_min_ << ", antiwindup_strat: " << antiwindup_strat_.to_string()
334 << std::endl;
335 }
336
337 double p_gain_ = 0.0;
338 double i_gain_ = 0.0;
339 double d_gain_ = 0.0;
340 double i_max_ =
341 std::numeric_limits<double>::infinity();
342 double i_min_ =
343 -std::numeric_limits<double>::infinity();
344 double u_max_ = std::numeric_limits<double>::infinity();
345 double u_min_ = -std::numeric_limits<double>::infinity();
347 };
348
363 Pid(
364 double p = 0.0, double i = 0.0, double d = 0.0,
365 double u_max = std::numeric_limits<double>::infinity(),
366 double u_min = -std::numeric_limits<double>::infinity(),
368
373 Pid(const Pid & source);
374
378 ~Pid();
379
395 bool initialize(
396 double p, double i, double d, double u_max, double u_min,
398
403 void reset();
404
410 void reset(bool save_i_term);
411
415 void clear_saved_iterm();
416
430 void get_gains(
431 double & p, double & i, double & d, double & u_max, double & u_min,
433
441
448 Gains get_gains_rt() { return gains_; }
449
466 bool set_gains(
467 double p, double i, double d, double u_max, double u_min,
469
478 bool set_gains(const Gains & gains);
479
490 [[nodiscard]] double compute_command(double error, const double & dt_s);
491
502 [[nodiscard]] double compute_command(double error, const rcl_duration_value_t & dt_ns);
503
514 [[nodiscard]] double compute_command(double error, const rclcpp::Duration & dt);
515
526 [[nodiscard]] double compute_command(double error, const std::chrono::nanoseconds & dt_ns);
527
539 [[nodiscard]] double compute_command(double error, double error_dot, const double & dt_s);
540
552 [[nodiscard]] double compute_command(
553 double error, double error_dot, const rcl_duration_value_t & dt_ns);
554
566 [[nodiscard]] double compute_command(double error, double error_dot, const rclcpp::Duration & dt);
567
579 [[nodiscard]] double compute_command(
580 double error, double error_dot, const std::chrono::nanoseconds & dt_ns);
581
585 void set_current_cmd(double cmd);
586
590 double get_current_cmd();
591
598 void get_current_pid_errors(double & pe, double & ie, double & de);
599
605 {
606 if (this == &source)
607 {
608 return *this;
609 }
610
611 // Copy the realtime box to then new PID class
612 gains_box_ = source.gains_box_;
613
614 // Reset the state of this PID controller
615 reset();
616
617 return *this;
618 }
619
620protected:
621 // local copy of the gains for the RT loop
622 Gains gains_{
623 0.0,
624 0.0,
625 0.0,
626 std::numeric_limits<double>::infinity(),
627 -std::numeric_limits<double>::infinity(),
629 // Store the PID gains in a realtime box to allow dynamic reconfigure to update it without
630 // blocking the realtime update loop
632
633 double p_error_last_ = 0;
634 double p_error_ = 0;
635 double d_error_ = 0;
636 double i_term_ = 0;
637 double cmd_ = 0;
638 double cmd_unsat_ = 0;
639};
640
641} // namespace control_toolbox
642
643#endif // CONTROL_TOOLBOX__PID_HPP_
A Low-pass filter class.
Definition low_pass_filter.hpp:79
Generic Proportional–Integral–Derivative (PID) controller.
Definition pid.hpp:255
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:604
double cmd_unsat_
Definition pid.hpp:638
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:329
double p_error_
Definition pid.hpp:634
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:333
double d_error_
Definition pid.hpp:635
Gains get_gains_rt()
Get PID gains for the controller.
Definition pid.hpp:448
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:637
double i_term_
Definition pid.hpp:636
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:167
double tracking_time_constant
Definition pid.hpp:165
double i_max
Definition pid.hpp:160
double i_min
Definition pid.hpp:161
Store gains in a struct to allow easier realtime box usage.
Definition pid.hpp:261
double d_gain_
Definition pid.hpp:339
double i_min_
Definition pid.hpp:342
double i_gain_
Definition pid.hpp:338
AntiWindupStrategy antiwindup_strat_
Definition pid.hpp:346
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:275
double u_min_
Definition pid.hpp:345
double u_max_
Definition pid.hpp:344
double i_max_
Definition pid.hpp:340
double p_gain_
Definition pid.hpp:337