From d30cb715ebae606a72cd98e67bd62bedb79e8f61 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Tue, 8 Jul 2025 20:14:09 +0200 Subject: [PATCH 1/2] Add twist to hardware interface --- .../ur_robot_driver/hardware_interface.hpp | 5 ++ ur_robot_driver/src/hardware_interface.cpp | 52 ++++++++++++++++++- ur_robot_driver/urdf/ur.ros2_control.xacro | 8 +++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp b/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp index 00da049f5..df581e10d 100644 --- a/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp +++ b/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp @@ -83,6 +83,7 @@ enum StoppingInterface STOP_FREEDRIVE, STOP_TOOL_CONTACT, STOP_TORQUE, + STOP_TWIST, }; // We define our own quaternion to use it as a buffer, since we need to pass pointers to the state @@ -179,6 +180,7 @@ class URPositionHardwareInterface : public hardware_interface::SystemInterface urcl::vector6d_t urcl_position_commands_old_; urcl::vector6d_t urcl_velocity_commands_; urcl::vector6d_t urcl_torque_commands_; + urcl::vector6d_t urcl_twist_commands_; urcl::vector6d_t urcl_joint_positions_; urcl::vector6d_t urcl_joint_velocities_; urcl::vector6d_t urcl_joint_efforts_; @@ -260,6 +262,8 @@ class URPositionHardwareInterface : public hardware_interface::SystemInterface urcl::vector6d_t passthrough_trajectory_accelerations_; double passthrough_trajectory_time_from_start_; + bool twist_controller_running_; + // payload stuff urcl::vector3d_t payload_center_of_gravity_; double payload_mass_; @@ -322,6 +326,7 @@ class URPositionHardwareInterface : public hardware_interface::SystemInterface const std::string FORCE_MODE_GPIO = "force_mode"; const std::string FREEDRIVE_MODE_GPIO = "freedrive_mode"; const std::string TOOL_CONTACT_GPIO = "tool_contact"; + const std::string TWIST_GPIO = "twist"; std::unordered_map> mode_compatibility_; }; diff --git a/ur_robot_driver/src/hardware_interface.cpp b/ur_robot_driver/src/hardware_interface.cpp index d8f1c7205..6c01276f4 100644 --- a/ur_robot_driver/src/hardware_interface.cpp +++ b/ur_robot_driver/src/hardware_interface.cpp @@ -65,6 +65,7 @@ URPositionHardwareInterface::URPositionHardwareInterface() mode_compatibility_[hardware_interface::HW_IF_POSITION][PASSTHROUGH_GPIO] = false; mode_compatibility_[hardware_interface::HW_IF_POSITION][FREEDRIVE_MODE_GPIO] = false; mode_compatibility_[hardware_interface::HW_IF_POSITION][TOOL_CONTACT_GPIO] = true; + mode_compatibility_[hardware_interface::HW_IF_POSITION][TWIST_GPIO] = false; mode_compatibility_[hardware_interface::HW_IF_VELOCITY][hardware_interface::HW_IF_POSITION] = false; mode_compatibility_[hardware_interface::HW_IF_VELOCITY][hardware_interface::HW_IF_EFFORT] = false; @@ -72,6 +73,7 @@ URPositionHardwareInterface::URPositionHardwareInterface() mode_compatibility_[hardware_interface::HW_IF_VELOCITY][PASSTHROUGH_GPIO] = false; mode_compatibility_[hardware_interface::HW_IF_VELOCITY][FREEDRIVE_MODE_GPIO] = false; mode_compatibility_[hardware_interface::HW_IF_VELOCITY][TOOL_CONTACT_GPIO] = true; + mode_compatibility_[hardware_interface::HW_IF_VELOCITY][TWIST_GPIO] = true; mode_compatibility_[hardware_interface::HW_IF_EFFORT][hardware_interface::HW_IF_POSITION] = false; mode_compatibility_[hardware_interface::HW_IF_EFFORT][hardware_interface::HW_IF_VELOCITY] = false; @@ -86,6 +88,7 @@ URPositionHardwareInterface::URPositionHardwareInterface() mode_compatibility_[FORCE_MODE_GPIO][PASSTHROUGH_GPIO] = true; mode_compatibility_[FORCE_MODE_GPIO][FREEDRIVE_MODE_GPIO] = false; mode_compatibility_[FORCE_MODE_GPIO][TOOL_CONTACT_GPIO] = false; + mode_compatibility_[FORCE_MODE_GPIO][TWIST_GPIO] = false; mode_compatibility_[PASSTHROUGH_GPIO][hardware_interface::HW_IF_POSITION] = false; mode_compatibility_[PASSTHROUGH_GPIO][hardware_interface::HW_IF_VELOCITY] = false; @@ -93,6 +96,7 @@ URPositionHardwareInterface::URPositionHardwareInterface() mode_compatibility_[PASSTHROUGH_GPIO][FORCE_MODE_GPIO] = true; mode_compatibility_[PASSTHROUGH_GPIO][FREEDRIVE_MODE_GPIO] = false; mode_compatibility_[PASSTHROUGH_GPIO][TOOL_CONTACT_GPIO] = true; + mode_compatibility_[PASSTHROUGH_GPIO][TWIST_GPIO] = false; mode_compatibility_[FREEDRIVE_MODE_GPIO][hardware_interface::HW_IF_POSITION] = false; mode_compatibility_[FREEDRIVE_MODE_GPIO][hardware_interface::HW_IF_VELOCITY] = false; @@ -100,6 +104,7 @@ URPositionHardwareInterface::URPositionHardwareInterface() mode_compatibility_[FREEDRIVE_MODE_GPIO][FORCE_MODE_GPIO] = false; mode_compatibility_[FREEDRIVE_MODE_GPIO][PASSTHROUGH_GPIO] = false; mode_compatibility_[FREEDRIVE_MODE_GPIO][TOOL_CONTACT_GPIO] = false; + mode_compatibility_[FREEDRIVE_MODE_GPIO][TWIST_GPIO] = false; mode_compatibility_[TOOL_CONTACT_GPIO][hardware_interface::HW_IF_POSITION] = true; mode_compatibility_[TOOL_CONTACT_GPIO][hardware_interface::HW_IF_VELOCITY] = true; @@ -107,6 +112,14 @@ URPositionHardwareInterface::URPositionHardwareInterface() mode_compatibility_[TOOL_CONTACT_GPIO][FORCE_MODE_GPIO] = false; mode_compatibility_[TOOL_CONTACT_GPIO][PASSTHROUGH_GPIO] = true; mode_compatibility_[TOOL_CONTACT_GPIO][FREEDRIVE_MODE_GPIO] = false; + mode_compatibility_[TOOL_CONTACT_GPIO][TWIST_GPIO] = true; + + mode_compatibility_[TWIST_GPIO][hardware_interface::HW_IF_POSITION] = false; + mode_compatibility_[TWIST_GPIO][hardware_interface::HW_IF_VELOCITY] = false; + mode_compatibility_[TWIST_GPIO][FORCE_MODE_GPIO] = false; + mode_compatibility_[TWIST_GPIO][PASSTHROUGH_GPIO] = false; + mode_compatibility_[TWIST_GPIO][FREEDRIVE_MODE_GPIO] = false; + mode_compatibility_[TWIST_GPIO][TOOL_CONTACT_GPIO] = true; } URPositionHardwareInterface::~URPositionHardwareInterface() @@ -131,12 +144,14 @@ URPositionHardwareInterface::on_init(const hardware_interface::HardwareComponent urcl_position_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; urcl_position_commands_old_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; urcl_velocity_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; + urcl_twist_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; position_controller_running_ = false; velocity_controller_running_ = false; torque_controller_running_ = false; freedrive_mode_controller_running_ = false; passthrough_trajectory_controller_running_ = false; tool_contact_controller_running_ = false; + twist_controller_running_ = false; runtime_state_ = static_cast(rtde::RUNTIME_STATE::STOPPED); pausing_state_ = PausingState::RUNNING; pausing_ramp_up_increment_ = 0.01; @@ -482,6 +497,19 @@ std::vector URPositionHardwareInterface::e &passthrough_trajectory_accelerations_[i])); } + command_interfaces.emplace_back( + hardware_interface::CommandInterface(tf_prefix + TWIST_GPIO, "linear_velocity_x", &urcl_twist_commands_[0])); + command_interfaces.emplace_back( + hardware_interface::CommandInterface(tf_prefix + TWIST_GPIO, "linear_velocity_y", &urcl_twist_commands_[1])); + command_interfaces.emplace_back( + hardware_interface::CommandInterface(tf_prefix + TWIST_GPIO, "linear_velocity_z", &urcl_twist_commands_[2])); + command_interfaces.emplace_back( + hardware_interface::CommandInterface(tf_prefix + TWIST_GPIO, "angular_velocity_x", &urcl_twist_commands_[3])); + command_interfaces.emplace_back( + hardware_interface::CommandInterface(tf_prefix + TWIST_GPIO, "angular_velocity_y", &urcl_twist_commands_[4])); + command_interfaces.emplace_back( + hardware_interface::CommandInterface(tf_prefix + TWIST_GPIO, "angular_velocity_z", &urcl_twist_commands_[5])); + command_interfaces.emplace_back(hardware_interface::CommandInterface( tf_prefix + TOOL_CONTACT_GPIO, "tool_contact_set_state", &tool_contact_set_state_)); @@ -884,6 +912,8 @@ hardware_interface::return_type URPositionHardwareInterface::write(const rclcpp: urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP, 0, urcl::RobotReceiveTimeout::millisec(1000 * 5.0 / static_cast(info_.rw_rate))); check_passthrough_trajectory_controller(); + } else if (twist_controller_running_) { + ur_driver_->writeJointCommand(urcl_twist_commands_, urcl::comm::ControlMode::MODE_SPEEDL, receive_timeout_); } else { ur_driver_->writeKeepalive(); } @@ -1170,6 +1200,9 @@ hardware_interface::return_type URPositionHardwareInterface::prepare_command_mod if (tool_contact_controller_running_) { control_modes[i].push_back(TOOL_CONTACT_GPIO); } + if (twist_controller_running_) { + control_modes[i].push_back(TWIST_GPIO); + } } auto is_mode_compatible = [this](const std::string& mode, const std::vector& other_modes) { @@ -1197,7 +1230,8 @@ hardware_interface::return_type URPositionHardwareInterface::prepare_command_mod { tf_prefix + FORCE_MODE_GPIO + "/type", FORCE_MODE_GPIO }, { tf_prefix + PASSTHROUGH_GPIO + "/setpoint_positions_" + std::to_string(i), PASSTHROUGH_GPIO }, { tf_prefix + FREEDRIVE_MODE_GPIO + "/async_success", FREEDRIVE_MODE_GPIO }, - { tf_prefix + TOOL_CONTACT_GPIO + "/tool_contact_set_state", TOOL_CONTACT_GPIO } + { tf_prefix + TOOL_CONTACT_GPIO + "/tool_contact_set_state", TOOL_CONTACT_GPIO }, + { tf_prefix + TWIST_GPIO + "/linear_velocity_x", TWIST_GPIO } }; for (auto& item : start_modes_to_check) { @@ -1247,7 +1281,8 @@ hardware_interface::return_type URPositionHardwareInterface::prepare_command_mod StoppingInterface::STOP_PASSTHROUGH }, { tf_prefix + FREEDRIVE_MODE_GPIO + "/async_success", FREEDRIVE_MODE_GPIO, StoppingInterface::STOP_FREEDRIVE }, { tf_prefix + TOOL_CONTACT_GPIO + "/tool_contact_set_state", TOOL_CONTACT_GPIO, - StoppingInterface::STOP_TOOL_CONTACT } + StoppingInterface::STOP_TOOL_CONTACT }, + { tf_prefix + TWIST_GPIO + "/linear_velocity_x", TWIST_GPIO, StoppingInterface::STOP_TWIST } }; for (auto& item : stop_modes_to_check) { if (key == std::get<0>(item)) { @@ -1312,6 +1347,7 @@ hardware_interface::return_type URPositionHardwareInterface::perform_command_mod freedrive_activated_ = false; freedrive_mode_abort_ = 1.0; } + if (stop_modes_.size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(), StoppingInterface::STOP_TOOL_CONTACT) != stop_modes_[0].end()) { tool_contact_controller_running_ = false; @@ -1319,6 +1355,12 @@ hardware_interface::return_type URPositionHardwareInterface::perform_command_mod ur_driver_->endToolContact(); } + if (stop_modes_.size() != 0 && + std::find(stop_modes_[0].begin(), stop_modes_[0].end(), StoppingInterface::STOP_TWIST) != stop_modes_[0].end()) { + twist_controller_running_ = false; + urcl_twist_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; + } + if (start_modes_.size() != 0 && std::find(start_modes_[0].begin(), start_modes_[0].end(), hardware_interface::HW_IF_POSITION) != start_modes_[0].end()) { velocity_controller_running_ = false; @@ -1366,6 +1408,12 @@ hardware_interface::return_type URPositionHardwareInterface::perform_command_mod std::find(start_modes_[0].begin(), start_modes_[0].end(), TOOL_CONTACT_GPIO) != start_modes_[0].end()) { tool_contact_controller_running_ = true; } + if (start_modes_[0].size() != 0 && + std::find(start_modes_[0].begin(), start_modes_[0].end(), TWIST_GPIO) != start_modes_[0].end()) { + velocity_controller_running_ = false; + position_controller_running_ = false; + twist_controller_running_ = true; + } start_modes_.clear(); stop_modes_.clear(); diff --git a/ur_robot_driver/urdf/ur.ros2_control.xacro b/ur_robot_driver/urdf/ur.ros2_control.xacro index b480434b0..0662ad15e 100644 --- a/ur_robot_driver/urdf/ur.ros2_control.xacro +++ b/ur_robot_driver/urdf/ur.ros2_control.xacro @@ -298,6 +298,14 @@ + + + + + + + + From 46028004c46afcd78cf4a484843cd6a9f46c518f Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Tue, 8 Jul 2025 21:11:09 +0200 Subject: [PATCH 2/2] Add twist controller --- ur_controllers/CMakeLists.txt | 7 + ur_controllers/controller_plugins.xml | 5 + .../ur_controllers/twist_controller.hpp | 75 +++++++++ ur_controllers/src/twist_controller.cpp | 155 ++++++++++++++++++ .../src/twist_controller_parameters.yaml | 7 + ur_robot_driver/config/ur_controllers.yaml | 7 + ur_robot_driver/launch/ur_control.launch.py | 1 + 7 files changed, 257 insertions(+) create mode 100644 ur_controllers/include/ur_controllers/twist_controller.hpp create mode 100644 ur_controllers/src/twist_controller.cpp create mode 100644 ur_controllers/src/twist_controller_parameters.yaml diff --git a/ur_controllers/CMakeLists.txt b/ur_controllers/CMakeLists.txt index 16f07953d..f88777abb 100644 --- a/ur_controllers/CMakeLists.txt +++ b/ur_controllers/CMakeLists.txt @@ -87,6 +87,11 @@ generate_parameter_library( src/passthrough_trajectory_controller_parameters.yaml ) +generate_parameter_library( + twist_controller_parameters + src/twist_controller_parameters.yaml +) + generate_parameter_library( ur_configuration_controller_parameters src/ur_configuration_controller_parameters.yaml @@ -100,6 +105,7 @@ add_library(${PROJECT_NAME} SHARED src/freedrive_mode_controller.cpp src/gpio_controller.cpp src/passthrough_trajectory_controller.cpp + src/twist_controller.cpp src/ur_configuration_controller.cpp) target_include_directories(${PROJECT_NAME} PRIVATE @@ -114,6 +120,7 @@ target_link_libraries(${PROJECT_NAME} freedrive_mode_controller_parameters passthrough_trajectory_controller_parameters ur_configuration_controller_parameters + twist_controller_parameters ${geometry_msgs_TARGETS} ${lifecycle_msgs_TARGETS} ${std_msgs_TARGETS} diff --git a/ur_controllers/controller_plugins.xml b/ur_controllers/controller_plugins.xml index f4c916aa3..ff9c3f6d6 100644 --- a/ur_controllers/controller_plugins.xml +++ b/ur_controllers/controller_plugins.xml @@ -39,4 +39,9 @@ Controller to use the tool contact functionality of the robot. + + + Controller to move a manipulator's TCP using Cartesian velocities. + + diff --git a/ur_controllers/include/ur_controllers/twist_controller.hpp b/ur_controllers/include/ur_controllers/twist_controller.hpp new file mode 100644 index 000000000..6c7b3a110 --- /dev/null +++ b/ur_controllers/include/ur_controllers/twist_controller.hpp @@ -0,0 +1,75 @@ +// Copyright 2025, Universal Robots A/S +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the {copyright_holder} nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef UR_CONTROLLERS__TWIST_CONTROLLER_HPP_ +#define UR_CONTROLLERS__TWIST_CONTROLLER_HPP_ + +#include + +#include +#include +#include + +#include "ur_controllers/twist_controller_parameters.hpp" + +namespace ur_controllers +{ +class TwistController : public controller_interface::ControllerInterface +{ +public: + controller_interface::InterfaceConfiguration command_interface_configuration() const override; + + controller_interface::InterfaceConfiguration state_interface_configuration() const override; + + controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; + + CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; + + CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; + + CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override; + + CallbackReturn on_init() override; + +private: + void reset(); + + using TwistStamped = geometry_msgs::msg::TwistStamped; + + bool subscriber_is_active_ = false; + rclcpp::Subscription::SharedPtr twist_command_subscriber_ = nullptr; + + realtime_tools::RealtimeThreadSafeBox received_twist_msg_; + TwistStamped command_msg_; + + std::shared_ptr param_listener_; + twist_controller::Params controller_params_; +}; +} // namespace ur_controllers + +#endif // UR_CONTROLLERS__TWIST_CONTROLLER_HPP_ diff --git a/ur_controllers/src/twist_controller.cpp b/ur_controllers/src/twist_controller.cpp new file mode 100644 index 000000000..9937ec130 --- /dev/null +++ b/ur_controllers/src/twist_controller.cpp @@ -0,0 +1,155 @@ +// Copyright 2025, Universal Robots A/S +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the {copyright_holder} nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +namespace ur_controllers +{ +controller_interface::CallbackReturn TwistController::on_init() +{ + param_listener_ = std::make_shared(get_node()); + controller_params_ = param_listener_->get_params(); + + return controller_interface::CallbackReturn::SUCCESS; +} + +controller_interface::CallbackReturn TwistController::on_configure(const rclcpp_lifecycle::State& /* previous_state */) +{ + twist_command_subscriber_ = get_node()->create_subscription( + "~/twist", rclcpp::SystemDefaultsQoS(), [this](const std::shared_ptr msg) -> void { + if (!subscriber_is_active_) { + RCLCPP_WARN(get_node()->get_logger(), "Can't accept new commands. subscriber is inactive"); + return; + } + if ((msg->header.stamp.sec == 0) && (msg->header.stamp.nanosec == 0)) { + RCLCPP_WARN_ONCE(get_node()->get_logger(), "Received TwistStamped with zero timestamp, setting it to current " + "time, this message will only be shown once"); + msg->header.stamp = get_node()->now(); + } + + if (msg->header.frame_id != controller_params_.tf_prefix + "base") { + // TODO(urfeex): Support transforming the twist? + RCLCPP_WARN(get_node()->get_logger(), + "Received TwistStamped with frame_id '%s'. Currently, it is only supported to command twists in " + "'%s'. The command will be ignored.", + msg->header.frame_id.c_str(), (controller_params_.tf_prefix + "base").c_str()); + return; + } + + // TODO(urfeex): Check timestamp. Do not accept old commands. + + received_twist_msg_.set(*msg); + }); + + return controller_interface::CallbackReturn::SUCCESS; +} + +controller_interface::InterfaceConfiguration TwistController::command_interface_configuration() const +{ + // No command interfaces currently + controller_interface::InterfaceConfiguration config; + config.type = controller_interface::interface_configuration_type::INDIVIDUAL; + + const std::string tf_prefix = controller_params_.tf_prefix; + config.names.push_back(tf_prefix + "twist/linear_velocity_x"); + config.names.push_back(tf_prefix + "twist/linear_velocity_y"); + config.names.push_back(tf_prefix + "twist/linear_velocity_z"); + config.names.push_back(tf_prefix + "twist/angular_velocity_x"); + config.names.push_back(tf_prefix + "twist/angular_velocity_y"); + config.names.push_back(tf_prefix + "twist/angular_velocity_z"); + + return config; +} + +controller_interface::InterfaceConfiguration TwistController::state_interface_configuration() const +{ + // TODO(urfeex): Add state interfaces for tcp speed + + controller_interface::InterfaceConfiguration config; + config.type = controller_interface::interface_configuration_type::NONE; + + return config; +} + +controller_interface::CallbackReturn TwistController::on_activate(const rclcpp_lifecycle::State& state) +{ + reset(); + subscriber_is_active_ = true; + + return ControllerInterface::on_activate(state); +} + +controller_interface::CallbackReturn TwistController::on_deactivate(const rclcpp_lifecycle::State&) +{ + subscriber_is_active_ = false; + reset(); + return CallbackReturn::SUCCESS; +} + +controller_interface::return_type TwistController::update(const rclcpp::Time& /*time*/, + const rclcpp::Duration& /*period*/) +{ + // In case we can't read from the subscriber's buffer, we will not update the command_msg_. + auto current_cmd = received_twist_msg_.try_get(); + if (current_cmd.has_value()) { + command_msg_ = current_cmd.value(); + } + + // TODO(urfeex): Check if the command_msg_ is valid, e.g., if the timestamp is not too old. + + bool success = true; + success &= command_interfaces_[0].set_value(command_msg_.twist.linear.x); + success &= command_interfaces_[1].set_value(command_msg_.twist.linear.y); + success &= command_interfaces_[2].set_value(command_msg_.twist.linear.z); + success &= command_interfaces_[3].set_value(command_msg_.twist.angular.x); + success &= command_interfaces_[4].set_value(command_msg_.twist.angular.y); + success &= command_interfaces_[5].set_value(command_msg_.twist.angular.z); + + if (!success) { + RCLCPP_ERROR(get_node()->get_logger(), "Failed to write to command interfaces."); + return controller_interface::return_type::ERROR; + } + + return controller_interface::return_type::OK; +} + +void TwistController::reset() +{ + command_msg_.twist.linear.x = 0.0; + command_msg_.twist.linear.y = 0.0; + command_msg_.twist.linear.z = 0.0; + command_msg_.twist.angular.x = 0.0; + command_msg_.twist.angular.y = 0.0; + command_msg_.twist.angular.z = 0.0; + received_twist_msg_.set(command_msg_); +} +} // namespace ur_controllers + +#include "pluginlib/class_list_macros.hpp" + +PLUGINLIB_EXPORT_CLASS(ur_controllers::TwistController, controller_interface::ControllerInterface) diff --git a/ur_controllers/src/twist_controller_parameters.yaml b/ur_controllers/src/twist_controller_parameters.yaml new file mode 100644 index 000000000..554de2381 --- /dev/null +++ b/ur_controllers/src/twist_controller_parameters.yaml @@ -0,0 +1,7 @@ +--- +twist_controller: + tf_prefix: { + type: string, + default_value: "", + description: "Urdf prefix of the corresponding arm" + } diff --git a/ur_robot_driver/config/ur_controllers.yaml b/ur_robot_driver/config/ur_controllers.yaml index c845dd759..70f4719e0 100644 --- a/ur_robot_driver/config/ur_controllers.yaml +++ b/ur_robot_driver/config/ur_controllers.yaml @@ -45,6 +45,9 @@ controller_manager: tool_contact_controller: type: ur_controllers/ToolContactController + twist_controller: + type: ur_controllers/TwistController + # The way this is currently implemented upstream doesn't really work for us. When using # position control, the robot will have a tracking error. However, limits will be enforced # from the currently reported position, effectively limiting the possible step size using the @@ -66,6 +69,10 @@ ur_configuration_controller: ros__parameters: tf_prefix: "$(var tf_prefix)" +twist_controller: + ros__parameters: + tf_prefix: "$(var tf_prefix)" + force_torque_sensor_broadcaster: ros__parameters: sensor_name: $(var tf_prefix)tcp_fts_sensor diff --git a/ur_robot_driver/launch/ur_control.launch.py b/ur_robot_driver/launch/ur_control.launch.py index 0613fddac..dcf899cfc 100644 --- a/ur_robot_driver/launch/ur_control.launch.py +++ b/ur_robot_driver/launch/ur_control.launch.py @@ -205,6 +205,7 @@ def controller_spawner(controllers, active=True): "passthrough_trajectory_controller", "freedrive_mode_controller", "tool_contact_controller", + "twist_controller", ] if activate_joint_controller.perform(context) == "true": controllers_active.append(initial_joint_controller.perform(context))