1#include "modulo_controllers/RobotControllerInterface.hpp"
3#include <ament_index_cpp/get_package_share_directory.hpp>
4#include <hardware_interface/types/hardware_interface_type_values.hpp>
6#include <state_representation/exceptions/JointNotFoundException.hpp>
8using namespace state_representation;
10namespace modulo_controllers {
12static const std::map<std::string, JointStateVariable> interface_map =
13 {{hardware_interface::HW_IF_POSITION, JointStateVariable::POSITIONS},
14 {hardware_interface::HW_IF_VELOCITY, JointStateVariable::VELOCITIES},
15 {hardware_interface::HW_IF_ACCELERATION, JointStateVariable::ACCELERATIONS},
16 {hardware_interface::HW_IF_EFFORT, JointStateVariable::TORQUES}};
21 bool robot_model_required,
const std::string& control_type,
bool load_geometries)
23 control_type_(control_type),
24 control_type_fixed_(false),
25 robot_model_required_(robot_model_required),
26 load_geometries_(load_geometries),
27 new_joint_command_ready_(false),
28 command_decay_factor_(0.0),
29 command_rate_limit_(std::numeric_limits<double>::infinity()) {
30 if (!control_type.empty() && interface_map.find(control_type) == interface_map.cend()) {
31 RCLCPP_ERROR(get_node()->get_logger(),
"Invalid control type: %s", control_type.c_str());
32 throw std::invalid_argument(
"Invalid control type: " + control_type);
38 std::make_shared<Parameter<std::string>>(
"task_space_frame"),
39 "The frame name in the robot model to use for kinematics calculations (defaults to the last frame in the "
43 "If true, re-arrange the 'joints' parameter into a physically correct order according to the robot model");
45 std::make_shared<Parameter<std::string>>(
"ft_sensor_name"),
46 "Optionally, the name of a force-torque sensor in the hardware interface");
48 std::make_shared<Parameter<std::string>>(
"ft_sensor_reference_frame"),
49 "The reference frame of the force-torque sensor in the robot model");
51 "command_half_life", 0.1,
52 "A time constant for the exponential decay of the commanded velocity, acceleration or torque if no new command "
55 "command_rate_limit", command_rate_limit_,
56 "The maximum allowable change in command on any interface expressed in command units / second");
57 return CallbackReturn::SUCCESS;
62 std::stringstream timestamp;
63 timestamp << std::time(
nullptr);
67 if (load_geometries_) {
68 robot_ = std::make_shared<robot_model::Model>(
hardware_name_, urdf_path, [](
const std::string& package_name) {
69 return ament_index_cpp::get_package_share_directory(package_name) +
"/";
71 RCLCPP_DEBUG(get_node()->get_logger(),
"Generated robot model with collision features");
74 RCLCPP_DEBUG(get_node()->get_logger(),
"Generated robot model");
76 }
catch (
const std::exception& ex) {
78 get_node()->get_logger(),
79 "Could not generate robot model with temporary urdf from string content at path %s: %s", urdf_path.c_str(),
83 if (robot_model_required_ &&
robot_ ==
nullptr) {
84 RCLCPP_ERROR(get_node()->get_logger(),
"Robot model is not available even though it's required by the controller.");
85 return CallbackReturn::ERROR;
95 get_node()->get_logger(),
"Provided task space frame %s does not exist in the robot model!",
97 return CallbackReturn::ERROR;
103 if (joints->is_empty() && !control_type_.empty()) {
105 get_node()->get_logger(),
"The 'joints' parameter is required for a non-zero control type %s!",
106 control_type_.c_str());
107 return CallbackReturn::ERROR;
109 joints_ = joints->get_parameter_value<std::vector<std::string>>();
113 if (joints_.size() !=
robot_->get_number_of_joints()) {
115 get_node()->get_logger(),
116 "The number of interface joints (%zu) does not match the number of joints in the robot model (%u)",
117 joints_.size(),
robot_->get_number_of_joints());
119 std::vector<std::string> ordered_joints;
120 for (
const auto& ordered_name :
robot_->get_joint_frames()) {
121 for (
const auto& joint_name : joints_) {
122 if (joint_name == ordered_name) {
123 ordered_joints.push_back(joint_name);
128 if (ordered_joints.size() < joints_.size()) {
130 get_node()->get_logger(),
"%zu interface joints were not found in the robot model",
131 joints_.size() - ordered_joints.size());
132 return CallbackReturn::ERROR;
134 joints_ = ordered_joints;
139 if (!control_type_.empty()) {
140 if (control_type_ == hardware_interface::HW_IF_POSITION) {
141 previous_joint_command_values_ = std::vector<double>(joints_.size(), std::numeric_limits<double>::quiet_NaN());
143 previous_joint_command_values_ = std::vector<double>(joints_.size(), 0.0);
145 for (
const auto& joint : joints_) {
148 control_type_fixed_ =
true;
152 if (!ft_sensor_name->is_empty()) {
154 auto ft_sensor_reference_frame =
get_parameter(
"ft_sensor_reference_frame");
155 if (ft_sensor_reference_frame->is_empty()) {
157 get_node()->get_logger(),
158 "The 'ft_sensor_reference_frame' parameter cannot be empty if a force-torque sensor is specified!");
159 return CallbackReturn::ERROR;
165 get_node()->get_logger(),
"The FT sensor reference frame '%s' does not exist on the robot model!",
167 return CallbackReturn::ERROR;
174 RCLCPP_DEBUG(get_node()->get_logger(),
"Configuration of RobotControllerInterface successful");
175 return CallbackReturn::SUCCESS;
188 }
catch (
const std::out_of_range&) {
190 get_node()->get_logger(),
"The force torque sensor '%s' does not have all the expected state interfaces!",
192 return CallbackReturn::FAILURE;
197 RCLCPP_DEBUG(get_node()->get_logger(),
"Activation of RobotControllerInterface successful");
198 return CallbackReturn::SUCCESS;
202 if (!control_type_.empty() && control_type_ != hardware_interface::HW_IF_POSITION) {
203 std::fill(previous_joint_command_values_.begin(), previous_joint_command_values_.end(), 0.0);
205 RCLCPP_DEBUG(get_node()->get_logger(),
"Deactivation of RobotControllerInterface successful");
206 return CallbackReturn::SUCCESS;
209controller_interface::return_type RobotControllerInterface::read_state_interfaces() {
211 if (status != controller_interface::return_type::OK) {
215 if (joint_state_.get_size() > 0 && joint_state_.is_empty()) {
216 RCLCPP_DEBUG(get_node()->get_logger(),
"Reading first joint state");
217 joint_state_.set_zero();
220 for (
const auto& joint : joints_) {
222 unsigned int joint_index;
224 joint_index = joint_state_.get_joint_index(joint);
225 }
catch (exceptions::JointNotFoundException& ex) {
226 RCLCPP_ERROR_STREAM_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000, ex.what());
227 return controller_interface::return_type::ERROR;
229 for (
const auto& [interface, value] : data) {
230 if (!std::isfinite(value)) {
231 RCLCPP_ERROR_THROTTLE(
232 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Value of state interface '%s/%s' is not finite",
233 joint.c_str(), interface.c_str());
234 return controller_interface::return_type::ERROR;
236 switch (interface_map.at(interface)) {
237 case JointStateVariable::POSITIONS:
238 joint_state_.set_position(value, joint_index);
240 case JointStateVariable::VELOCITIES:
241 joint_state_.set_velocity(value, joint_index);
243 case JointStateVariable::ACCELERATIONS:
244 joint_state_.set_acceleration(value, joint_index);
246 case JointStateVariable::TORQUES:
247 joint_state_.set_torque(value, joint_index);
257 std::vector<double> wrench = {ft_sensor.at(
"force.x"), ft_sensor.at(
"force.y"), ft_sensor.at(
"force.z"),
258 ft_sensor.at(
"torque.x"), ft_sensor.at(
"torque.y"), ft_sensor.at(
"torque.z")};
259 if (std::any_of(wrench.begin(), wrench.end(), [](
double v) { return !std::isfinite(v); })) {
260 RCLCPP_ERROR_THROTTLE(
261 get_node()->get_logger(), *get_node()->get_clock(), 1000,
262 "Found non-finite values in the force torque sensor '%s' state interfaces",
ft_sensor_name_.c_str());
263 return controller_interface::return_type::ERROR;
265 ft_sensor_.set_wrench(wrench);
268 return controller_interface::return_type::OK;
271controller_interface::return_type RobotControllerInterface::write_command_interfaces(
const rclcpp::Duration& period) {
272 if (!control_type_.empty()) {
273 double decay = 1.0 - command_decay_factor_ * period.seconds();
274 decay = std::max(0.0, decay);
275 double rate_limit = command_rate_limit_ * period.seconds();
277 auto joint_command_values = joint_command_values_.readFromRT();
278 if (joint_command_values ==
nullptr) {
279 new_joint_command_ready_ =
false;
282 for (std::size_t index = 0; index < joints_.size(); ++index) {
283 double previous_command = previous_joint_command_values_.at(index);
284 if (new_joint_command_ready_) {
285 auto new_command = joint_command_values->at(index);
286 if (std::isfinite(previous_command) && std::abs(new_command - previous_command) > rate_limit) {
287 double command_offset = new_command - previous_command > 0.0 ? rate_limit : -rate_limit;
288 new_command = previous_command + command_offset;
291 previous_joint_command_values_.at(index) = new_command;
292 }
else if (control_type_ != hardware_interface::HW_IF_POSITION) {
293 auto new_command = previous_command * decay;
295 previous_joint_command_values_.at(index) = new_command;
298 new_joint_command_ready_ =
false;
311 return cartesian_state_;
323 if (!ft_sensor_.is_empty() && (ft_sensor_.get_reference_frame() == cartesian_state_.get_name())) {
324 auto ft_sensor_in_robot_frame = cartesian_state_ * ft_sensor_;
325 cartesian_state_.set_wrench(ft_sensor_in_robot_frame.get_wrench());
331 if (!joint_command) {
332 RCLCPP_DEBUG(get_node()->get_logger(),
"set_joint_command called with an empty JointState");
336 std::vector<double> joint_command_values;
337 joint_command_values.reserve(joints_.size());
339 for (
const auto& joint : joints_) {
341 switch (interface_map.at(control_type_)) {
342 case JointStateVariable::POSITIONS:
343 joint_command_values.push_back(joint_command.get_position(joint));
345 case JointStateVariable::VELOCITIES:
346 joint_command_values.push_back(joint_command.get_velocity(joint));
348 case JointStateVariable::ACCELERATIONS:
349 joint_command_values.push_back(joint_command.get_acceleration(joint));
351 case JointStateVariable::TORQUES:
352 joint_command_values.push_back(joint_command.get_torque(joint));
357 }
catch (
const state_representation::exceptions::JointNotFoundException& ex) {
358 RCLCPP_WARN_STREAM_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000, ex.what());
362 if (joint_command_values.size() == joints_.size()) {
363 joint_command_values_.writeFromNonRT(joint_command_values);
364 new_joint_command_ready_ =
true;
366 RCLCPP_WARN_THROTTLE(
367 get_node()->get_logger(), *get_node()->get_clock(), 1000,
368 "Unable to set command values from provided joint command");
373 if ((parameter->get_name() ==
"command_half_life" || parameter->get_name() ==
"command_rate_limit")
374 && parameter->get_parameter_value<
double>() < 0.0) {
376 get_node()->get_logger(),
"Parameter value of '%s' should be greater than 0", parameter->get_name().c_str());
383 return control_type_;
387 if (control_type_fixed_) {
388 throw std::runtime_error(
"Control type is fixed and cannot be changed anymore");
390 if (!control_type.empty() && interface_map.find(control_type) == interface_map.cend()) {
391 throw std::runtime_error(
"Invalid control type: " + control_type);
393 control_type_ = control_type;
std::shared_ptr< state_representation::ParameterInterface > get_parameter(const std::string &name) const
Get a parameter by name.
T get_parameter_value(const std::string &name) const
Get a parameter value by name.
void add_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter, const std::string &description, bool read_only=false)
Add a parameter.
virtual controller_interface::return_type write_command_interfaces(const rclcpp::Duration &period)
Write the command interfaces.
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.
double get_state_interface(const std::string &name, const std::string &interface) const
Get the value of a state interface by name.
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.
void add_command_interface(const std::string &name, const std::string &interface)
Add a command interface to the controller by name.
Base controller class that automatically associates joints with a JointState object.
RobotControllerInterface()
Default constructor.
std::string ft_sensor_name_
The name of a force torque sensor in the hardware description.
const state_representation::CartesianState & get_cartesian_state()
Access the Cartesian state object.
CallbackReturn on_activate() override
Activate the controller.
bool on_validate_parameter_callback(const std::shared_ptr< state_representation::ParameterInterface > ¶meter) override
Parameter validation function to be redefined by derived controller classes.
const state_representation::JointState & get_joint_state()
Access the joint state object.
std::string ft_sensor_reference_frame_
The sensing reference frame.
CallbackReturn on_deactivate() override
Deactivate the controller.
void set_control_type(const std::string &control_type)
Set the control type of the controller after construction.
const state_representation::CartesianWrench & get_ft_sensor()
Access the Cartesian wrench object.
std::shared_ptr< robot_model::Model > robot_
Robot model object generated from URDF.
void compute_cartesian_state()
Compute the Cartesian state from forward kinematics of the current joint state.
void set_joint_command(const state_representation::JointState &joint_command)
Set the joint command object.
CallbackReturn add_interfaces() override
Add interfaces like parameters, signals, services, and predicates to the controller.
std::string task_space_frame_
The frame in task space for forward kinematics calculations, if applicable.
CallbackReturn on_configure() override
Configure the controller.
std::string get_control_type() const
Get the control type of the controller, empty or one of [position, velocity, effort or acceleration].