Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PID] Add support for saving i-term when PID is reset #180

Open
wants to merge 8 commits into
base: ros2-master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 54 additions & 9 deletions include/control_toolbox/pid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ namespace control_toolbox
be subclassed to provide more specific controls
based on a particular control loop.

This class also allows for retention of integral
term on reset. This is useful for control loops
that are enabled/disabled with a constant steady-state
external disturbance. Once the integrator cancels
out the external disturbance, disabling/resetting/
re-enabling closed-loop control does not require
the integrator to wind up again.

In particular, this class implements the standard
pid equation:

Expand All @@ -83,6 +91,8 @@ namespace control_toolbox

\param i_clamp Min/max bounds for the integral windup, the clamp is applied to the \f$i_{term}\f$

\param save_iterm boolean indicating if integral term is retained on reset()

\section Usage

To use the Pid class, you should first call some version of init()
Expand Down Expand Up @@ -114,7 +124,7 @@ class CONTROL_TOOLBOX_PUBLIC Pid
struct Gains
{
/*!
* \brief Optional constructor for passing in values without antiwindup
* \brief Optional constructor for passing in values without antiwindup and save i-term
*
* \param p The proportional gain.
* \param i The integral gain.
Expand All @@ -125,36 +135,61 @@ class CONTROL_TOOLBOX_PUBLIC Pid
* \throws An std::invalid_argument exception is thrown if i_min > i_max
*/
Gains(double p, double i, double d, double i_max, double i_min)
: p_gain_(p), i_gain_(i), d_gain_(d), i_max_(i_max), i_min_(i_min), antiwindup_(true)
: p_gain_(p), i_gain_(i), d_gain_(d), i_max_(i_max), i_min_(i_min), antiwindup_(false),
save_iterm_(false)
{
}

/*!
* \brief Optional constructor for passing in values
* \brief Optional constructor for passing in values without save i-term
*
* \param p The proportional gain.
* \param i The integral gain.
* \param d The derivative gain.
* \param i_max The max integral windup.
* \param i_min The min integral windup.
* \param antiwindup If true, antiwindup is enabled and i_max/i_min are enforced
* \param antiwindup If true, antiwindup is enabled and i_max/i_min are enforced.
*
* \throws An std::invalid_argument exception is thrown if i_min > i_max
*/
Gains(double p, double i, double d, double i_max, double i_min, bool antiwindup)
: p_gain_(p), i_gain_(i), d_gain_(d), i_max_(i_max), i_min_(i_min), antiwindup_(antiwindup)
: p_gain_(p), i_gain_(i), d_gain_(d), i_max_(i_max), i_min_(i_min), antiwindup_(antiwindup),
save_iterm_(false)
{
}

/*!
* \brief Optional constructor for passing in values
*
* \param p The proportional gain.
* \param i The integral gain.
* \param d The derivative gain.
* \param i_max The max integral windup.
* \param i_min The min integral windup.
* \param antiwindup If true, antiwindup is enabled and i_max/i_min are enforced.
* \param save_iterm If true, save integral term when PID is reset.
*
* \throws An std::invalid_argument exception is thrown if i_min > i_max
*/
Gains(double p, double i, double d, double i_max, double i_min, bool antiwindup,
bool save_iterm) : p_gain_(p), i_gain_(i), d_gain_(d), i_max_(i_max), i_min_(i_min),
antiwindup_(antiwindup), save_iterm_(save_iterm)
{
}

// Default constructor
Gains() : p_gain_(0.0), i_gain_(0.0), d_gain_(0.0), i_max_(0.0), i_min_(0.0), antiwindup_(false)
Gains() : p_gain_(0.0), i_gain_(0.0), d_gain_(0.0), i_max_(0.0), i_min_(0.0),
antiwindup_(false), save_iterm_(false)
{
}

double p_gain_; /**< Proportional gain. */
double i_gain_; /**< Integral gain. */
double d_gain_; /**< Derivative gain. */
double i_max_; /**< Maximum allowable integral term. */
double i_min_; /**< Minimum allowable integral term. */
bool antiwindup_; /**< Antiwindup. */
bool save_iterm_; /**< Save integral term. */
};

/*!
Expand All @@ -173,7 +208,7 @@ class CONTROL_TOOLBOX_PUBLIC Pid
*/
Pid(
double p = 0.0, double i = 0.0, double d = 0.0, double i_max = 0.0, double i_min = -0.0,
bool antiwindup = false);
bool antiwindup = false, bool save_iterm = false);

/**
* \brief Copy constructor required for preventing mutexes from being copied
Expand All @@ -199,13 +234,19 @@ class CONTROL_TOOLBOX_PUBLIC Pid
*
* \note New gains are not applied if i_min_ > i_max_
*/
void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup = false);
void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup = false,
bool save_iterm = false);

/*!
* \brief Reset the state of this PID controller
*/
void reset();

/*!
* \brief Clear the saved integrator output of this controller
*/
void clear_saved_iterm();

/*!
* \brief Get PID gains for the controller.
* \param p The proportional gain.
Expand All @@ -226,6 +267,9 @@ class CONTROL_TOOLBOX_PUBLIC Pid
*/
void getGains(
double & p, double & i, double & d, double & i_max, double & i_min, bool & antiwindup);
void getGains(
double & p, double & i, double & d, double & i_max, double & i_min, bool & antiwindup,
bool & save_iterm);

/*!
* \brief Get PID gains for the controller.
Expand All @@ -244,7 +288,8 @@ class CONTROL_TOOLBOX_PUBLIC Pid
*
* \note New gains are not applied if i_min > i_max
*/
void setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false);
void setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false,
bool save_iterm = false);

/*!
* \brief Set PID gains for the controller.
Expand Down
8 changes: 6 additions & 2 deletions include/control_toolbox/pid_ros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,12 @@ class CONTROL_TOOLBOX_PUBLIC PidROS
* \param i_max The max integral windup.
* \param i_min The min integral windup.
* \param antiwindup antiwindup.
* \param save_iterm save integrator output between resets.
*
* \note New gains are not applied if i_min_ > i_max_
*/
void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup);
void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup,
bool save_iterm = false);

/*!
* \brief Initialize the PID controller based on already set parameters
Expand Down Expand Up @@ -153,10 +155,12 @@ class CONTROL_TOOLBOX_PUBLIC PidROS
* \param i_max The max integral windup.
* \param i_min The min integral windup.
* \param antiwindup antiwindup.
* \param save_iterm save integrator output between resets.
*
* \note New gains are not applied if i_min > i_max
*/
void setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false);
void setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false,
bool save_iterm = false);

/*!
* \brief Set PID gains for the controller.
Expand Down
45 changes: 35 additions & 10 deletions src/pid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,16 @@

namespace control_toolbox
{
Pid::Pid(double p, double i, double d, double i_max, double i_min, bool antiwindup)
Pid::Pid(double p, double i, double d, double i_max, double i_min, bool antiwindup, bool save_iterm)
: gains_buffer_()
{
if (i_min > i_max) {
throw std::invalid_argument("received i_min > i_max");
}
setGains(p, i, d, i_max, i_min, antiwindup);
setGains(p, i, d, i_max, i_min, antiwindup, save_iterm);

// Initialize saved i-term values
clear_saved_iterm();

reset();
}
Expand All @@ -62,15 +65,19 @@ Pid::Pid(const Pid & source)
// Copy the realtime buffer to the new PID class
gains_buffer_ = source.gains_buffer_;

// Initialize saved i-term values
clear_saved_iterm();

// Reset the state of this PID controller
reset();
}

Pid::~Pid() {}

void Pid::initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup)
void Pid::initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup,
bool save_iterm)
{
setGains(p, i, d, i_max, i_min, antiwindup);
setGains(p, i, d, i_max, i_min, antiwindup, save_iterm);

reset();
}
Expand All @@ -79,19 +86,35 @@ void Pid::reset()
{
p_error_last_ = 0.0;
p_error_ = 0.0;
i_error_ = 0.0;
d_error_ = 0.0;
cmd_ = 0.0;

// If last integral error is already zero, just return
if (std::fabs(i_error_) < std::numeric_limits<double>::epsilon())
{
return;
}

// Get the gain parameters from the realtime buffer
Gains gains = *gains_buffer_.readFromRT();
// Check to see if we should reset integral error here
if (!gains.save_iterm_) i_error_ = 0.0;
}

void Pid::clear_saved_iterm()
{
i_error_ = 0.0;
}

void Pid::getGains(double & p, double & i, double & d, double & i_max, double & i_min)
{
bool antiwindup;
getGains(p, i, d, i_max, i_min, antiwindup);
bool antiwindup, save_iterm;
getGains(p, i, d, i_max, i_min, antiwindup, save_iterm);
}

void Pid::getGains(
double & p, double & i, double & d, double & i_max, double & i_min, bool & antiwindup)
double & p, double & i, double & d, double & i_max, double & i_min, bool & antiwindup,
bool & save_iterm)
{
Gains gains = *gains_buffer_.readFromRT();

Expand All @@ -101,13 +124,15 @@ void Pid::getGains(
i_max = gains.i_max_;
i_min = gains.i_min_;
antiwindup = gains.antiwindup_;
save_iterm = gains.save_iterm_;
}

Pid::Gains Pid::getGains() { return *gains_buffer_.readFromRT(); }

void Pid::setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup)
void Pid::setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup,
bool save_iterm)
{
Gains gains(p, i, d, i_max, i_min, antiwindup);
Gains gains(p, i, d, i_max, i_min, antiwindup, save_iterm);

setGains(gains);
}
Expand Down
23 changes: 17 additions & 6 deletions src/pid_ros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ bool PidROS::initPid()
double p, i, d, i_min, i_max;
p = i = d = i_min = i_max = std::numeric_limits<double>::quiet_NaN();
bool antiwindup = false;
bool save_iterm = false;
bool all_params_available = true;
all_params_available &= getDoubleParam(param_prefix_ + "p", p);
all_params_available &= getDoubleParam(param_prefix_ + "i", i);
Expand All @@ -177,12 +178,13 @@ bool PidROS::initPid()
all_params_available &= getDoubleParam(param_prefix_ + "i_clamp_min", i_min);

getBooleanParam(param_prefix_ + "antiwindup", antiwindup);
getBooleanParam(param_prefix_ + "save_iterm", save_iterm);

if (all_params_available) {
setParameterEventCallback();
}

pid_.initPid(p, i, d, i_max, i_min, antiwindup);
pid_.initPid(p, i, d, i_max, i_min, antiwindup, save_iterm);

return all_params_available;
}
Expand All @@ -194,19 +196,21 @@ void PidROS::declareParam(const std::string & param_name, rclcpp::ParameterValue
}
}

void PidROS::initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup)
void PidROS::initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup,
bool save_iterm)
{
if (i_min > i_max) {
RCLCPP_ERROR(node_logging_->get_logger(), "received i_min > i_max, skip new gains");
} else {
pid_.initPid(p, i, d, i_max, i_min, antiwindup);
pid_.initPid(p, i, d, i_max, i_min, antiwindup, save_iterm);

declareParam(param_prefix_ + "p", rclcpp::ParameterValue(p));
declareParam(param_prefix_ + "i", rclcpp::ParameterValue(i));
declareParam(param_prefix_ + "d", rclcpp::ParameterValue(d));
declareParam(param_prefix_ + "i_clamp_max", rclcpp::ParameterValue(i_max));
declareParam(param_prefix_ + "i_clamp_min", rclcpp::ParameterValue(i_min));
declareParam(param_prefix_ + "antiwindup", rclcpp::ParameterValue(antiwindup));
declareParam(param_prefix_ + "save_iterm", rclcpp::ParameterValue(save_iterm));

setParameterEventCallback();
}
Expand Down Expand Up @@ -237,7 +241,8 @@ double PidROS::computeCommand(double error, double error_dot, rclcpp::Duration d

Pid::Gains PidROS::getGains() { return pid_.getGains(); }

void PidROS::setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup)
void PidROS::setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup,
bool save_iterm)
{
if (i_min > i_max) {
RCLCPP_ERROR(node_logging_->get_logger(), "received i_min > i_max, skip new gains");
Expand All @@ -247,9 +252,10 @@ void PidROS::setGains(double p, double i, double d, double i_max, double i_min,
rclcpp::Parameter(param_prefix_ + "d", d),
rclcpp::Parameter(param_prefix_ + "i_clamp_max", i_max),
rclcpp::Parameter(param_prefix_ + "i_clamp_min", i_min),
rclcpp::Parameter(param_prefix_ + "antiwindup", antiwindup)});
rclcpp::Parameter(param_prefix_ + "antiwindup", antiwindup),
rclcpp::Parameter(param_prefix_ + "save_iterm", save_iterm)});

pid_.setGains(p, i, d, i_max, i_min, antiwindup);
pid_.setGains(p, i, d, i_max, i_min, antiwindup, save_iterm);
}
}

Expand Down Expand Up @@ -309,6 +315,8 @@ void PidROS::printValues()
<< " I_Min: " << gains.i_min_ << "\n"
<< " Antiwindup: " << gains.antiwindup_
<< "\n"
<< " Save I-Term: " << gains.save_iterm_
<< "\n"
<< " P_Error: " << p_error_ << "\n"
<< " I_Error: " << i_error_ << "\n"
<< " D_Error: " << d_error_ << "\n"
Expand Down Expand Up @@ -355,6 +363,9 @@ void PidROS::setParameterEventCallback()
} else if (param_name == param_prefix_ + "antiwindup") {
gains.antiwindup_ = parameter.get_value<bool>();
changed = true;
} else if (param_name == param_prefix_ + "save_iterm") {
gains.save_iterm_ = parameter.get_value<bool>();
changed = true;
}
} catch (const rclcpp::exceptions::InvalidParameterTypeException & e) {
RCLCPP_INFO_STREAM(node_logging_->get_logger(), "Please use the right type: " << e.what());
Expand Down
Loading
Loading