diff --git a/joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp b/joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp index ae370df0a6..6b90cb07f2 100644 --- a/joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp +++ b/joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp @@ -24,6 +24,7 @@ #include "control_msgs/action/follow_joint_trajectory.hpp" #include "control_msgs/msg/joint_trajectory_controller_state.hpp" #include "control_msgs/srv/query_trajectory_state.hpp" +#include "control_msgs/srv/set_scaling_factor.hpp" #include "control_toolbox/pid.hpp" #include "controller_interface/controller_interface.hpp" #include "hardware_interface/types/hardware_interface_type_values.hpp" @@ -53,6 +54,14 @@ using namespace std::chrono_literals; // NOLINT namespace joint_trajectory_controller { +struct TimeData +{ + TimeData() : time(0.0), period(rclcpp::Duration::from_nanoseconds(0.0)), uptime(0.0) {} + rclcpp::Time time; + rclcpp::Duration period; + rclcpp::Time uptime; +}; + class JointTrajectoryController : public controller_interface::ControllerInterface { public: @@ -162,6 +171,10 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa // reserved storage for result of the command when closed loop pid adapter is used std::vector tmp_command_; + // Things around speed scaling + double scaling_factor_{}; + realtime_tools::RealtimeBuffer time_data_; + // Timeout to consider commands old double cmd_timeout_; // True if holding position or repeating last trajectory point in case of success @@ -299,8 +312,15 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa void resize_joint_trajectory_point_command( trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size); + bool set_scaling_factor( + control_msgs::srv::SetScalingFactor::Request::SharedPtr req, + control_msgs::srv::SetScalingFactor::Response::SharedPtr resp); + urdf::Model model_; + realtime_tools::RealtimeBuffer scaling_factor_rt_buff_; + rclcpp::Service::SharedPtr set_scaling_factor_srv_; + /** * @brief Assigns the values from a trajectory point interface to a joint interface. * diff --git a/joint_trajectory_controller/src/joint_trajectory_controller.cpp b/joint_trajectory_controller/src/joint_trajectory_controller.cpp index 4a4b63cb75..7ba4fab997 100644 --- a/joint_trajectory_controller/src/joint_trajectory_controller.cpp +++ b/joint_trajectory_controller/src/joint_trajectory_controller.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -100,12 +101,17 @@ JointTrajectoryController::command_interface_configuration() const conf.names.push_back(joint_name + "/" + interface_type); } } + if (!params_.speed_scaling_command_interface_name.empty()) + { + conf.names.push_back(params_.speed_scaling_command_interface_name); + } return conf; } controller_interface::InterfaceConfiguration JointTrajectoryController::state_interface_configuration() const { + const auto logger = get_node()->get_logger(); controller_interface::InterfaceConfiguration conf; conf.type = controller_interface::interface_configuration_type::INDIVIDUAL; conf.names.reserve(dof_ * params_.state_interfaces.size()); @@ -116,12 +122,37 @@ JointTrajectoryController::state_interface_configuration() const conf.names.push_back(joint_name + "/" + interface_type); } } + if (!params_.speed_scaling_state_interface_name.empty()) + { + RCLCPP_INFO( + logger, "Using scaling state from the hardware from interface %s.", + params_.speed_scaling_state_interface_name.c_str()); + conf.names.push_back(params_.speed_scaling_state_interface_name); + } return conf; } controller_interface::return_type JointTrajectoryController::update( const rclcpp::Time & time, const rclcpp::Duration & period) { + if (params_.speed_scaling_state_interface_name.empty()) + { + scaling_factor_ = *(scaling_factor_rt_buff_.readFromRT()); + } + else + { + if (state_interfaces_.back().get_name() == params_.speed_scaling_state_interface_name) + { + scaling_factor_ = state_interfaces_.back().get_value(); + } + else + { + RCLCPP_ERROR( + get_node()->get_logger(), "Speed scaling interface (%s) not found in hardware interface.", + params_.speed_scaling_state_interface_name.c_str()); + } + } + if (get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) { return controller_interface::return_type::OK; @@ -163,6 +194,17 @@ controller_interface::return_type JointTrajectoryController::update( // currently carrying out a trajectory if (has_active_trajectory()) { + // Adjust time with scaling factor + TimeData time_data; + time_data.time = time; + rcl_duration_value_t t_period = (time_data.time - time_data_.readFromRT()->time).nanoseconds(); + time_data.period = rclcpp::Duration::from_nanoseconds(t_period) * scaling_factor_; + time_data.uptime = time_data_.readFromRT()->uptime + time_data.period; + rclcpp::Time traj_time = + time_data_.readFromRT()->uptime + rclcpp::Duration::from_nanoseconds(t_period); + time_data_.reset(); + time_data_.initRT(time_data); + bool first_sample = false; // if sampling the first time, set the point before you sample if (!traj_external_point_ptr_->is_sampled_already()) @@ -171,19 +213,19 @@ controller_interface::return_type JointTrajectoryController::update( if (params_.open_loop_control) { traj_external_point_ptr_->set_point_before_trajectory_msg( - time, last_commanded_state_, joints_angle_wraparound_); + traj_time, last_commanded_state_, joints_angle_wraparound_); } else { traj_external_point_ptr_->set_point_before_trajectory_msg( - time, state_current_, joints_angle_wraparound_); + traj_time, state_current_, joints_angle_wraparound_); } } // find segment for current timestamp TrajectoryPointConstIter start_segment_itr, end_segment_itr; const bool valid_point = traj_external_point_ptr_->sample( - time, interpolation_method_, state_desired_, start_segment_itr, end_segment_itr); + traj_time, interpolation_method_, state_desired_, start_segment_itr, end_segment_itr); if (valid_point) { @@ -444,7 +486,8 @@ bool JointTrajectoryController::read_state_from_command_interfaces(JointTrajecto auto interface_has_values = [](const auto & joint_interface) { return std::find_if( - joint_interface.begin(), joint_interface.end(), [](const auto & interface) + joint_interface.begin(), joint_interface.end(), + [](const auto & interface) { return std::isnan(interface.get().get_value()); }) == joint_interface.end(); }; @@ -514,7 +557,8 @@ bool JointTrajectoryController::read_commands_from_command_interfaces( auto interface_has_values = [](const auto & joint_interface) { return std::find_if( - joint_interface.begin(), joint_interface.end(), [](const auto & interface) + joint_interface.begin(), joint_interface.end(), + [](const auto & interface) { return std::isnan(interface.get().get_value()); }) == joint_interface.end(); }; @@ -874,10 +918,21 @@ controller_interface::CallbackReturn JointTrajectoryController::on_configure( resize_joint_trajectory_point(state_error_, dof_); resize_joint_trajectory_point(last_commanded_state_, dof_); + // create services query_state_srv_ = get_node()->create_service( std::string(get_node()->get_name()) + "/query_state", std::bind(&JointTrajectoryController::query_state_service, this, _1, _2)); + set_scaling_factor_srv_ = get_node()->create_service( + "~/set_scaling_factor", std::bind( + &JointTrajectoryController::set_scaling_factor, this, + std::placeholders::_1, std::placeholders::_2)); + + // set scaling factor to low value default + RCLCPP_INFO( + logger, "Setting initial scaling factor to %2f", params_.scaling_factor_initial_default); + scaling_factor_rt_buff_.writeFromNonRT(params_.scaling_factor_initial_default); + return CallbackReturn::SUCCESS; } @@ -892,6 +947,12 @@ controller_interface::CallbackReturn JointTrajectoryController::on_activate( // parse remaining parameters default_tolerances_ = get_segment_tolerances(params_); + // Setup time_data buffer used for scaling + TimeData time_data; + time_data.time = get_node()->now(); + time_data.period = rclcpp::Duration::from_nanoseconds(0); + time_data.uptime = get_node()->now(); + time_data_.initRT(time_data); // order all joints in the storage for (const auto & interface : params_.command_interfaces) @@ -1568,6 +1629,43 @@ void JointTrajectoryController::resize_joint_trajectory_point_command( } } +bool JointTrajectoryController::set_scaling_factor( + control_msgs::srv::SetScalingFactor::Request::SharedPtr req, + control_msgs::srv::SetScalingFactor::Response::SharedPtr resp) +{ + if (req->scaling_factor < 0 && req->scaling_factor > 1) + { + RCLCPP_WARN( + get_node()->get_logger(), "Scaling factor has to be in range [0, 1]. Ignoring input!"); + resp->success = false; + return true; + } + + RCLCPP_INFO(get_node()->get_logger(), "New scaling factor will be %f", req->scaling_factor); + if (params_.speed_scaling_command_interface_name.empty()) + { + if (!params_.speed_scaling_state_interface_name.empty()) + { + RCLCPP_WARN( + get_node()->get_logger(), + "Setting the scaling factor while only one-way communication with the hardware is setup. " + "This will likely get overwritten by the hardware again. If available, please also setup " + "the speed_scaling_command_interface_name"); + } + scaling_factor_rt_buff_.writeFromNonRT(req->scaling_factor); + } + else + { + if (get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) + { + // TODO(Felix): Use proper adressing here. + command_interfaces_.back().set_value(static_cast(req->scaling_factor)); + } + } + resp->success = true; + return true; +} + bool JointTrajectoryController::has_active_trajectory() const { return traj_external_point_ptr_ != nullptr && traj_external_point_ptr_->has_trajectory_msg(); diff --git a/joint_trajectory_controller/src/joint_trajectory_controller_parameters.yaml b/joint_trajectory_controller/src/joint_trajectory_controller_parameters.yaml index b17229cab8..1d67b16ead 100644 --- a/joint_trajectory_controller/src/joint_trajectory_controller_parameters.yaml +++ b/joint_trajectory_controller/src/joint_trajectory_controller_parameters.yaml @@ -39,6 +39,23 @@ joint_trajectory_controller: "joint_trajectory_controller::state_interface_type_combinations": null, } } + scaling_factor_initial_default: { + type: double, + default_value: 1.0, + description: "The initial value of the scaling factor if not exchange with hardware takes place." + } + speed_scaling_state_interface_name: { + type: string, + default_value: "", + read_only: true, + description: "Fully qualified name of the speed scaling state interface name" + } + speed_scaling_command_interface_name: { + type: string, + default_value: "", + read_only: true, + description: "Command interface name used for setting the speed scaling factor on the hardware." + } allow_partial_joints_goal: { type: bool, default_value: false,