Skip to content

Commit 2d31b1b

Browse files
mergify[bot]destoglbmagyarchristophfroehlich
authored
Enable setting of initial state in HW components (backport #1046) (#1064)
(cherry picked from commit cf4448d) --------- Co-authored-by: Dr. Denis <denis@stoglrobotics.de> Co-authored-by: Bence Magyar <bence.magyar.robotics@gmail.com> Co-authored-by: Christoph Froehlich <christoph.froehlich@ait.ac.at>
1 parent 3b592a6 commit 2d31b1b

File tree

5 files changed

+194
-101
lines changed

5 files changed

+194
-101
lines changed

controller_manager/doc/userdoc.rst

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,27 @@ The limits will be applied after you log out and in again.
3939
Parameters
4040
-----------
4141

42-
activate_components_on_start (optional; list<string>; default: empty)
43-
Define which hardware components should be activated when controller manager is started.
42+
hardware_components_initial_state
43+
Map of parameters for controlled lifecycle management of hardware components.
4444
The names of the components are defined as attribute of ``<ros2_control>``-tag in ``robot_description``.
45-
All other components will stay ``UNCONFIGURED``.
46-
If this and ``configure_components_on_start`` are empty, all available components will be activated.
47-
If this or ``configure_components_on_start`` are not empty, any component not in either list will be in unconfigured state.
45+
Hardware components found in ``robot_description``, but without explicit state definition will be immediately activated.
46+
Detailed explanation of each parameter is given below.
47+
The full structure of the map is given in the following example:
4848

49+
.. code-block:: yaml
4950
50-
configure_components_on_start (optional; list<string>; default: empty)
51-
Define which hardware components should be configured when controller manager is started.
52-
The names of the components are defined as attribute of ``<ros2_control>``-tag in ``robot_description``.
53-
All other components will stay ``UNCONFIGURED``.
54-
If this and ``activate_components_on_start`` are empty, all available components will be activated.
55-
If this or ``activate_components_on_start`` are not empty, any component not in either list will be in unconfigured state.
51+
hardware_components_initial_state:
52+
unconfigured:
53+
- "arm1"
54+
- "arm2"
55+
inactive:
56+
- "base3"
57+
58+
hardware_components_initial_state.unconfigured (optional; list<string>; default: empty)
59+
Defines which hardware components will be only loaded immediately when controller manager is started.
5660

61+
hardware_components_initial_state.inactive (optional; list<string>; default: empty)
62+
Defines which hardware components will be configured immediately when controller manager is started.
5763

5864
robot_description (mandatory; string)
5965
String with the URDF string as robot description.

controller_manager/src/controller_manager.cpp

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -367,46 +367,100 @@ void ControllerManager::init_resource_manager(const std::string & robot_descript
367367
// TODO(destogl): manage this when there is an error - CM should not die because URDF is wrong...
368368
resource_manager_->load_urdf(robot_description);
369369

370+
// Get all components and if they are not defined in parameters activate them automatically
371+
auto components_to_activate = resource_manager_->get_components_status();
372+
370373
using lifecycle_msgs::msg::State;
371374

372-
std::vector<std::string> configure_components_on_start = std::vector<std::string>({});
373-
if (get_parameter("configure_components_on_start", configure_components_on_start))
375+
auto set_components_to_state =
376+
[&](const std::string & parameter_name, rclcpp_lifecycle::State state)
374377
{
375-
RCLCPP_WARN_STREAM(
376-
get_logger(),
377-
"[Deprecated]: Usage of parameter \"activate_components_on_start\" is deprecated. Use "
378-
"hardware_spawner instead.");
379-
rclcpp_lifecycle::State inactive_state(
380-
State::PRIMARY_STATE_INACTIVE, hardware_interface::lifecycle_state_names::INACTIVE);
381-
for (const auto & component : configure_components_on_start)
378+
std::vector<std::string> components_to_set = std::vector<std::string>({});
379+
if (get_parameter(parameter_name, components_to_set))
382380
{
383-
resource_manager_->set_component_state(component, inactive_state);
381+
for (const auto & component : components_to_set)
382+
{
383+
if (component.empty())
384+
{
385+
continue;
386+
}
387+
if (components_to_activate.find(component) == components_to_activate.end())
388+
{
389+
RCLCPP_WARN(
390+
get_logger(), "Hardware component '%s' is unknown, therefore not set in '%s' state.",
391+
component.c_str(), state.label().c_str());
392+
}
393+
else
394+
{
395+
RCLCPP_INFO(
396+
get_logger(), "Setting component '%s' to '%s' state.", component.c_str(),
397+
state.label().c_str());
398+
resource_manager_->set_component_state(component, state);
399+
components_to_activate.erase(component);
400+
}
401+
}
384402
}
403+
};
404+
405+
// unconfigured (loaded only)
406+
set_components_to_state(
407+
"hardware_components_initial_state.unconfigured",
408+
rclcpp_lifecycle::State(
409+
State::PRIMARY_STATE_UNCONFIGURED, hardware_interface::lifecycle_state_names::UNCONFIGURED));
410+
411+
// inactive (configured)
412+
// BEGIN: Keep old functionality on for backwards compatibility
413+
std::vector<std::string> configure_components_on_start = std::vector<std::string>({});
414+
get_parameter("configure_components_on_start", configure_components_on_start);
415+
if (!configure_components_on_start.empty())
416+
{
417+
RCLCPP_WARN(
418+
get_logger(),
419+
"[Deprecated]: Parameter 'configure_components_on_start' is deprecated. "
420+
"Use 'hardware_interface_state_after_start.inactive' instead, to set component's initial "
421+
"state to 'inactive'. Don't use this parameters in combination with the new "
422+
"'hardware_interface_state_after_start' parameter structure.");
423+
set_components_to_state(
424+
"configure_components_on_start",
425+
rclcpp_lifecycle::State(
426+
State::PRIMARY_STATE_INACTIVE, hardware_interface::lifecycle_state_names::INACTIVE));
427+
}
428+
// END: Keep old functionality on humble backwards compatibility (Remove at the end of 2023)
429+
else
430+
{
431+
set_components_to_state(
432+
"hardware_components_initial_state.inactive",
433+
rclcpp_lifecycle::State(
434+
State::PRIMARY_STATE_INACTIVE, hardware_interface::lifecycle_state_names::INACTIVE));
385435
}
386436

437+
// BEGIN: Keep old functionality on for backwards compatibility
387438
std::vector<std::string> activate_components_on_start = std::vector<std::string>({});
388-
if (get_parameter("activate_components_on_start", activate_components_on_start))
439+
get_parameter("activate_components_on_start", activate_components_on_start);
440+
if (!activate_components_on_start.empty())
389441
{
390-
RCLCPP_WARN_STREAM(
442+
RCLCPP_WARN(
391443
get_logger(),
392-
"[Deprecated]: Usage of parameter \"activate_components_on_start\" is deprecated. Use "
393-
"hardware_spawner instead.");
444+
"[Deprecated]: Parameter 'activate_components_on_start' is deprecated. "
445+
"Components are activated per default. Don't use this parameters in combination with the new "
446+
"'hardware_components_initial_state' parameter structure.");
394447
rclcpp_lifecycle::State active_state(
395448
State::PRIMARY_STATE_ACTIVE, hardware_interface::lifecycle_state_names::ACTIVE);
396449
for (const auto & component : activate_components_on_start)
397450
{
398451
resource_manager_->set_component_state(component, active_state);
399452
}
400453
}
401-
// if both parameter are empty or non-existing preserve behavior where all components are
402-
// activated per default
403-
if (configure_components_on_start.empty() && activate_components_on_start.empty())
454+
// END: Keep old functionality on humble for backwards compatibility (Remove at the end of 2023)
455+
else
404456
{
405-
RCLCPP_WARN_STREAM(
406-
get_logger(),
407-
"[Deprecated]: Automatic activation of all hardware components will not be supported in the "
408-
"future anymore. Use hardware_spawner instead.");
409-
resource_manager_->activate_all_components();
457+
// activate all other components
458+
for (const auto & [component, state] : components_to_activate)
459+
{
460+
rclcpp_lifecycle::State active_state(
461+
State::PRIMARY_STATE_ACTIVE, hardware_interface::lifecycle_state_names::ACTIVE);
462+
resource_manager_->set_component_state(component, active_state);
463+
}
410464
}
411465
}
412466

controller_manager/test/test_hardware_management_srvs.cpp

Lines changed: 98 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ using hardware_interface::lifecycle_state_names::ACTIVE;
3535
using hardware_interface::lifecycle_state_names::FINALIZED;
3636
using hardware_interface::lifecycle_state_names::INACTIVE;
3737
using hardware_interface::lifecycle_state_names::UNCONFIGURED;
38+
using hardware_interface::lifecycle_state_names::UNKNOWN;
3839

3940
using ros2_control_test_assets::TEST_ACTUATOR_HARDWARE_CLASS_TYPE;
4041
using ros2_control_test_assets::TEST_ACTUATOR_HARDWARE_COMMAND_INTERFACES;
@@ -69,9 +70,11 @@ class TestControllerManagerHWManagementSrvs : public TestControllerManagerSrvs
6970
cm_->set_parameter(
7071
rclcpp::Parameter("robot_description", ros2_control_test_assets::minimal_robot_urdf));
7172
cm_->set_parameter(rclcpp::Parameter(
72-
"activate_components_on_start", std::vector<std::string>({TEST_ACTUATOR_HARDWARE_NAME})));
73+
"hardware_components_initial_state.unconfigured",
74+
std::vector<std::string>({TEST_SYSTEM_HARDWARE_NAME})));
7375
cm_->set_parameter(rclcpp::Parameter(
74-
"configure_components_on_start", std::vector<std::string>({TEST_SENSOR_HARDWARE_NAME})));
76+
"hardware_components_initial_state.inactive",
77+
std::vector<std::string>({TEST_SENSOR_HARDWARE_NAME})));
7578

7679
std::string robot_description = "";
7780
cm_->get_parameter("robot_description", robot_description);
@@ -201,38 +204,6 @@ class TestControllerManagerHWManagementSrvs : public TestControllerManagerSrvs
201204
}
202205
};
203206

204-
class TestControllerManagerHWManagementSrvsWithoutParams
205-
: public TestControllerManagerHWManagementSrvs
206-
{
207-
public:
208-
void SetUp() override
209-
{
210-
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
211-
cm_ = std::make_shared<controller_manager::ControllerManager>(
212-
std::make_unique<hardware_interface::ResourceManager>(), executor_, TEST_CM_NAME);
213-
run_updater_ = false;
214-
215-
// TODO(destogl): separate this to init_tests method where parameter can be set for each test
216-
// separately
217-
cm_->set_parameter(
218-
rclcpp::Parameter("robot_description", ros2_control_test_assets::minimal_robot_urdf));
219-
220-
std::string robot_description = "";
221-
cm_->get_parameter("robot_description", robot_description);
222-
if (robot_description.empty())
223-
{
224-
throw std::runtime_error(
225-
"Unable to initialize resource manager, no robot description found.");
226-
}
227-
228-
auto msg = std_msgs::msg::String();
229-
msg.data = robot_description;
230-
cm_->robot_description_callback(msg);
231-
232-
SetUpSrvsCMExecutor();
233-
}
234-
};
235-
236207
TEST_F(TestControllerManagerHWManagementSrvs, list_hardware_components)
237208
{
238209
// Default status after start:
@@ -390,6 +361,38 @@ TEST_F(TestControllerManagerHWManagementSrvs, selective_activate_deactivate_comp
390361
}));
391362
}
392363

364+
class TestControllerManagerHWManagementSrvsWithoutParams
365+
: public TestControllerManagerHWManagementSrvs
366+
{
367+
public:
368+
void SetUp() override
369+
{
370+
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
371+
cm_ = std::make_shared<controller_manager::ControllerManager>(
372+
std::make_unique<hardware_interface::ResourceManager>(), executor_, TEST_CM_NAME);
373+
run_updater_ = false;
374+
375+
// TODO(destogl): separate this to init_tests method where parameter can be set for each test
376+
// separately
377+
cm_->set_parameter(
378+
rclcpp::Parameter("robot_description", ros2_control_test_assets::minimal_robot_urdf));
379+
380+
std::string robot_description = "";
381+
cm_->get_parameter("robot_description", robot_description);
382+
if (robot_description.empty())
383+
{
384+
throw std::runtime_error(
385+
"Unable to initialize resource manager, no robot description found.");
386+
}
387+
388+
auto msg = std_msgs::msg::String();
389+
msg.data = robot_description;
390+
cm_->robot_description_callback(msg);
391+
392+
SetUpSrvsCMExecutor();
393+
}
394+
};
395+
393396
TEST_F(TestControllerManagerHWManagementSrvsWithoutParams, test_default_activation_of_all_hardware)
394397
{
395398
// "configure_components_on_start" and "activate_components_on_start" are not set (empty)
@@ -413,3 +416,64 @@ TEST_F(TestControllerManagerHWManagementSrvsWithoutParams, test_default_activati
413416
{{false, false, false, false}, {false, false, false, false, false, false, false}}, // system
414417
}));
415418
}
419+
420+
// BEGIN: Deprecated parameters
421+
class TestControllerManagerHWManagementSrvsOldParameters
422+
: public TestControllerManagerHWManagementSrvs
423+
{
424+
public:
425+
void SetUp() override
426+
{
427+
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
428+
cm_ = std::make_shared<controller_manager::ControllerManager>(
429+
std::make_unique<hardware_interface::ResourceManager>(), executor_, TEST_CM_NAME);
430+
run_updater_ = false;
431+
432+
cm_->set_parameter(
433+
rclcpp::Parameter("robot_description", ros2_control_test_assets::minimal_robot_urdf));
434+
cm_->set_parameter(rclcpp::Parameter(
435+
"activate_components_on_start", std::vector<std::string>({TEST_ACTUATOR_HARDWARE_NAME})));
436+
cm_->set_parameter(rclcpp::Parameter(
437+
"configure_components_on_start", std::vector<std::string>({TEST_SENSOR_HARDWARE_NAME})));
438+
439+
std::string robot_description = "";
440+
cm_->get_parameter("robot_description", robot_description);
441+
if (robot_description.empty())
442+
{
443+
throw std::runtime_error(
444+
"Unable to initialize resource manager, no robot description found.");
445+
}
446+
447+
auto msg = std_msgs::msg::String();
448+
msg.data = robot_description;
449+
cm_->robot_description_callback(msg);
450+
451+
SetUpSrvsCMExecutor();
452+
}
453+
};
454+
455+
TEST_F(TestControllerManagerHWManagementSrvsOldParameters, list_hardware_components)
456+
{
457+
// Default status after start:
458+
// checks if "configure_components_on_start" and "activate_components_on_start" are correctly read
459+
460+
list_hardware_components_and_check(
461+
// actuator, sensor, system
462+
std::vector<uint8_t>(
463+
{LFC_STATE::PRIMARY_STATE_ACTIVE, LFC_STATE::PRIMARY_STATE_INACTIVE,
464+
LFC_STATE::PRIMARY_STATE_UNCONFIGURED}),
465+
std::vector<std::string>({ACTIVE, INACTIVE, UNCONFIGURED}),
466+
std::vector<std::vector<std::vector<bool>>>({
467+
// is available
468+
{{true, true}, {true, true, true}}, // actuator
469+
{{}, {true}}, // sensor
470+
{{false, false, false, false}, {false, false, false, false, false, false, false}}, // system
471+
}),
472+
std::vector<std::vector<std::vector<bool>>>({
473+
// is claimed
474+
{{false, false}, {false, false, false}}, // actuator
475+
{{}, {false}}, // sensor
476+
{{false, false, false, false}, {false, false, false, false, false, false, false}}, // system
477+
}));
478+
}
479+
// END: Deprecated parameters

hardware_interface/include/hardware_interface/resource_manager.hpp

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
6161
* \param[in] validate_interfaces boolean argument indicating whether the exported
6262
* interfaces ought to be validated. Defaults to true.
6363
* \param[in] activate_all boolean argument indicating if all resources should be immediately
64-
* activated. Currently used only in tests. In typical applications use parameters
65-
* "autostart_components" and "autoconfigure_components" instead.
64+
* activated. Currently used only in tests.
6665
*/
6766
explicit ResourceManager(
6867
const std::string & urdf, bool validate_interfaces = true, bool activate_all = false);
@@ -375,7 +374,7 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
375374
* Reads from all active hardware components.
376375
*
377376
* Part of the real-time critical update loop.
378-
* It is realtime-safe if used hadware interfaces are implemented adequately.
377+
* It is realtime-safe if used hardware interfaces are implemented adequately.
379378
*/
380379
HardwareReadWriteStatus read(const rclcpp::Time & time, const rclcpp::Duration & period);
381380

@@ -384,18 +383,10 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
384383
* Writes to all active hardware components.
385384
*
386385
* Part of the real-time critical update loop.
387-
* It is realtime-safe if used hadware interfaces are implemented adequately.
386+
* It is realtime-safe if used hardware interfaces are implemented adequately.
388387
*/
389388
HardwareReadWriteStatus write(const rclcpp::Time & time, const rclcpp::Duration & period);
390389

391-
/// Activates all available hardware components in the system.
392-
/**
393-
* All available hardware components int the ros2_control framework are activated.
394-
* This is used to preserve default behavior from previous versions where all hardware components
395-
* are activated per default.
396-
*/
397-
void activate_all_components();
398-
399390
/// Checks whether a command interface is registered under the given key.
400391
/**
401392
* \param[in] key string identifying the interface to check.

hardware_interface/src/resource_manager.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,28 +1387,6 @@ void ResourceManager::validate_storage(
13871387
throw std::runtime_error(err_msg);
13881388
}
13891389
}
1390-
13911390
// END: private methods
13921391

1393-
// Temporary method to keep old interface and reduce framework changes in the PRs
1394-
void ResourceManager::activate_all_components()
1395-
{
1396-
using lifecycle_msgs::msg::State;
1397-
rclcpp_lifecycle::State active_state(
1398-
State::PRIMARY_STATE_ACTIVE, hardware_interface::lifecycle_state_names::ACTIVE);
1399-
1400-
for (auto & component : resource_storage_->actuators_)
1401-
{
1402-
set_component_state(component.get_name(), active_state);
1403-
}
1404-
for (auto & component : resource_storage_->sensors_)
1405-
{
1406-
set_component_state(component.get_name(), active_state);
1407-
}
1408-
for (auto & component : resource_storage_->systems_)
1409-
{
1410-
set_component_state(component.get_name(), active_state);
1411-
}
1412-
}
1413-
14141392
} // namespace hardware_interface

0 commit comments

Comments
 (0)