39 using namespace std::literals;
43 static const rclcpp::Logger
LOGGER = rclcpp::get_logger(
"moveit_servo.pose_tracking");
48 void declareOrGetParam(T& output_value,
const std::string& param_name,
const rclcpp::Node::SharedPtr& node,
49 const rclcpp::Logger& logger,
const T default_value = T{})
53 if (node->has_parameter(param_name))
55 node->get_parameter<T>(param_name, output_value);
59 output_value = node->declare_parameter<T>(param_name, default_value);
62 catch (
const rclcpp::exceptions::InvalidParameterTypeException& e)
64 RCLCPP_WARN_STREAM(logger,
"InvalidParameterTypeException(" << param_name <<
"): " << e.what());
65 RCLCPP_ERROR_STREAM(logger,
"Error getting parameter \'" << param_name <<
"\', check parameter type in YAML file");
69 RCLCPP_INFO_STREAM(logger,
"Found parameter - " << param_name <<
": " << output_value);
78 , servo_parameters_(servo_parameters)
80 , loop_rate_(1.0 / servo_parameters->publish_period)
81 , transform_buffer_(node_->get_clock())
82 , transform_listener_(transform_buffer_)
83 , stop_requested_(false)
87 robot_model_ = planning_scene_monitor_->getRobotModel();
90 initializePID(x_pid_config_, cartesian_position_pids_);
91 initializePID(y_pid_config_, cartesian_position_pids_);
92 initializePID(z_pid_config_, cartesian_position_pids_);
93 initializePID(angular_pid_config_, cartesian_orientation_pids_);
96 servo_ = std::make_unique<moveit_servo::Servo>(node_, servo_parameters_, planning_scene_monitor_);
100 target_pose_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
101 "target_pose", rclcpp::SystemDefaultsQoS(),
102 [
this](
const geometry_msgs::msg::PoseStamped::ConstSharedPtr& msg) {
return targetPoseCallback(msg); });
105 twist_stamped_pub_ = node_->create_publisher<geometry_msgs::msg::TwistStamped>(
106 servo_->getParameters()->cartesian_command_in_topic, rclcpp::SystemDefaultsQoS());
110 const double angular_tolerance,
const double target_pose_timeout)
113 stop_requested_ =
false;
116 const rclcpp::Time start_time = node_->now();
118 while ((!haveRecentTargetPose(target_pose_timeout) || !haveRecentEndEffectorPose(target_pose_timeout)) &&
119 ((node_->now() - start_time).seconds() < target_pose_timeout))
121 if (
servo_->getCommandFrameTransform(command_frame_transform_))
123 command_frame_transform_stamp_ = node_->now();
125 std::this_thread::sleep_for(1ms);
128 if (!haveRecentTargetPose(target_pose_timeout))
130 RCLCPP_ERROR_STREAM(LOGGER,
"The target pose was not updated recently. Aborting.");
141 if (satisfiesPoseTolerance(positional_tolerance, angular_tolerance))
143 RCLCPP_INFO_STREAM(LOGGER,
"The target pose is achieved!");
147 if (
servo_->getCommandFrameTransform(command_frame_transform_))
149 command_frame_transform_stamp_ = node_->now();
153 if (!haveRecentEndEffectorPose(target_pose_timeout))
155 RCLCPP_ERROR_STREAM(LOGGER,
"The end effector pose was not updated in time. Aborting.");
162 RCLCPP_INFO_STREAM(LOGGER,
"Halting servo motion, a stop was requested.");
168 twist_stamped_pub_->publish(*calculateTwistCommand());
170 if (!loop_rate_.sleep())
172 RCLCPP_WARN_STREAM_THROTTLE(LOGGER, *node_->get_clock(),
LOG_THROTTLE_PERIOD,
"Target control rate was missed");
180 void PoseTracking::readROSParams()
182 const std::string ns =
"moveit_servo";
187 if (!planning_scene_monitor_->getRobotModel()->hasJointModelGroup(move_group_name_))
189 RCLCPP_ERROR_STREAM(LOGGER,
"Unable to find the specified joint model group: " << move_group_name_);
192 double publish_period;
195 x_pid_config_.
dt = publish_period;
196 y_pid_config_.
dt = publish_period;
197 z_pid_config_.
dt = publish_period;
198 angular_pid_config_.
dt = publish_period;
223 void PoseTracking::initializePID(
const PIDConfig& pid_config, std::vector<control_toolbox::Pid>& pid_vector)
225 bool use_anti_windup =
true;
226 pid_vector.push_back(control_toolbox::Pid(pid_config.k_p, pid_config.k_i, pid_config.k_d, pid_config.windup_limit,
227 -pid_config.windup_limit, use_anti_windup));
230 bool PoseTracking::haveRecentTargetPose(
const double timespan)
232 std::lock_guard<std::mutex> lock(target_pose_mtx_);
233 return ((node_->now() - target_pose_.header.stamp).seconds() < timespan);
236 bool PoseTracking::haveRecentEndEffectorPose(
const double timespan)
238 return ((node_->now() - command_frame_transform_stamp_).seconds() < timespan);
241 bool PoseTracking::satisfiesPoseTolerance(
const Eigen::Vector3d& positional_tolerance,
const double angular_tolerance)
243 std::lock_guard<std::mutex> lock(target_pose_mtx_);
244 double x_error = target_pose_.pose.position.x - command_frame_transform_.translation()(0);
245 double y_error = target_pose_.pose.position.y - command_frame_transform_.translation()(1);
246 double z_error = target_pose_.pose.position.z - command_frame_transform_.translation()(2);
252 return ((std::abs(x_error) < positional_tolerance(0)) && (std::abs(y_error) < positional_tolerance(1)) &&
253 (std::abs(z_error) < positional_tolerance(2)) && (std::abs(*angular_error_) < angular_tolerance));
256 void PoseTracking::targetPoseCallback(
const geometry_msgs::msg::PoseStamped::ConstSharedPtr& msg)
258 std::lock_guard<std::mutex> lock(target_pose_mtx_);
261 if (target_pose_.header.frame_id != planning_frame_)
265 geometry_msgs::msg::TransformStamped target_to_planning_frame = transform_buffer_.lookupTransform(
266 planning_frame_, target_pose_.header.frame_id, rclcpp::Time(0), rclcpp::Duration(100ms));
267 tf2::doTransform(target_pose_, target_pose_, target_to_planning_frame);
270 target_pose_.header.stamp = node_->now();
272 catch (
const tf2::TransformException& ex)
274 RCLCPP_WARN_STREAM(LOGGER, ex.what());
280 geometry_msgs::msg::TwistStamped::ConstSharedPtr PoseTracking::calculateTwistCommand()
283 auto msg = moveit::util::make_shared_from_pool<geometry_msgs::msg::TwistStamped>();
286 geometry_msgs::msg::Twist& twist = msg->twist;
287 Eigen::Quaterniond q_desired;
291 std::lock_guard<std::mutex> lock(target_pose_mtx_);
292 msg->header.frame_id = target_pose_.header.frame_id;
295 twist.linear.x = cartesian_position_pids_[0].computeCommand(
296 target_pose_.pose.position.x - command_frame_transform_.translation()(0), loop_rate_.period().count());
297 twist.linear.y = cartesian_position_pids_[1].computeCommand(
298 target_pose_.pose.position.y - command_frame_transform_.translation()(1), loop_rate_.period().count());
299 twist.linear.z = cartesian_position_pids_[2].computeCommand(
300 target_pose_.pose.position.z - command_frame_transform_.translation()(2), loop_rate_.period().count());
306 q_desired = Eigen::Quaterniond(target_pose_.pose.orientation.w, target_pose_.pose.orientation.x,
307 target_pose_.pose.orientation.y, target_pose_.pose.orientation.z);
310 Eigen::Quaterniond q_current(command_frame_transform_.rotation());
311 Eigen::Quaterniond q_error = q_desired * q_current.inverse();
314 Eigen::AngleAxisd axis_angle(q_error);
316 angular_error_ = axis_angle.angle();
318 double ang_vel_magnitude =
319 cartesian_orientation_pids_[0].computeCommand(*angular_error_, loop_rate_.period().count());
320 twist.angular.x = ang_vel_magnitude * axis_angle.axis()[0];
321 twist.angular.y = ang_vel_magnitude * axis_angle.axis()[1];
322 twist.angular.z = ang_vel_magnitude * axis_angle.axis()[2];
324 msg->header.stamp = node_->now();
331 stop_requested_ =
true;
334 auto msg = moveit::util::make_shared_from_pool<geometry_msgs::msg::TwistStamped>();
336 std::lock_guard<std::mutex> lock(target_pose_mtx_);
337 msg->header.frame_id = target_pose_.header.frame_id;
339 msg->header.stamp = node_->now();
340 twist_stamped_pub_->publish(*msg);
343 void PoseTracking::doPostMotionReset()
346 stop_requested_ =
false;
350 cartesian_position_pids_[0].reset();
351 cartesian_position_pids_[1].reset();
352 cartesian_position_pids_[2].reset();
353 cartesian_orientation_pids_[0].reset();
357 const double x_derivative_gain,
const double y_proportional_gain,
358 const double y_integral_gain,
const double y_derivative_gain,
359 const double z_proportional_gain,
const double z_integral_gain,
360 const double z_derivative_gain,
const double angular_proportional_gain,
361 const double angular_integral_gain,
const double angular_derivative_gain)
365 x_pid_config_.
k_p = x_proportional_gain;
366 x_pid_config_.
k_i = x_integral_gain;
367 x_pid_config_.
k_d = x_derivative_gain;
368 y_pid_config_.
k_p = y_proportional_gain;
369 y_pid_config_.
k_i = y_integral_gain;
370 y_pid_config_.
k_d = y_derivative_gain;
371 z_pid_config_.
k_p = z_proportional_gain;
372 z_pid_config_.
k_i = z_integral_gain;
373 z_pid_config_.
k_d = z_derivative_gain;
375 angular_pid_config_.
k_p = angular_proportional_gain;
376 angular_pid_config_.
k_i = angular_integral_gain;
377 angular_pid_config_.
k_d = angular_derivative_gain;
379 cartesian_position_pids_.clear();
380 cartesian_orientation_pids_.clear();
381 initializePID(x_pid_config_, cartesian_position_pids_);
382 initializePID(y_pid_config_, cartesian_position_pids_);
383 initializePID(z_pid_config_, cartesian_position_pids_);
384 initializePID(angular_pid_config_, cartesian_orientation_pids_);
391 double dummy1, dummy2;
392 cartesian_position_pids_.at(0).getCurrentPIDErrors(x_error, dummy1, dummy2);
393 cartesian_position_pids_.at(1).getCurrentPIDErrors(y_error, dummy1, dummy2);
394 cartesian_position_pids_.at(2).getCurrentPIDErrors(z_error, dummy1, dummy2);
395 cartesian_orientation_pids_.at(0).getCurrentPIDErrors(orientation_error, dummy1, dummy2);
400 std::lock_guard<std::mutex> lock(target_pose_mtx_);
401 target_pose_ = geometry_msgs::msg::PoseStamped();
402 target_pose_.header.stamp = rclcpp::Time(RCL_ROS_TIME);
407 return servo_->getCommandFrameTransform(transform);
void getPIDErrors(double &x_error, double &y_error, double &z_error, double &orientation_error)
PoseTrackingStatusCode moveToPose(const Eigen::Vector3d &positional_tolerance, const double angular_tolerance, const double target_pose_timeout)
std::unique_ptr< moveit_servo::Servo > servo_
bool getCommandFrameTransform(geometry_msgs::msg::TransformStamped &transform)
void stopMotion()
A method for a different thread to stop motion and return early from control loop.
void resetTargetPose()
Re-initialize the target pose to an empty message. Can be used to reset motion between waypoints.
void updatePIDConfig(const double x_proportional_gain, const double x_integral_gain, const double x_derivative_gain, const double y_proportional_gain, const double y_integral_gain, const double y_derivative_gain, const double z_proportional_gain, const double z_integral_gain, const double z_derivative_gain, const double angular_proportional_gain, const double angular_integral_gain, const double angular_derivative_gain)
Change PID parameters. Motion is stopped before the update.
Vec3fX< details::Vec3Data< double > > Vector3d
void declareOrGetParam(const std::string ¶m_name, T &output_value, const T &default_value, const rclcpp::Node::SharedPtr &node)
@ NO_RECENT_END_EFFECTOR_POSE
const rclcpp::Logger LOGGER
constexpr size_t LOG_THROTTLE_PERIOD
std::shared_ptr< const ServoParameters > SharedConstPtr