41#if __has_include(<realtime_tools/realtime_helpers.hpp>)
43#include <realtime_tools/realtime_helpers.hpp>
45#include <realtime_tools/thread_priority.hpp>
57rclcpp::Time convertClockType(
const rclcpp::Time& time, rcl_clock_type_t new_clock_type)
59 if (time.get_clock_type() != new_clock_type)
61 return rclcpp::Time(time.nanoseconds(), new_clock_type);
69 return node_->get_node_base_interface();
75 if (servo_loop_thread_.joinable())
76 servo_loop_thread_.join();
80 : node_{ std::make_shared<
rclcpp::Node>(
"servo_node", options) }
81 , stop_servo_{ false }
82 , servo_paused_{ false }
83 , new_joint_jog_msg_{ false }
84 , new_twist_msg_{ false }
85 , new_pose_msg_{ false }
90 if (realtime_tools::configure_sched_fifo(servo_params_.thread_priority))
92 RCLCPP_INFO_STREAM(node_->get_logger(),
"Enabled SCHED_FIFO and higher thread priority.");
96 RCLCPP_WARN_STREAM(node_->get_logger(),
"Could not enable FIFO RT scheduling policy. Continuing with the default.");
100 if (!realtime_tools::has_realtime_kernel())
102 RCLCPP_WARN_STREAM(node_->get_logger(),
"Realtime kernel is recommended for better performance.");
105 std::shared_ptr<servo::ParamListener> servo_param_listener =
106 std::make_shared<servo::ParamListener>(node_,
"moveit_servo");
110 servo_ = std::make_unique<Servo>(node_, servo_param_listener, planning_scene_monitor_);
112 servo_params_ = servo_->getParams();
115 joint_jog_subscriber_ = node_->create_subscription<control_msgs::msg::JointJog>(
116 servo_params_.joint_command_in_topic, rclcpp::SystemDefaultsQoS(),
117 [
this](
const control_msgs::msg::JointJog::ConstSharedPtr& msg) {
return jointJogCallback(msg); });
120 twist_subscriber_ = node_->create_subscription<geometry_msgs::msg::TwistStamped>(
121 servo_params_.cartesian_command_in_topic, rclcpp::SystemDefaultsQoS(),
122 [
this](
const geometry_msgs::msg::TwistStamped::ConstSharedPtr& msg) {
return twistCallback(msg); });
125 pose_subscriber_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
126 servo_params_.pose_command_in_topic, rclcpp::SystemDefaultsQoS(),
127 [
this](
const geometry_msgs::msg::PoseStamped::ConstSharedPtr& msg) {
return poseCallback(msg); });
129 if (servo_params_.command_out_type ==
"trajectory_msgs/JointTrajectory")
131 trajectory_publisher_ = node_->create_publisher<trajectory_msgs::msg::JointTrajectory>(
132 servo_params_.command_out_topic, rclcpp::SystemDefaultsQoS());
134 else if (servo_params_.command_out_type ==
"std_msgs/Float64MultiArray")
136 multi_array_publisher_ = node_->create_publisher<std_msgs::msg::Float64MultiArray>(servo_params_.command_out_topic,
137 rclcpp::SystemDefaultsQoS());
141 node_->create_publisher<moveit_msgs::msg::ServoStatus>(servo_params_.status_topic, rclcpp::SystemDefaultsQoS());
144 switch_command_type_ = node_->create_service<moveit_msgs::srv::ServoCommandType>(
145 "~/switch_command_type", [
this](
const std::shared_ptr<moveit_msgs::srv::ServoCommandType::Request>& request,
146 const std::shared_ptr<moveit_msgs::srv::ServoCommandType::Response>& response) {
147 return switchCommandType(request, response);
151 pause_servo_ = node_->create_service<std_srvs::srv::SetBool>(
152 "~/pause_servo", [
this](
const std::shared_ptr<std_srvs::srv::SetBool::Request>& request,
153 const std::shared_ptr<std_srvs::srv::SetBool::Response>& response) {
154 return pauseServo(request, response);
158 servo_loop_thread_ = std::thread(&ServoNode::servoLoop,
this);
161void ServoNode::pauseServo(
const std::shared_ptr<std_srvs::srv::SetBool::Request>& request,
162 const std::shared_ptr<std_srvs::srv::SetBool::Response>& response)
164 if (servo_paused_ == request->data)
166 std::string message =
"Requested pause state is already active.";
167 RCLCPP_INFO(node_->get_logger(),
"%s", message.c_str());
168 response->success =
true;
169 response->message = message;
172 std::lock_guard<std::mutex> lock_guard(lock_);
173 servo_paused_ = request->data;
174 response->success = (servo_paused_ == request->data);
177 servo_->setCollisionChecking(
false);
178 response->message =
"Servoing disabled";
183 last_commanded_state_ = servo_->getCurrentRobotState(
true );
184 servo_->resetSmoothing(last_commanded_state_);
187 joint_cmd_rolling_window_.clear();
190 servo_->setCollisionChecking(
true);
191 response->message =
"Servoing enabled";
195void ServoNode::switchCommandType(
const std::shared_ptr<moveit_msgs::srv::ServoCommandType::Request>& request,
196 const std::shared_ptr<moveit_msgs::srv::ServoCommandType::Response>& response)
198 const bool is_valid = (request->command_type >=
static_cast<int8_t
>(
CommandType::MIN)) &&
202 servo_->setCommandType(
static_cast<CommandType>(request->command_type));
206 RCLCPP_WARN_STREAM(node_->get_logger(),
"Unknown command type " << request->command_type <<
" requested");
208 response->success = (request->command_type ==
static_cast<int8_t
>(servo_->getCommandType()));
211void ServoNode::jointJogCallback(
const control_msgs::msg::JointJog::ConstSharedPtr& msg)
213 latest_joint_jog_ = *msg;
214 new_joint_jog_msg_ =
true;
217void ServoNode::twistCallback(
const geometry_msgs::msg::TwistStamped::ConstSharedPtr& msg)
219 latest_twist_ = *msg;
220 new_twist_msg_ =
true;
223void ServoNode::poseCallback(
const geometry_msgs::msg::PoseStamped::ConstSharedPtr& msg)
226 new_pose_msg_ =
true;
229std::optional<KinematicState> ServoNode::processJointJogCommand(
const moveit::core::RobotStatePtr& robot_state)
231 std::optional<KinematicState> next_joint_state = std::nullopt;
233 new_twist_msg_ = new_pose_msg_ =
false;
235 if (!latest_joint_jog_.displacements.empty())
237 RCLCPP_WARN(node_->get_logger(),
"Joint jog command displacements field is not yet supported, ignoring.");
238 latest_joint_jog_.displacements.clear();
241 const bool command_stale = (node_->now() - latest_joint_jog_.header.stamp) >=
242 rclcpp::Duration::from_seconds(servo_params_.incoming_command_timeout);
245 JointJogCommand command{ latest_joint_jog_.joint_names, latest_joint_jog_.velocities };
246 next_joint_state = servo_->getNextJointState(robot_state, command);
250 new_joint_jog_msg_ =
false;
255 auto result = servo_->smoothHalt(last_commanded_state_);
256 new_joint_jog_msg_ = !result.first;
257 if (new_joint_jog_msg_)
259 next_joint_state = result.second;
260 RCLCPP_DEBUG_STREAM(node_->get_logger(),
"Joint jog command timed out. Halting to a stop.");
264 return next_joint_state;
267std::optional<KinematicState> ServoNode::processTwistCommand(
const moveit::core::RobotStatePtr& robot_state)
269 std::optional<KinematicState> next_joint_state = std::nullopt;
273 new_joint_jog_msg_ = new_pose_msg_ =
false;
275 const bool command_stale = (node_->now() - latest_twist_.header.stamp) >=
276 rclcpp::Duration::from_seconds(servo_params_.incoming_command_timeout);
279 const Eigen::Vector<double, 6> velocities{ latest_twist_.twist.linear.x, latest_twist_.twist.linear.y,
280 latest_twist_.twist.linear.z, latest_twist_.twist.angular.x,
281 latest_twist_.twist.angular.y, latest_twist_.twist.angular.z };
282 const TwistCommand command{ latest_twist_.header.frame_id, velocities };
283 next_joint_state = servo_->getNextJointState(robot_state, command);
286 new_twist_msg_ =
false;
291 auto result = servo_->smoothHalt(last_commanded_state_);
292 new_twist_msg_ = !result.first;
295 next_joint_state = result.second;
296 RCLCPP_DEBUG_STREAM(node_->get_logger(),
"Twist command timed out. Halting to a stop.");
300 return next_joint_state;
303std::optional<KinematicState> ServoNode::processPoseCommand(
const moveit::core::RobotStatePtr& robot_state)
305 std::optional<KinematicState> next_joint_state = std::nullopt;
309 new_joint_jog_msg_ = new_twist_msg_ =
false;
311 const bool command_stale = (node_->now() - latest_pose_.header.stamp) >=
312 rclcpp::Duration::from_seconds(servo_params_.incoming_command_timeout);
316 next_joint_state = servo_->getNextJointState(robot_state, command);
319 new_pose_msg_ =
false;
324 auto result = servo_->smoothHalt(last_commanded_state_);
325 new_pose_msg_ = !result.first;
328 next_joint_state = result.second;
329 RCLCPP_DEBUG_STREAM(node_->get_logger(),
"Pose command timed out. Halting to a stop.");
333 return next_joint_state;
336void ServoNode::servoLoop()
338 moveit_msgs::msg::ServoStatus status_msg;
339 std::optional<KinematicState> next_joint_state = std::nullopt;
340 rclcpp::WallRate servo_frequency(1 / servo_params_.publish_period);
342 const auto servo_node_start = node_->now();
346 while (servo_node_start >
347 convertClockType(planning_scene_monitor_->getLastUpdateTime(), servo_node_start.get_clock_type()))
349 RCLCPP_INFO(node_->get_logger(),
"Waiting for planning scene monitor to receive robot state update.");
350 rclcpp::sleep_for(std::chrono::seconds(1));
353 RCLCPP_INFO(node_->get_logger(),
"Waiting to receive robot state update.");
354 rclcpp::sleep_for(std::chrono::seconds(1));
356 KinematicState current_state = servo_->getCurrentRobotState(
true );
357 last_commanded_state_ = current_state;
359 servo_->resetSmoothing(current_state);
362 moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
363 const moveit::core::JointModelGroup* joint_model_group =
364 robot_state->getJointModelGroup(servo_params_.move_group_name);
366 while (rclcpp::ok() && !stop_servo_)
371 servo_->resetSmoothing(current_state);
372 servo_frequency.sleep();
377 std::lock_guard<std::mutex> lock_guard(lock_);
378 const bool use_trajectory = servo_params_.command_out_type ==
"trajectory_msgs/JointTrajectory";
379 const auto cur_time = node_->now();
381 if (use_trajectory && !joint_cmd_rolling_window_.empty() && joint_cmd_rolling_window_.back().time_stamp > cur_time)
383 current_state = joint_cmd_rolling_window_.back();
388 joint_cmd_rolling_window_.clear();
389 current_state = servo_->getCurrentRobotState(
false );
390 current_state.velocities *= 0.0;
394 robot_state->setJointGroupPositions(joint_model_group, current_state.positions);
395 robot_state->setJointGroupVelocities(joint_model_group, current_state.velocities);
397 next_joint_state = std::nullopt;
398 const CommandType expected_type = servo_->getCommandType();
402 next_joint_state = processJointJogCommand(robot_state);
406 next_joint_state = processTwistCommand(robot_state);
410 next_joint_state = processPoseCommand(robot_state);
412 else if (new_joint_jog_msg_ || new_twist_msg_ || new_pose_msg_)
414 new_joint_jog_msg_ = new_twist_msg_ = new_pose_msg_ =
false;
415 RCLCPP_WARN_STREAM(node_->get_logger(),
"Command type has not been set, cannot accept input");
423 auto& next_joint_state_value = next_joint_state.value();
424 updateSlidingWindow(next_joint_state_value, joint_cmd_rolling_window_, servo_params_.max_expected_latency,
428 trajectory_publisher_->publish(msg.value());
435 last_commanded_state_ = next_joint_state.value();
440 last_commanded_state_ = current_state = servo_->getCurrentRobotState(
false);
441 updateSlidingWindow(current_state, joint_cmd_rolling_window_, servo_params_.max_expected_latency, cur_time);
442 servo_->resetSmoothing(current_state);
445 status_msg.code =
static_cast<int8_t
>(servo_->getStatus());
446 status_msg.message = servo_->getStatusMessage();
447 status_publisher_->publish(status_msg);
450 servo_frequency.sleep();
456#include "rclcpp_components/register_node_macro.hpp"
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface()
ServoNode(const rclcpp::NodeOptions &options)
std::optional< trajectory_msgs::msg::JointTrajectory > composeTrajectoryMessage(const servo::Params &servo_params, const std::deque< KinematicState > &joint_cmd_rolling_window)
Create a trajectory message from a rolling window queue of joint state commands. Method optionally re...
std_msgs::msg::Float64MultiArray composeMultiArrayMessage(const servo::Params &servo_params, const KinematicState &joint_state)
Create a Float64MultiArray message from the given joint state.
void updateSlidingWindow(KinematicState &next_joint_state, std::deque< KinematicState > &joint_cmd_rolling_window, double max_expected_latency, const rclcpp::Time &cur_time)
Adds a new joint state command to a queue containing commands over a time window. Also modifies the v...
PoseCommand poseFromPoseStamped(const geometry_msgs::msg::PoseStamped &msg)
Convert a PoseStamped message to a Servo Pose.
planning_scene_monitor::PlanningSceneMonitorPtr createPlanningSceneMonitor(const rclcpp::Node::SharedPtr &node, const servo::Params &servo_params)
Creates the planning scene monitor used by servo.
void setNodeLoggerName(const std::string &name)
Call once after creating a node to initialize logging namespaces.