Skip to content

Commit 289f6e0

Browse files
authored
Implemented a controller coordinator (#63)
* Fixed stale demos * Update package versions * Fix obsolete tf2 header * Added a coordinator to activate controllers/hardware * Address pr comments
1 parent c8d798e commit 289f6e0

File tree

30 files changed

+401
-11
lines changed

30 files changed

+401
-11
lines changed

auv_control_demos/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog for package auv_control_demos
22

3+
## 0.3.0 (2025-06-07)
4+
35
## 0.2.1 (2025-06-03)
46

57
- Updates the individual_controller and chained_controllers demos to use the

auv_control_demos/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<package format="3">
44

55
<name>auv_control_demos</name>
6-
<version>0.2.1</version>
6+
<version>0.3.0</version>
77
<description>Example package that includes demos for using auv_controllers in individual and chained modes</description>
88

99
<maintainer email="mitchcol@oregonstate.edu">Colin Mitchell</maintainer>

auv_control_msgs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog for package auv_control_msgs
22

3+
## 0.3.0 (2025-06-07)
4+
35
## 0.2.1 (2025-06-03)
46

57
## 0.2.0 (2025-05-03)

auv_control_msgs/package.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<?xml version="1.0"?>
22
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
33
<package format="3">
4+
45
<name>auv_control_msgs</name>
5-
<version>0.2.1</version>
6+
<version>0.3.0</version>
67
<description>Custom messages for AUV controllers</description>
78

89
<maintainer email="rakeshvivek97@gmail.com">Rakesh Vivekanandan</maintainer>

auv_controllers/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog for package auv_controllers
22

3+
## 0.3.0 (2025-06-07)
4+
5+
- Implements the controller coordinator
6+
37
## 0.2.1 (2025-06-03)
48

59
- Fixes the auv_control_demos configurations

auv_controllers/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<package format="3">
44

55
<name>auv_controllers</name>
6-
<version>0.2.1</version>
6+
<version>0.3.0</version>
77
<description>Meta package for auv_controllers</description>
88

99
<maintainer email="evanp922@gmail.com">Evan Palmer</maintainer>

controller_common/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog for package controller_common
22

3+
## 0.3.0 (2025-06-07)
4+
35
## 0.2.1 (2025-06-03)
46

57
## 0.2.0 (2025-05-03)

controller_common/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<package format="3">
44

55
<name>controller_common</name>
6-
<version>0.2.1</version>
6+
<version>0.3.0</version>
77
<description>Common interfaces for controllers used in this project</description>
88

99
<maintainer email="evanp922@gmail.com">Evan Palmer</maintainer>

controller_coordinator/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Changelog for package controller_coordinator
2+
3+
## 0.3.0 (2025-06-07)
4+
5+
- Implements a simple service endpoint for activating and deactivating a
6+
control system.

controller_coordinator/CMakeLists.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
cmake_minimum_required(VERSION 3.23)
2+
project(controller_coordinator)
3+
4+
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
5+
add_compile_options(-Wall -Wextra -Wpedantic)
6+
endif()
7+
8+
include(GNUInstallDirs)
9+
10+
find_package(rclcpp REQUIRED)
11+
find_package(controller_manager_msgs REQUIRED)
12+
find_package(ament_cmake REQUIRED)
13+
find_package(generate_parameter_library REQUIRED)
14+
find_package(std_srvs REQUIRED)
15+
16+
generate_parameter_library(controller_coordinator_parameters src/coordinator_parameters.yaml)
17+
18+
add_executable(controller_coordinator)
19+
target_sources(controller_coordinator PRIVATE src/coordinator.cpp)
20+
21+
target_compile_features(controller_coordinator PUBLIC cxx_std_20)
22+
target_link_libraries(
23+
controller_coordinator
24+
PUBLIC
25+
controller_coordinator_parameters
26+
rclcpp::rclcpp
27+
${controller_manager_msgs_TARGETS}
28+
${std_srvs_TARGETS}
29+
)
30+
31+
install(
32+
TARGETS
33+
controller_coordinator
34+
controller_coordinator_parameters
35+
DESTINATION
36+
lib/controller_coordinator
37+
)
38+
39+
ament_package()

controller_coordinator/LICENSE

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Permission is hereby granted, free of charge, to any person obtaining a copy
2+
of this software and associated documentation files (the "Software"), to deal
3+
in the Software without restriction, including without limitation the rights
4+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5+
copies of the Software, and to permit persons to whom the Software is
6+
furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in
9+
all copies or substantial portions of the Software.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
14+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
17+
THE SOFTWARE.

controller_coordinator/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Controller Coordinator
2+
3+
The controller coordinator is a high-level interface for activating and
4+
deactivating a control system. This is useful in scenarios where you want to
5+
switch between a custom control framework and a company-provided control
6+
framework.
7+
8+
## Clients
9+
10+
- controller_manager/set_hardware_component_state [controller_manager_msgs::srv::SetHardwareComponentState]
11+
- controller_manager/switch_controller [controller_manager_msgs::srv::SwitchController]
12+
13+
## Services
14+
15+
- controller_coordinator/activate [std_srvs/srv/SetBool]

controller_coordinator/package.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
5+
<name>controller_coordinator</name>
6+
<version>0.3.0</version>
7+
<description>A high-level node used to load and activate/deactivate control systems</description>
8+
9+
<maintainer email="evanp922@gmail.com">Evan Palmer</maintainer>
10+
<license>MIT</license>
11+
12+
<url type="repository">https://github.com/Robotic-Decision-Making-Lab/auv_controllers.git</url>
13+
<url type="bugtracker">https://github.com/Robotic-Decision-Making-Lab/auv_controllers/issues</url>
14+
15+
<author>Evan Palmer</author>
16+
17+
<buildtool_depend>ament_cmake</buildtool_depend>
18+
19+
<depend>rclcpp</depend>
20+
<depend>std_srvs</depend>
21+
<depend>controller_manager_msgs</depend>
22+
<depend>generate_parameter_library</depend>
23+
24+
<export>
25+
<build_type>ament_cmake</build_type>
26+
</export>
27+
</package>
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright 2025, Evan Palmer
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in all
11+
// copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
// SOFTWARE.
20+
21+
#include "coordinator.hpp"
22+
23+
#include <ranges>
24+
25+
#include "lifecycle_msgs/msg/state.hpp"
26+
27+
namespace coordinator
28+
{
29+
30+
ControllerCoordinator::ControllerCoordinator()
31+
: rclcpp::Node("controller_coordinator"),
32+
activate_hardware_request_(std::make_shared<controller_manager_msgs::srv::SetHardwareComponentState::Request>()),
33+
deactivate_hardware_request_(std::make_shared<controller_manager_msgs::srv::SetHardwareComponentState::Request>()),
34+
activate_controllers_request_(std::make_shared<controller_manager_msgs::srv::SwitchController::Request>()),
35+
deactivate_controllers_request_(std::make_shared<controller_manager_msgs::srv::SwitchController::Request>())
36+
{
37+
param_listener_ = std::make_shared<controller_coordinator::ParamListener>(this->get_node_parameters_interface());
38+
params_ = param_listener_->get_params();
39+
40+
client_callback_group_ = this->create_callback_group(rclcpp::CallbackGroupType::Reentrant, true);
41+
42+
// helper function used to wait for services to come up
43+
// this will block indefinitely
44+
auto wait_for_service = [this](const auto & client, const std::string & service_name) {
45+
while (!client->wait_for_service(std::chrono::seconds(1))) {
46+
RCLCPP_INFO(this->get_logger(), "Waiting for %s service to come up", service_name.c_str()); // NOLINT
47+
}
48+
RCLCPP_INFO(this->get_logger(), "%s service available", service_name.c_str()); // NOLINT
49+
};
50+
51+
// create clients
52+
const std::string hardware_service = "controller_manager/set_hardware_component_state";
53+
hardware_client_ = this->create_client<controller_manager_msgs::srv::SetHardwareComponentState>(
54+
hardware_service, rclcpp::ServicesQoS(), client_callback_group_);
55+
wait_for_service(hardware_client_, hardware_service);
56+
57+
const std::string switch_controller_name = "controller_manager/switch_controller";
58+
switch_controller_client_ = this->create_client<controller_manager_msgs::srv::SwitchController>(
59+
switch_controller_name, rclcpp::ServicesQoS(), client_callback_group_);
60+
wait_for_service(switch_controller_client_, switch_controller_name);
61+
62+
// pre-configure the hardware activation/deactivation requests
63+
activate_hardware_request_->name = params_.hardware_interface;
64+
activate_hardware_request_->target_state.id = lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE;
65+
66+
deactivate_hardware_request_->name = params_.hardware_interface;
67+
deactivate_hardware_request_->target_state.id = lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE;
68+
69+
// pre-configure the controller activation/deactivation requests
70+
activate_controllers_request_->activate_controllers = params_.controller_sequence;
71+
activate_controllers_request_->strictness = controller_manager_msgs::srv::SwitchController::Request::STRICT;
72+
activate_controllers_request_->activate_asap = true;
73+
activate_controllers_request_->timeout = rclcpp::Duration::from_seconds(params_.timeout);
74+
75+
deactivate_controllers_request_->deactivate_controllers = params_.controller_sequence;
76+
deactivate_controllers_request_->strictness = controller_manager_msgs::srv::SwitchController::Request::STRICT;
77+
deactivate_controllers_request_->activate_asap = true;
78+
deactivate_controllers_request_->timeout = rclcpp::Duration::from_seconds(params_.timeout);
79+
80+
// create a service endpoint for users to activate or deactivate their system
81+
service_callback_group_ = this->create_callback_group(rclcpp::CallbackGroupType::Reentrant, true);
82+
activate_system_service_ = this->create_service<std_srvs::srv::SetBool>(
83+
"~/activate",
84+
[this](
85+
const std::shared_ptr<rmw_request_id_t> /*request_header*/,
86+
const std::shared_ptr<std_srvs::srv::SetBool::Request> request,
87+
const std::shared_ptr<std_srvs::srv::SetBool::Response> response) {
88+
response->success = true;
89+
if (request->data) {
90+
RCLCPP_INFO(this->get_logger(), "Activating thruster hardware interface and controllers"); // NOLINT
91+
92+
// activate the hardware interface
93+
hardware_client_->async_send_request(
94+
activate_hardware_request_,
95+
[this, &response](
96+
rclcpp::Client<controller_manager_msgs::srv::SetHardwareComponentState>::SharedFuture result_response) {
97+
const auto & result = result_response.get();
98+
if (result->ok) {
99+
RCLCPP_INFO(this->get_logger(), "Successfully activated thruster hardware interface"); // NOLINT
100+
} else {
101+
RCLCPP_ERROR(this->get_logger(), "Failed to activate thruster hardware interface"); // NOLINT
102+
response->success = false;
103+
response->message = "Failed to activate thruster hardware interface";
104+
}
105+
});
106+
107+
// activate the controllers
108+
switch_controller_client_->async_send_request(
109+
activate_controllers_request_,
110+
[this,
111+
&response](rclcpp::Client<controller_manager_msgs::srv::SwitchController>::SharedFuture result_response) {
112+
const auto & result = result_response.get();
113+
if (result->ok) {
114+
RCLCPP_INFO(this->get_logger(), "Successfully activated controllers"); // NOLINT
115+
} else {
116+
RCLCPP_ERROR(this->get_logger(), "Failed to activate controllers"); // NOLINT
117+
response->success = false;
118+
response->message = "Failed to activate controllers";
119+
}
120+
});
121+
} else {
122+
RCLCPP_INFO(this->get_logger(), "Deactivating controllers and thruster hardware interface"); // NOLINT
123+
124+
// deactivate the hardware interface
125+
hardware_client_->async_send_request(
126+
deactivate_hardware_request_,
127+
[this, &response](
128+
rclcpp::Client<controller_manager_msgs::srv::SetHardwareComponentState>::SharedFuture result_response) {
129+
const auto & result = result_response.get();
130+
if (result->ok) {
131+
RCLCPP_INFO(this->get_logger(), "Successfully deactivated thruster hardware interface"); // NOLINT
132+
} else {
133+
RCLCPP_ERROR(this->get_logger(), "Failed to deactivate thruster hardware interface"); // NOLINT
134+
response->success = false;
135+
response->message = "Failed to deactivate thruster hardware interface";
136+
}
137+
});
138+
139+
// deactivate the controllers
140+
switch_controller_client_->async_send_request(
141+
deactivate_controllers_request_,
142+
[this,
143+
&response](rclcpp::Client<controller_manager_msgs::srv::SwitchController>::SharedFuture result_response) {
144+
const auto & result = result_response.get();
145+
if (result->ok) {
146+
RCLCPP_INFO(this->get_logger(), "Successfully deactivated controllers"); // NOLINT
147+
} else {
148+
RCLCPP_ERROR(this->get_logger(), "Failed to deactivate controllers"); // NOLINT
149+
response->success = false;
150+
response->message = "Failed to deactivate controllers";
151+
}
152+
});
153+
}
154+
},
155+
rclcpp::ServicesQoS(),
156+
service_callback_group_);
157+
}
158+
159+
} // namespace coordinator
160+
161+
auto main(int argc, char * argv[]) -> int
162+
{
163+
rclcpp::init(argc, argv);
164+
rclcpp::executors::MultiThreadedExecutor executor;
165+
auto node = std::make_shared<coordinator::ControllerCoordinator>();
166+
executor.add_node(node->get_node_base_interface());
167+
executor.spin();
168+
rclcpp::shutdown();
169+
return 0;
170+
}

0 commit comments

Comments
 (0)