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

Async Function Handler for Controllers #1489

Open
wants to merge 85 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
857d5ac
Add first version of the async controllers
saikishor Apr 9, 2024
d6f81b5
removed extra notify one
saikishor Apr 10, 2024
0f5da8e
added async_update_ready_ to use with conditional variable to avoid s…
saikishor Apr 10, 2024
6770413
call notify_one after setting the async_update_ready
saikishor Apr 10, 2024
3627e75
add precommit changes
saikishor Apr 10, 2024
0797918
add missing this in the conditional variable callback
saikishor Apr 10, 2024
d809480
change atomic bool to normal bool
saikishor Apr 10, 2024
37504fa
add wait_for_update_to_finish method
saikishor Apr 10, 2024
bf3f0c4
add notify_one in different places to wait properly to finish update …
saikishor Apr 11, 2024
9295c67
added unique lock and check with try_lock for triggering calls
saikishor Apr 11, 2024
90c06d7
use try_to_lock for better checking of owning the lock or not
saikishor Apr 11, 2024
82e75ef
add current update time and period to be able to update them
saikishor Apr 11, 2024
503cb81
update documentation of the async method
saikishor Apr 11, 2024
ce07ff7
added some comments about the issue with the get_state method
saikishor Apr 11, 2024
16dc408
use atomic_bool
saikishor Apr 11, 2024
1ec3d4d
minor change in the logic
saikishor Apr 11, 2024
59883d2
initialize the duration to fix the compilation error
saikishor Apr 11, 2024
42f088a
Move the main async logic into a separate method
saikishor Apr 11, 2024
281d947
Add async function handler to handle parsed functions
saikishor Apr 11, 2024
5b40559
add missing template typenames on functions
saikishor Apr 12, 2024
9138384
set wait_for_update_to_finish to const
saikishor Apr 12, 2024
85e7cf2
change async_update_ready_ to atomic bool
saikishor Apr 12, 2024
0c1e470
use also async_update_stop_ also in the conditional wait predicate fu…
saikishor Apr 12, 2024
4cd1a3f
Add some utility methods
saikishor Apr 12, 2024
15d6cc1
modify the std::function arguments of async_update method
saikishor Apr 12, 2024
c185332
Fix the issue with the overidding template names and typo in the std:…
saikishor Apr 13, 2024
b5f03f8
fix the wait for update to finish method
saikishor Apr 13, 2024
126fd13
Check if the parsed functions are valid or not in the init method
saikishor Apr 14, 2024
c2be007
unlock the mutex before notifying the conditional variable
saikishor Apr 14, 2024
c5308a6
Do not check if the thread is joinable in the is_initialized method
saikishor Apr 14, 2024
2fd3130
added a explicit method to join the async thread
saikishor Apr 14, 2024
e11ff43
Add missing includes
saikishor Apr 14, 2024
2625bf8
change the way the initialization in done
saikishor Apr 14, 2024
ecda382
Added tests for the new async function handler
saikishor Apr 14, 2024
e583265
extend the tests with another loop
saikishor Apr 14, 2024
35947a5
Added test case to check for triggering
saikishor Apr 14, 2024
a39e89e
Also test the case of triggering without initialization
saikishor Apr 14, 2024
4e78196
Add test triggering the handler for several cycles
saikishor Apr 14, 2024
c1c3fcd
Add the test cases of changes in the lifecycle state
saikishor Apr 14, 2024
bec8ba4
remove is_async method and added is_preempted method + changes to the…
saikishor Apr 14, 2024
5b20cc2
Use the AsyncFunctionHandler instead of implementing in the controlle…
saikishor Apr 15, 2024
a6ba1fa
Add documentation to the methods
saikishor Apr 15, 2024
b2b540e
remove the arguments to initialize_async_update_thread method
saikishor Apr 15, 2024
4ff830d
simplify the is_initialized method check
saikishor Apr 15, 2024
6d59639
rename preempt_async_update to stop_async_update
saikishor Apr 15, 2024
d598488
Separate the exceptions for the parsed methods
saikishor Apr 15, 2024
6597521
change the exception inside the initialize_async_update_thread
saikishor Apr 15, 2024
5270d21
move the test implementations to the cpp file
saikishor Apr 15, 2024
4f99cfd
Remove the unlock when preempted as the lock goes out of scope
saikishor Apr 15, 2024
1710486
rename async_update_ready_ to trigger_in_progress_
saikishor Apr 15, 2024
5c96084
update the docs
saikishor Apr 15, 2024
de0f764
rename the method wait_for_update_to_finish to wait_for_trigger_cycle…
saikishor Apr 15, 2024
3e55fd1
Add documentation to the trigger_update method and add it to the cont…
saikishor Apr 15, 2024
6b470f4
added a method to stop the async update cycle before deactivating the…
saikishor Apr 15, 2024
c379aa2
return std::pair to check if the trigger is successful or not
saikishor Apr 16, 2024
17fd952
update the runtime error throw message
saikishor Apr 16, 2024
5f12d9b
rename is_preempted to is_stopped for the context of the threads
saikishor Apr 18, 2024
d366e7b
change EXPECT_THROW to ASSERT_THROW
saikishor Apr 18, 2024
516281a
Update the documentation of the init return entity
saikishor Apr 18, 2024
2c61b8b
Fix the doc of stop_async_update_cycle
saikishor Apr 18, 2024
7ee444d
add minor information
saikishor Apr 18, 2024
d14be47
add missing functional include
saikishor Apr 18, 2024
092e8f7
check if lock is owned or not before in the condition
saikishor Apr 21, 2024
e56c82b
Use separated arguments for better clarity
saikishor Apr 21, 2024
2285b80
Move the lock within a scope to avoid exclusive unlock within the thread
saikishor Apr 22, 2024
098b9fb
notify other waiting threads before stopping
saikishor Apr 22, 2024
d242107
Scope the lock to avoid manual unlock in the trigger_async_update method
saikishor Apr 22, 2024
eda5100
Update async_update_stop_ within the scope of the lock to avoid missi…
saikishor Apr 22, 2024
500ab50
Add new conditional variable to have unexpected behavior when working…
saikishor Apr 22, 2024
df2f9f8
Changes for the starting thread upon activation
saikishor Apr 21, 2024
8d40931
remove unused notify at the end of the thread
saikishor Apr 22, 2024
e632776
simplify the logic inside the thread
saikishor May 17, 2024
b648843
add pre-commit formatting changes
saikishor May 17, 2024
22db7df
remove the AsyncControllerThread integration to replace it with Async…
saikishor May 17, 2024
b9bc86a
move the async function handler to the realtime_tools and integrate f…
saikishor Jun 12, 2024
3f6c6f9
start the async thread when configuring the controller
saikishor Jun 12, 2024
23e1b9a
remove the get_state bind for API change
saikishor Jun 13, 2024
c3e4ddf
add new API naming changes
saikishor Jul 17, 2024
f7d015d
added thread priority argument to be able to set the scheduler priority
saikishor Aug 5, 2024
531a934
Merge branch 'master' into async_controllers
bmagyar Sep 12, 2024
e627946
stop the thread on cleanup of the controller
saikishor Sep 13, 2024
d61544b
wait for cycle to finish in the switch controllers before deactivatin…
saikishor Sep 13, 2024
e0736a6
fix the thread_priority parameter declaration type
saikishor Sep 13, 2024
ee1f9e2
change conditioning to not trigger logging for a failed return as well
saikishor Sep 13, 2024
6c80b9e
Add tests for the async controller
saikishor Sep 13, 2024
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
1 change: 1 addition & 0 deletions controller_interface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ endif()
set(THIS_PACKAGE_INCLUDE_DEPENDS
hardware_interface
rclcpp_lifecycle
realtime_tools
)

find_package(ament_cmake REQUIRED)
Expand Down
112 changes: 0 additions & 112 deletions controller_interface/include/controller_interface/async_controller.hpp

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "controller_interface/visibility_control.h"
#include "realtime_tools/async_function_handler.hpp"

#include "hardware_interface/handle.hpp"
#include "hardware_interface/loaned_command_interface.hpp"
Expand Down Expand Up @@ -153,6 +155,21 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
CONTROLLER_INTERFACE_PUBLIC
virtual return_type update(const rclcpp::Time & time, const rclcpp::Duration & period) = 0;

/**
* Trigger update method. This method is used by the controller_manager to trigger the update
* method of the controller.
* The method is used to trigger the update method of the controller synchronously or
* asynchronously, based on the controller configuration.
* **The method called in the (real-time) control loop.**
*
* \param[in] time The time at the start of this control loop iteration
* \param[in] period The measured time taken by the last control loop iteration
* \returns return_type::OK if update is successfully, otherwise return_type::ERROR.
*/
CONTROLLER_INTERFACE_PUBLIC
std::pair<bool, return_type> trigger_update(
const rclcpp::Time & time, const rclcpp::Duration & period);

CONTROLLER_INTERFACE_PUBLIC
std::shared_ptr<rclcpp_lifecycle::LifecycleNode> get_node();

Expand Down Expand Up @@ -267,12 +284,26 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
CONTROLLER_INTERFACE_PUBLIC
virtual bool is_in_chained_mode() const = 0;

/**
* Method to wait for any running async update cycle to finish after finishing the current cycle.
* This is needed to be called before deactivating the controller by the controller_manager, so
* that the interfaces still exist when the controller finishes its cycle and then it's exits.
*
* \note **The method is not real-time safe and shouldn't be called in the control loop.**
*
* If the controller is running in async mode, the method will wait for the current async update
* to finish. If the controller is not running in async mode, the method will do nothing.
*/
CONTROLLER_INTERFACE_PUBLIC
void wait_for_trigger_update_to_finish();

protected:
std::vector<hardware_interface::LoanedCommandInterface> command_interfaces_;
std::vector<hardware_interface::LoanedStateInterface> state_interfaces_;

private:
std::shared_ptr<rclcpp_lifecycle::LifecycleNode> node_;
std::unique_ptr<realtime_tools::AsyncFunctionHandler<return_type>> async_handler_;
unsigned int update_rate_ = 0;
bool is_async_ = false;
std::string urdf_ = "";
Expand Down
4 changes: 4 additions & 0 deletions controller_interface/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
<buildtool_depend>ament_cmake_gen_version_h</buildtool_depend>

<build_depend>hardware_interface</build_depend>
<build_depend>realtime_tools</build_depend>
<build_depend>rclcpp_lifecycle</build_depend>
<build_depend>sensor_msgs</build_depend>

<build_export_depend>hardware_interface</build_export_depend>
<build_export_depend>realtime_tools</build_export_depend>
<build_export_depend>rclcpp_lifecycle</build_export_depend>

<exec_depend>realtime_tools</exec_depend>

<test_depend>ament_cmake_gmock</test_depend>
<test_depend>sensor_msgs</test_depend>

Expand Down
45 changes: 43 additions & 2 deletions controller_interface/src/controller_interface_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "lifecycle_msgs/msg/state.hpp"
Expand All @@ -36,6 +35,7 @@ return_type ControllerInterfaceBase::init(
{
auto_declare<int>("update_rate", cm_update_rate);
auto_declare<bool>("is_async", false);
auto_declare<int>("thread_priority", 50);
}
catch (const std::exception & e)
{
Expand All @@ -56,7 +56,14 @@ return_type ControllerInterfaceBase::init(
std::bind(&ControllerInterfaceBase::on_configure, this, std::placeholders::_1));

node_->register_on_cleanup(
std::bind(&ControllerInterfaceBase::on_cleanup, this, std::placeholders::_1));
[this](const rclcpp_lifecycle::State & previous_state) -> CallbackReturn
{
if (is_async() && async_handler_ && async_handler_->is_running())
{
async_handler_->stop_thread();
}
return on_cleanup(previous_state);
});

node_->register_on_activate(
std::bind(&ControllerInterfaceBase::on_activate, this, std::placeholders::_1));
Expand Down Expand Up @@ -88,6 +95,20 @@ const rclcpp_lifecycle::State & ControllerInterfaceBase::configure()
update_rate_ = static_cast<unsigned int>(get_node()->get_parameter("update_rate").as_int());
is_async_ = get_node()->get_parameter("is_async").as_bool();
}
if (is_async_)
{
const unsigned int thread_priority =
static_cast<unsigned int>(get_node()->get_parameter("thread_priority").as_int());
RCLCPP_INFO_STREAM(
get_node()->get_logger(),
"Starting async handler with scheduler priority: " << thread_priority);
async_handler_ = std::make_unique<realtime_tools::AsyncFunctionHandler<return_type>>();
async_handler_->init(
std::bind(
&ControllerInterfaceBase::update, this, std::placeholders::_1, std::placeholders::_2),
thread_priority);
async_handler_->start_thread();
}

return get_node()->configure();
}
Expand All @@ -111,6 +132,19 @@ const rclcpp_lifecycle::State & ControllerInterfaceBase::get_lifecycle_state() c
return node_->get_current_state();
}

std::pair<bool, return_type> ControllerInterfaceBase::trigger_update(
const rclcpp::Time & time, const rclcpp::Duration & period)
{
if (is_async())
{
return async_handler_->trigger_async_callback(time, period);
}
else
{
return std::make_pair(true, update(time, period));
}
}

std::shared_ptr<rclcpp_lifecycle::LifecycleNode> ControllerInterfaceBase::get_node()
{
if (!node_.get())
Expand All @@ -135,4 +169,11 @@ bool ControllerInterfaceBase::is_async() const { return is_async_; }

const std::string & ControllerInterfaceBase::get_robot_description() const { return urdf_; }

void ControllerInterfaceBase::wait_for_trigger_update_to_finish()
{
if (is_async() && async_handler_ && async_handler_->is_running())
{
async_handler_->wait_for_trigger_cycle_to_finish();
}
}
} // namespace controller_interface
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <utility>
#include <vector>

#include "controller_interface/async_controller.hpp"
#include "controller_interface/chainable_controller_interface.hpp"
#include "controller_interface/controller_interface.hpp"
#include "controller_interface/controller_interface_base.hpp"
Expand Down Expand Up @@ -606,9 +605,6 @@ class ControllerManager : public rclcpp::Node
};

SwitchParams switch_params_;

std::unordered_map<std::string, std::unique_ptr<controller_interface::AsyncControllerThread>>
async_controller_threads_;
};

} // namespace controller_manager
Expand Down
Loading
Loading