Modulo 5.0.0
Loading...
Searching...
No Matches
ControllerInterface.cpp
1#include "modulo_controllers/ControllerInterface.hpp"
2
3using namespace modulo_core;
4using namespace state_representation;
5using namespace std::chrono_literals;
6
7namespace modulo_controllers {
8
9ControllerInterface::ControllerInterface(bool claim_all_state_interfaces)
10 : BaseControllerInterface(), claim_all_state_interfaces_(claim_all_state_interfaces), on_init_called_(false) {}
11
12rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn ControllerInterface::on_init() {
14 if (status != CallbackReturn::SUCCESS) {
15 return status;
16 }
17 on_init_called_ = true;
18
19 try {
20 add_parameter(std::make_shared<Parameter<std::string>>("hardware_name"), "The name of the hardware interface");
22 std::make_shared<Parameter<std::vector<std::string>>>("joints"),
23 "A vector of joint names that the controller will claim");
25 "activation_timeout", 1.0, "The seconds to wait for valid data on the state interfaces before activating");
26
27 return add_interfaces();
28 } catch (const std::exception& e) {
29 RCLCPP_ERROR(get_node()->get_logger(), "Exception thrown during on_init stage with message: %s \n", e.what());
30 }
31 return CallbackReturn::ERROR;
32}
33
34rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn ControllerInterface::add_interfaces() {
35 return CallbackReturn::SUCCESS;
36}
37
38rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
39ControllerInterface::on_configure(const rclcpp_lifecycle::State& previous_state) {
40 auto status = BaseControllerInterface::on_configure(previous_state);
41 if (status != CallbackReturn::SUCCESS) {
42 return status;
43 }
44
45 if (!on_init_called_) {
46 RCLCPP_ERROR(
47 get_node()->get_logger(),
48 "The controller has not been properly initialized! Derived controller classes must call "
49 "'ControllerInterface::on_init()' during their own initialization before being configured.");
50 return CallbackReturn::ERROR;
51 }
52
53 auto hardware_name = get_parameter("hardware_name");
54 if (hardware_name->is_empty()) {
55 RCLCPP_ERROR(get_node()->get_logger(), "Parameter 'hardware_name' cannot be empty");
56 return CallbackReturn::ERROR;
57 }
58 hardware_name_ = hardware_name->get_parameter_value<std::string>();
59
60 RCLCPP_DEBUG(get_node()->get_logger(), "Configuration of ControllerInterface successful");
61 try {
62 return on_configure();
63 } catch (const std::exception& ex) {
64 RCLCPP_ERROR_STREAM(get_node()->get_logger(), ex.what());
65 }
66 return CallbackReturn::ERROR;
67}
68
69rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn ControllerInterface::on_configure() {
70 return CallbackReturn::SUCCESS;
71}
72
73void ControllerInterface::add_state_interface(const std::string& name, const std::string& interface) {
74 add_interface(name, interface, state_interface_names_, "state");
75}
76
77void ControllerInterface::add_command_interface(const std::string& name, const std::string& interface) {
78 add_interface(name, interface, command_interface_names_, "command");
79}
80
81void ControllerInterface::add_interface(
82 const std::string& name, const std::string& interface, std::vector<std::string>& list, const std::string& type) {
83 if (get_node()->get_current_state().label() != "configuring") {
84 throw std::runtime_error("Interfaces can only be added when the controller is in state 'configuring'");
85 }
86 auto full_name = name + "/" + interface;
87 if (std::find(list.cbegin(), list.cend(), full_name) == list.cend()) {
88 list.push_back(full_name);
89 RCLCPP_DEBUG(
90 get_node()->get_logger(), "Adding interface '%s' to the list of desired %s interfaces", full_name.c_str(),
91 type.c_str());
92 } else {
93 RCLCPP_WARN(
94 get_node()->get_logger(), "Interface '%s' is already in the list of desired %s interfaces", full_name.c_str(),
95 type.c_str());
96 }
97}
98
99controller_interface::InterfaceConfiguration ControllerInterface::command_interface_configuration() const {
100 controller_interface::InterfaceConfiguration command_interfaces_config;
101 if (command_interface_names_.empty()) {
102 RCLCPP_DEBUG(get_node()->get_logger(), "List of command interfaces is empty, not claiming any interfaces.");
103 command_interfaces_config.type = controller_interface::interface_configuration_type::NONE;
104 return command_interfaces_config;
105 }
106
107 command_interfaces_config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
108 for (const auto& interface : command_interface_names_) {
109 RCLCPP_DEBUG(get_node()->get_logger(), "Claiming command interface '%s'", interface.c_str());
110 command_interfaces_config.names.push_back(interface);
111 }
112
113 return command_interfaces_config;
114}
115
116controller_interface::InterfaceConfiguration ControllerInterface::state_interface_configuration() const {
117 controller_interface::InterfaceConfiguration state_interfaces_config;
118 if (claim_all_state_interfaces_) {
119 RCLCPP_DEBUG(get_node()->get_logger(), "Claiming all state interfaces.");
120 state_interfaces_config.type = controller_interface::interface_configuration_type::ALL;
121 return state_interfaces_config;
122 }
123
124 state_interfaces_config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
125 for (const auto& interface : state_interface_names_) {
126 RCLCPP_DEBUG(get_node()->get_logger(), "Claiming state interface '%s'", interface.c_str());
127 state_interfaces_config.names.push_back(interface);
128 }
129
130 return state_interfaces_config;
131}
132
133rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
134ControllerInterface::on_activate(const rclcpp_lifecycle::State&) {
135 // initialize the map of command data from all available interfaces
136 command_interface_data_ = std::vector<double>(command_interfaces_.size());
137 for (unsigned int i = 0; i < command_interfaces_.size(); ++i) {
138 const auto& command_interface = command_interfaces_.at(i);
139 if (command_interface_indices_.find(command_interface.get_prefix_name()) == command_interface_indices_.cend()) {
140 command_interface_indices_.insert_or_assign(
141 command_interface.get_prefix_name(), std::unordered_map<std::string, unsigned int>());
142 }
143 command_interface_indices_.at(command_interface.get_prefix_name())
144 .insert_or_assign(command_interface.get_interface_name(), i);
145 command_interface_data_.at(i) = command_interface.get_value();
146 }
147
148 // initialize the map of state data from all available interfaces
149 for (const auto& state_interface : state_interfaces_) {
150 if (state_interface_data_.find(state_interface.get_prefix_name()) == state_interface_data_.cend()) {
151 state_interface_data_.insert_or_assign(
152 state_interface.get_prefix_name(), std::unordered_map<std::string, double>());
153 }
154 state_interface_data_.at(state_interface.get_prefix_name())
155 .insert_or_assign(state_interface.get_interface_name(), state_interface.get_value());
156 }
157
158 auto status = CallbackReturn::ERROR;
159 try {
160 status = on_activate();
161 } catch (const std::exception& ex) {
162 RCLCPP_ERROR_STREAM(get_node()->get_logger(), ex.what());
163 }
164 if (status != CallbackReturn::SUCCESS) {
165 return status;
166 }
167
168 auto start_time = get_node()->get_clock()->now();
169 auto activation_timeout = rclcpp::Duration::from_seconds(get_parameter_value<double>("activation_timeout"));
170 while (read_state_interfaces() != controller_interface::return_type::OK) {
171 RCLCPP_DEBUG_THROTTLE(
172 get_node()->get_logger(), *get_node()->get_clock(), 1000,
173 "Activation is not possible yet; the controller did not receive valid states from hardware");
174 if ((get_node()->get_clock()->now() - start_time) > activation_timeout) {
175 release_interfaces();
176 RCLCPP_ERROR(
177 get_node()->get_logger(),
178 "Activation was not successful; the controller did not receive valid states from hardware");
179 return CallbackReturn::FAILURE;
180 }
181 }
182
183 RCLCPP_DEBUG(get_node()->get_logger(), "Activation of ControllerInterface successful");
184 return CallbackReturn::SUCCESS;
185}
186
187rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn ControllerInterface::on_activate() {
188 return CallbackReturn::SUCCESS;
189}
190
191rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
192ControllerInterface::on_deactivate(const rclcpp_lifecycle::State&) {
193 try {
194 return on_deactivate();
195 } catch (const std::exception& ex) {
196 RCLCPP_ERROR_STREAM(get_node()->get_logger(), ex.what());
197 }
198 return CallbackReturn::ERROR;
199}
200
201rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn ControllerInterface::on_deactivate() {
202 return CallbackReturn::SUCCESS;
203}
204
205controller_interface::return_type
206ControllerInterface::update(const rclcpp::Time& time, const rclcpp::Duration& period) {
207 auto status = read_state_interfaces();
208 if (status != controller_interface::return_type::OK) {
209 return status;
210 }
211
212 try {
213 status = evaluate(time, period.to_chrono<std::chrono::nanoseconds>());
214 } catch (const std::exception& e) {
215 RCLCPP_ERROR_THROTTLE(
216 get_node()->get_logger(), *get_node()->get_clock(), 1000, "Exception during evaluate(): %s \n", e.what());
217 return controller_interface::return_type::ERROR;
218 }
219 if (status != controller_interface::return_type::OK) {
220 return status;
221 }
222
223 if (command_interface_data_.empty()) {
224 return controller_interface::return_type::OK;
225 }
226
227 controller_interface::return_type ret;
228 if (get_command_mutex().try_lock()) {
229 missed_locks_ = 0;
230 ret = write_command_interfaces(period);
231 get_command_mutex().unlock();
232 } else {
233 if (missed_locks_ > 2) {
234 RCLCPP_ERROR(
235 get_node()->get_logger(),
236 "Controller is unable to acquire lock for command interfaces, returning an error now");
237 ret = controller_interface::return_type::ERROR;
238 }
239 ++missed_locks_;
240 RCLCPP_WARN(get_node()->get_logger(), "Unable to acquire lock for command interfaces (%u/3)", missed_locks_);
241 }
242
243 return ret;
244}
245
246controller_interface::return_type ControllerInterface::read_state_interfaces() {
247 for (const auto& state_interface : state_interfaces_) {
248 state_interface_data_.at(state_interface.get_prefix_name()).at(state_interface.get_interface_name()) =
249 state_interface.get_value();
250 }
251
252 return controller_interface::return_type::OK;
253}
254
255controller_interface::return_type ControllerInterface::write_command_interfaces(const rclcpp::Duration&) {
256 for (auto& command_interface : command_interfaces_) {
257 command_interface.set_value(command_interface_data_.at(
258 command_interface_indices_.at(command_interface.get_prefix_name()).at(command_interface.get_interface_name())));
259 }
260 return controller_interface::return_type::OK;
261}
262
263std::unordered_map<std::string, double> ControllerInterface::get_state_interfaces(const std::string& name) const {
264 return state_interface_data_.at(name);
265}
266
267double ControllerInterface::get_state_interface(const std::string& name, const std::string& interface) const {
268 return state_interface_data_.at(name).at(interface);
269}
270
271double ControllerInterface::get_command_interface(const std::string& name, const std::string& interface) const {
272 return command_interfaces_.at(command_interface_indices_.at(name).at(interface)).get_value();
273}
274
275void ControllerInterface::set_command_interface(const std::string& name, const std::string& interface, double value) {
276 try {
277 command_interface_data_.at(command_interface_indices_.at(name).at(interface)) = value;
278 } catch (const std::out_of_range&) {
279 RCLCPP_WARN_THROTTLE(
280 get_node()->get_logger(), *get_node()->get_clock(), 1000,
281 "set_command_interface called with an unknown name/interface: %s/%s", name.c_str(), interface.c_str());
282 }
283}
284
285bool ControllerInterface::on_validate_parameter_callback(const std::shared_ptr<ParameterInterface>& parameter) {
286 if (parameter->get_name() == "activation_timeout") {
287 auto value = parameter->get_parameter_value<double>();
288 if (value < 0.0 || value > std::numeric_limits<double>::max()) {
289 RCLCPP_ERROR(get_node()->get_logger(), "Value of parameter 'activation_timeout' cannot be negative");
290 return false;
291 }
292 }
293 return true;
294}
295
296}// namespace modulo_controllers
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 > &parameter, 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.
bool on_validate_parameter_callback(const std::shared_ptr< state_representation::ParameterInterface > &parameter) override
Parameter validation function to be redefined by derived controller classes.
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.
Modulo Core.