1#include "modulo_controllers/BaseControllerInterface.hpp"
3#include <console_bridge/console.h>
5#include <lifecycle_msgs/msg/state.hpp>
7#include <modulo_core/exceptions.hpp>
8#include <modulo_core/translators/message_readers.hpp>
11struct overloaded : Ts... {
12 using Ts::operator()...;
15overloaded(Ts...) -> overloaded<Ts...>;
18using namespace state_representation;
19using namespace std::chrono_literals;
21namespace modulo_controllers {
24 : controller_interface::
ControllerInterface(), input_validity_period_(std::numeric_limits<double>::quiet_NaN()) {}
29 pre_set_parameter_cb_handle_ = get_node()->add_pre_set_parameters_callback(
30 [
this](std::vector<rclcpp::Parameter>& parameters) {
return this->pre_set_parameters_callback(parameters); });
31 on_set_parameter_cb_handle_ = get_node()->add_on_set_parameters_callback(
32 [
this](
const std::vector<rclcpp::Parameter>& parameters) -> rcl_interfaces::msg::SetParametersResult {
33 return this->on_set_parameters_callback(parameters);
37 parameter_map_.set_parameter(std::make_shared<Parameter<int>>(
"update_rate"));
38 read_only_parameters_.insert_or_assign(
"update_rate",
false);
39 parameter_map_.set_parameter(std::make_shared<Parameter<bool>>(
"is_async",
false));
40 read_only_parameters_.insert_or_assign(
"is_async",
false);
42 add_parameter<double>(
"predicate_publishing_rate", 10.0,
"The rate at which to publish controller predicates");
44 "input_validity_period", 1.0,
"The maximum age of an input state before discarding it as expired");
45 return CallbackReturn::SUCCESS;
48rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
54 if (predicates_.size()) {
55 predicate_publisher_ =
56 get_node()->create_publisher<modulo_interfaces::msg::PredicateCollection>(
"/predicates", qos_);
57 predicate_message_.node = get_node()->get_fully_qualified_name();
59 predicate_timer_ = get_node()->create_wall_timer(
61 [
this]() { this->publish_predicates(); });
63 if (assignments_map_.get_parameters().size()) {
64 assignment_publisher_ = get_node()->create_publisher<modulo_interfaces::msg::Assignment>(
"/assignments", qos_);
67 RCLCPP_DEBUG(get_node()->get_logger(),
"Configuration of BaseControllerInterface successful");
68 return CallbackReturn::SUCCESS;
72 const std::shared_ptr<ParameterInterface>& parameter,
const std::string& description,
bool read_only) {
73 rclcpp::Parameter ros_param;
79 if (!get_node()->has_parameter(parameter->get_name())) {
80 RCLCPP_DEBUG(get_node()->get_logger(),
"Adding parameter '%s'.", parameter->get_name().c_str());
81 parameter_map_.set_parameter(parameter);
82 read_only_parameters_.insert_or_assign(parameter->get_name(),
false);
84 rcl_interfaces::msg::ParameterDescriptor descriptor;
85 descriptor.description = description;
86 descriptor.read_only = read_only;
88 set_parameters_result_.successful =
true;
89 set_parameters_result_.reason =
"";
90 if (parameter->is_empty()) {
91 descriptor.dynamic_typing =
true;
93 get_node()->declare_parameter(parameter->get_name(), rclcpp::ParameterValue{}, descriptor);
95 get_node()->declare_parameter(parameter->get_name(), ros_param.get_parameter_value(), descriptor);
97 std::vector<rclcpp::Parameter> ros_parameters{get_node()->get_parameters({parameter->get_name()})};
98 pre_set_parameters_callback(ros_parameters);
99 auto result = on_set_parameters_callback(ros_parameters);
100 if (!result.successful) {
101 get_node()->undeclare_parameter(parameter->get_name());
104 read_only_parameters_.at(parameter->get_name()) = read_only;
105 }
catch (
const std::exception& ex) {
106 parameter_map_.remove_parameter(parameter->get_name());
107 read_only_parameters_.erase(parameter->get_name());
111 RCLCPP_DEBUG(get_node()->get_logger(),
"Parameter '%s' already exists.", parameter->get_name().c_str());
117 return parameter_map_.get_parameter(name);
118 }
catch (
const state_representation::exceptions::InvalidParameterException& ex) {
123void BaseControllerInterface::pre_set_parameters_callback(std::vector<rclcpp::Parameter>& parameters) {
124 if (pre_set_parameter_callback_called_) {
125 pre_set_parameter_callback_called_ =
false;
128 rcl_interfaces::msg::SetParametersResult result;
129 result.successful =
true;
130 for (
auto& ros_parameter : parameters) {
132 auto parameter = parameter_map_.get_parameter(ros_parameter.get_name());
133 if (read_only_parameters_.at(ros_parameter.get_name())) {
134 RCLCPP_DEBUG(get_node()->get_logger(),
"Parameter '%s' is read only.", ros_parameter.get_name().c_str());
140 if (!validate_parameter(new_parameter)) {
141 result.successful =
false;
142 result.reason +=
"Validation of parameter '" + ros_parameter.get_name() +
"' returned false!";
143 }
else if (!new_parameter->is_empty()) {
148 }
catch (
const std::exception& ex) {
149 result.successful =
false;
150 result.reason += ex.what();
153 set_parameters_result_ = result;
156rcl_interfaces::msg::SetParametersResult
157BaseControllerInterface::on_set_parameters_callback(
const std::vector<rclcpp::Parameter>&) {
158 auto result = set_parameters_result_;
159 set_parameters_result_.successful =
true;
160 set_parameters_result_.reason =
"";
164bool BaseControllerInterface::validate_parameter(
const std::shared_ptr<ParameterInterface>& parameter) {
165 if (parameter->get_name() ==
"predicate_publishing_rate" || parameter->get_name() ==
"input_validity_period") {
166 auto value = parameter->get_parameter_value<
double>();
167 if (value <= 0.0 || value > std::numeric_limits<double>::max()) {
169 get_node()->get_logger(),
"Value of of parameter '%s' should be greater than 0",
170 parameter->get_name().c_str());
182 add_predicate(predicate_name, [predicate_value]() {
return predicate_value; });
186 const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function) {
187 if (predicate_name.empty()) {
188 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add predicate: Provide a non empty string as a name.");
191 if (predicates_.find(predicate_name) != predicates_.end()) {
193 get_node()->get_logger(),
"Predicate with name '%s' already exists, overwriting.", predicate_name.c_str());
195 RCLCPP_DEBUG(get_node()->get_logger(),
"Adding predicate '%s'.", predicate_name.c_str());
198 predicates_.insert_or_assign(predicate_name,
Predicate(predicate_function));
199 }
catch (
const std::exception& ex) {
201 get_node()->get_logger(),
"Failed to evaluate callback of predicate '%s', returning false: %s",
202 predicate_name.c_str(), ex.what());
207 auto predicate_it = predicates_.find(predicate_name);
208 if (predicate_it == predicates_.end()) {
209 RCLCPP_ERROR_THROTTLE(
210 get_node()->get_logger(), *get_node()->get_clock(), 1000,
211 "Failed to get predicate '%s': Predicate does not exists, returning false.", predicate_name.c_str());
215 return predicate_it->second.get_value();
216 }
catch (
const std::exception& ex) {
217 RCLCPP_ERROR_THROTTLE(
218 get_node()->get_logger(), *get_node()->get_clock(), 1000,
219 "Failed to evaluate callback of predicate '%s', returning false: %s", predicate_name.c_str(), ex.what());
225 set_predicate(predicate_name, [predicate_value]() {
return predicate_value; });
229 const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function) {
230 auto predicate_it = predicates_.find(predicate_name);
231 if (predicate_it == predicates_.end()) {
232 RCLCPP_ERROR_THROTTLE(
233 get_node()->get_logger(), *get_node()->get_clock(), 1000,
234 "Failed to set predicate '%s': Predicate does not exist.", predicate_name.c_str());
237 predicate_it->second.set_predicate(predicate_function);
238 if (
auto new_predicate = predicate_it->second.query(); new_predicate) {
239 publish_predicate(predicate_name, *new_predicate);
244 if (trigger_name.empty()) {
245 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add trigger: Provide a non empty string as a name.");
248 if (std::find(triggers_.cbegin(), triggers_.cend(), trigger_name) != triggers_.cend()) {
250 get_node()->get_logger(),
"Failed to add trigger: there is already a trigger with name '%s'.",
251 trigger_name.c_str());
254 if (predicates_.find(trigger_name) != predicates_.end()) {
256 get_node()->get_logger(),
"Failed to add trigger: there is already a predicate with name '%s'.",
257 trigger_name.c_str());
260 triggers_.push_back(trigger_name);
265 if (std::find(triggers_.cbegin(), triggers_.cend(), trigger_name) == triggers_.cend()) {
267 get_node()->get_logger(),
"Failed to trigger: could not find trigger with name '%s'.", trigger_name.c_str());
272 predicates_.at(trigger_name).set_predicate([]() {
return false; });
275modulo_interfaces::msg::Predicate
276BaseControllerInterface::get_predicate_message(
const std::string& name,
bool value)
const {
277 modulo_interfaces::msg::Predicate message;
278 message.predicate = name;
279 message.value = value;
283void BaseControllerInterface::publish_predicate(
const std::string& predicate_name,
bool value)
const {
284 if (predicate_publisher_ ==
nullptr) {
285 RCLCPP_ERROR_STREAM_THROTTLE(
286 get_node()->get_logger(), *get_node()->get_clock(), 1000,
287 "No predicate publisher configured. Make sure to add predicates `on_init` of the controller.");
290 auto message(predicate_message_);
291 message.predicates.push_back(get_predicate_message(predicate_name, value));
292 predicate_publisher_->publish(message);
295void BaseControllerInterface::publish_predicates() {
296 if (predicate_publisher_ ==
nullptr) {
297 RCLCPP_ERROR_STREAM_THROTTLE(
298 get_node()->get_logger(), *get_node()->get_clock(), 1000,
299 "No predicate publisher configured. Make sure to add predicates `on_init` of the controller.");
302 auto message(predicate_message_);
303 for (
auto predicate_it = predicates_.begin(); predicate_it != predicates_.end(); ++predicate_it) {
304 if (
auto new_predicate = predicate_it->second.query(); new_predicate) {
305 message.predicates.push_back(get_predicate_message(predicate_it->first, *new_predicate));
308 if (message.predicates.size()) {
309 predicate_publisher_->publish(message);
313std::string BaseControllerInterface::validate_and_declare_signal(
314 const std::string& signal_name,
const std::string& type,
const std::string& default_topic,
bool fixed_topic) {
315 auto parsed_signal_name = modulo_utils::parsing::parse_topic_name(signal_name);
316 if (parsed_signal_name.empty()) {
318 get_node()->get_logger(),
319 "The parsed signal name for %s '%s' is empty. Provide a string with valid characters for the signal name "
321 type.c_str(), signal_name.c_str());
324 if (signal_name != parsed_signal_name) {
326 get_node()->get_logger(),
327 "The parsed signal name for %s '%s' is '%s'. Use the parsed signal name to refer to this %s and its topic "
329 type.c_str(), signal_name.c_str(), parsed_signal_name.c_str(), type.c_str());
331 if (inputs_.find(parsed_signal_name) != inputs_.end()) {
332 RCLCPP_WARN(get_node()->get_logger(),
"Signal '%s' already exists as input.", parsed_signal_name.c_str());
335 if (outputs_.find(parsed_signal_name) != outputs_.end()) {
336 RCLCPP_WARN(get_node()->get_logger(),
"Signal '%s' already exists as output", parsed_signal_name.c_str());
339 auto topic = default_topic.empty() ?
"~/" + parsed_signal_name : default_topic;
340 auto parameter_name = parsed_signal_name +
"_topic";
341 if (get_node()->has_parameter(parameter_name) &&
get_parameter(parameter_name)->is_empty()) {
345 parameter_name, topic,
"Signal topic name of " + type +
" '" + parsed_signal_name +
"'", fixed_topic);
348 get_node()->get_logger(),
"Declared %s '%s' and parameter '%s' with value '%s'.", type.c_str(),
349 parsed_signal_name.c_str(), parameter_name.c_str(), topic.c_str());
350 return parsed_signal_name;
353void BaseControllerInterface::create_input(
354 const ControllerInput& input,
const std::string& name,
const std::string& topic_name) {
355 auto parsed_name = validate_and_declare_signal(name,
"input", topic_name);
356 if (!parsed_name.empty()) {
357 inputs_.insert_or_assign(parsed_name, input);
361void BaseControllerInterface::add_inputs() {
362 for (
auto& [name, input] : inputs_) {
367 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<EncodedState>>&) {
368 subscriptions_.push_back(create_subscription<EncodedState>(name, topic));
370 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>&) {
371 subscriptions_.push_back(create_subscription<std_msgs::msg::Bool>(name, topic));
373 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>&) {
374 subscriptions_.push_back(create_subscription<std_msgs::msg::Float64>(name, topic));
376 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>&) {
377 subscriptions_.push_back(create_subscription<std_msgs::msg::Float64MultiArray>(name, topic));
379 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>&) {
380 subscriptions_.push_back(create_subscription<std_msgs::msg::Int32>(name, topic));
382 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>&) {
383 subscriptions_.push_back(create_subscription<std_msgs::msg::String>(name, topic));
385 [&](
const std::any&) {
386 custom_input_configuration_callables_.at(name)(name, topic);
389 }
catch (
const std::exception& ex) {
390 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add input '%s': %s", name.c_str(), ex.what());
395void BaseControllerInterface::create_output(
396 const PublisherVariant& publishers,
const std::string& name,
const std::string& topic_name) {
397 auto parsed_name = validate_and_declare_signal(name,
"output", topic_name);
398 if (!parsed_name.empty()) {
399 outputs_.insert_or_assign(parsed_name, publishers);
403void BaseControllerInterface::add_outputs() {
404 for (
auto& [name, publishers] : outputs_) {
409 [&](EncodedStatePublishers& pub) {
410 std::get<1>(pub) = get_node()->create_publisher<EncodedState>(topic, qos_);
411 std::get<2>(pub) = std::make_shared<realtime_tools::RealtimePublisher<EncodedState>>(std::get<1>(pub));
413 [&](BoolPublishers& pub) {
414 pub.first = get_node()->create_publisher<std_msgs::msg::Bool>(topic, qos_);
415 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Bool>>(pub.first);
417 [&](DoublePublishers& pub) {
418 pub.first = get_node()->create_publisher<std_msgs::msg::Float64>(topic, qos_);
419 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64>>(pub.first);
421 [&](DoubleVecPublishers& pub) {
422 pub.first = get_node()->create_publisher<std_msgs::msg::Float64MultiArray>(topic, qos_);
424 std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64MultiArray>>(pub.first);
426 [&](IntPublishers& pub) {
427 pub.first = get_node()->create_publisher<std_msgs::msg::Int32>(topic, qos_);
428 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Int32>>(pub.first);
430 [&](StringPublishers& pub) {
431 pub.first = get_node()->create_publisher<std_msgs::msg::String>(topic, qos_);
432 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::String>>(pub.first);
434 [&](CustomPublishers& pub) {
435 custom_output_configuration_callables_.at(name)(pub, name);
438 }
catch (
const std::bad_any_cast& ex) {
439 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add custom output '%s': %s", name.c_str(), ex.what());
440 }
catch (
const std::exception& ex) {
441 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add output '%s': %s", name.c_str(), ex.what());
446bool BaseControllerInterface::check_input_valid(
const std::string& name)
const {
447 if (inputs_.find(name) == inputs_.end()) {
448 RCLCPP_WARN_THROTTLE(
449 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Could not find input '%s'", name.c_str());
452 if (
static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(
453 std::chrono::steady_clock::now() - inputs_.at(name).timestamp)
456 >= input_validity_period_) {
463BaseControllerInterface::validate_service_name(
const std::string& service_name,
const std::string& type)
const {
464 std::string parsed_service_name = modulo_utils::parsing::parse_topic_name(service_name);
465 if (parsed_service_name.empty()) {
467 get_node()->get_logger(),
468 "The parsed service name for %s service '%s' is empty. Provide a string with valid characters for the service "
471 type.c_str(), service_name.c_str());
474 if (service_name != parsed_service_name) {
476 get_node()->get_logger(),
477 "The parsed name for '%s' service '%s' is '%s'. Use the parsed name to refer to this service.", type.c_str(),
478 service_name.c_str(), parsed_service_name.c_str());
480 if (empty_services_.find(parsed_service_name) != empty_services_.cend()) {
482 get_node()->get_logger(),
"Service with name '%s' already exists as empty service.",
483 parsed_service_name.c_str());
486 if (string_services_.find(parsed_service_name) != string_services_.cend()) {
488 get_node()->get_logger(),
"Service with name '%s' already exists as string service.",
489 parsed_service_name.c_str());
492 RCLCPP_DEBUG(get_node()->get_logger(),
"Adding %s service '%s'.", type.c_str(), parsed_service_name.c_str());
493 return parsed_service_name;
498 create_service<true>(service_name, callback);
502 const std::string& service_name,
504 create_service<true>(service_name, callback);
509 create_service<false>(service_name, callback);
513 const std::string& service_name,
515 create_service<false>(service_name, callback);
519 if (tf_buffer_ ==
nullptr || tf_listener_ ==
nullptr) {
520 RCLCPP_DEBUG(get_node()->get_logger(),
"Adding TF buffer and listener.");
521 console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_NONE);
522 tf_buffer_ = std::make_shared<tf2_ros::Buffer>(get_node()->get_clock());
523 tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_, get_node());
525 RCLCPP_DEBUG(get_node()->get_logger(),
"TF buffer and listener already exist.");
530 const std::string& frame,
const std::string& reference_frame,
const tf2::TimePoint& time_point,
531 const tf2::Duration& duration) {
532 auto transform = lookup_ros_transform(frame, reference_frame, time_point, duration);
533 state_representation::CartesianPose result(frame, reference_frame);
539 const std::string& frame,
const std::string& reference_frame,
double validity_period,
540 const tf2::Duration& duration) {
542 lookup_ros_transform(frame, reference_frame, tf2::TimePoint(std::chrono::microseconds(0)), duration);
543 if (validity_period > 0.0
544 && (get_node()->get_clock()->now() - transform.header.stamp).seconds() > validity_period) {
547 state_representation::CartesianPose result(frame, reference_frame);
552geometry_msgs::msg::TransformStamped BaseControllerInterface::lookup_ros_transform(
553 const std::string& frame,
const std::string& reference_frame,
const tf2::TimePoint& time_point,
554 const tf2::Duration& duration) {
555 if (tf_buffer_ ==
nullptr || tf_listener_ ==
nullptr) {
557 "Failed to lookup transform: No TF buffer / listener configured.");
560 return tf_buffer_->lookupTransform(reference_frame, frame, time_point, duration);
561 }
catch (
const tf2::TransformException& ex) {
563 std::string(
"Failed to lookup transform: ").append(ex.what()));
568 if (tf_broadcaster_ ==
nullptr) {
569 RCLCPP_DEBUG(get_node()->get_logger(),
"Adding TF broadcaster.");
570 console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_NONE);
571 tf_broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(get_node());
573 RCLCPP_DEBUG(get_node()->get_logger(),
"TF broadcaster already exists.");
578 if (static_tf_broadcaster_ ==
nullptr) {
579 RCLCPP_DEBUG(get_node()->get_logger(),
"Adding static TF broadcaster.");
580 console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_NONE);
581 static_tf_broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(get_node());
583 RCLCPP_DEBUG(get_node()->get_logger(),
"Static TF broadcaster already exists.");
588 send_transforms(std::vector<state_representation::CartesianPose>{transform});
592 publish_transforms(transforms, tf_broadcaster_);
600 const std::vector<state_representation::CartesianPose>& transforms) {
601 publish_transforms(transforms, static_tf_broadcaster_,
true);
613 return get_lifecycle_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE;
617 return command_mutex_;
void send_static_transform(const state_representation::CartesianPose &transform)
Send a static transform to TF.
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.
void add_static_tf_broadcaster()
Configure a static transform broadcaster.
rclcpp::QoS get_qos() const
Getter of the Quality of Service attribute.
void add_service_lockfree(const std::string &service_name, const std::function< ControllerServiceResponse(void)> &callback)
void send_static_transforms(const std::vector< state_representation::CartesianPose > &transforms)
Send a vector of static transforms to TF.
state_representation::CartesianPose lookup_transform(const std::string &frame, const std::string &reference_frame, const tf2::TimePoint &time_point, const tf2::Duration &duration)
Look up a transform from TF.
void add_tf_broadcaster()
Configure a transform broadcaster.
bool is_active() const
Check if the controller is currently in state active or not.
void send_transform(const state_representation::CartesianPose &transform)
Send a transform to TF.
void set_parameter_value(const std::string &name, const T &value)
Set the value of a parameter.
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.
void trigger(const std::string &trigger_name)
Latch the trigger with the provided name.
void set_qos(const rclcpp::QoS &qos)
Set the Quality of Service for ROS publishers and subscribers.
void add_tf_listener()
Configure a transform buffer and listener.
void set_predicate(const std::string &predicate_name, bool predicate_value)
Set the value of the predicate given as parameter, if the predicate is not found does not do anything...
void add_predicate(const std::string &predicate_name, bool predicate_value)
Add a predicate to the map of predicates.
bool get_predicate(const std::string &predicate_name) const
Get the logical value of a predicate.
virtual bool on_validate_parameter_callback(const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Parameter validation function to be redefined by derived controller classes.
BaseControllerInterface()
Default constructor.
void add_service(const std::string &service_name, const std::function< ControllerServiceResponse(void)> &callback)
Add a service to trigger a callback function with no input arguments.
std::timed_mutex & get_command_mutex()
Get the reference to the command mutex.
void add_trigger(const std::string &trigger_name)
Add a trigger to the controller.
void send_transforms(const std::vector< state_representation::CartesianPose > &transforms)
Send a vector of transforms to TF.
An exception class to notify errors with parameters in modulo classes.
An exception class to notify incompatibility when translating parameters from different sources.
rclcpp::Parameter write_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Write a ROS Parameter from a ParameterInterface pointer.
rclcpp::ParameterType get_ros_parameter_type(const state_representation::ParameterType ¶meter_type)
Given a state representation parameter type, get the corresponding ROS parameter type.
void copy_parameter_value(const std::shared_ptr< const state_representation::ParameterInterface > &source_parameter, const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Copy the value of one parameter interface into another.
void read_message(state_representation::CartesianState &state, const geometry_msgs::msg::Accel &message)
Convert a ROS geometry_msgs::msg::Accel to a CartesianState.
std::shared_ptr< state_representation::ParameterInterface > read_parameter_const(const rclcpp::Parameter &ros_parameter, const std::shared_ptr< const state_representation::ParameterInterface > ¶meter)
Update the parameter value of a ParameterInterface from a ROS Parameter object only if the two parame...
Response structure to be returned by controller services.