43 #include <rclcpp/rclcpp.hpp>
47 #pragma GCC diagnostic ignored "-Wold-style-cast"
51 constexpr
double ROBOT_STATE_WAIT_TIME = 5.0;
52 constexpr
double STOPPED_VELOCITY_EPS = 1e-4;
58 Servo::Servo(
const rclcpp::Node::SharedPtr& node, std::shared_ptr<const servo::ParamListener> servo_param_listener,
62 , servo_param_listener_{ std::move(servo_param_listener) }
65 servo_params_ = servo_param_listener_->get_params();
67 if (!validateParams(servo_params_))
69 RCLCPP_ERROR_STREAM(logger_,
"Got invalid parameters, exiting.");
70 std::exit(EXIT_FAILURE);
73 if (!planning_scene_monitor_->getStateMonitor()->waitForCompleteState(servo_params_.move_group_name,
74 ROBOT_STATE_WAIT_TIME))
76 RCLCPP_ERROR(logger_,
"Timeout waiting for current state");
77 std::exit(EXIT_FAILURE);
80 moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
83 if (servo_params_.use_smoothing)
89 RCLCPP_WARN(logger_,
"No smoothing plugin loaded");
94 std::make_unique<CollisionMonitor>(planning_scene_monitor_, servo_params_, std::ref(collision_velocity_scale_));
95 collision_monitor_->start();
99 const auto& move_group_joint_names = planning_scene_monitor_->getRobotModel()
100 ->getJointModelGroup(servo_params_.move_group_name)
101 ->getActiveJointModelNames();
103 for (
const auto& sub_group_name : planning_scene_monitor_->getRobotModel()->getJointModelGroupNames())
106 if (sub_group_name == servo_params_.move_group_name)
110 const auto& subgroup_joint_names =
111 planning_scene_monitor_->getRobotModel()->getJointModelGroup(sub_group_name)->getActiveJointModelNames();
115 for (
const auto& joint_name : subgroup_joint_names)
118 const auto move_group_iterator =
119 std::find(move_group_joint_names.cbegin(), move_group_joint_names.cend(), joint_name);
121 new_map.insert(std::make_pair<std::string, std::size_t>(
122 std::string(joint_name),
std::distance(move_group_joint_names.cbegin(), move_group_iterator)));
125 joint_name_to_index_maps_.insert(
126 std::make_pair<std::string, JointNameToMoveGroupIndexMap>(std::string(sub_group_name), std::move(new_map)));
128 RCLCPP_INFO_STREAM(logger_,
"Servo initialized successfully");
136 void Servo::setSmoothingPlugin()
141 pluginlib::ClassLoader<online_signal_smoothing::SmoothingBaseClass> smoothing_loader(
142 "moveit_core",
"online_signal_smoothing::SmoothingBaseClass");
143 smoother_ = smoothing_loader.createUniqueInstance(servo_params_.smoothing_filter_plugin_name);
145 catch (pluginlib::PluginlibException& ex)
147 RCLCPP_ERROR(logger_,
"Exception while loading the smoothing plugin '%s': '%s'",
148 servo_params_.smoothing_filter_plugin_name.c_str(), ex.what());
149 std::exit(EXIT_FAILURE);
153 moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
154 const int num_joints =
155 robot_state->getJointModelGroup(servo_params_.move_group_name)->getActiveJointModelNames().size();
156 if (!smoother_->initialize(node_, planning_scene_monitor_->getRobotModel(), num_joints))
158 RCLCPP_ERROR(logger_,
"Smoothing plugin could not be initialized");
159 std::exit(EXIT_FAILURE);
182 check_collision ? collision_monitor_->start() : collision_monitor_->stop();
185 bool Servo::validateParams(
const servo::Params& servo_params)
const
187 bool params_valid =
true;
188 auto robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
189 auto joint_model_group = robot_state->getJointModelGroup(servo_params.move_group_name);
190 const std::string check_yaml_string =
" Check the parameters YAML file used to launch this node.";
191 if (joint_model_group ==
nullptr)
193 RCLCPP_ERROR_STREAM(logger_,
"The parameter 'move_group_name': `" << servo_params.move_group_name <<
'`'
194 <<
" is not valid." << check_yaml_string);
195 params_valid =
false;
198 if (servo_params.hard_stop_singularity_threshold <= servo_params.lower_singularity_threshold)
200 RCLCPP_ERROR_STREAM(logger_,
"The parameter 'hard_stop_singularity_threshold' "
201 "should be greater than the parameter 'lower_singularity_threshold'. But the "
202 "'hard_stop_singularity_threshold' is: '"
203 << servo_params.hard_stop_singularity_threshold
204 <<
"' and the 'lower_singularity_threshold' is: '"
205 << servo_params.lower_singularity_threshold <<
"'" << check_yaml_string);
206 params_valid =
false;
209 if (!servo_params.publish_joint_positions && !servo_params.publish_joint_velocities &&
210 !servo_params.publish_joint_accelerations)
212 RCLCPP_ERROR_STREAM(logger_,
"At least one of the parameters: 'publish_joint_positions' / "
213 "'publish_joint_velocities' / "
214 "'publish_joint_accelerations' must be true. But they are all false."
215 << check_yaml_string);
216 params_valid =
false;
219 if ((servo_params.command_out_type ==
"std_msgs/Float64MultiArray") && servo_params.publish_joint_positions &&
220 servo_params.publish_joint_velocities)
223 logger_,
"When publishing a std_msgs/Float64MultiArray, "
224 "either the parameter 'publish_joint_positions' OR the parameter 'publish_joint_velocities' must "
225 "be set to true. But both are set to false."
226 << check_yaml_string);
227 params_valid =
false;
230 if (servo_params.scene_collision_proximity_threshold < servo_params.self_collision_proximity_threshold)
232 RCLCPP_ERROR_STREAM(logger_,
"The parameter 'self_collision_proximity_threshold' should probably be less "
233 "than or equal to the parameter 'scene_collision_proximity_threshold'. But "
234 "'self_collision_proximity_threshold' is: '"
235 << servo_params.self_collision_proximity_threshold
236 <<
"' and 'scene_collision_proximity_threshold' is: '"
237 << servo_params.scene_collision_proximity_threshold <<
"'" << check_yaml_string);
238 params_valid =
false;
241 if (!servo_params.active_subgroup.empty() && servo_params.active_subgroup != servo_params.move_group_name &&
242 !joint_model_group->isSubgroup(servo_params.active_subgroup))
244 RCLCPP_ERROR_STREAM(logger_,
"The parameter 'active_subgroup': '"
245 << servo_params.active_subgroup
246 <<
"' does not name a valid subgroup of 'joint group': '"
247 << servo_params.move_group_name <<
"'" << check_yaml_string);
248 params_valid =
false;
250 if (servo_params.joint_limit_margins.size() !=
251 robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveVariableCount())
255 "The parameter 'joint_limit_margins' must have the same number of elements as the number of joints in the "
256 "move group. The size of 'joint_limit_margins' is '"
257 << servo_params.joint_limit_margins.size() <<
"' but the number of joints of the move group '"
258 << servo_params.move_group_name <<
"' is '"
259 << robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveVariableCount() <<
"'"
260 << check_yaml_string);
262 params_valid =
false;
264 if (servo_params.joint_limit_margins.size() !=
265 robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveVariableCount())
267 RCLCPP_ERROR(logger_,
268 "Parameter 'joint_limit_margins' must have the same number of elements as the number of joints in the "
270 "Size of 'joint_limit_margins' is '%li', but number of joints in '%s' is '%i'. "
271 "Check the parameters YAML file used to launch this node.",
272 servo_params.joint_limit_margins.size(), servo_params.move_group_name.c_str(),
273 robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveVariableCount());
274 params_valid =
false;
281 "The publish period (%f sec) parameter must be less than 1/%d of the max expected latency parameter (%f sec).",
283 params_valid =
false;
289 bool Servo::updateParams()
291 bool params_updated =
false;
292 if (servo_param_listener_->is_old(servo_params_))
294 const auto params = servo_param_listener_->get_params();
296 if (validateParams(params))
298 if (params.override_velocity_scaling_factor != servo_params_.override_velocity_scaling_factor)
300 RCLCPP_INFO_STREAM(logger_,
"override_velocity_scaling_factor changed to : "
301 << std::to_string(params.override_velocity_scaling_factor));
304 servo_params_ = params;
305 params_updated =
true;
309 RCLCPP_WARN_STREAM(logger_,
"Parameters will not be updated.");
312 return params_updated;
317 return servo_params_;
322 return servo_status_;
332 return expected_command_type_;
337 expected_command_type_ = command_type;
344 bounded_state.joint_names = target_state.
joint_names;
346 std::stringstream halting_joint_names;
347 for (
const int idx : joints_to_halt)
349 halting_joint_names << bounded_state.joint_names[idx] +
" ";
351 RCLCPP_WARN_STREAM(logger_,
"Joint position limit reached on joints: " << halting_joint_names.str());
353 const bool all_joint_halt =
360 bounded_state.positions = current_state.
positions;
365 bounded_state.positions = target_state.
positions;
366 bounded_state.velocities = target_state.
velocities;
367 for (
const int idx : joints_to_halt)
369 bounded_state.positions[idx] = current_state.
positions[idx];
370 bounded_state.velocities[idx] = 0.0;
374 return bounded_state;
377 Eigen::VectorXd Servo::jointDeltaFromCommand(
const ServoInput& command,
const moveit::core::RobotStatePtr& robot_state)
380 const auto& active_subgroup_name =
381 servo_params_.active_subgroup.empty() ? servo_params_.move_group_name : servo_params_.active_subgroup;
382 const auto& joint_name_group_index_map = (active_subgroup_name != servo_params_.move_group_name) ?
383 joint_name_to_index_maps_.at(servo_params_.active_subgroup) :
386 const int num_joints =
387 robot_state->getJointModelGroup(servo_params_.move_group_name)->getActiveJointModelNames().size();
388 Eigen::VectorXd joint_position_deltas(num_joints);
389 joint_position_deltas.setZero();
394 if (command.index() ==
static_cast<size_t>(expected_type))
399 joint_name_group_index_map);
400 servo_status_ = delta_result.first;
407 if (planning_frame_maybe.has_value())
409 const auto& planning_frame = *planning_frame_maybe;
410 const auto command_in_planning_frame_maybe = toPlanningFrame(std::get<TwistCommand>(command), planning_frame);
411 if (command_in_planning_frame_maybe.has_value())
413 delta_result =
jointDeltaFromTwist(*command_in_planning_frame_maybe, robot_state, servo_params_,
414 planning_frame, joint_name_group_index_map);
415 servo_status_ = delta_result.first;
420 RCLCPP_ERROR_STREAM(logger_,
"Could not transform twist command to planning frame.");
426 RCLCPP_ERROR(logger_,
"No IK solver for planning group %s.", active_subgroup_name.c_str());
436 if (planning_frame_maybe.has_value() && ee_frame_maybe.has_value())
438 const auto& planning_frame = *planning_frame_maybe;
439 const auto command_in_planning_frame_maybe = toPlanningFrame(std::get<PoseCommand>(command), planning_frame);
440 if (command_in_planning_frame_maybe.has_value())
442 delta_result =
jointDeltaFromPose(*command_in_planning_frame_maybe, robot_state, servo_params_,
443 planning_frame, *ee_frame_maybe, joint_name_group_index_map);
444 servo_status_ = delta_result.first;
449 RCLCPP_ERROR_STREAM(logger_,
"Could not transform pose command to planning frame.");
455 RCLCPP_ERROR(logger_,
"No IK solver for planning group %s.", active_subgroup_name.c_str());
461 joint_position_deltas = delta_result.second;
467 RCLCPP_WARN_STREAM(logger_,
"Incoming servo command type does not match known command types.");
470 return joint_position_deltas;
483 robot_state->getJointModelGroup(servo_params_.move_group_name);
488 const int num_joints = joint_names.size();
496 Eigen::VectorXd joint_position_delta = jointDeltaFromCommand(command, robot_state);
498 if (collision_velocity_scale_ > 0 && collision_velocity_scale_ < 1)
502 else if (collision_velocity_scale_ == 0)
515 target_state.
velocities = joint_position_delta / servo_params_.publish_period;
519 target_state.
velocities, joint_bounds, servo_params_.override_velocity_scaling_factor);
520 if (joint_velocity_limit_scale < 1.0)
522 RCLCPP_DEBUG_STREAM(logger_,
"Joint velocity limit scaling applied by a factor of " << joint_velocity_limit_scale);
524 target_state.
velocities *= joint_velocity_limit_scale;
540 const std::vector<int> joints_to_halt =
544 if (!joints_to_halt.empty())
547 target_state = haltJoints(joints_to_halt, current_state, target_state);
557 std::optional<Eigen::Isometry3d> Servo::getPlanningToCommandFrameTransform(
const std::string& command_frame,
558 const std::string& planning_frame)
const
560 const moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
561 if (robot_state->knowsFrameTransform(command_frame) && (robot_state->knowsFrameTransform(planning_frame)))
563 return robot_state->getGlobalLinkTransform(planning_frame).inverse() *
564 robot_state->getGlobalLinkTransform(command_frame);
570 return tf2::transformToEigen(
571 planning_scene_monitor_->getTFClient()->lookupTransform(planning_frame, command_frame, rclcpp::Time(0)));
573 catch (tf2::TransformException& ex)
575 RCLCPP_ERROR(logger_,
"Failed to get planning to command frame transform: %s", ex.what());
581 std::optional<TwistCommand> Servo::toPlanningFrame(
const TwistCommand& command,
const std::string& planning_frame)
const
583 Eigen::VectorXd transformed_twist = command.velocities;
585 if (command.frame_id != planning_frame)
588 const auto planning_to_command_tf_maybe = getPlanningToCommandFrameTransform(command.frame_id, planning_frame);
589 if (!planning_to_command_tf_maybe.has_value())
593 const auto& planning_to_command_tf = *planning_to_command_tf_maybe;
595 if (servo_params_.apply_twist_commands_about_ee_frame)
598 const auto planning_to_command_rotation = planning_to_command_tf.linear();
600 planning_to_command_rotation *
601 Eigen::Vector3d(command.velocities[0], command.velocities[1], command.velocities[2]);
603 planning_to_command_rotation *
604 Eigen::Vector3d(command.velocities[3], command.velocities[4], command.velocities[5]);
607 transformed_twist.head<3>() = translation_vector;
608 transformed_twist.tail<3>() = angular_vector;
616 Eigen::MatrixXd adjoint(6, 6);
618 const Eigen::Matrix3d& rotation = planning_to_command_tf.rotation();
619 const Eigen::Vector3d& translation = planning_to_command_tf.translation();
621 Eigen::Matrix3d skew_translation;
622 skew_translation.row(0) << 0, -translation(2), translation(1);
623 skew_translation.row(1) << translation(2), 0, -translation(0);
624 skew_translation.row(2) << -translation(1), translation(0), 0;
626 adjoint.topLeftCorner(3, 3) = skew_translation * rotation;
627 adjoint.topRightCorner(3, 3) = rotation;
628 adjoint.bottomLeftCorner(3, 3) = rotation;
629 adjoint.bottomRightCorner(3, 3).setZero();
631 transformed_twist = adjoint * transformed_twist;
635 return TwistCommand{ planning_frame, transformed_twist };
638 std::optional<PoseCommand> Servo::toPlanningFrame(
const PoseCommand& command,
const std::string& planning_frame)
const
640 const auto planning_to_command_tf_maybe = getPlanningToCommandFrameTransform(command.frame_id, planning_frame);
641 if (!planning_to_command_tf_maybe)
646 const auto& planning_to_command_tf = *planning_to_command_tf_maybe;
647 return PoseCommand{ planning_frame, planning_to_command_tf * command.pose };
652 moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
658 auto target_state = halt_state;
661 bool stopped = (target_state.
velocities.cwiseAbs().array() < STOPPED_VELOCITY_EPS).all();
672 target_state.
velocities *= collision_velocity_scale_;
674 for (
long i = 0; i < halt_state.
positions.size(); ++i)
677 const double vel = target_state.
velocities[i];
678 target_state.
velocities[i] = (std::abs(vel) > STOPPED_VELOCITY_EPS) ? vel : 0.0;
685 return std::make_pair(stopped, target_state);
const JointBoundsVector & getActiveJointModelsBounds() const
Get the bounds for all the active joints.
const std::vector< std::string > & getActiveJointModelNames() const
Get the names of the active joints in this group. These are the names of the joints returned by getJo...
void setCommandType(const CommandType &command_type)
Set the type of incoming servo command.
CommandType getCommandType() const
Get the type of command that servo is currently expecting.
std::string getStatusMessage() const
Get the message associated with the current servo status.
std::pair< bool, KinematicState > smoothHalt(const KinematicState &halt_state)
Smoothly halt at a commanded state when command goes stale.
KinematicState getCurrentRobotState() const
Get the current state of the robot as given by planning scene monitor.
StatusCode getStatus() const
Get the current status of servo.
servo::Params & getParams()
Returns the most recent servo parameters.
void resetSmoothing(const KinematicState &state)
Resets the smoothing plugin, if set, to a specified state.
Servo(const rclcpp::Node::SharedPtr &node, std::shared_ptr< const servo::ParamListener > servo_param_listener, const planning_scene_monitor::PlanningSceneMonitorPtr &planning_scene_monitor)
void setCollisionChecking(const bool check_collision)
Start/Stop collision checking thread.
KinematicState getNextJointState(const moveit::core::RobotStatePtr &robot_state, const ServoInput &command)
Computes the joint state required to follow the given command.
void doSmoothing(KinematicState &state)
Applies smoothing to an input state, if a smoothing plugin is set.
rclcpp::Logger getLogger()
Vec3fX< details::Vec3Data< double > > Vector3d
std::vector< const JointModel::Bounds * > JointBoundsVector
JointDeltaResult jointDeltaFromTwist(const TwistCommand &command, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const std::string &planning_frame, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Compute the change in joint position for the given twist command.
std::optional< std::string > getIKSolverTipFrame(const moveit::core::RobotStatePtr &robot_state, const std::string &group_name)
Get the tip (end-effector) frame of the current active joint group or subgroup's IK solver.
JointDeltaResult jointDeltaFromJointJog(const JointJogCommand &command, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Compute the change in joint position for the given joint jog command.
std::optional< std::string > getIKSolverBaseFrame(const moveit::core::RobotStatePtr &robot_state, const std::string &group_name)
Get the base frame of the current active joint group or subgroup's IK solver.
std::vector< int > jointsToHalt(const Eigen::VectorXd &positions, const Eigen::VectorXd &velocities, const moveit::core::JointBoundsVector &joint_bounds, const std::vector< double > &margins)
Finds the joints that are exceeding allowable position limits.
std::variant< JointJogCommand, TwistCommand, PoseCommand > ServoInput
@ DECELERATE_FOR_COLLISION
double jointLimitVelocityScalingFactor(const Eigen::VectorXd &velocities, const moveit::core::JointBoundsVector &joint_bounds, double scaling_override)
Apply velocity scaling based on joint limits. If the robot model does not have velocity limits define...
std::pair< StatusCode, Eigen::VectorXd > JointDeltaResult
const std::unordered_map< StatusCode, std::string > SERVO_STATUS_CODE_MAP({ { StatusCode::INVALID, "Invalid" }, { StatusCode::NO_WARNING, "No warnings" }, { StatusCode::DECELERATE_FOR_APPROACHING_SINGULARITY, "Moving closer to a singularity, decelerating" }, { StatusCode::HALT_FOR_SINGULARITY, "Very close to a singularity, emergency stop" }, { StatusCode::DECELERATE_FOR_LEAVING_SINGULARITY, "Moving away from a singularity, decelerating" }, { StatusCode::DECELERATE_FOR_COLLISION, "Close to a collision, decelerating" }, { StatusCode::HALT_FOR_COLLISION, "Collision detected, emergency stop" }, { StatusCode::JOINT_BOUND, "Close to a joint bound (position or velocity), halting" } })
constexpr int MIN_POINTS_FOR_TRAJ_MSG
JointDeltaResult jointDeltaFromPose(const PoseCommand &command, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const std::string &planning_frame, const std::string &ee_frame, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Compute the change in joint position for the given pose command.
std::unordered_map< std::string, std::size_t > JointNameToMoveGroupIndexMap
KinematicState extractRobotState(const moveit::core::RobotStatePtr &robot_state, const std::string &move_group_name)
Extract the state from a RobotStatePtr instance.
Main namespace for MoveIt.
double distance(const urdf::Pose &transform)
Eigen::VectorXd positions
std::vector< std::string > joint_names
Eigen::VectorXd accelerations
Eigen::VectorXd velocities