Skip to content

Commit 1640afa

Browse files
mergify[bot]saikishorchristophfroehlich
authored
Use RealtimeThreadSafeBox for PID class (backport #387) (#413)
Co-authored-by: Sai Kishor Kothakota <[email protected]> Co-authored-by: Christoph Fröhlich <[email protected]>
1 parent 04d4d10 commit 1640afa

File tree

4 files changed

+82
-39
lines changed

4 files changed

+82
-39
lines changed

control_toolbox/include/control_toolbox/pid.hpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
#include "fmt/format.h"
4343
#include "rclcpp/duration.hpp"
44-
#include "realtime_tools/realtime_buffer.hpp"
44+
#include "realtime_tools/realtime_thread_safe_box.hpp"
4545

4646
namespace control_toolbox
4747
{
@@ -273,7 +273,7 @@ class Pid
273273
{
274274
public:
275275
/*!
276-
* \brief Store gains in a struct to allow easier realtime buffer usage
276+
* \brief Store gains in a struct to allow easier realtime box usage
277277
*/
278278
struct Gains
279279
{
@@ -560,6 +560,8 @@ class Pid
560560
* \param d The derivative gain.
561561
* \param i_max Upper integral clamp.
562562
* \param i_min Lower integral clamp.
563+
*
564+
* \note This method is not RT safe
563565
*/
564566
void get_gains(double & p, double & i, double & d, double & i_max, double & i_min);
565567

@@ -585,6 +587,8 @@ class Pid
585587
the integral error to prevent windup; otherwise, constrains the
586588
integral contribution to the control output. i_max and
587589
i_min are applied in both scenarios.
590+
*
591+
* \note This method is not RT safe
588592
*/
589593
[[deprecated("Use get_gains overload with AntiWindupStrategy argument.")]]
590594
void get_gains(
@@ -615,6 +619,8 @@ class Pid
615619
* \param antiwindup_strat Specifies the anti-windup strategy. Options: 'back_calculation',
616620
'conditional_integration', or 'none'. Note that the 'back_calculation' strategy use the
617621
tracking_time_constant parameter to tune the anti-windup behavior.
622+
*
623+
* \note This method is not RT safe
618624
*/
619625
void get_gains(
620626
double & p, double & i, double & d, double & u_max, double & u_min,
@@ -623,15 +629,27 @@ class Pid
623629
/*!
624630
* \brief Get PID gains for the controller.
625631
* \return gains A struct of the PID gain values
632+
*
633+
* \note This method is not RT safe
626634
*/
627635
Gains get_gains();
628636

629637
/*!
630638
* \brief Get PID gains for the controller.
631639
* \return gains A struct of the PID gain values
640+
*
641+
* \note This method is not RT safe
632642
*/
633643
[[deprecated("Use get_gains() instead")]] Gains getGains();
634644

645+
/*!
646+
* \brief Get PID gains for the controller.
647+
* \return gains A struct of the PID gain values
648+
*
649+
* \note This method can be called from the RT loop
650+
*/
651+
Gains get_gains_rt() { return gains_; }
652+
635653
/*!
636654
* \brief Set PID gains for the controller.
637655
* \param p The proportional gain.
@@ -646,6 +664,7 @@ class Pid
646664
* \return True if all parameters are successfully set, False otherwise.
647665
*
648666
* \note New gains are not applied if i_min > i_max
667+
* \note This method is not RT safe
649668
*/
650669
[[deprecated("Use set_gains with AntiWindupStrategy instead.")]]
651670
bool set_gains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false);
@@ -681,6 +700,7 @@ class Pid
681700
* \return True if all parameters are successfully set, False otherwise.
682701
*
683702
* \note New gains are not applied if i_min_ > i_max_ or u_min > u_max
703+
* \note This method is not RT safe
684704
*/
685705
bool set_gains(
686706
double p, double i, double d, double u_max, double u_min,
@@ -692,6 +712,7 @@ class Pid
692712
* \return True if all parameters are successfully set, False otherwise.
693713
*
694714
* \note New gains are not applied if gains.i_min_ > gains.i_max_
715+
* \note This method is not RT safe
695716
*/
696717
bool set_gains(const Gains & gains);
697718

@@ -885,8 +906,8 @@ class Pid
885906
return *this;
886907
}
887908

888-
// Copy the realtime buffer to then new PID class
889-
gains_buffer_ = source.gains_buffer_;
909+
// Copy the realtime box to then new PID class
910+
gains_box_ = source.gains_box_;
890911

891912
// Reset the state of this PID controller
892913
reset();
@@ -895,9 +916,17 @@ class Pid
895916
}
896917

897918
protected:
898-
// Store the PID gains in a realtime buffer to allow dynamic reconfigure to update it without
919+
// local copy of the gains for the RT loop
920+
Gains gains_{
921+
0.0,
922+
0.0,
923+
0.0,
924+
std::numeric_limits<double>::infinity(),
925+
-std::numeric_limits<double>::infinity(),
926+
AntiWindupStrategy()};
927+
// Store the PID gains in a realtime box to allow dynamic reconfigure to update it without
899928
// blocking the realtime update loop
900-
realtime_tools::RealtimeBuffer<Gains> gains_buffer_;
929+
realtime_tools::RealtimeThreadSafeBox<Gains> gains_box_{gains_};
901930

902931
double p_error_last_ = 0; /** Save state for derivative state calculation. */
903932
double p_error_ = 0; /** Error. */

control_toolbox/include/control_toolbox/pid_ros.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ class PidROS
257257
/*!
258258
* \brief Get PID gains for the controller.
259259
* \return gains A struct of the PID gain values
260+
*
261+
* \note This method is not RT safe
260262
*/
261263
Pid::Gains get_gains();
262264

@@ -280,6 +282,7 @@ class PidROS
280282
* \return True if all parameters are successfully set, False otherwise.
281283
*
282284
* \note New gains are not applied if i_min > i_max
285+
* \note This method is not RT safe
283286
*/
284287
[[deprecated("Use set_gains with AntiWindupStrategy instead.")]]
285288
bool set_gains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false);
@@ -298,6 +301,7 @@ class PidROS
298301
* \return True if all parameters are successfully set, False otherwise.
299302
*
300303
* \note New gains are not applied if u_min_ > u_max_.
304+
* \note This method is not RT safe
301305
*/
302306
bool set_gains(
303307
double p, double i, double d, double u_max, double u_min,
@@ -326,6 +330,7 @@ class PidROS
326330
* \return True if all parameters are successfully set, False otherwise.
327331
*
328332
* \note New gains are not applied if gains.i_min_ > gains.i_max_
333+
* \note This method is not RT safe
329334
*/
330335
bool set_gains(const Pid::Gains & gains);
331336

control_toolbox/src/pid.cpp

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ constexpr double UMAX_INFINITY = std::numeric_limits<double>::infinity();
5454
#pragma GCC diagnostic push
5555
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
5656
Pid::Pid(double p, double i, double d, double i_max, double i_min, bool antiwindup)
57-
: gains_buffer_()
5857
{
5958
if (i_min > i_max)
6059
{
@@ -77,7 +76,6 @@ Pid::Pid(double p, double i, double d, double i_max, double i_min, bool antiwind
7776
Pid::Pid(
7877
double p, double i, double d, double u_max, double u_min,
7978
const AntiWindupStrategy & antiwindup_strat)
80-
: gains_buffer_()
8179
{
8280
if (u_min > u_max)
8381
{
@@ -93,8 +91,8 @@ Pid::Pid(
9391

9492
Pid::Pid(const Pid & source)
9593
{
96-
// Copy the realtime buffer to the new PID class
97-
gains_buffer_ = source.gains_buffer_;
94+
// Copy the realtime box to the new PID class
95+
gains_box_ = source.gains_box_;
9896

9997
// Initialize saved i-term values
10098
clear_saved_iterm();
@@ -147,6 +145,9 @@ void Pid::reset(bool save_i_term)
147145
{
148146
clear_saved_iterm();
149147
}
148+
149+
// blocking, as reset() is not called in the RT thread
150+
gains_ = gains_box_.get();
150151
}
151152

152153
void Pid::clear_saved_iterm() { i_term_ = 0.0; }
@@ -177,8 +178,7 @@ void Pid::get_gains(
177178
double & p, double & i, double & d, double & u_max, double & u_min,
178179
AntiWindupStrategy & antiwindup_strat)
179180
{
180-
Gains gains = *gains_buffer_.readFromRT();
181-
181+
Gains gains = get_gains();
182182
p = gains.p_gain_;
183183
i = gains.i_gain_;
184184
d = gains.d_gain_;
@@ -187,7 +187,11 @@ void Pid::get_gains(
187187
antiwindup_strat = gains.antiwindup_strat_;
188188
}
189189

190-
Pid::Gains Pid::get_gains() { return *gains_buffer_.readFromRT(); }
190+
Pid::Gains Pid::get_gains()
191+
{
192+
// blocking, as get_gains() is called from non-RT thread
193+
return gains_box_.get();
194+
}
191195

192196
#pragma GCC diagnostic push
193197
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -253,7 +257,8 @@ bool Pid::set_gains(const Gains & gains_in)
253257
gains.antiwindup_strat_.tracking_time_constant = gains.p_gain_ / gains.i_gain_;
254258
}
255259
}
256-
gains_buffer_.writeFromNonRT(gains);
260+
// blocking, as set_gains() is called from non-RT thread
261+
gains_box_.set(gains);
257262
return true;
258263
}
259264
return false;
@@ -326,8 +331,12 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
326331
{
327332
throw std::invalid_argument("Pid is called with negative dt");
328333
}
329-
// Get the gain parameters from the realtime buffer
330-
Gains gains = *gains_buffer_.readFromRT();
334+
// Get the gain parameters from the realtime box
335+
auto gains_opt = gains_box_.try_get();
336+
if (gains_opt.has_value())
337+
{
338+
gains_ = gains_opt.value();
339+
}
331340

332341
double p_term, d_term;
333342
p_error_ = error; // This is error = target - state
@@ -341,57 +350,58 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
341350
}
342351

343352
// Calculate proportional contribution to command
344-
p_term = gains.p_gain_ * p_error_;
353+
p_term = gains_.p_gain_ * p_error_;
345354

346355
// Calculate derivative contribution to command
347-
d_term = gains.d_gain_ * d_error_;
356+
d_term = gains_.d_gain_ * d_error_;
348357

349-
if (gains.antiwindup_strat_.type == AntiWindupStrategy::UNDEFINED)
358+
if (gains_.antiwindup_strat_.type == AntiWindupStrategy::UNDEFINED)
350359
{
351360
throw std::runtime_error(
352361
"PID: Antiwindup strategy cannot be UNDEFINED. Please set a valid antiwindup strategy.");
353362
}
354363

355364
// Calculate integral contribution to command
356365
const bool is_error_in_deadband_zone =
357-
control_toolbox::is_zero(error, gains.antiwindup_strat_.error_deadband);
358-
if (!is_error_in_deadband_zone && gains.antiwindup_strat_.type == AntiWindupStrategy::LEGACY)
366+
control_toolbox::is_zero(error, gains_.antiwindup_strat_.error_deadband);
367+
if (!is_error_in_deadband_zone && gains_.antiwindup_strat_.type == AntiWindupStrategy::LEGACY)
359368
{
360-
if (gains.antiwindup_strat_.legacy_antiwindup)
369+
if (gains_.antiwindup_strat_.legacy_antiwindup)
361370
{
362371
// Prevent i_term_ from climbing higher than permitted by i_max_/i_min_
363-
i_term_ = std::clamp(i_term_ + gains.i_gain_ * dt_s * p_error_, gains.i_min_, gains.i_max_);
372+
i_term_ =
373+
std::clamp(i_term_ + gains_.i_gain_ * dt_s * p_error_, gains_.i_min_, gains_.i_max_);
364374
}
365375
else
366376
{
367-
i_term_ += gains.i_gain_ * dt_s * p_error_;
377+
i_term_ += gains_.i_gain_ * dt_s * p_error_;
368378
}
369379
}
370380

371381
// Compute the command
372382
if (
373-
!gains.antiwindup_strat_.legacy_antiwindup &&
374-
gains.antiwindup_strat_.type == AntiWindupStrategy::LEGACY)
383+
!gains_.antiwindup_strat_.legacy_antiwindup &&
384+
gains_.antiwindup_strat_.type == AntiWindupStrategy::LEGACY)
375385
{
376386
// Limit i_term so that the limit is meaningful in the output
377-
cmd_unsat_ = p_term + std::clamp(i_term_, gains.i_min_, gains.i_max_) + d_term;
387+
cmd_unsat_ = p_term + std::clamp(i_term_, gains_.i_min_, gains_.i_max_) + d_term;
378388
}
379389
else
380390
{
381391
cmd_unsat_ = p_term + i_term_ + d_term;
382392
}
383393

384-
if (std::isfinite(gains.u_min_) || std::isfinite(gains.u_max_))
394+
if (std::isfinite(gains_.u_min_) || std::isfinite(gains_.u_max_))
385395
{
386-
if (gains.u_min_ > gains.u_max_)
396+
if (gains_.u_min_ > gains_.u_max_)
387397
{
388398
throw std::runtime_error("Pid: Error while saturating the command : u_min > u_max");
389399
}
390-
if (std::isnan(gains.u_min_) || std::isnan(gains.u_max_))
400+
if (std::isnan(gains_.u_min_) || std::isnan(gains_.u_max_))
391401
{
392402
throw std::runtime_error("Pid: Error while saturating the command : u_min or u_max is NaN");
393403
}
394-
cmd_ = std::clamp(cmd_unsat_, gains.u_min_, gains.u_max_);
404+
cmd_ = std::clamp(cmd_unsat_, gains_.u_min_, gains_.u_max_);
395405
}
396406
else
397407
{
@@ -401,17 +411,17 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
401411
if (!is_error_in_deadband_zone)
402412
{
403413
if (
404-
gains.antiwindup_strat_.type == AntiWindupStrategy::BACK_CALCULATION &&
405-
!is_zero(gains.i_gain_))
414+
gains_.antiwindup_strat_.type == AntiWindupStrategy::BACK_CALCULATION &&
415+
!is_zero(gains_.i_gain_))
406416
{
407-
i_term_ += dt_s * (gains.i_gain_ * error +
408-
1 / gains.antiwindup_strat_.tracking_time_constant * (cmd_ - cmd_unsat_));
417+
i_term_ += dt_s * (gains_.i_gain_ * error +
418+
1 / gains_.antiwindup_strat_.tracking_time_constant * (cmd_ - cmd_unsat_));
409419
}
410-
else if (gains.antiwindup_strat_.type == AntiWindupStrategy::CONDITIONAL_INTEGRATION)
420+
else if (gains_.antiwindup_strat_.type == AntiWindupStrategy::CONDITIONAL_INTEGRATION)
411421
{
412422
if (!(!is_zero(cmd_unsat_ - cmd_) && error * cmd_unsat_ > 0))
413423
{
414-
i_term_ += dt_s * gains.i_gain_ * error;
424+
i_term_ += dt_s * gains_.i_gain_ * error;
415425
}
416426
}
417427
}

control_toolbox/src/pid_ros.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ bool PidROS::set_gains(const Pid::Gains & gains)
453453

454454
void PidROS::publish_pid_state(double cmd, double error, rclcpp::Duration dt)
455455
{
456-
Pid::Gains gains = pid_.get_gains();
456+
Pid::Gains gains = pid_.get_gains_rt();
457457

458458
double p_error, i_term, d_error;
459459
get_current_pid_errors(p_error, i_term, d_error);
@@ -525,7 +525,6 @@ void PidROS::set_parameter_event_callback()
525525
rcl_interfaces::msg::SetParametersResult result;
526526
result.successful = true;
527527

528-
/// @note don't use getGains, it's rt
529528
Pid::Gains gains = pid_.get_gains();
530529
bool changed = false;
531530
// The saturation parameter is special, it can change the u_min and u_max parameters

0 commit comments

Comments
 (0)