Skip to content

Commit 8f0b668

Browse files
authored
Merge branch 'master' into add/async/detached/policy
2 parents ec77418 + bb87986 commit 8f0b668

File tree

14 files changed

+656
-33
lines changed

14 files changed

+656
-33
lines changed

controller_manager/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,20 @@ if(BUILD_TESTING)
184184
DESTINATION lib
185185
)
186186

187+
add_library(test_controller_failed_activate SHARED
188+
test/test_controller_failed_activate/test_controller_failed_activate.cpp
189+
)
190+
target_link_libraries(test_controller_failed_activate PUBLIC
191+
controller_manager
192+
)
193+
target_compile_definitions(test_controller_failed_activate PRIVATE "CONTROLLER_MANAGER_BUILDING_DLL")
194+
pluginlib_export_plugin_description_file(
195+
controller_interface test/test_controller_failed_activate/test_controller_failed_activate.xml)
196+
install(
197+
TARGETS test_controller_failed_activate
198+
DESTINATION lib
199+
)
200+
187201
ament_add_gmock(test_release_interfaces
188202
test/test_release_interfaces.cpp
189203
APPEND_ENV AMENT_PREFIX_PATH=${ament_index_build_path}_$<CONFIG>
@@ -192,6 +206,7 @@ if(BUILD_TESTING)
192206
controller_manager
193207
test_controller
194208
test_controller_with_interfaces
209+
test_controller_failed_activate
195210
ros2_control_test_assets::ros2_control_test_assets
196211
)
197212

controller_manager/doc/userdoc.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,12 @@ Hardware and Controller Errors
403403
If the hardware during it's ``read`` or ``write`` method returns ``return_type::ERROR``, the controller manager will stop all controllers that are using the hardware's command and state interfaces.
404404
Likewise, if a controller returns ``return_type::ERROR`` from its ``update`` method, the controller manager will deactivate the respective controller. In future, the controller manager will try to start any fallback controllers if available.
405405

406+
Factors that affect Determinism
407+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
408+
When run under the conditions determined in the above section, the determinism is assured up to the limitations of the hardware and the real-time kernel. However, there are some situations that can affect determinism:
409+
410+
* When a controller fails to activate, the controller_manager will call the methods ``prepare_command_mode_switch`` and ``perform_command_mode_switch`` to stop the started interfaces. These calls can cause jitter in the main control loop.
411+
406412
Support for Asynchronous Updates
407413
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
408414
For some applications, it is desirable to run a controller at a lower frequency than the controller manager's update rate. For instance, if the ``update_rate`` for the controller manager is 100Hz, the sum of the execution times of all controllers' ``update`` calls and hardware components ``read`` and ``write`` calls must be below 10ms. If one controller requires 15ms of execution time, it cannot be executed synchronously without affecting the overall system update rate. Running a controller asynchronously can be beneficial in this scenario.

controller_manager/src/controller_manager.cpp

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -393,21 +393,8 @@ rclcpp::NodeOptions get_cm_node_options()
393393
ControllerManager::ControllerManager(
394394
std::shared_ptr<rclcpp::Executor> executor, const std::string & manager_node_name,
395395
const std::string & node_namespace, const rclcpp::NodeOptions & options)
396-
: rclcpp::Node(manager_node_name, node_namespace, options),
397-
diagnostics_updater_(this),
398-
executor_(executor),
399-
loader_(
400-
std::make_shared<pluginlib::ClassLoader<controller_interface::ControllerInterface>>(
401-
kControllerInterfaceNamespace, kControllerInterfaceClassName)),
402-
chainable_loader_(
403-
std::make_shared<pluginlib::ClassLoader<controller_interface::ChainableControllerInterface>>(
404-
kControllerInterfaceNamespace, kChainableControllerInterfaceClassName)),
405-
cm_node_options_(options)
396+
: ControllerManager(executor, "", false, manager_node_name, node_namespace, options)
406397
{
407-
initialize_parameters();
408-
resource_manager_ =
409-
std::make_unique<hardware_interface::ResourceManager>(trigger_clock_, this->get_logger());
410-
init_controller_manager();
411398
}
412399

413400
ControllerManager::ControllerManager(
@@ -428,13 +415,18 @@ ControllerManager::ControllerManager(
428415
{
429416
initialize_parameters();
430417
hardware_interface::ResourceManagerParams params;
431-
params.robot_description = urdf;
418+
params.robot_description = robot_description_;
432419
params.clock = trigger_clock_;
433420
params.logger = this->get_logger();
434421
params.activate_all = activate_all_hw_components;
435422
params.update_rate = static_cast<unsigned int>(params_->update_rate);
436423
params.executor = executor_;
437-
resource_manager_ = std::make_unique<hardware_interface::ResourceManager>(params, true);
424+
params.allow_controller_activation_with_inactive_hardware =
425+
params_->defaults.allow_controller_activation_with_inactive_hardware;
426+
params.return_failed_hardware_names_on_return_deactivate_write_cycle_ =
427+
params_->defaults.deactivate_controllers_on_hardware_self_deactivate;
428+
resource_manager_ =
429+
std::make_unique<hardware_interface::ResourceManager>(params, !robot_description_.empty());
438430
init_controller_manager();
439431
}
440432

@@ -2189,6 +2181,7 @@ void ControllerManager::activate_controllers(
21892181
const std::vector<ControllerSpec> & rt_controller_list,
21902182
const std::vector<std::string> controllers_to_activate)
21912183
{
2184+
std::vector<std::string> failed_controllers_command_interfaces;
21922185
for (const auto & controller_name : controllers_to_activate)
21932186
{
21942187
auto found_it = std::find_if(
@@ -2297,35 +2290,38 @@ void ControllerManager::activate_controllers(
22972290
}
22982291
controller->assign_interfaces(std::move(command_loans), std::move(state_loans));
22992292

2293+
auto new_state = controller->get_lifecycle_state();
23002294
try
23012295
{
23022296
found_it->periodicity_statistics->reset();
23032297
found_it->execution_time_statistics->reset();
2304-
const auto new_state = controller->get_node()->activate();
2305-
if (new_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
2306-
{
2307-
RCLCPP_ERROR(
2308-
get_logger(),
2309-
"After activation, controller '%s' is in state '%s' (%d), expected '%s' (%d).",
2310-
controller->get_node()->get_name(), new_state.label().c_str(), new_state.id(),
2311-
hardware_interface::lifecycle_state_names::ACTIVE,
2312-
lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE);
2313-
}
2298+
new_state = controller->get_node()->activate();
23142299
}
23152300
catch (const std::exception & e)
23162301
{
23172302
RCLCPP_ERROR(
23182303
get_logger(), "Caught exception of type : %s while activating the controller '%s': %s",
23192304
typeid(e).name(), controller_name.c_str(), e.what());
2320-
controller->release_interfaces();
2321-
continue;
23222305
}
23232306
catch (...)
23242307
{
23252308
RCLCPP_ERROR(
23262309
get_logger(), "Caught unknown exception while activating the controller '%s'",
23272310
controller_name.c_str());
2311+
}
2312+
if (new_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
2313+
{
2314+
RCLCPP_ERROR(
2315+
get_logger(),
2316+
"After activation, controller '%s' is in state '%s' (%d), expected '%s' (%d). Releasing "
2317+
"interfaces!",
2318+
controller->get_node()->get_name(), new_state.label().c_str(), new_state.id(),
2319+
hardware_interface::lifecycle_state_names::ACTIVE,
2320+
lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE);
23282321
controller->release_interfaces();
2322+
failed_controllers_command_interfaces.insert(
2323+
failed_controllers_command_interfaces.end(), command_interface_names.begin(),
2324+
command_interface_names.end());
23292325
continue;
23302326
}
23312327

@@ -2337,6 +2333,18 @@ void ControllerManager::activate_controllers(
23372333
resource_manager_->make_controller_reference_interfaces_available(controller_name);
23382334
}
23392335
}
2336+
// Now prepare and perform the stop interface switching as this is needed for exclusive
2337+
// interfaces
2338+
if (
2339+
!failed_controllers_command_interfaces.empty() &&
2340+
(!resource_manager_->prepare_command_mode_switch({}, failed_controllers_command_interfaces) ||
2341+
!resource_manager_->perform_command_mode_switch({}, failed_controllers_command_interfaces)))
2342+
{
2343+
RCLCPP_ERROR(
2344+
get_logger(),
2345+
"Error switching back the interfaces in the hardware when the controller activation "
2346+
"failed.");
2347+
}
23402348
}
23412349

23422350
void ControllerManager::activate_controllers_asap(

controller_manager/src/controller_manager_parameters.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ controller_manager:
5454
}
5555
}
5656

57+
allow_controller_activation_with_inactive_hardware: {
58+
type: bool,
59+
default_value: false,
60+
read_only: true,
61+
description: "If true, controllers are allowed to claim resources from inactive hardware components. If false, controllers can only claim resources from active hardware components. \
62+
However, it is not recommended to set this parameter to true for the safety reasons with the hardware and unexpected movement, this is purely added for backward compatibility.",
63+
}
64+
65+
deactivate_controllers_on_hardware_self_deactivate: {
66+
type: bool,
67+
default_value: true,
68+
read_only: true,
69+
description: "If set to true, when a hardware component returns DEACTIVATE on the write cycle, controllers using those interfaces will be deactivated. When set to false, controllers using \
70+
those interfaces will continue to run. It is not recommended to set this parameter to false for safety reasons with hardware. This will be the default behaviour of the controller \
71+
manager and this parameter will be removed in future releases. Please use with caution.",
72+
}
73+
5774
diagnostics:
5875
threshold:
5976
controller_manager:
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2021 Department of Engineering Cybernetics, NTNU.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "test_controller_failed_activate.hpp"
16+
17+
#include <memory>
18+
#include <string>
19+
20+
#include "lifecycle_msgs/msg/transition.hpp"
21+
22+
namespace test_controller_failed_activate
23+
{
24+
TestControllerFailedActivate::TestControllerFailedActivate()
25+
: controller_interface::ControllerInterface()
26+
{
27+
}
28+
29+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
30+
TestControllerFailedActivate::on_init()
31+
{
32+
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
33+
}
34+
35+
controller_interface::return_type TestControllerFailedActivate::update(
36+
const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/)
37+
{
38+
return controller_interface::return_type::OK;
39+
}
40+
41+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
42+
TestControllerFailedActivate::on_configure(const rclcpp_lifecycle::State & /*previous_state&*/)
43+
{
44+
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
45+
}
46+
47+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
48+
TestControllerFailedActivate::on_activate(const rclcpp_lifecycle::State & /*previous_state&*/)
49+
{
50+
// Simply simulate a controller that can not be activated
51+
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
52+
}
53+
54+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
55+
TestControllerFailedActivate::on_cleanup(const rclcpp_lifecycle::State & /*previous_state*/)
56+
{
57+
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
58+
}
59+
60+
} // namespace test_controller_failed_activate
61+
62+
#include "pluginlib/class_list_macros.hpp"
63+
64+
PLUGINLIB_EXPORT_CLASS(
65+
test_controller_failed_activate::TestControllerFailedActivate,
66+
controller_interface::ControllerInterface)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2020 Department of Engineering Cybernetics, NTNU
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef TEST_CONTROLLER_FAILED_ACTIVATE__TEST_CONTROLLER_FAILED_ACTIVATE_HPP_
16+
#define TEST_CONTROLLER_FAILED_ACTIVATE__TEST_CONTROLLER_FAILED_ACTIVATE_HPP_
17+
18+
#include <memory>
19+
#include <string>
20+
21+
#include "controller_manager/controller_manager.hpp"
22+
23+
namespace test_controller_failed_activate
24+
{
25+
// Corresponds to the name listed within the pluginglib xml
26+
constexpr char TEST_CONTROLLER_WITH_INTERFACES_CLASS_NAME[] =
27+
"controller_manager/test_controller_failed_activate";
28+
// Corresponds to the command interface to claim
29+
constexpr char TEST_CONTROLLER_COMMAND_INTERFACE[] = "joint2/velocity";
30+
class TestControllerFailedActivate : public controller_interface::ControllerInterface
31+
{
32+
public:
33+
TestControllerFailedActivate();
34+
35+
virtual ~TestControllerFailedActivate() = default;
36+
37+
controller_interface::InterfaceConfiguration command_interface_configuration() const override
38+
{
39+
return controller_interface::InterfaceConfiguration{
40+
controller_interface::interface_configuration_type::INDIVIDUAL,
41+
{TEST_CONTROLLER_COMMAND_INTERFACE}};
42+
}
43+
44+
controller_interface::InterfaceConfiguration state_interface_configuration() const override
45+
{
46+
return controller_interface::InterfaceConfiguration{
47+
controller_interface::interface_configuration_type::NONE};
48+
}
49+
50+
controller_interface::return_type update(
51+
const rclcpp::Time & time, const rclcpp::Duration & period) override;
52+
53+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_init() override;
54+
55+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_configure(
56+
const rclcpp_lifecycle::State & previous_state) override;
57+
58+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_activate(
59+
const rclcpp_lifecycle::State & previous_state) override;
60+
61+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_cleanup(
62+
const rclcpp_lifecycle::State & previous_state) override;
63+
};
64+
65+
} // namespace test_controller_failed_activate
66+
67+
#endif // TEST_CONTROLLER_FAILED_ACTIVATE__TEST_CONTROLLER_FAILED_ACTIVATE_HPP_
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<library path="test_controller_failed_activate">
2+
3+
<class name="controller_manager/test_controller_failed_activate" type="test_controller_failed_activate::TestControllerFailedActivate" base_class_type="controller_interface::ControllerInterface">
4+
<description>
5+
Controller used for testing
6+
</description>
7+
</class>
8+
9+
</library>

0 commit comments

Comments
 (0)