From 205a1e24b256264423881235c4b3857b983002b9 Mon Sep 17 00:00:00 2001 From: ahcorde Date: Wed, 17 Jun 2020 17:59:00 +0200 Subject: [PATCH] Refactor the Pid class to be completely ROS agnostic and added a ROS 2 wrapper Signed-off-by: ahcorde --- CMakeLists.txt | 1 + include/control_toolbox/pid.hpp | 87 ++------- include/control_toolbox/pidROS.hpp | 176 +++++++++++++++++ src/pid.cpp | 171 ++--------------- src/pidROS.cpp | 299 +++++++++++++++++++++++++++++ test/pid_parameters_tests.cpp | 130 ++++++++----- test/pid_publisher_tests.cpp | 19 +- test/pid_tests.cpp | 76 ++++---- 8 files changed, 631 insertions(+), 328 deletions(-) create mode 100644 include/control_toolbox/pidROS.hpp create mode 100644 src/pidROS.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cfebe4e9..f433f1a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(${PROJECT_NAME} SHARED src/dither.cpp src/limited_proxy.cpp src/pid.cpp + src/pidROS.cpp src/sine_sweep.cpp src/sinusoid.cpp) target_include_directories(${PROJECT_NAME} PUBLIC include) diff --git a/include/control_toolbox/pid.hpp b/include/control_toolbox/pid.hpp index 7159ed6a..6ca071ce 100644 --- a/include/control_toolbox/pid.hpp +++ b/include/control_toolbox/pid.hpp @@ -76,8 +76,6 @@ namespace control_toolbox
  • \f$ p_{error} = p_{state} - p_{target} \f$. - \section ROS ROS interface - \param p Proportional gain \param d Derivative gain @@ -86,8 +84,6 @@ namespace control_toolbox \param i_clamp Min/max bounds for the integral windup, the clamp is applied to the \f$i_{term}\f$ - \param publish_state Enable publishing internal controller state on the `state` topic. May break real-time guarantees due to clock_gettime system call. - \section Usage To use the Pid class, you should first call some version of init() @@ -110,10 +106,6 @@ namespace control_toolbox */ /***************************************************/ -// alias for convenience -using NodeParamsIfacePtr = rclcpp::node_interfaces::NodeParametersInterface::SharedPtr; -using NodePtr = rclcpp::Node::SharedPtr; - class Pid { public: @@ -150,9 +142,9 @@ class Pid * initialize Pid-gains and integral term limits. * Does not initialize dynamic reconfigure for PID gains * - * \param p The proportional gain. - * \param i The integral gain. - * \param d The derivative gain. + * \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. */ @@ -175,39 +167,14 @@ class Pid * \brief Zeros out Pid values and initialize Pid-gains and integral term limits * Does not initialize the node's parameter interface for PID gains * - * \param p The proportional gain. - * \param i The integral gain. - * \param d The derivative gain. + * \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. */ void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup = false); - /*! - * \brief Zeros out Pid values and initialize Pid-gains and integral term limits - * Initializes the node's parameter interface for PID gains - * - * \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. - */ - void initPid( - double p, double i, double d, double i_max, double i_min, NodeParamsIfacePtr node_param_iface); - - void initPid( - double p, double i, double d, double i_max, double i_min, bool antiwindup, - NodeParamsIfacePtr node_param_iface); - - /*! - * \brief Initialize real-time pid state publisher - * - * \param node A pointer to the node. - */ - - void initPublisher(NodePtr node, std::string topic_prefix = ""); - /*! * \brief Reset the state of this PID controller */ @@ -215,9 +182,9 @@ class Pid /*! * \brief Get PID gains for the controller. - * \param p The proportional gain. - * \param i The integral gain. - * \param d The derivative gain. + * \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. */ @@ -233,9 +200,9 @@ class Pid /*! * \brief Set PID gains for the controller. - * \param p The proportional gain. - * \param i The integral gain. - * \param d The derivative gain. + * \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. */ @@ -253,11 +220,11 @@ class Pid * and the timestep \c dt. * * \param error Error since last call (error = target - state) - * \param dt Change in time since last call + * \param dt Change in time since last call in seconds * * \returns PID command */ - double computeCommand(double error, rclcpp::Duration dt); + double computeCommand(double error, double dt); /*! * \brief Set the PID error and compute the PID command with nonuniform @@ -266,11 +233,11 @@ class Pid * * \param error Error since last call (error = target - state) * \param error_dot d(Error)/dt since last call - * \param dt Change in time since last call + * \param dt Change in time since last call in seconds * * \returns PID command */ - double computeCommand(double error, double error_dot, rclcpp::Duration dt); + double computeCommand(double error, double error_dot, double dt); /*! * \brief Set current command for this PID controller @@ -288,12 +255,7 @@ class Pid * \param ie The integral error. * \param de The derivative error. */ - void getCurrentPIDErrors(double * pe, double * ie, double * de); - - /*! - * \brief Print to console the current parameters - */ - void printValues(const rclcpp::Logger & logger); + void getCurrentPIDErrors(double & pe, double & ie, double & de); /*! * @brief Custom assignment operator @@ -315,30 +277,15 @@ class Pid } private: - void setParameterEventCallback(); - -private: - using ClockPtr = rclcpp::Clock::SharedPtr; - using PidStateMsg = control_msgs::msg::PidState; - // Store the PID gains in a realtime buffer to allow dynamic reconfigure to update it without // blocking the realtime update loop realtime_tools::RealtimeBuffer gains_buffer_; - std::shared_ptr> state_pub_; - std::shared_ptr> rt_state_pub_; - ClockPtr clock_; - double p_error_last_; /**< _Save position state for derivative state calculation. */ double p_error_; /**< Position error. */ double i_error_; /**< Integral of position error. */ double d_error_; /**< Derivative of position error. */ double cmd_; /**< Command to send. */ - - NodeParamsIfacePtr node_param_iface_; - - using OnSetParamsCallbackPtr = rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr; - OnSetParamsCallbackPtr parameter_callback_; }; } // namespace control_toolbox diff --git a/include/control_toolbox/pidROS.hpp b/include/control_toolbox/pidROS.hpp new file mode 100644 index 00000000..db8a47e8 --- /dev/null +++ b/include/control_toolbox/pidROS.hpp @@ -0,0 +1,176 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2008, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Willow Garage nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ +#ifndef CONTROL_TOOLBOX__PIDROS_HPP_ +#define CONTROL_TOOLBOX__PIDROS_HPP_ + +#include +#include + +#include "control_msgs/msg/pid_state.hpp" + +#include "rclcpp/clock.hpp" +#include "rclcpp/duration.hpp" +#include "rclcpp/node.hpp" + +#include "realtime_tools/realtime_buffer.h" +#include "realtime_tools/realtime_publisher.h" + +#include "control_toolbox/pid.hpp" + + +namespace control_toolbox +{ + +class PidROS +{ +public: + /*! + * \brief Destructor of PidROS class. + * + * The node is passed to this class to handler the ROS parameters, this class allows + * to add a prefix to the pid parameters + * + * \param node ROS node + * \param topic_prefix prefix to add to the pid parameters. + */ + explicit PidROS(rclcpp::Node::SharedPtr & node, std::string topic_prefix = std::string("")); + + /*! + * \brief Destructor of PidROS class. + */ + ~PidROS(); + + /*! + * \brief Initialize the PID controller and set the paramaters + * \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. + */ + void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup); + + /*! + * \brief Initialize the PID controller based on already setted parameters + * \return True is all parameters are set (p, i, d, i_min and i_max), False otherwise + */ + bool initPid(); + + /*! + * \brief Reset the state of this PID controller + */ + void reset(); + + /*! + * \brief Set the PID error and compute the PID command with nonuniform time + * step size. The derivative error is computed from the change in the error + * and the timestep \c dt. + * + * \param error Error since last call (error = target - state) + * \param dt Change in time since last call in seconds + * + * \returns PID command + */ + double computeCommand(double error, rclcpp::Duration dt); + + /*! + * \brief Get PID gains for the controller. + * \return gains A struct of the PID gain values + */ + Pid::Gains getGains(); + + /*! + * \brief Set PID gains for the controller. + * \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. + */ + void setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup = false); + + /*! + * \brief Set PID gains for the controller. + * \param gains A struct of the PID gain values + */ + void setGains(const Pid::Gains & gains); + + /*! + * \brief Set current command for this PID controller + */ + void setCurrentCmd(double cmd); + + /*! + * \brief Return current command for this PID controller + */ + double getCurrentCmd(); + + /*! + * \brief Return PID error terms for the controller. + * \param pe[out] The proportional error. + * \param ie[out] The integral error. + * \param de[out] The derivative error. + */ + void getCurrentPIDErrors(double & pe, double & ie, double & de); + + /*! + * \brief Print to console the current parameters + */ + void printValues(); + +private: + void setParameterEventCallback(); + + void publishPIDState(double cmd, double error, rclcpp::Duration dt); + + void declare_param(const std::string & param_name, rclcpp::ParameterValue param_value); + + bool getDoubleParam(const std::string & param_name, double & value); + + bool getBooleanParam(const std::string & param_name, bool & value); + + rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr parameter_callback_; + + rclcpp::Node::SharedPtr node_; + + std::shared_ptr> rt_state_pub_; + std::shared_ptr> state_pub_; + + Pid pid_; + std::string topic_prefix_; +}; + +} // namespace control_toolbox + +#endif // CONTROL_TOOLBOX__PIDROS_HPP_ diff --git a/src/pid.cpp b/src/pid.cpp index 8e727bed..bb3a5c0a 100644 --- a/src/pid.cpp +++ b/src/pid.cpp @@ -61,8 +61,7 @@ T clamp(T val, T low, T high) } Pid::Pid(double p, double i, double d, double i_max, double i_min, bool antiwindup) -: gains_buffer_(), - node_param_iface_(nullptr), parameter_callback_(nullptr) +: gains_buffer_() { setGains(p, i, d, i_max, i_min, antiwindup); @@ -80,41 +79,6 @@ Pid::Pid(const Pid & source) Pid::~Pid() {} -void Pid::initPid( - double p, double i, double d, double i_max, double i_min, NodeParamsIfacePtr node_param_iface) -{ - const Pid::Gains gains = getGains(); - initPid(p, i, d, i_max, i_min, gains.antiwindup_, node_param_iface); -} - -void Pid::initPid( - double p, double i, double d, double i_max, double i_min, bool antiwindup, - NodeParamsIfacePtr node_param_iface) -{ - initPid(p, i, d, i_max, i_min, antiwindup); - - node_param_iface_ = node_param_iface; - - // declare parameters if necessary - if (node_param_iface_) { - auto declare_param = [this]( - const std::string & param_name, rclcpp::ParameterValue param_value) { - if (!node_param_iface_->has_parameter(param_name)) { - node_param_iface_->declare_parameter(param_name, param_value); - } - }; - - declare_param("p", rclcpp::ParameterValue(p)); - declare_param("i", rclcpp::ParameterValue(i)); - declare_param("d", rclcpp::ParameterValue(d)); - declare_param("i_clamp_max", rclcpp::ParameterValue(i_max)); - declare_param("i_clamp_min", rclcpp::ParameterValue(i_min)); - declare_param("antiwindup", rclcpp::ParameterValue(antiwindup)); - } - - setParameterEventCallback(); -} - void Pid::initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup) { setGains(p, i, d, i_max, i_min, antiwindup); @@ -122,18 +86,6 @@ void Pid::initPid(double p, double i, double d, double i_max, double i_min, bool reset(); } -void Pid::initPublisher(NodePtr node, std::string topic_prefix) -{ - rclcpp::QoS qos(10); - qos.reliable().transient_local(); - - state_pub_ = node->create_publisher(topic_prefix + "/pid_state", qos); - rt_state_pub_.reset( - new realtime_tools::RealtimePublisher(state_pub_)); - - clock_ = node->get_clock(); -} - void Pid::reset() { p_error_last_ = 0.0; @@ -177,35 +129,26 @@ void Pid::setGains(double p, double i, double d, double i_max, double i_min, boo void Pid::setGains(const Gains & gains) { gains_buffer_.writeFromNonRT(gains); - - // update node parameters - if (node_param_iface_) { - node_param_iface_->set_parameters( - {rclcpp::Parameter("p", gains.p_gain_), rclcpp::Parameter("i", gains.i_gain_), - rclcpp::Parameter("d", gains.d_gain_), rclcpp::Parameter("i_clamp_max", gains.i_max_), - rclcpp::Parameter("i_clamp_min", gains.i_min_), - rclcpp::Parameter("antiwindup", gains.antiwindup_)}); - } } -double Pid::computeCommand(double error, rclcpp::Duration dt) +double Pid::computeCommand(double error, double dt) { - if (dt == rclcpp::Duration(0, 0) || std::isnan(error) || std::isinf(error)) { + if (dt == 0.0 || std::isnan(error) || std::isinf(error)) { return 0.0; } double error_dot = d_error_; // Calculate the derivative error - if (dt.seconds() > 0.0) { - error_dot = (error - p_error_last_) / dt.seconds(); + if (dt > 0.0) { + error_dot = (error - p_error_last_) / dt; p_error_last_ = error; } return computeCommand(error, error_dot, dt); } -double Pid::computeCommand(double error, double error_dot, rclcpp::Duration dt) +double Pid::computeCommand(double error, double error_dot, double dt) { // Get the gain parameters from the realtime buffer Gains gains = *gains_buffer_.readFromRT(); @@ -215,7 +158,7 @@ double Pid::computeCommand(double error, double error_dot, rclcpp::Duration dt) d_error_ = error_dot; if ( - dt == rclcpp::Duration(0, 0) || std::isnan(error) || std::isinf(error) || + dt == 0.0 || std::isnan(error) || std::isinf(error) || std::isnan(error_dot) || std::isinf(error_dot)) { return 0.0; @@ -225,7 +168,7 @@ double Pid::computeCommand(double error, double error_dot, rclcpp::Duration dt) p_term = gains.p_gain_ * p_error_; // Calculate the integral of the position error - i_error_ += dt.seconds() * p_error_; + i_error_ += dt * p_error_; if (gains.antiwindup_ && gains.i_gain_ != 0) { // Prevent i_error_ from climbing higher than permitted by i_max_/i_min_ @@ -248,26 +191,6 @@ double Pid::computeCommand(double error, double error_dot, rclcpp::Duration dt) // Compute the command cmd_ = p_term + i_term + d_term; - // Publish controller state if configured - if (rt_state_pub_) { - if (rt_state_pub_->trylock()) { - rt_state_pub_->msg_.header.stamp = clock_->now(); - rt_state_pub_->msg_.timestep = dt; - rt_state_pub_->msg_.error = error; - rt_state_pub_->msg_.error_dot = error_dot; - rt_state_pub_->msg_.p_error = p_error_; - rt_state_pub_->msg_.i_error = i_error_; - rt_state_pub_->msg_.d_error = d_error_; - rt_state_pub_->msg_.p_term = p_term; - rt_state_pub_->msg_.i_term = i_term; - rt_state_pub_->msg_.d_term = d_term; - rt_state_pub_->msg_.i_max = gains.i_max_; - rt_state_pub_->msg_.i_min = gains.i_min_; - rt_state_pub_->msg_.output = cmd_; - rt_state_pub_->unlockAndPublish(); - } - } - return cmd_; } @@ -275,83 +198,13 @@ void Pid::setCurrentCmd(double cmd) {cmd_ = cmd;} double Pid::getCurrentCmd() {return cmd_;} -void Pid::getCurrentPIDErrors(double * pe, double * ie, double * de) +void Pid::getCurrentPIDErrors(double & pe, double & ie, double & de) { // Get the gain parameters from the realtime buffer Gains gains = *gains_buffer_.readFromRT(); - *pe = p_error_; - *ie = i_error_; - *de = d_error_; -} - -void Pid::printValues(const rclcpp::Logger & logger) -{ - Gains gains = getGains(); - - RCLCPP_INFO_STREAM( - logger, - "Current Values of PID Class:\n" << - " P Gain: " << gains.p_gain_ << "\n" << - " I Gain: " << gains.i_gain_ << "\n" << - " D Gain: " << gains.d_gain_ << "\n" << - " I_Max: " << gains.i_max_ << "\n" << - " I_Min: " << gains.i_min_ << "\n" << - " Antiwindup: " << gains.antiwindup_ << "\n" << - " P_Error_Last: " << p_error_last_ << "\n" << - " P_Error: " << p_error_ << "\n" << - " I_Error: " << i_error_ << "\n" << - " D_Error: " << d_error_ << "\n" << - " Command: " << cmd_ - ); -} - -void Pid::setParameterEventCallback() -{ - auto on_parameter_event_callback = [this](const std::vector & parameters) { - rcl_interfaces::msg::SetParametersResult result; - result.successful = true; - - /// @note don't use getGains, it's rt - Gains gains = *gains_buffer_.readFromNonRT(); - - for (auto & parameter : parameters) { - const std::string param_name = parameter.get_name(); - try { - if (param_name == "p") { - gains.p_gain_ = parameter.get_value(); - } else if (param_name == "i") { - gains.i_gain_ = parameter.get_value(); - } else if (param_name == "d") { - gains.d_gain_ = parameter.get_value(); - } else if (param_name == "i_clamp_max") { - gains.i_max_ = parameter.get_value(); - } else if (param_name == "i_clamp_min") { - gains.i_min_ = parameter.get_value(); - } else if (param_name == "antiwindup") { - gains.antiwindup_ = parameter.get_value(); - } else { - result.successful = false; - result.reason = "Invalid parameter"; - } - } catch (const rclcpp::exceptions::InvalidParameterTypeException & e) { - RCLCPP_INFO_STREAM( - rclcpp::get_logger("control_toolbox::pid"), "Please use the right type: " << e.what()); - } - } - - if (result.successful) { - /// @note don't call setGains() from inside a callback - gains_buffer_.writeFromNonRT(gains); - } - - return result; - }; - - if (node_param_iface_) { - parameter_callback_ = - node_param_iface_->add_on_set_parameters_callback(on_parameter_event_callback); - } + pe = p_error_; + ie = i_error_; + de = d_error_; } - } // namespace control_toolbox diff --git a/src/pidROS.cpp b/src/pidROS.cpp new file mode 100644 index 00000000..e878c391 --- /dev/null +++ b/src/pidROS.cpp @@ -0,0 +1,299 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2008, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Willow Garage nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* + Author: Melonee Wise + Contributors: Dave Coleman, Jonathan Bohren, Bob Holmberg, Wim Meeussen + Desc: Implements a standard proportional-integral-derivative controller +*/ + +#include +#include +#include +#include +#include + +#include "control_toolbox/pidROS.hpp" + +namespace control_toolbox +{ + +PidROS::PidROS(rclcpp::Node::SharedPtr & node, std::string topic_prefix) +: node_(node) +{ + if (topic_prefix.back() != '.' && !topic_prefix.empty()) { + topic_prefix_ = topic_prefix + "."; + } else { + topic_prefix_ = topic_prefix; + } + + state_pub_ = node->create_publisher( + topic_prefix + "/pid_state", rclcpp::SensorDataQoS()); + rt_state_pub_.reset( + new realtime_tools::RealtimePublisher(state_pub_)); +} + +PidROS::~PidROS() +{ +} + +bool PidROS::getBooleanParam(const std::string & param_name, bool & value) +{ + rclcpp::Parameter param; + if (node_->has_parameter(param_name)) { + node_->get_parameter(param_name, param); + value = param.as_bool(); + return true; + } else { + return false; + } +} + +bool PidROS::getDoubleParam(const std::string & param_name, double & value) +{ + rclcpp::Parameter param; + if (node_->has_parameter(param_name)) { + node_->get_parameter(param_name, param); + value = param.as_double(); + RCLCPP_INFO_STREAM( + node_->get_logger(), "parameter '" << param_name << "' in node '" << node_->get_name() << + "' value is " << value << std::endl); + return true; + } else { + RCLCPP_INFO_STREAM( + node_->get_logger(), "parameter '" << param_name << "' in node '" << node_->get_name() << + "' does not exists" << std::endl); + return false; + } +} + +bool PidROS::initPid() +{ + double p, i, d, i_min, i_max; + bool antiwindup = false; + bool all_param_available = true; + all_param_available &= getDoubleParam(topic_prefix_ + "p", p); + all_param_available &= getDoubleParam(topic_prefix_ + "i", i); + all_param_available &= getDoubleParam(topic_prefix_ + "d", d); + all_param_available &= getDoubleParam(topic_prefix_ + "i_clamp_max", i_max); + all_param_available &= getDoubleParam(topic_prefix_ + "i_clamp_min", i_min); + + getBooleanParam(topic_prefix_ + "antiwindup", antiwindup); + + if (all_param_available) { + setParameterEventCallback(); + } + + pid_.initPid(p, i, d, i_max, i_min, antiwindup); + + return all_param_available; +} + +void PidROS::declare_param(const std::string & param_name, rclcpp::ParameterValue param_value) +{ + if (!node_->has_parameter(param_name)) { + node_->declare_parameter(param_name, param_value); + } +} + +void PidROS::initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup) +{ + pid_.initPid(p, i, d, i_max, i_min, antiwindup); + + declare_param("p", rclcpp::ParameterValue(p)); + declare_param("i", rclcpp::ParameterValue(i)); + declare_param("d", rclcpp::ParameterValue(d)); + declare_param("i_clamp_max", rclcpp::ParameterValue(i_max)); + declare_param("i_clamp_min", rclcpp::ParameterValue(i_min)); + declare_param("antiwindup", rclcpp::ParameterValue(antiwindup)); + + setParameterEventCallback(); +} + +void PidROS::reset() +{ + pid_.reset(); +} + +double PidROS::computeCommand(double error, rclcpp::Duration dt) +{ + double cmd_ = pid_.computeCommand(error, dt.seconds()); + publishPIDState(cmd_, error, dt); + + return cmd_; +} + +Pid::Gains PidROS::getGains() +{ + return pid_.getGains(); +} + +void PidROS::setGains(double p, double i, double d, double i_max, double i_min, bool antiwindup) +{ + node_->set_parameters( + { + rclcpp::Parameter(topic_prefix_ + "p", p), + rclcpp::Parameter(topic_prefix_ + "i", i), + rclcpp::Parameter(topic_prefix_ + "d", d), + rclcpp::Parameter(topic_prefix_ + "i_clamp_max", i_max), + rclcpp::Parameter(topic_prefix_ + "i_clamp_min", i_min), + rclcpp::Parameter(topic_prefix_ + "antiwindup", antiwindup) + } + ); + + pid_.setGains(p, i, d, i_max, i_min, antiwindup); +} + +void PidROS::publishPIDState(double cmd, double error, rclcpp::Duration dt) +{ + Pid::Gains gains = pid_.getGains(); + + double p_error_, i_error_, d_error_; + getCurrentPIDErrors(p_error_, i_error_, d_error_); + + // Publish controller state if configured + if (rt_state_pub_) { + if (rt_state_pub_->trylock()) { + rt_state_pub_->msg_.header.stamp = rclcpp::Clock().now(); + rt_state_pub_->msg_.timestep = dt; + rt_state_pub_->msg_.error = error; + // rt_state_pub_->msg_.error_dot = error_dot; + rt_state_pub_->msg_.p_error = p_error_; + rt_state_pub_->msg_.i_error = i_error_; + rt_state_pub_->msg_.d_error = d_error_; + rt_state_pub_->msg_.p_term = gains.p_gain_; + rt_state_pub_->msg_.i_term = gains.i_gain_; + rt_state_pub_->msg_.d_term = gains.d_gain_; + rt_state_pub_->msg_.i_max = gains.i_max_; + rt_state_pub_->msg_.i_min = gains.i_min_; + rt_state_pub_->msg_.output = cmd; + rt_state_pub_->unlockAndPublish(); + } + } +} + +void PidROS::setCurrentCmd(double cmd) +{ + pid_.setCurrentCmd(cmd); +} + +double PidROS::getCurrentCmd() +{ + return pid_.getCurrentCmd(); +} + +void PidROS::getCurrentPIDErrors(double & pe, double & ie, double & de) +{ + double _pe, _ie, _de; + pid_.getCurrentPIDErrors(_pe, _ie, _de); + pe = _pe; + ie = _ie; + de = _de; +} + +void PidROS::printValues() +{ + Pid::Gains gains = pid_.getGains(); + + double p_error_, i_error_, d_error_; + getCurrentPIDErrors(p_error_, i_error_, d_error_); + + RCLCPP_INFO_STREAM( + node_->get_logger(), + "Current Values of PID Class:\n" << + " P Gain: " << gains.p_gain_ << "\n" << + " I Gain: " << gains.i_gain_ << "\n" << + " D Gain: " << gains.d_gain_ << "\n" << + " I_Max: " << gains.i_max_ << "\n" << + " I_Min: " << gains.i_min_ << "\n" << + " Antiwindup: " << gains.antiwindup_ << "\n" << + " P_Error: " << p_error_ << "\n" << + " I_Error: " << i_error_ << "\n" << + " D_Error: " << d_error_ << "\n" << + " Command: " << getCurrentCmd(); + ); +} + +void PidROS::setGains(const Pid::Gains & gains) +{ + pid_.setGains(gains); +} + +void PidROS::setParameterEventCallback() +{ + auto on_parameter_event_callback = [this](const std::vector & parameters) { + rcl_interfaces::msg::SetParametersResult result; + result.successful = true; + + /// @note don't use getGains, it's rt + Pid::Gains gains = pid_.getGains(); + + for (auto & parameter : parameters) { + const std::string param_name = parameter.get_name(); + try { + if (param_name == "p") { + gains.p_gain_ = parameter.get_value(); + } else if (param_name == "i") { + gains.i_gain_ = parameter.get_value(); + } else if (param_name == "d") { + gains.d_gain_ = parameter.get_value(); + } else if (param_name == "i_clamp_max") { + gains.i_max_ = parameter.get_value(); + } else if (param_name == "i_clamp_min") { + gains.i_min_ = parameter.get_value(); + } else if (param_name == "antiwindup") { + gains.antiwindup_ = parameter.get_value(); + } else { + result.successful = false; + result.reason = "Invalid parameter"; + } + } catch (const rclcpp::exceptions::InvalidParameterTypeException & e) { + RCLCPP_INFO_STREAM( + node_->get_logger(), "Please use the right type: " << e.what()); + } + } + + if (result.successful) { + /// @note don't call setGains() from inside a callback + pid_.setGains(gains); + } + + return result; + }; + + parameter_callback_ = + node_->add_on_set_parameters_callback(on_parameter_event_callback); +} + +} // namespace control_toolbox diff --git a/test/pid_parameters_tests.cpp b/test/pid_parameters_tests.cpp index 2763f4a0..85584b77 100644 --- a/test/pid_parameters_tests.cpp +++ b/test/pid_parameters_tests.cpp @@ -11,24 +11,24 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - #include -#include +#include + +#include "control_toolbox/pidROS.hpp" -#include -#include -#include -#include +#include "rclcpp/executors.hpp" +#include "rclcpp/node.hpp" +#include "rclcpp/parameter.hpp" +#include "rclcpp/utilities.hpp" -using control_toolbox::Pid; using rclcpp::executors::MultiThreadedExecutor; TEST(PidParametersTest, InitPidTest) { - rclcpp::Node node("pid_parameters_test"); + rclcpp::Node::SharedPtr node = std::make_shared("pid_parameters_test"); - Pid pid; + control_toolbox::PidROS pid(node); const double P = 1.0; const double I = 2.0; @@ -36,31 +36,31 @@ TEST(PidParametersTest, InitPidTest) const double I_MAX = 10.0; const double I_MIN = -10.0; - ASSERT_NO_THROW(pid.initPid(P, I, D, I_MAX, I_MIN, node.get_node_parameters_interface())); + ASSERT_NO_THROW(pid.initPid(P, I, D, I_MAX, I_MIN, false)); rclcpp::Parameter param; // check paramters were set - ASSERT_TRUE(node.get_parameter("p", param)); + ASSERT_TRUE(node->get_parameter("p", param)); ASSERT_EQ(param.get_value(), P); - ASSERT_TRUE(node.get_parameter("i", param)); + ASSERT_TRUE(node->get_parameter("i", param)); ASSERT_EQ(param.get_value(), I); - ASSERT_TRUE(node.get_parameter("d", param)); + ASSERT_TRUE(node->get_parameter("d", param)); ASSERT_EQ(param.get_value(), D); - ASSERT_TRUE(node.get_parameter("i_clamp_max", param)); + ASSERT_TRUE(node->get_parameter("i_clamp_max", param)); ASSERT_EQ(param.get_value(), I_MAX); - ASSERT_TRUE(node.get_parameter("i_clamp_min", param)); + ASSERT_TRUE(node->get_parameter("i_clamp_min", param)); ASSERT_EQ(param.get_value(), I_MIN); - ASSERT_TRUE(node.get_parameter("antiwindup", param)); + ASSERT_TRUE(node->get_parameter("antiwindup", param)); ASSERT_FALSE(param.get_value()); // check gains were set - Pid::Gains gains = pid.getGains(); + control_toolbox::Pid::Gains gains = pid.getGains(); ASSERT_EQ(gains.p_gain_, P); ASSERT_EQ(gains.i_gain_, I); ASSERT_EQ(gains.d_gain_, D); @@ -71,9 +71,9 @@ TEST(PidParametersTest, InitPidTest) TEST(PidParametersTest, InitPidWithAntiwindupTest) { - rclcpp::Node node("pid_parameters_test"); + rclcpp::Node::SharedPtr node = std::make_shared("pid_parameters_test"); - Pid pid; + control_toolbox::PidROS pid(node); const double P = 1.0; const double I = 2.0; @@ -82,31 +82,30 @@ TEST(PidParametersTest, InitPidWithAntiwindupTest) const double I_MIN = -10.0; const bool ANTIWINDUP = true; - ASSERT_NO_THROW( - pid.initPid(P, I, D, I_MAX, I_MIN, ANTIWINDUP, node.get_node_parameters_interface())); + pid.initPid(P, I, D, I_MAX, I_MIN, ANTIWINDUP); rclcpp::Parameter param; - ASSERT_TRUE(node.get_parameter("p", param)); + ASSERT_TRUE(node->get_parameter("p", param)); ASSERT_EQ(param.get_value(), P); - ASSERT_TRUE(node.get_parameter("i", param)); + ASSERT_TRUE(node->get_parameter("i", param)); ASSERT_EQ(param.get_value(), I); - ASSERT_TRUE(node.get_parameter("d", param)); + ASSERT_TRUE(node->get_parameter("d", param)); ASSERT_EQ(param.get_value(), D); - ASSERT_TRUE(node.get_parameter("i_clamp_max", param)); + ASSERT_TRUE(node->get_parameter("i_clamp_max", param)); ASSERT_EQ(param.get_value(), I_MAX); - ASSERT_TRUE(node.get_parameter("i_clamp_min", param)); + ASSERT_TRUE(node->get_parameter("i_clamp_min", param)); ASSERT_EQ(param.get_value(), I_MIN); - ASSERT_TRUE(node.get_parameter("antiwindup", param)); + ASSERT_TRUE(node->get_parameter("antiwindup", param)); ASSERT_EQ(param.get_value(), ANTIWINDUP); // check gains were set - Pid::Gains gains = pid.getGains(); + control_toolbox::Pid::Gains gains = pid.getGains(); ASSERT_EQ(gains.p_gain_, P); ASSERT_EQ(gains.i_gain_, I); ASSERT_EQ(gains.d_gain_, D); @@ -117,9 +116,9 @@ TEST(PidParametersTest, InitPidWithAntiwindupTest) TEST(PidParametersTest, SetParametersTest) { - rclcpp::Node node("pid_parameters_test"); + rclcpp::Node::SharedPtr node = std::make_shared("pid_parameters_test"); - Pid pid; + control_toolbox::PidROS pid(node); const double P = 1.0; const double I = 2.0; @@ -128,34 +127,33 @@ TEST(PidParametersTest, SetParametersTest) const double I_MIN = -10.0; const bool ANTIWINDUP = true; - ASSERT_NO_THROW( - pid.initPid(P, I, D, I_MAX, I_MIN, ANTIWINDUP, node.get_node_parameters_interface())); + pid.initPid(P, I, D, I_MAX, I_MIN, ANTIWINDUP); rcl_interfaces::msg::SetParametersResult set_result; // unknown parameter name ASSERT_THROW( - set_result = node.set_parameter(rclcpp::Parameter("unknown", 0.0)), + set_result = node->set_parameter(rclcpp::Parameter("unknown", 0.0)), rclcpp::exceptions::ParameterNotDeclaredException); - ASSERT_NO_THROW(set_result = node.set_parameter(rclcpp::Parameter("p", P))); + ASSERT_NO_THROW(set_result = node->set_parameter(rclcpp::Parameter("p", P))); ASSERT_TRUE(set_result.successful); - ASSERT_NO_THROW(set_result = node.set_parameter(rclcpp::Parameter("i", I))); + ASSERT_NO_THROW(set_result = node->set_parameter(rclcpp::Parameter("i", I))); ASSERT_TRUE(set_result.successful); - ASSERT_NO_THROW(set_result = node.set_parameter(rclcpp::Parameter("d", D))); + ASSERT_NO_THROW(set_result = node->set_parameter(rclcpp::Parameter("d", D))); ASSERT_TRUE(set_result.successful); - ASSERT_NO_THROW(set_result = node.set_parameter(rclcpp::Parameter("i_clamp_max", I_MAX))); + ASSERT_NO_THROW(set_result = node->set_parameter(rclcpp::Parameter("i_clamp_max", I_MAX))); ASSERT_TRUE(set_result.successful); - ASSERT_NO_THROW(set_result = node.set_parameter(rclcpp::Parameter("i_clamp_min", I_MIN))); + ASSERT_NO_THROW(set_result = node->set_parameter(rclcpp::Parameter("i_clamp_min", I_MIN))); ASSERT_TRUE(set_result.successful); - ASSERT_NO_THROW(set_result = node.set_parameter(rclcpp::Parameter("antiwindup", ANTIWINDUP))); + ASSERT_NO_THROW(set_result = node->set_parameter(rclcpp::Parameter("antiwindup", ANTIWINDUP))); ASSERT_TRUE(set_result.successful); // process callbacks - rclcpp::spin_some(node.get_node_base_interface()); + rclcpp::spin_some(node->get_node_base_interface()); // check gains were set using the parameters - Pid::Gains gains = pid.getGains(); + control_toolbox::Pid::Gains gains = pid.getGains(); ASSERT_EQ(gains.p_gain_, P); ASSERT_EQ(gains.i_gain_, I); ASSERT_EQ(gains.d_gain_, D); @@ -166,9 +164,9 @@ TEST(PidParametersTest, SetParametersTest) TEST(PidParametersTest, GetParametersTest) { - rclcpp::Node node("pid_parameters_test"); + rclcpp::Node::SharedPtr node = std::make_shared("pid_parameters_test"); - Pid pid; + control_toolbox::PidROS pid(node); const double P = 1.0; const double I = 2.0; @@ -177,31 +175,59 @@ TEST(PidParametersTest, GetParametersTest) const double I_MIN = -10.0; const bool ANTIWINDUP = true; - ASSERT_NO_THROW( - pid.initPid(0.0, 0.0, 0.0, 0.0, 0.0, false, node.get_node_parameters_interface())); - ASSERT_NO_THROW(pid.setGains(P, I, D, I_MAX, I_MIN, ANTIWINDUP)); + pid.initPid(0.0, 0.0, 0.0, 0.0, 0.0, false); + pid.setGains(P, I, D, I_MAX, I_MIN, ANTIWINDUP); rclcpp::Parameter param; - ASSERT_TRUE(node.get_parameter("p", param)); + ASSERT_TRUE(node->get_parameter("p", param)); ASSERT_EQ(param.get_value(), P); - ASSERT_TRUE(node.get_parameter("i", param)); + ASSERT_TRUE(node->get_parameter("i", param)); ASSERT_EQ(param.get_value(), I); - ASSERT_TRUE(node.get_parameter("d", param)); + ASSERT_TRUE(node->get_parameter("d", param)); ASSERT_EQ(param.get_value(), D); - ASSERT_TRUE(node.get_parameter("i_clamp_max", param)); + ASSERT_TRUE(node->get_parameter("i_clamp_max", param)); ASSERT_EQ(param.get_value(), I_MAX); - ASSERT_TRUE(node.get_parameter("i_clamp_min", param)); + ASSERT_TRUE(node->get_parameter("i_clamp_min", param)); ASSERT_EQ(param.get_value(), I_MIN); - ASSERT_TRUE(node.get_parameter("antiwindup", param)); + ASSERT_TRUE(node->get_parameter("antiwindup", param)); ASSERT_EQ(param.get_value(), ANTIWINDUP); } +TEST(PidParametersTest, GetParametersFromParams) +{ + rclcpp::Node::SharedPtr node = std::make_shared("pid_parameters_test"); + + control_toolbox::PidROS pid(node); + + const double P = 1.0; + const double I = 2.0; + const double D = 3.0; + const double I_MAX = 10.0; + const double I_MIN = -10.0; + const bool ANTIWINDUP = true; + + ASSERT_FALSE(pid.initPid()); + + node->declare_parameter("p", P); + node->declare_parameter("i", I); + node->declare_parameter("d", D); + node->declare_parameter("i_clamp_max", I_MAX); + node->declare_parameter("i_clamp_min", I_MIN); + + ASSERT_TRUE(pid.initPid()); + + rclcpp::Parameter param; + + ASSERT_TRUE(node->get_parameter("p", param)); + ASSERT_EQ(param.get_value(), P); +} + int main(int argc, char ** argv) { testing::InitGoogleTest(&argc, argv); diff --git a/test/pid_publisher_tests.cpp b/test/pid_publisher_tests.cpp index 24142aff..17fee3d1 100644 --- a/test/pid_publisher_tests.cpp +++ b/test/pid_publisher_tests.cpp @@ -18,7 +18,7 @@ #include #include -#include "control_toolbox/pid.hpp" +#include "control_toolbox/pidROS.hpp" #include "gtest/gtest.h" @@ -27,35 +27,36 @@ #include "rclcpp/node.hpp" #include "rclcpp/utilities.hpp" -using control_toolbox::Pid; using PidStateMsg = control_msgs::msg::PidState; using rclcpp::executors::MultiThreadedExecutor; TEST(PidPublihserTest, PublishTest) { - const size_t ATTEMPTS = 10; + const size_t ATTEMPTS = 100; const std::chrono::milliseconds DELAY(250); auto node = std::make_shared("pid_publisher_test"); - Pid pid(1.0, 1.0, 1.0, 5.0, -5.0); + control_toolbox::PidROS pid_ros(node); - ASSERT_NO_THROW(pid.initPublisher(node)); + pid_ros.initPid(1.0, 1.0, 1.0, 5.0, -5.0, false); bool callback_called = false; - PidStateMsg::SharedPtr last_state_msg; - auto state_callback = [&](const PidStateMsg::SharedPtr) + control_msgs::msg::PidState::SharedPtr last_state_msg; + auto state_callback = [&](const control_msgs::msg::PidState::SharedPtr) { callback_called = true; }; - auto state_sub = node->create_subscription("pid_state", 10, state_callback); + auto state_sub = node->create_subscription( + "/pid_state", rclcpp::SensorDataQoS(), state_callback); - double command = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + double command = pid_ros.computeCommand(-0.5, rclcpp::Duration(1, 0)); EXPECT_EQ(-1.5, command); // wait for callback for (size_t i = 0; i < ATTEMPTS && !callback_called; ++i) { + double command = pid_ros.computeCommand(-0.5, rclcpp::Duration(1, 0)); rclcpp::spin_some(node); std::this_thread::sleep_for(DELAY); } diff --git a/test/pid_tests.cpp b/test/pid_tests.cpp index 62381221..0114d222 100644 --- a/test/pid_tests.cpp +++ b/test/pid_tests.cpp @@ -53,13 +53,13 @@ TEST(ParameterTest, ITermBadIBoundsTest) double cmd = 0.0; double pe, ie, de; - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); - pid.getCurrentPIDErrors(&pe, &ie, &de); + cmd = pid.computeCommand(-1.0, 1.0); + pid.getCurrentPIDErrors(pe, ie, de); EXPECT_FALSE(std::isinf(ie)); EXPECT_FALSE(std::isnan(cmd)); - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); - pid.getCurrentPIDErrors(&pe, &ie, &de); + cmd = pid.computeCommand(-1.0, 1.0); + pid.getCurrentPIDErrors(pe, ie, de); EXPECT_FALSE(std::isinf(ie)); EXPECT_FALSE(std::isnan(cmd)); } @@ -76,11 +76,11 @@ TEST(ParameterTest, integrationClampTest) double cmd = 0.0; // Test lower limit - cmd = pid.computeCommand(-10.03, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-10.03, 1.0); EXPECT_EQ(-1.0, cmd); // Test upper limit - cmd = pid.computeCommand(30.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(30.0, 1.0); EXPECT_EQ(1.0, cmd); } @@ -100,13 +100,13 @@ TEST(ParameterTest, integrationClampZeroGainTest) double cmd = 0.0; double pe, ie, de; - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); - pid.getCurrentPIDErrors(&pe, &ie, &de); + cmd = pid.computeCommand(-1.0, 1.0); + pid.getCurrentPIDErrors(pe, ie, de); EXPECT_LE(i_min, cmd); EXPECT_LE(cmd, i_max); EXPECT_EQ(0.0, cmd); - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); EXPECT_LE(i_min, cmd); EXPECT_LE(cmd, i_max); EXPECT_EQ(0.0, cmd); @@ -126,16 +126,16 @@ TEST(ParameterTest, integrationAntiwindupTest) double cmd = 0.0; double pe, ie, de; - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); EXPECT_EQ(-1.0, cmd); - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); EXPECT_EQ(-1.0, cmd); - cmd = pid.computeCommand(0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.5, 1.0); EXPECT_EQ(0.0, cmd); - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); EXPECT_EQ(-1.0, cmd); } @@ -153,16 +153,16 @@ TEST(ParameterTest, negativeIntegrationAntiwindupTest) double cmd = 0.0; double pe, ie, de; - cmd = pid.computeCommand(0.1, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.1, 1.0); EXPECT_EQ(-0.2, cmd); - cmd = pid.computeCommand(0.1, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.1, 1.0); EXPECT_EQ(-0.2, cmd); - cmd = pid.computeCommand(-0.05, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.05, 1.0); EXPECT_EQ(-0.075, cmd); - cmd = pid.computeCommand(0.1, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.1, 1.0); EXPECT_EQ(-0.2, cmd); } @@ -221,7 +221,7 @@ TEST(ParameterTest, gainSettingCopyPIDTest) // Send update command to populate errors ------------------------------------------------- pid1.setCurrentCmd(10); - pid1.computeCommand(20, rclcpp::Duration(1, 0)); + pid1.computeCommand(20, 1.0); // Test copy constructor ------------------------------------------------- Pid pid2(pid1); @@ -238,7 +238,7 @@ TEST(ParameterTest, gainSettingCopyPIDTest) // Test that errors are zero double pe2, ie2, de2; - pid2.getCurrentPIDErrors(&pe2, &ie2, &de2); + pid2.getCurrentPIDErrors(pe2, ie2, de2); EXPECT_EQ(0.0, pe2); EXPECT_EQ(0.0, ie2); EXPECT_EQ(0.0, de2); @@ -259,7 +259,7 @@ TEST(ParameterTest, gainSettingCopyPIDTest) // Test that errors are zero double pe3, ie3, de3; - pid3.getCurrentPIDErrors(&pe3, &ie3, &de3); + pid3.getCurrentPIDErrors(pe3, ie3, de3); EXPECT_EQ(0.0, pe3); EXPECT_EQ(0.0, ie3); EXPECT_EQ(0.0, de3); @@ -268,7 +268,7 @@ TEST(ParameterTest, gainSettingCopyPIDTest) pid1.reset(); double pe1, ie1, de1; - pid1.getCurrentPIDErrors(&pe1, &ie1, &de1); + pid1.getCurrentPIDErrors(pe1, ie1, de1); EXPECT_EQ(0.0, pe1); EXPECT_EQ(0.0, ie1); EXPECT_EQ(0.0, de1); @@ -289,22 +289,22 @@ TEST(CommandTest, proportionalOnlyTest) double cmd = 0.0; // If initial error = 0, p-gain = 1, dt = 1 - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect command = error EXPECT_EQ(-0.5, cmd); // If call again - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect the same as before EXPECT_EQ(-0.5, cmd); // If call again doubling the error - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); // Then expect the command doubled EXPECT_EQ(-1.0, cmd); // If call with positive error - cmd = pid.computeCommand(0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.5, 1.0); // Then expect always command = error EXPECT_EQ(0.5, cmd); } @@ -321,26 +321,26 @@ TEST(CommandTest, integralOnlyTest) double cmd = 0.0; // If initial error = 0, i-gain = 1, dt = 1 - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect command = error EXPECT_EQ(-0.5, cmd); // If call again with same arguments - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect the integral part to double the command EXPECT_EQ(-1.0, cmd); // Call again with no error - cmd = pid.computeCommand(0.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.0, 1.0); // Expect the integral part to keep the previous command because it ensures error = 0 EXPECT_EQ(-1.0, cmd); // Double check that the integral contribution keep the previous command - cmd = pid.computeCommand(0.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(0.0, 1.0); EXPECT_EQ(-1.0, cmd); // Finally call again with positive error to see if the command changes in the opposite direction - cmd = pid.computeCommand(1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(1.0, 1.0); // Expect that the command is cleared since error = -1 * previous command, i-gain = 1, dt = 1 EXPECT_EQ(0.0, cmd); } @@ -357,27 +357,27 @@ TEST(CommandTest, derivativeOnlyTest) double cmd = 0.0; // If initial error = 0, d-gain = 1, dt = 1 - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect command = error EXPECT_EQ(-0.5, cmd); // If call again with same error - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect command = 0 due to no variation on error EXPECT_EQ(0.0, cmd); // If call again with same error and smaller control period - cmd = pid.computeCommand(-0.5, rclcpp::Duration(100 * 10 ^ 6)); + cmd = pid.computeCommand(-0.5, 0.1); // Then expect command = 0 again EXPECT_EQ(0.0, cmd); // If the error increases, with dt = 1 - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); // Then expect the command = change in dt EXPECT_EQ(-0.5, cmd); // If error decreases, with dt = 1 - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect always the command = change in dt (note the sign flip) EXPECT_EQ(0.5, cmd); } @@ -394,17 +394,17 @@ TEST(CommandTest, completePIDTest) // All contributions are tested, here few tests check that they sum up correctly // If initial error = 0, all gains = 1, dt = 1 - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect command = 3x error EXPECT_EQ(-1.5, cmd); // If call again with same arguments, no error change, but integration do its part - cmd = pid.computeCommand(-0.5, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-0.5, 1.0); // Then expect command = 3x error again EXPECT_EQ(-1.5, cmd); // If call again increasing the error - cmd = pid.computeCommand(-1.0, rclcpp::Duration(1, 0)); + cmd = pid.computeCommand(-1.0, 1.0); // Then expect command equals to p = -1, i = -2.0 (i.e. - 0.5 - 0.5 - 1.0), d = -0.5 EXPECT_EQ(-3.5, cmd); }