Skip to content

Commit 9c4cd49

Browse files
authored
Integrate joint limit enforcement into ros2_control framework functional with Async controllers and components (ros-controls#2047)
1 parent 25be71d commit 9c4cd49

File tree

16 files changed

+735
-6
lines changed

16 files changed

+735
-6
lines changed

controller_manager/doc/userdoc.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ robot_description [std_msgs::msg::String]
7979
Parameters
8080
-----------
8181

82+
enforce_command_limits (optional; bool; default: ``false`` for Jazzy and earlier distro and ``true`` for Rolling and newer distros)
83+
Enforces the joint limits to the joint command interfaces.
84+
If the command is outside the limits, the command is clamped to be within the limits depending on the type of configured joint limits in the URDF.
85+
If the command is within the limits, the command is passed through without any changes.
86+
8287
<controller_name>.type
8388
Name of a plugin exported using ``pluginlib`` for a controller.
8489
This is a class from which controller's instance with name "``controller_name``" is created.

controller_manager/include/controller_manager/controller_manager.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ class ControllerManager : public rclcpp::Node
657657
std::string robot_description_;
658658
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr robot_description_subscription_;
659659
rclcpp::TimerBase::SharedPtr robot_description_notification_timer_;
660+
bool enforce_command_limits_;
660661

661662
controller_manager::MovingAverageStatistics periodicity_stats_;
662663

controller_manager/src/controller_manager.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,8 @@ ControllerManager::ControllerManager(
438438
chainable_loader_(
439439
std::make_shared<pluginlib::ClassLoader<controller_interface::ChainableControllerInterface>>(
440440
kControllerInterfaceNamespace, kChainableControllerInterfaceClassName)),
441-
cm_node_options_(options)
441+
cm_node_options_(options),
442+
robot_description_(resource_manager_->get_robot_description())
442443
{
443444
initialize_parameters();
444445
init_controller_manager();
@@ -498,6 +499,7 @@ void ControllerManager::init_controller_manager()
498499
// Get parameters needed for RT "update" loop to work
499500
if (is_resource_manager_initialized())
500501
{
502+
resource_manager_->import_joint_limiters(robot_description_);
501503
init_services();
502504
}
503505
else
@@ -559,6 +561,13 @@ void ControllerManager::init_controller_manager()
559561
}
560562
RCLCPP_INFO(get_logger(), "Shutting down the controller manager.");
561563
}));
564+
565+
// Declare the enforce_command_limits parameter such a way that it is enabled by default for
566+
// rolling and newer alone
567+
enforce_command_limits_ =
568+
this->get_parameter_or("enforce_command_limits", RCLCPP_VERSION_MAJOR >= 29 ? true : false);
569+
RCLCPP_INFO_EXPRESSION(
570+
get_logger(), enforce_command_limits_, "Enforcing command limits is enabled...");
562571
}
563572

564573
void ControllerManager::initialize_parameters()
@@ -612,6 +621,7 @@ void ControllerManager::robot_description_callback(const std_msgs::msg::String &
612621

613622
void ControllerManager::init_resource_manager(const std::string & robot_description)
614623
{
624+
resource_manager_->import_joint_limiters(robot_description_);
615625
if (!resource_manager_->load_and_initialize_components(robot_description, update_rate_))
616626
{
617627
RCLCPP_WARN(
@@ -2871,6 +2881,11 @@ controller_interface::return_type ControllerManager::update(
28712881
publish_activity();
28722882
}
28732883

2884+
if (enforce_command_limits_)
2885+
{
2886+
resource_manager_->enforce_command_limits(period);
2887+
}
2888+
28742889
// there are controllers to (de)activate
28752890
if (switch_params_.do_switch)
28762891
{

controller_manager/test/test_controller_manager_hardware_error_handling.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ class TestControllerManagerWithTestableCM
6666
public testing::WithParamInterface<Strictness>
6767
{
6868
public:
69+
TestControllerManagerWithTestableCM()
70+
: ControllerManagerFixture<TestableControllerManager>(
71+
ros2_control_test_assets::minimal_robot_urdf_no_limits)
72+
{
73+
}
74+
6975
void SetupAndConfigureControllers(int strictness)
7076
{
7177
test_controller_actuator = std::make_shared<test_controller::TestController>();

controller_manager/test/test_controllers_chaining_with_controller_manager.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
#include <memory>
16+
#include <regex>
1617
#include <string>
1718
#include <unordered_map>
1819
#include <utility>
@@ -106,9 +107,13 @@ class TestControllerChainingWithControllerManager
106107
void SetUp()
107108
{
108109
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
110+
const std::regex velocity_pattern(R"(velocity\s*=\s*"-?[0-9]+(\.[0-9]+)?")");
111+
const std::string velocity_replacement = R"(velocity="10000.0")";
112+
const std::string diffbot_urdf_large_limits = std::regex_replace(
113+
ros2_control_test_assets::diffbot_urdf, velocity_pattern, velocity_replacement);
109114
cm_ = std::make_shared<TestableControllerManager>(
110115
std::make_unique<hardware_interface::ResourceManager>(
111-
ros2_control_test_assets::diffbot_urdf, rm_node_->get_node_clock_interface(),
116+
diffbot_urdf_large_limits, rm_node_->get_node_clock_interface(),
112117
rm_node_->get_node_logging_interface(), true),
113118
executor_, TEST_CM_NAME);
114119
run_updater_ = false;

doc/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ controller_manager
9090
* The ``cpu_affinity`` parameter can now accept of types ``int`` or ``int_array`` to bind the process to a specific CPU core or multiple CPU cores. (`#1915 <https://github.com/ros-controls/ros2_control/pull/1915>`_).
9191
* The ``pal_statistics`` is now integrated into the controller_manager, so that the controllers, hardware components and the controller_manager can be easily introspected and monitored using the topics ``~/introspection_data/names`` and ``~/introspection_data/values`` (`#1918 <https://github.com/ros-controls/ros2_control/pull/1918>`_).
9292
* A python module ``test_utils`` was added to the ``controller_manager`` package to help with integration testing (`#1955 <https://github.com/ros-controls/ros2_control/pull/1955>`_).
93+
* A new parameter ``enforce_command_limits`` is introduced to be able to enable and disable the enforcement of the command limits (`#1989 <https://github.com/ros-controls/ros2_control/pull/1989>`_).
9394
* A latched topic ``~/activity`` has been added to the controller_manager to publish the activity of the controller_manager, where the change in states of the controllers and the hardware components are published. (`#2006 <https://github.com/ros-controls/ros2_control/pull/2006>`_).
9495
* The controller manager will use a monotonic clock for triggering read-update-write cycles, but when the ``use_sim_time`` parameter is set to true, it will use the ROS Clock for triggering. When monotonic clock is being used, all the hardware components will receive the monotonic time in their read and write method, instead the controllers will always receive the ROS time in their update method irrespective of the clock being used. (`#2046 <https://github.com/ros-controls/ros2_control/pull/2046>`_).
9596

hardware_interface/include/hardware_interface/handle.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#define HARDWARE_INTERFACE__HANDLE_HPP_
1717

1818
#include <algorithm>
19+
#include <atomic>
20+
#include <functional>
1921
#include <limits>
2022
#include <memory>
2123
#include <mutex>
@@ -379,13 +381,40 @@ class CommandInterface : public Handle
379381

380382
CommandInterface(CommandInterface && other) = default;
381383

384+
void set_on_set_command_limiter(std::function<double(double, bool &)> on_set_command_limiter)
385+
{
386+
on_set_command_limiter_ = on_set_command_limiter;
387+
}
388+
389+
/// A setter for the value of the command interface that triggers the limiter.
390+
/**
391+
* @param value The value to be set.
392+
* @return True if the value was set successfully, false otherwise.
393+
*/
394+
template <typename T>
395+
[[nodiscard]] bool set_limited_value(const T & value)
396+
{
397+
if constexpr (std::is_same_v<T, double>)
398+
{
399+
return set_value(on_set_command_limiter_(value, is_command_limited_));
400+
}
401+
else
402+
{
403+
return set_value(value);
404+
}
405+
}
406+
407+
const bool & is_limited() const { return is_command_limited_; }
408+
382409
void registerIntrospection() const
383410
{
384411
if (value_ptr_ || std::holds_alternative<double>(value_))
385412
{
386413
std::function<double()> f = [this]()
387414
{ return value_ptr_ ? *value_ptr_ : std::get<double>(value_); };
388415
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name(), f);
416+
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION(
417+
"command_interface." + get_name() + ".is_limited", &is_command_limited_);
389418
}
390419
}
391420

@@ -394,12 +423,23 @@ class CommandInterface : public Handle
394423
if (value_ptr_ || std::holds_alternative<double>(value_))
395424
{
396425
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name());
426+
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION(
427+
"command_interface." + get_name() + ".is_limited");
397428
}
398429
}
399430

400431
using Handle::Handle;
401432

402433
using SharedPtr = std::shared_ptr<CommandInterface>;
434+
435+
private:
436+
bool is_command_limited_ = false;
437+
std::function<double(double, bool &)> on_set_command_limiter_ =
438+
[](double value, bool & is_limited)
439+
{
440+
is_limited = false;
441+
return value;
442+
};
403443
};
404444

405445
} // namespace hardware_interface

hardware_interface/include/hardware_interface/loaned_command_interface.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class LoanedCommandInterface
114114
{
115115
unsigned int nr_tries = 0;
116116
++set_value_statistics_.total_counter;
117-
while (!command_interface_.set_value(value))
117+
while (!command_interface_.set_limited_value(value))
118118
{
119119
++set_value_statistics_.failed_counter;
120120
++nr_tries;

hardware_interface/include/hardware_interface/resource_manager.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ class ResourceManager
119119
virtual bool load_and_initialize_components(
120120
const std::string & urdf, const unsigned int update_rate = 100);
121121

122+
/**
123+
* @brief Import joint limiters from the URDF.
124+
* @param urdf string containing the URDF.
125+
*/
126+
void import_joint_limiters(const std::string & urdf);
127+
122128
/**
123129
* @brief if the resource manager load_and_initialize_components(...) function has been called
124130
* this returns true. We want to permit to loading the urdf later on, but we currently don't want
@@ -456,6 +462,14 @@ class ResourceManager
456462
return_type set_component_state(
457463
const std::string & component_name, rclcpp_lifecycle::State & target_state);
458464

465+
/**
466+
* Enforce the command limits for the position, velocity, effort, and acceleration interfaces.
467+
* @note This method is RT-safe
468+
* @return true if the command interfaces are out of limits and the limits are enforced
469+
* @return false if the command interfaces values are within limits
470+
*/
471+
bool enforce_command_limits(const rclcpp::Duration & period);
472+
459473
/// Reads all loaded hardware components.
460474
/**
461475
* Reads from all active hardware components.
@@ -493,6 +507,8 @@ class ResourceManager
493507
*/
494508
void set_on_component_state_switch_callback(std::function<void()> callback);
495509

510+
const std::string & get_robot_description() const;
511+
496512
protected:
497513
/// Gets the logger for the resource manager
498514
/**

0 commit comments

Comments
 (0)