1#include "modulo_controllers/ControllerInterface.hpp"
4using namespace state_representation;
5using namespace std::chrono_literals;
7namespace modulo_controllers {
14 if (status != CallbackReturn::SUCCESS) {
17 on_init_called_ =
true;
20 add_parameter(std::make_shared<Parameter<std::string>>(
"hardware_name"),
"The name of the hardware interface");
22 std::make_shared<Parameter<std::string>>(
"robot_description"),
23 "The string formatted content of the controller's URDF description");
25 std::make_shared<Parameter<std::vector<std::string>>>(
"joints"),
26 "A vector of joint names that the controller will claim");
28 "activation_timeout", 1.0,
"The seconds to wait for valid data on the state interfaces before activating");
31 }
catch (
const std::exception& e) {
32 RCLCPP_ERROR(get_node()->get_logger(),
"Exception thrown during on_init stage with message: %s \n", e.what());
34 return CallbackReturn::ERROR;
38 return CallbackReturn::SUCCESS;
41rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
44 if (status != CallbackReturn::SUCCESS) {
48 if (!on_init_called_) {
50 get_node()->get_logger(),
51 "The controller has not been properly initialized! Derived controller classes must call "
52 "'ControllerInterface::on_init()' during their own initialization before being configured.");
53 return CallbackReturn::ERROR;
57 if (hardware_name->is_empty()) {
58 RCLCPP_ERROR(get_node()->get_logger(),
"Parameter 'hardware_name' cannot be empty");
59 return CallbackReturn::ERROR;
61 hardware_name_ = hardware_name->get_parameter_value<std::string>();
63 RCLCPP_DEBUG(get_node()->get_logger(),
"Configuration of ControllerInterface successful");
66 }
catch (
const std::exception& ex) {
67 RCLCPP_ERROR_STREAM(get_node()->get_logger(), ex.what());
69 return CallbackReturn::ERROR;
73 return CallbackReturn::SUCCESS;
77 add_interface(name, interface, state_interface_names_,
"state");
81 add_interface(name, interface, command_interface_names_,
"command");
84void ControllerInterface::add_interface(
85 const std::string& name,
const std::string& interface, std::vector<std::string>& list,
const std::string& type) {
86 if (get_node()->get_current_state().label() !=
"configuring") {
87 throw std::runtime_error(
"Interfaces can only be added when the controller is in state 'configuring'");
89 auto full_name = name +
"/" + interface;
90 if (std::find(list.cbegin(), list.cend(), full_name) == list.cend()) {
91 list.push_back(full_name);
93 get_node()->get_logger(),
"Adding interface '%s' to the list of desired %s interfaces", full_name.c_str(),
97 get_node()->get_logger(),
"Interface '%s' is already in the list of desired %s interfaces", full_name.c_str(),
103 controller_interface::InterfaceConfiguration command_interfaces_config;
104 if (command_interface_names_.empty()) {
105 RCLCPP_DEBUG(get_node()->get_logger(),
"List of command interfaces is empty, not claiming any interfaces.");
106 command_interfaces_config.type = controller_interface::interface_configuration_type::NONE;
107 return command_interfaces_config;
110 command_interfaces_config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
111 for (
const auto& interface : command_interface_names_) {
112 RCLCPP_DEBUG(get_node()->get_logger(),
"Claiming command interface '%s'", interface.c_str());
113 command_interfaces_config.names.push_back(interface);
116 return command_interfaces_config;
120 controller_interface::InterfaceConfiguration state_interfaces_config;
121 if (claim_all_state_interfaces_) {
122 RCLCPP_DEBUG(get_node()->get_logger(),
"Claiming all state interfaces.");
123 state_interfaces_config.type = controller_interface::interface_configuration_type::ALL;
124 return state_interfaces_config;
127 state_interfaces_config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
128 for (
const auto& interface : state_interface_names_) {
129 RCLCPP_DEBUG(get_node()->get_logger(),
"Claiming state interface '%s'", interface.c_str());
130 state_interfaces_config.names.push_back(interface);
133 return state_interfaces_config;
136rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
139 command_interface_data_ = std::vector<double>(command_interfaces_.size());
140 for (
unsigned int i = 0; i < command_interfaces_.size(); ++i) {
141 const auto& command_interface = command_interfaces_.at(i);
142 if (command_interface_indices_.find(command_interface.get_prefix_name()) == command_interface_indices_.cend()) {
143 command_interface_indices_.insert_or_assign(
144 command_interface.get_prefix_name(), std::unordered_map<std::string, unsigned int>());
146 command_interface_indices_.at(command_interface.get_prefix_name())
147 .insert_or_assign(command_interface.get_interface_name(), i);
148 command_interface_data_.at(i) = command_interface.get_value();
152 for (
const auto& state_interface : state_interfaces_) {
153 if (state_interface_data_.find(state_interface.get_prefix_name()) == state_interface_data_.cend()) {
154 state_interface_data_.insert_or_assign(
155 state_interface.get_prefix_name(), std::unordered_map<std::string, double>());
157 state_interface_data_.at(state_interface.get_prefix_name())
158 .insert_or_assign(state_interface.get_interface_name(), state_interface.get_value());
161 auto status = CallbackReturn::ERROR;
164 }
catch (
const std::exception& ex) {
165 RCLCPP_ERROR_STREAM(get_node()->get_logger(), ex.what());
167 if (status != CallbackReturn::SUCCESS) {
171 auto start_time = get_node()->get_clock()->now();
174 RCLCPP_DEBUG_THROTTLE(
175 get_node()->get_logger(), *get_node()->get_clock(), 1000,
176 "Activation is not possible yet; the controller did not receive valid states from hardware");
177 if ((get_node()->get_clock()->now() - start_time) > activation_timeout) {
178 release_interfaces();
180 get_node()->get_logger(),
181 "Activation was not successful; the controller did not receive valid states from hardware");
182 return CallbackReturn::FAILURE;
186 RCLCPP_DEBUG(get_node()->get_logger(),
"Activation of ControllerInterface successful");
187 return CallbackReturn::SUCCESS;
191 return CallbackReturn::SUCCESS;
194rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
198 }
catch (
const std::exception& ex) {
199 RCLCPP_ERROR_STREAM(get_node()->get_logger(), ex.what());
201 return CallbackReturn::ERROR;
205 return CallbackReturn::SUCCESS;
208controller_interface::return_type
211 if (status != controller_interface::return_type::OK) {
216 status =
evaluate(time, period.to_chrono<std::chrono::nanoseconds>());
217 }
catch (
const std::exception& e) {
218 RCLCPP_ERROR_THROTTLE(
219 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Exception during evaluate(): %s \n", e.what());
220 return controller_interface::return_type::ERROR;
222 if (status != controller_interface::return_type::OK) {
226 if (command_interface_data_.empty()) {
227 return controller_interface::return_type::OK;
230 controller_interface::return_type ret;
236 if (missed_locks_ > 2) {
238 get_node()->get_logger(),
239 "Controller is unable to acquire lock for command interfaces, returning an error now");
240 ret = controller_interface::return_type::ERROR;
243 RCLCPP_WARN(get_node()->get_logger(),
"Unable to acquire lock for command interfaces (%u/3)", missed_locks_);
250 for (
const auto& state_interface : state_interfaces_) {
251 state_interface_data_.at(state_interface.get_prefix_name()).at(state_interface.get_interface_name()) =
252 state_interface.get_value();
255 return controller_interface::return_type::OK;
259 for (
auto& command_interface : command_interfaces_) {
260 command_interface.set_value(command_interface_data_.at(
261 command_interface_indices_.at(command_interface.get_prefix_name()).at(command_interface.get_interface_name())));
263 return controller_interface::return_type::OK;
267 return state_interface_data_.at(name);
271 return state_interface_data_.at(name).at(interface);
275 return command_interfaces_.at(command_interface_indices_.at(name).at(interface)).get_value();
280 command_interface_data_.at(command_interface_indices_.at(name).at(interface)) = value;
281 }
catch (
const std::out_of_range&) {
282 RCLCPP_WARN_THROTTLE(
283 get_node()->get_logger(), *get_node()->get_clock(), 1000,
284 "set_command_interface called with an unknown name/interface: %s/%s", name.c_str(), interface.c_str());
Base controller class to combine ros2_control, control libraries and modulo.
std::shared_ptr< state_representation::ParameterInterface > get_parameter(const std::string &name) const
Get a parameter by name.
CallbackReturn on_configure(const rclcpp_lifecycle::State &previous_state) override
Add signals.
T get_parameter_value(const std::string &name) const
Get a parameter value by name.
CallbackReturn on_init() override
Declare parameters and register the on_set_parameters callback.
void add_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter, const std::string &description, bool read_only=false)
Add a parameter.
std::timed_mutex & get_command_mutex()
Get the reference to the command mutex.
virtual CallbackReturn add_interfaces()
Add interfaces like parameters, signals, services, and predicates to the controller.
controller_interface::InterfaceConfiguration command_interface_configuration() const final
Configure the command interfaces.
virtual controller_interface::return_type write_command_interfaces(const rclcpp::Duration &period)
Write the command interfaces.
virtual CallbackReturn on_configure()
Configure the controller.
virtual controller_interface::return_type evaluate(const rclcpp::Time &time, const std::chrono::nanoseconds &period)=0
The control logic callback.
virtual controller_interface::return_type read_state_interfaces()
Read the state interfaces.
std::unordered_map< std::string, double > get_state_interfaces(const std::string &name) const
Get a map containing the state interfaces by name of the parent tag.
CallbackReturn on_init() override
Declare parameters.
ControllerInterface(bool claim_all_state_interfaces=false)
Default constructor.
void add_state_interface(const std::string &name, const std::string &interface)
Add a state interface to the controller by name.
double get_command_interface(const std::string &name, const std::string &interface) const
Get the value of a command interface by name.
virtual CallbackReturn on_activate()
Activate the controller.
double get_state_interface(const std::string &name, const std::string &interface) const
Get the value of a state interface by name.
controller_interface::return_type update(const rclcpp::Time &time, const rclcpp::Duration &period) final
Read the state interfaces, perform control evaluation and write the command interfaces.
void set_command_interface(const std::string &name, const std::string &interface, double value)
Set the value of a command interface by name.
std::string hardware_name_
The hardware name provided by a parameter.
virtual CallbackReturn on_deactivate()
Deactivate the controller.
void add_command_interface(const std::string &name, const std::string &interface)
Add a command interface to the controller by name.
controller_interface::InterfaceConfiguration state_interface_configuration() const final
Configure the state interfaces.