Skip to content

Commit 1506412

Browse files
authored
Add ControllerInterfaceParams to initialize the Controllers (#2390)
1 parent aebc2b8 commit 1506412

File tree

7 files changed

+206
-18
lines changed

7 files changed

+206
-18
lines changed

controller_interface/include/controller_interface/controller_interface_base.hpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
#include <memory>
1919
#include <string>
20+
#include <unordered_map>
2021
#include <utility>
2122
#include <vector>
2223

2324
#include "realtime_tools/async_function_handler.hpp"
2425

26+
#include "controller_interface/controller_interface_params.hpp"
2527
#include "hardware_interface/handle.hpp"
2628
#include "hardware_interface/introspection.hpp"
2729
#include "hardware_interface/loaned_command_interface.hpp"
@@ -151,10 +153,15 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
151153
*/
152154
virtual void release_interfaces();
153155

156+
[[deprecated(
157+
"Use the init(const controller_interface::ControllerInterfaceParams & params) method instead. "
158+
"This method will be removed in the future ROS 2 releases.")]]
154159
return_type init(
155160
const std::string & controller_name, const std::string & urdf, unsigned int cm_update_rate,
156161
const std::string & node_namespace, const rclcpp::NodeOptions & node_options);
157162

163+
return_type init(const controller_interface::ControllerInterfaceParams & params);
164+
158165
/// Custom configure method to read additional parameters for controller-nodes
159166
/*
160167
* Override default implementation for configure of LifecycleNode to get parameters.
@@ -201,6 +208,17 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
201208

202209
const std::string & get_robot_description() const;
203210

211+
/**
212+
* Get the unordered map of joint limits that are defined in the robot description.
213+
*/
214+
const std::unordered_map<std::string, joint_limits::JointLimits> & get_hard_joint_limits() const;
215+
216+
/**
217+
* Get the unordered map of soft joint limits that are defined in the robot description.
218+
*/
219+
const std::unordered_map<std::string, joint_limits::SoftJointLimits> & get_soft_joint_limits()
220+
const;
221+
204222
/**
205223
* Method used by the controller_manager for base NodeOptions to instantiate the Lifecycle node
206224
* of the controller upon loading the controller.
@@ -358,9 +376,8 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
358376

359377
std::shared_ptr<rclcpp_lifecycle::LifecycleNode> node_;
360378
std::unique_ptr<realtime_tools::AsyncFunctionHandler<return_type>> async_handler_;
361-
unsigned int update_rate_ = 0;
362379
bool is_async_ = false;
363-
std::string urdf_ = "";
380+
controller_interface::ControllerInterfaceParams ctrl_itf_params_;
364381
std::atomic_bool skip_async_triggers_ = false;
365382
ControllerUpdateStats trigger_stats_;
366383

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2025 ros2_control Development Team
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 CONTROLLER_INTERFACE__CONTROLLER_INTERFACE_PARAMS_HPP_
16+
#define CONTROLLER_INTERFACE__CONTROLLER_INTERFACE_PARAMS_HPP_
17+
18+
#include <string>
19+
#include <unordered_map>
20+
21+
#include "joint_limits/joint_limits.hpp"
22+
#include "rclcpp/node_options.hpp"
23+
24+
/**
25+
* @brief Parameters for the Controller Interface
26+
* This struct holds the parameters required to initialize a controller interface.
27+
*
28+
* @var controller_name Name of the controller.
29+
* @var robot_description The URDF or SDF description of the robot.
30+
* @var cm_update_rate The update rate of the controller manager in Hz.
31+
* @var node_namespace The namespace for the controller node.
32+
* @var node_options Options for the controller node.
33+
* @var joint_limits A map of joint names to their limits.
34+
* @var soft_joint_limits A map of joint names to their soft limits.
35+
*
36+
* This struct is used to pass parameters to the controller interface during initialization.
37+
* It allows for easy configuration of the controller's behavior and interaction with the robot's
38+
* joints.
39+
*/
40+
namespace controller_interface
41+
{
42+
43+
struct ControllerInterfaceParams
44+
{
45+
ControllerInterfaceParams() = default;
46+
47+
std::string controller_name = "";
48+
std::string robot_description = "";
49+
unsigned int update_rate = 0;
50+
51+
std::string node_namespace = "";
52+
rclcpp::NodeOptions node_options = rclcpp::NodeOptions();
53+
54+
std::unordered_map<std::string, joint_limits::JointLimits> hard_joint_limits = {};
55+
std::unordered_map<std::string, joint_limits::SoftJointLimits> soft_joint_limits = {};
56+
};
57+
58+
} // namespace controller_interface
59+
60+
#endif // CONTROLLER_INTERFACE__CONTROLLER_INTERFACE_PARAMS_HPP_

controller_interface/src/controller_interface_base.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,29 @@ return_type ControllerInterfaceBase::init(
4343
const std::string & controller_name, const std::string & urdf, unsigned int cm_update_rate,
4444
const std::string & node_namespace, const rclcpp::NodeOptions & node_options)
4545
{
46-
urdf_ = urdf;
47-
update_rate_ = cm_update_rate;
46+
controller_interface::ControllerInterfaceParams params;
47+
params.controller_name = controller_name;
48+
params.robot_description = urdf;
49+
params.update_rate = cm_update_rate;
50+
params.node_namespace = node_namespace;
51+
params.node_options = node_options;
52+
53+
return init(params);
54+
}
55+
56+
return_type ControllerInterfaceBase::init(
57+
const controller_interface::ControllerInterfaceParams & params)
58+
{
59+
ctrl_itf_params_ = params;
4860
node_ = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
49-
controller_name, node_namespace, node_options,
61+
ctrl_itf_params_.controller_name, ctrl_itf_params_.node_namespace,
62+
ctrl_itf_params_.node_options,
5063
false); // disable LifecycleNode service interfaces
5164

5265
try
5366
{
5467
// no rclcpp::ParameterValue unsigned int specialization
55-
auto_declare<int>("update_rate", static_cast<int>(update_rate_));
68+
auto_declare<int>("update_rate", static_cast<int>(ctrl_itf_params_.update_rate));
5669

5770
auto_declare<bool>("is_async", false);
5871
auto_declare<int>("thread_priority", 50);
@@ -149,17 +162,17 @@ const rclcpp_lifecycle::State & ControllerInterfaceBase::configure()
149162
RCLCPP_ERROR(get_node()->get_logger(), "Update rate cannot be a negative value!");
150163
return get_lifecycle_state();
151164
}
152-
if (update_rate_ != 0u && update_rate > update_rate_)
165+
if (ctrl_itf_params_.update_rate != 0u && update_rate > ctrl_itf_params_.update_rate)
153166
{
154167
RCLCPP_WARN(
155168
get_node()->get_logger(),
156169
"The update rate of the controller : '%ld Hz' cannot be higher than the update rate of the "
157170
"controller manager : '%d Hz'. Setting it to the update rate of the controller manager.",
158-
update_rate, update_rate_);
171+
update_rate, ctrl_itf_params_.update_rate);
159172
}
160173
else
161174
{
162-
update_rate_ = static_cast<unsigned int>(update_rate);
175+
ctrl_itf_params_.update_rate = static_cast<unsigned int>(update_rate);
163176
}
164177
is_async_ = get_node()->get_parameter("is_async").as_bool();
165178
}
@@ -273,11 +286,29 @@ std::shared_ptr<const rclcpp_lifecycle::LifecycleNode> ControllerInterfaceBase::
273286
return node_;
274287
}
275288

276-
unsigned int ControllerInterfaceBase::get_update_rate() const { return update_rate_; }
289+
unsigned int ControllerInterfaceBase::get_update_rate() const
290+
{
291+
return ctrl_itf_params_.update_rate;
292+
}
277293

278294
bool ControllerInterfaceBase::is_async() const { return is_async_; }
279295

280-
const std::string & ControllerInterfaceBase::get_robot_description() const { return urdf_; }
296+
const std::string & ControllerInterfaceBase::get_robot_description() const
297+
{
298+
return ctrl_itf_params_.robot_description;
299+
}
300+
301+
const std::unordered_map<std::string, joint_limits::JointLimits> &
302+
ControllerInterfaceBase::get_hard_joint_limits() const
303+
{
304+
return ctrl_itf_params_.hard_joint_limits;
305+
}
306+
307+
const std::unordered_map<std::string, joint_limits::SoftJointLimits> &
308+
ControllerInterfaceBase::get_soft_joint_limits() const
309+
{
310+
return ctrl_itf_params_.soft_joint_limits;
311+
}
281312

282313
void ControllerInterfaceBase::wait_for_trigger_update_to_finish()
283314
{

controller_interface/test/test_controller_interface.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,21 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
104104
node_options_arguments.push_back("-p");
105105
node_options_arguments.push_back("update_rate:=2812");
106106
node_options = node_options.arguments(node_options_arguments);
107-
ASSERT_EQ(
108-
controller.init(TEST_CONTROLLER_NAME, "", 5000.0, "", node_options),
109-
controller_interface::return_type::OK);
107+
controller_interface::ControllerInterfaceParams params;
108+
params.controller_name = TEST_CONTROLLER_NAME;
109+
params.robot_description = "";
110+
params.update_rate = 5000; // set a different update rate than the one in the node options
111+
params.node_namespace = "";
112+
params.node_options = node_options;
113+
joint_limits::JointLimits joint_limits;
114+
joint_limits.has_velocity_limits = true;
115+
joint_limits.max_velocity = 1.0;
116+
params.hard_joint_limits["joint1"] = joint_limits;
117+
joint_limits::SoftJointLimits soft_joint_limits;
118+
soft_joint_limits.min_position = -1.0;
119+
soft_joint_limits.max_position = 1.0;
120+
params.soft_joint_limits["joint1"] = soft_joint_limits;
121+
ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
110122

111123
// initialize executor to be able to get parameter update
112124
auto executor =
@@ -122,6 +134,21 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
122134

123135
// update_rate is set to controller_manager's rate
124136
ASSERT_EQ(controller.get_update_rate(), 5000u);
137+
const auto hard_limits = controller.get_hard_joint_limits();
138+
const auto soft_limits = controller.get_soft_joint_limits();
139+
ASSERT_THAT(hard_limits, testing::SizeIs(1));
140+
ASSERT_TRUE(hard_limits.find("joint1") != hard_limits.end());
141+
ASSERT_THAT(hard_limits.at("joint1").max_velocity, joint_limits.max_velocity);
142+
ASSERT_TRUE(hard_limits.at("joint1").has_velocity_limits);
143+
ASSERT_FALSE(hard_limits.at("joint1").has_position_limits);
144+
ASSERT_FALSE(hard_limits.at("joint1").has_acceleration_limits);
145+
ASSERT_FALSE(hard_limits.at("joint1").has_deceleration_limits);
146+
ASSERT_FALSE(hard_limits.at("joint1").has_jerk_limits);
147+
ASSERT_FALSE(hard_limits.at("joint1").has_effort_limits);
148+
ASSERT_THAT(soft_limits, testing::SizeIs(1));
149+
ASSERT_TRUE(soft_limits.find("joint1") != soft_limits.end());
150+
ASSERT_THAT(soft_limits.at("joint1").min_position, soft_joint_limits.min_position);
151+
ASSERT_THAT(soft_limits.at("joint1").max_position, soft_joint_limits.max_position);
125152

126153
// Even after configure is 0
127154
controller.configure();
@@ -156,6 +183,23 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
156183
controller.configure();
157184
ASSERT_EQ(controller.get_update_rate(), 623u);
158185

186+
// Should stay same after multiple cleanups as it is set during initialization
187+
const auto hard_limits_final = controller.get_hard_joint_limits();
188+
const auto soft_limits_final = controller.get_soft_joint_limits();
189+
ASSERT_THAT(hard_limits_final, testing::SizeIs(1));
190+
ASSERT_TRUE(hard_limits_final.find("joint1") != hard_limits_final.end());
191+
ASSERT_THAT(hard_limits_final.at("joint1").max_velocity, joint_limits.max_velocity);
192+
ASSERT_TRUE(hard_limits_final.at("joint1").has_velocity_limits);
193+
ASSERT_FALSE(hard_limits_final.at("joint1").has_position_limits);
194+
ASSERT_FALSE(hard_limits_final.at("joint1").has_acceleration_limits);
195+
ASSERT_FALSE(hard_limits_final.at("joint1").has_deceleration_limits);
196+
ASSERT_FALSE(hard_limits_final.at("joint1").has_jerk_limits);
197+
ASSERT_FALSE(hard_limits_final.at("joint1").has_effort_limits);
198+
ASSERT_THAT(soft_limits_final, testing::SizeIs(1));
199+
ASSERT_TRUE(soft_limits_final.find("joint1") != soft_limits_final.end());
200+
ASSERT_THAT(soft_limits_final.at("joint1").min_position, soft_joint_limits.min_position);
201+
ASSERT_THAT(soft_limits_final.at("joint1").max_position, soft_joint_limits.max_position);
202+
159203
executor->cancel();
160204
controller.get_node()->shutdown();
161205
rclcpp::shutdown();

controller_manager/src/controller_manager.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,10 +2024,15 @@ controller_interface::ControllerInterfaceBaseSharedPtr ControllerManager::add_co
20242024
// Catch whatever exception the controller might throw
20252025
try
20262026
{
2027-
if (
2028-
controller.c->init(
2029-
controller.info.name, robot_description_, get_update_rate(), get_namespace(),
2030-
controller_node_options) == controller_interface::return_type::ERROR)
2027+
controller_interface::ControllerInterfaceParams controller_params;
2028+
controller_params.controller_name = controller.info.name;
2029+
controller_params.robot_description = robot_description_;
2030+
controller_params.update_rate = get_update_rate();
2031+
controller_params.node_namespace = get_namespace();
2032+
controller_params.node_options = controller_node_options;
2033+
controller_params.hard_joint_limits = resource_manager_->get_hard_joint_limits();
2034+
controller_params.soft_joint_limits = resource_manager_->get_soft_joint_limits();
2035+
if (controller.c->init(controller_params) == controller_interface::return_type::ERROR)
20312036
{
20322037
to.clear();
20332038
RCLCPP_ERROR(

hardware_interface/include/hardware_interface/resource_manager.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,19 @@ class ResourceManager
516516
*/
517517
const std::unordered_map<std::string, HardwareComponentInfo> & get_components_status();
518518

519+
/// Return the unordered map of hard joint limits.
520+
/**
521+
* \return unordered map of hard joint limits.
522+
*/
523+
const std::unordered_map<std::string, joint_limits::JointLimits> & get_hard_joint_limits() const;
524+
525+
/// Return the unordered map of soft joint limits.
526+
/**
527+
* \return unordered map of soft joint limits.
528+
*/
529+
const std::unordered_map<std::string, joint_limits::SoftJointLimits> & get_soft_joint_limits()
530+
const;
531+
519532
/// Prepare the hardware components for a new command interface mode
520533
/**
521534
* Hardware components are asked to prepare a new command interface claim.

hardware_interface/src/resource_manager.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ class ResourceStorage
739739
for (const auto & [joint_name, limits] : hw_info.limits)
740740
{
741741
std::vector<joint_limits::SoftJointLimits> soft_limits;
742+
hard_joint_limits_.insert({joint_name, limits});
742743
const std::vector<joint_limits::JointLimits> hard_limits{limits};
743744
joint_limits::JointInterfacesCommandLimiterData data;
744745
data.set_joint_name(joint_name);
@@ -747,6 +748,7 @@ class ResourceStorage
747748
if (hw_info.soft_limits.find(joint_name) != hw_info.soft_limits.end())
748749
{
749750
soft_limits = {hw_info.soft_limits.at(joint_name)};
751+
soft_joint_limits_.insert({joint_name, hw_info.soft_limits.at(joint_name)});
750752
RCLCPP_INFO(
751753
get_logger(), "Using SoftJointLimiter for joint '%s' in hardware '%s' : '%s'",
752754
joint_name.c_str(), hw_info.name.c_str(), soft_limits[0].to_string().c_str());
@@ -1347,6 +1349,10 @@ class ResourceStorage
13471349

13481350
std::string robot_description_;
13491351

1352+
// Unordered map of the hard and soft limits for the joints
1353+
std::unordered_map<std::string, joint_limits::JointLimits> hard_joint_limits_;
1354+
std::unordered_map<std::string, joint_limits::SoftJointLimits> soft_joint_limits_;
1355+
13501356
/// The callback to be called when a component state is switched
13511357
std::function<void()> on_component_state_switch_callback_ = nullptr;
13521358

@@ -1957,6 +1963,18 @@ ResourceManager::get_components_status()
19571963
return resource_storage_->hardware_info_map_;
19581964
}
19591965

1966+
const std::unordered_map<std::string, joint_limits::JointLimits> &
1967+
ResourceManager::get_hard_joint_limits() const
1968+
{
1969+
return resource_storage_->hard_joint_limits_;
1970+
}
1971+
1972+
const std::unordered_map<std::string, joint_limits::SoftJointLimits> &
1973+
ResourceManager::get_soft_joint_limits() const
1974+
{
1975+
return resource_storage_->soft_joint_limits_;
1976+
}
1977+
19601978
// CM API: Called in "callback/slow"-thread
19611979
bool ResourceManager::prepare_command_mode_switch(
19621980
const std::vector<std::string> & start_interfaces,

0 commit comments

Comments
 (0)