From 55eeb09ef64e831c9a6d15049cf7f6fa64109b53 Mon Sep 17 00:00:00 2001 From: Mateus Menezes Date: Fri, 9 Feb 2024 07:37:21 -0300 Subject: [PATCH] Move hardware interface README content to sphinx documentation (#1342) --- hardware_interface/README.md | 132 +------------- .../doc/different_update_rates_userdoc.rst | 169 ++++++++++++++++++ .../doc/hardware_components_userdoc.rst | 1 + 3 files changed, 173 insertions(+), 129 deletions(-) create mode 100644 hardware_interface/doc/different_update_rates_userdoc.rst diff --git a/hardware_interface/README.md b/hardware_interface/README.md index 343158b698..76480201cb 100644 --- a/hardware_interface/README.md +++ b/hardware_interface/README.md @@ -1,131 +1,5 @@ -robot agnostic hardware_interface package. -This package will eventually be moved into its own repo. +# Overview -## Best practice for `hardware_interface` implementation -In the following section you can find some advices which will help you implement interface -for your specific hardware. +For detailed information about this package, please see the [ros2_control Documentation]! -### Best practice for having different update rate for each `hardware_interface` by counting loops -Current implementation of [ros2_control main node](https://github.com/ros-controls/ros2_control/blob/master/controller_manager/src/ros2_control_node.cpp) -has one update rate that controls the rate of the [`read()`](https://github.com/ros-controls/ros2_control/blob/fe462926416d527d1da163bc3eabd02ee1de9be9/hardware_interface/include/hardware_interface/system_interface.hpp#L169) and [`write()`](https://github.com/ros-controls/ros2_control/blob/fe462926416d527d1da163bc3eabd02ee1de9be9/hardware_interface/include/hardware_interface/system_interface.hpp#L178) -calls in [`hardware_interface(s)`](https://github.com/ros-controls/ros2_control/blob/master/hardware_interface/include/hardware_interface/system_interface.hpp). -In this section suggestion on how to run each interface implementation on its own update rate is introduced. - -1. Add parameters of main control loop update rate and desired update rate for your hardware interface. -``` - - - - - - - - my_system_interface/MySystemHardware - ${main_loop_update_rate} - ${desired_hw_update_rate} - - ... - - - - - -``` - -2. In you [`on_init()`](https://github.com/ros-controls/ros2_control/blob/fe462926416d527d1da163bc3eabd02ee1de9be9/hardware_interface/include/hardware_interface/system_interface.hpp#L94) specific implementation fetch desired parameters: -``` -namespace my_system_interface -{ -hardware_interface::CallbackReturn MySystemHardware::on_init( - const hardware_interface::HardwareInfo & info) -{ - if ( - hardware_interface::SystemInterface::on_init(info) != - hardware_interface::CallbackReturn::SUCCESS) - { - return hardware_interface::CallbackReturn::ERROR; - } - - // declaration in *.hpp file --> unsigned int main_loop_update_rate_, desired_hw_update_rate_ = 100 ; - main_loop_update_rate_ = stoi(info_.hardware_parameters["main_loop_update_rate"]); - desired_hw_update_rate_ = stoi(info_.hardware_parameters["desired_hw_update_rate"]); - - ... -} -... -} // my_system_interface -``` - -3. In your `on_activate()` specific implementation reset internal loop counter -``` -hardware_interface::CallbackReturn MySystemHardware::on_activate( - const rclcpp_lifecycle::State & /*previous_state*/) -{ - // declaration in *.hpp file --> unsigned int update_loop_counter_ ; - update_loop_counter_ = 0; - ... -} -``` - -4. In your `read(const rclcpp::Time & time, const rclcpp::Duration & period)` and/or - `write(const rclcpp::Time & time, const rclcpp::Duration & period)` - specific implementations decide if you should interfere with your hardware -``` -hardware_interface::return_type MySystemHardware::read(const rclcpp::Time & time, const rclcpp::Duration & period) -{ - - bool hardware_go = main_loop_update_rate_ == 0 || - desired_hw_update_rate_ == 0 || - ((update_loop_counter_ % desired_hw_update_rate_) == 0); - - if (hardware_go){ - // hardware comms and operations - - } - ... - - // update counter - ++update_loop_counter_; - update_loop_counter_ %= main_loop_update_rate_; -} -``` - -### Best practice for having different update rate for each `hardware_interface` by measuring elapsed time -Another way to decide if hardware communication should be executed in the`read(const rclcpp::Time & time, const rclcpp::Duration & period)` and/or -`write(const rclcpp::Time & time, const rclcpp::Duration & period)` implementations is to measure elapsed time since last pass: - -``` -hardware_interface::CallbackReturn MySystemHardware::on_activate( - const rclcpp_lifecycle::State & /*previous_state*/) -{ - // declaration in *.hpp file --> bool first_read_pass_, first_write_pass_ = true ; - first_read_pass_ = first_write_pass_ = true; - ... -} - -hardware_interface::return_type MySystemHardware::read(const rclcpp::Time & time, const rclcpp::Duration & period) -{ - if (first_read_pass_ || (time - last_read_time_ ) > period) - { - first_read_pass_ = false; - // declaration in *.hpp file --> rclcpp::Time last_read_time_ ; - last_read_time_ = time; - // hardware comms and operations - ... - } - ... -} - -hardware_interface::return_type MySystemHardware::write(const rclcpp::Time & time, const rclcpp::Duration & period) -{ - if (first_write_pass_ || (time - last_write_time_ ) > period) - { - first_write_pass_ = false; - // declaration in *.hpp file --> rclcpp::Time last_write_time_ ; - last_write_time_ = time; - // hardware comms and operations - ... - } - ... -} -``` +[ros2_control Documentation]: https://control.ros.org/master/doc/ros2_control/hardware_interface/doc/hardware_components_userdoc.html diff --git a/hardware_interface/doc/different_update_rates_userdoc.rst b/hardware_interface/doc/different_update_rates_userdoc.rst new file mode 100644 index 0000000000..23f5e3564a --- /dev/null +++ b/hardware_interface/doc/different_update_rates_userdoc.rst @@ -0,0 +1,169 @@ +:github_url: https://github.com/ros-controls/ros2_control/blob/{REPOS_FILE_BRANCH}/hardware_interface/doc/different_update_rates_userdoc.rst + +.. _different_update_rates_userdoc: + +Different update rates for Hardware Components +=============================================================================== + +In the following sections you can find some advice which will help you to implement Hardware +Components with update rates different from the main control loop. + +By counting loops +------------------------------------------------------------------------------- + +Current implementation of +`ros2_control main node `_ +has one update rate that controls the rate of the +`read(...) `_ +and `write(...) `_ +calls in `hardware_interface(s) `_. +To achieve different update rates for your hardware component you can use the following steps: + +.. _step-1: + +1. Add parameters of main control loop update rate and desired update rate for your hardware component + + .. code:: xml + + + + + + + + + my_system_interface/MySystemHardware + ${main_loop_update_rate} + ${desired_hw_update_rate} + + ... + + + + + + +.. _step-2: + +2. In you ``on_init()`` specific implementation fetch the desired parameters + + .. code:: cpp + + namespace my_system_interface + { + hardware_interface::CallbackReturn MySystemHardware::on_init( + const hardware_interface::HardwareInfo & info) + { + if ( + hardware_interface::SystemInterface::on_init(info) != + hardware_interface::CallbackReturn::SUCCESS) + { + return hardware_interface::CallbackReturn::ERROR; + } + + // declaration in *.hpp file --> unsigned int main_loop_update_rate_, desired_hw_update_rate_ = 100 ; + main_loop_update_rate_ = stoi(info_.hardware_parameters["main_loop_update_rate"]); + desired_hw_update_rate_ = stoi(info_.hardware_parameters["desired_hw_update_rate"]); + + ... + } + ... + } // my_system_interface + +3. In your ``on_activate`` specific implementation reset internal loop counter + + .. code:: cpp + + hardware_interface::CallbackReturn MySystemHardware::on_activate( + const rclcpp_lifecycle::State & /*previous_state*/) + { + // declaration in *.hpp file --> unsigned int update_loop_counter_ ; + update_loop_counter_ = 0; + ... + } + +4. In your ``read(const rclcpp::Time & time, const rclcpp::Duration & period)`` + and/or ``write(const rclcpp::Time & time, const rclcpp::Duration & period)`` + specific implementations decide if you should interfere with your hardware + + .. code:: cpp + + hardware_interface::return_type MySystemHardware::read(const rclcpp::Time & time, const rclcpp::Duration & period) + { + + bool hardware_go = main_loop_update_rate_ == 0 || + desired_hw_update_rate_ == 0 || + ((update_loop_counter_ % desired_hw_update_rate_) == 0); + + if (hardware_go){ + // hardware comms and operations + ... + } + ... + + // update counter + ++update_loop_counter_; + update_loop_counter_ %= main_loop_update_rate_; + } + +By measuring elapsed time +------------------------------------------------------------------------------- + +Another way to decide if hardware communication should be executed in the +``read(const rclcpp::Time & time, const rclcpp::Duration & period)`` and/or +``write(const rclcpp::Time & time, const rclcpp::Duration & period)`` +implementations is to measure elapsed time since last pass: + +1. In your ``on_activate`` specific implementation reset the flags that indicate + that this is the first pass of the ``read`` and ``write`` methods + + .. code:: cpp + + hardware_interface::CallbackReturn MySystemHardware::on_activate( + const rclcpp_lifecycle::State & /*previous_state*/) + { + // declaration in *.hpp file --> bool first_read_pass_, first_write_pass_ = true ; + first_read_pass_ = first_write_pass_ = true; + ... + } + +2. In your ``read(const rclcpp::Time & time, const rclcpp::Duration & period)`` + and/or ``write(const rclcpp::Time & time, const rclcpp::Duration & period)`` + specific implementations decide if you should interfere with your hardware + + .. code:: cpp + + hardware_interface::return_type MySystemHardware::read(const rclcpp::Time & time, const rclcpp::Duration & period) + { + if (first_read_pass_ || (time - last_read_time_ ) > desired_hw_update_period_) + { + first_read_pass_ = false; + // declaration in *.hpp file --> rclcpp::Time last_read_time_ ; + last_read_time_ = time; + // hardware comms and operations + ... + } + ... + } + + hardware_interface::return_type MySystemHardware::write(const rclcpp::Time & time, const rclcpp::Duration & period) + { + if (first_write_pass_ || (time - last_write_time_ ) > desired_hw_update_period_) + { + first_write_pass_ = false; + // declaration in *.hpp file --> rclcpp::Time last_write_time_ ; + last_write_time_ = time; + // hardware comms and operations + ... + } + ... + } + +.. note:: + + The approach to get the desired update period value from the URDF and assign it to the variable + ``desired_hw_update_period_`` is the same as in the previous section (|step-1|_ and |step-2|_) but + with a different parameter name. + +.. |step-1| replace:: step 1 +.. |step-2| replace:: step 2 diff --git a/hardware_interface/doc/hardware_components_userdoc.rst b/hardware_interface/doc/hardware_components_userdoc.rst index 1a8c5e611b..404e90ee03 100644 --- a/hardware_interface/doc/hardware_components_userdoc.rst +++ b/hardware_interface/doc/hardware_components_userdoc.rst @@ -16,6 +16,7 @@ Guidelines and Best Practices Hardware Interface Types Writing a Hardware Component + Different Update Rates Handling of errors that happen during read() and write() calls