franka_gripper_sim.cpp
Go to the documentation of this file.
1 #include <cmath>
2 #include <memory>
3 
6 
7 namespace franka_gazebo {
8 
10 using control_msgs::GripperCommandAction;
11 using franka_gripper::GraspAction;
12 using franka_gripper::HomingAction;
13 using franka_gripper::MoveAction;
14 using franka_gripper::StopAction;
15 
17  std::string ns = nh.getNamespace();
18  std::string arm_id;
19 
20  if (not nh.getParam("arm_id", arm_id)) {
21  ROS_ERROR_STREAM_NAMED("FrankaGripperSim",
22  "Could not find required parameter '" << ns << "/arm_id'");
23  return false;
24  }
25  std::string finger1 = arm_id + "_finger_joint1";
26  std::string finger2 = arm_id + "_finger_joint2";
27 
28  nh.param<double>("move/width_tolerance", this->tolerance_move_, kDefaultMoveWidthTolerance);
29  nh.param<double>("gripper_action/width_tolerance", this->tolerance_gripper_action_,
31  nh.param<double>("gripper_action/speed", this->speed_default_, kDefaultGripperActionSpeed);
32  nh.param<double>("grasp/resting_threshold", this->speed_threshold_, kGraspRestingThreshold);
33  nh.param<int>("grasp/consecutive_samples", this->speed_samples_, kGraspConsecutiveSamples);
34 
35  try {
36  this->finger1_ = hw->getHandle(finger1);
37  this->finger2_ = hw->getHandle(finger2);
39  ROS_ERROR_STREAM_NAMED("FrankaGripperSim", "Could not get joint handle(s): " << ex.what());
40  return false;
41  }
42 
43  if (not this->pid1_.initParam(ns + "/finger1/gains")) {
44  return false;
45  }
46  if (not this->pid2_.initParam(ns + "/finger2/gains")) {
47  return false;
48  }
49 
50  pub_.init(nh, "joint_states", 1);
51  pub_.lock();
52  pub_.msg_.name = {finger1, finger2};
53  pub_.unlock();
54 
55  this->action_stop_ = std::make_unique<SimpleActionServer<StopAction>>(
56  nh, "stop", boost::bind(&FrankaGripperSim::onStopGoal, this, _1), false);
57  this->action_stop_->start();
58 
59  this->action_homing_ = std::make_unique<SimpleActionServer<HomingAction>>(
60  nh, "homing", boost::bind(&FrankaGripperSim::onHomingGoal, this, _1), false);
61  this->action_homing_->registerPreemptCallback([&]() {
62  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "Homing Action cancelled");
63  this->setState(State::IDLE);
64  });
65  this->action_homing_->start();
66 
67  this->action_move_ = std::make_unique<SimpleActionServer<MoveAction>>(
68  nh, "move", boost::bind(&FrankaGripperSim::onMoveGoal, this, _1), false);
69  this->action_move_->registerPreemptCallback([&]() {
70  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "Moving Action cancelled");
71  this->setState(State::IDLE);
72  });
73  this->action_move_->start();
74 
75  this->action_grasp_ = std::make_unique<SimpleActionServer<GraspAction>>(
76  nh, "grasp", boost::bind(&FrankaGripperSim::onGraspGoal, this, _1), false);
77  this->action_grasp_->registerPreemptCallback([&]() {
78  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "Grasping Action cancelled");
79  this->setState(State::IDLE);
80  });
81  this->action_grasp_->start();
82 
83  this->action_gc_ = std::make_unique<SimpleActionServer<GripperCommandAction>>(
84  nh, "gripper_action", boost::bind(&FrankaGripperSim::onGripperActionGoal, this, _1), false);
85  this->action_gc_->registerPreemptCallback([&]() {
86  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "Gripper Command Action cancelled");
87  this->setState(State::IDLE);
88  });
89  this->action_gc_->start();
90 
91  ROS_INFO_STREAM_NAMED("FrankaGripperSim",
92  "Successfully initialized Franka Gripper Controller for joints '"
93  << finger1 << "' and '" << finger2 << "'");
94  return true;
95 }
96 
97 void FrankaGripperSim::starting(const ros::Time& /*unused*/) {
98  transition(State::IDLE, Config());
99  this->pid1_.reset();
100  this->pid2_.reset();
101 }
102 
103 void FrankaGripperSim::update(const ros::Time& now, const ros::Duration& period) {
104  if (rate_trigger_() and pub_.trylock()) {
105  pub_.msg_.header.stamp = now;
106  pub_.msg_.position = {this->finger1_.getPosition(), this->finger2_.getPosition()};
107  pub_.msg_.velocity = {this->finger1_.getVelocity(), this->finger2_.getVelocity()};
108  pub_.msg_.effort = {this->finger1_.getEffort(), this->finger2_.getEffort()};
110  }
111 
112  // Read state threadsafe
113  double width = this->finger1_.getPosition() + this->finger2_.getPosition();
114  this->mutex_.lock();
115  State state = this->state_;
116  auto tolerance = this->config_.tolerance;
117  double w_d = this->config_.width_desired;
118  double dw_d = this->config_.speed_desired * std::copysign(1.0, w_d - width);
119  this->mutex_.unlock();
120  if (state == State::IDLE) {
121  // Track position of other finger to simulate mimicked joints + high damping
122  control(this->finger1_, this->pid1_, this->finger2_.getPosition(), 0, 0, period);
123  control(this->finger2_, this->pid2_, this->finger1_.getPosition(), 0, 0, period);
124  return;
125  }
126 
127  // Compute control signal and send to joints
128  double w1_d = this->finger1_.getPosition() + 0.5 * dw_d * period.toSec();
129  double w2_d = this->finger2_.getPosition() + 0.5 * dw_d * period.toSec();
130 
131  // Only in case when we hold we want to add the desired force, in any other state don't add
132  // anything extra to the command
133  double f_d = 0;
134 
135  if (state == State::HOLDING) {
136  // When an object is grasped, next to the force to apply, also track the other finger
137  // to not make both fingers drift away from middle simultaneously
138  w1_d = this->finger2_.getPosition();
139  w2_d = this->finger1_.getPosition();
140  std::lock_guard<std::mutex> lock(this->mutex_);
141  f_d = this->config_.force_desired / 2.0;
142  }
143 
144  control(this->finger1_, this->pid1_, w1_d, 0.5 * dw_d, f_d, period);
145  control(this->finger2_, this->pid2_, w2_d, 0.5 * dw_d, f_d, period);
146 
147  if (w_d - tolerance.inner < width and width < w_d + tolerance.outer) {
148  // Goal reached, update statemachine
149  if (state == State::MOVING) {
150  // Done with move motion, switch to idle again
151  transition(State::IDLE, Config{.width_desired = this->config_.width_desired,
152  .speed_desired = 0,
153  .force_desired = 0,
154  .tolerance = this->config_.tolerance});
155  return;
156  }
157  }
158 
159  if (state == State::GRASPING or state == State::MOVING) {
160  // Since the velocity signal is noisy it can easily happen that one sample is below the
161  // threshold To avoid abortion because of noise, we have to read at least N consecutive number
162  // of samples before interpreting something was grasped (or not)
163  static int speed_threshold_counter = 0;
164  double speed = this->finger1_.getVelocity() + this->finger2_.getVelocity();
165  if (std::abs(speed) <= this->speed_threshold_) {
166  speed_threshold_counter++;
167  } else {
168  speed_threshold_counter = 0;
169  }
170 
171  if (speed_threshold_counter >= this->speed_samples_) {
172  if (state == State::GRASPING) {
173  // Done with grasp motion, switch to holding, i.e. keep position & force
174  transition(State::HOLDING, Config{.width_desired = width,
175  .speed_desired = 0,
176  .force_desired = this->config_.force_desired,
177  .tolerance = this->config_.tolerance});
178  } else {
179  // Moving failed due to object between fingers. Switch to idle.
180  transition(State::IDLE, Config{.width_desired = width,
181  .speed_desired = 0,
182  .force_desired = 0,
183  .tolerance = this->config_.tolerance});
184  }
185  speed_threshold_counter = 0;
186  }
187  }
188 }
189 
192  double q_d,
193  double dq_d,
194  double f_d,
195  const ros::Duration& period) {
196  double error = q_d - joint.getPosition();
197  double derror = dq_d - joint.getVelocity();
198  joint.setCommand(pid.computeCommand(error, derror, period) + f_d);
199 }
200 
201 void FrankaGripperSim::setState(const State&& state) {
202  std::lock_guard<std::mutex> lock(this->mutex_);
203  this->state_ = state;
204 }
205 
206 void FrankaGripperSim::setConfig(const Config&& config) {
207  std::lock_guard<std::mutex> lock(this->mutex_);
208  this->config_ = config;
209 }
210 
211 void FrankaGripperSim::transition(const State&& state, const Config&& config) {
212  std::lock_guard<std::mutex> lock(this->mutex_);
213  this->state_ = state;
214  this->config_ = config;
215 }
216 
217 void FrankaGripperSim::interrupt(const std::string& message, const State& except) {
218  if (except != State::MOVING and this->action_move_ != nullptr and
219  this->action_move_->isActive()) {
220  franka_gripper::MoveResult result;
221  result.success = static_cast<decltype(result.success)>(false);
222  result.error = message;
223  this->action_move_->setAborted(result, result.error);
224  }
225  if (except != State::GRASPING and this->action_grasp_ != nullptr and
226  this->action_grasp_->isActive()) {
227  franka_gripper::GraspResult result;
228  result.success = static_cast<decltype(result.success)>(false);
229  result.error = message;
230  this->action_grasp_->setAborted(result, result.error);
231  }
232  if (except != State::MOVING and this->action_homing_ != nullptr and
233  this->action_homing_->isActive()) {
234  franka_gripper::HomingResult result;
235  result.success = static_cast<decltype(result.success)>(false);
236  result.error = message;
237  this->action_homing_->setAborted(result, result.error);
238  }
239 }
240 
242  State original = this->state_; // copy
243 
244  ros::Rate rate(30);
245  while (ros::ok()) {
246  {
247  std::lock_guard<std::mutex> lock(this->mutex_);
248  if (this->state_ != original) {
249  return;
250  }
251  }
252  rate.sleep();
253  }
254 }
255 
256 void FrankaGripperSim::onStopGoal(const franka_gripper::StopGoalConstPtr& /*goal*/) {
257  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "Stop Action goal received");
258 
259  interrupt("Command interrupted, because stop action was called", State::IDLE);
260 
261  transition(State::IDLE, Config{.width_desired = this->config_.width_desired,
262  .speed_desired = 0,
263  .force_desired = 0,
264  .tolerance = this->config_.tolerance});
265 
266  franka_gripper::StopResult result;
267  result.success = static_cast<decltype(result.success)>(true);
268  action_stop_->setSucceeded(result);
269 }
270 
271 void FrankaGripperSim::onHomingGoal(const franka_gripper::HomingGoalConstPtr& /*goal*/) {
272  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "New Homing Action goal received");
273 
274  if (this->state_ != State::IDLE) {
275  this->interrupt("Command interrupted, because new homing action called", State::MOVING);
276  }
277 
278  franka_gripper::GraspEpsilon eps;
279  eps.inner = this->tolerance_move_;
280  eps.outer = this->tolerance_move_;
281  transition(
282  State::MOVING,
283  Config{.width_desired = 0, .speed_desired = 0.02, .force_desired = 0, .tolerance = eps});
284 
286 
287  if (not this->action_homing_->isActive()) {
288  // Homing Action was interrupted from another action goal callback and already preempted.
289  // Don't try to resend result now
290  return;
291  }
293  .speed_desired = 0.02,
294  .force_desired = 0,
295  .tolerance = eps});
296 
298  if (not this->action_homing_->isActive()) {
299  // Homing Action was interrupted from another action goal callback and already preempted.
300  // Don't try to resend result now
301  return;
302  }
303 
304  franka_gripper::HomingResult result;
305  if (this->state_ != State::IDLE) {
306  result.success = static_cast<decltype(result.success)>(false);
307  result.error = "Unexpected state transistion: The gripper not in IDLE as expected";
308  action_homing_->setAborted(result, result.error);
309  return;
310  }
311 
312  result.success = static_cast<decltype(result.success)>(true);
313  action_homing_->setSucceeded(result);
314 }
315 
316 void FrankaGripperSim::onMoveGoal(const franka_gripper::MoveGoalConstPtr& goal) {
317  ROS_INFO_STREAM_NAMED("FrankaGripperSim",
318  "New Move Action Goal received: " << goal->width << " m");
319  if (goal->speed < 0) {
320  franka_gripper::MoveResult result;
321  result.success = static_cast<decltype(result.success)>(false);
322  result.error = "Only positive speeds allowed";
323  action_move_->setAborted(result, result.error);
324  return;
325  }
326 
327  if (goal->width < 0 or goal->width > kMaxFingerWidth or not std::isfinite(goal->width)) {
328  franka_gripper::MoveResult result;
329  result.success = static_cast<decltype(result.success)>(false);
330  result.error = "Target width has to lie between 0 .. " + std::to_string(kMaxFingerWidth);
331  action_move_->setAborted(result, result.error);
332  return;
333  }
334 
335  if (this->state_ != State::IDLE) {
336  interrupt("Command interrupted, because new move action called", State::MOVING);
337  }
338 
339  bool move_succeeded = move(goal->width, goal->speed);
340 
341  if (not this->action_move_->isActive()) {
342  // Move Action was interrupted from another action goal callback and already preempted.
343  // Don't try to resend result now
344  return;
345  }
346 
347  franka_gripper::MoveResult result;
348  if (not move_succeeded) {
349  result.success = static_cast<decltype(result.success)>(false);
350  result.error = "Unexpected state transistion: The gripper not in IDLE as expected";
351  action_move_->setAborted(result, result.error);
352  return;
353  }
354  franka_gripper::GraspEpsilon eps;
355  eps.inner = this->tolerance_move_;
356  eps.outer = this->tolerance_move_;
357 
358  double width = this->finger1_.getPosition() + this->finger2_.getPosition(); // recalculate
359  bool ok = goal->width - eps.inner < width and width < goal->width + eps.outer;
360  result.success = static_cast<decltype(result.success)>(ok);
361  action_move_->setSucceeded(result);
362 }
363 
364 void FrankaGripperSim::onGraspGoal(const franka_gripper::GraspGoalConstPtr& goal) {
365  ROS_INFO_STREAM_NAMED("FrankaGripperSim",
366  "New Grasp Action Goal received: " << goal->force << "N");
367 
368  if (goal->width >= kMaxFingerWidth or goal->width < 0) {
369  franka_gripper::GraspResult result;
370  result.success = static_cast<decltype(result.success)>(false);
371  result.error =
372  "Can only grasp inside finger width from [0 .. " + std::to_string(kMaxFingerWidth) + "[";
373  action_grasp_->setAborted(result, result.error);
374  return;
375  }
376  if (goal->speed < 0) {
377  franka_gripper::GraspResult result;
378  result.success = static_cast<decltype(result.success)>(false);
379  result.error = "Only positive speeds allowed";
380  action_grasp_->setAborted(result, result.error);
381  return;
382  }
383 
384  if (this->state_ != State::IDLE) {
385  interrupt("Command interrupted, because new grasp action called", State::GRASPING);
386  }
387 
388  bool grasp_succeeded = grasp(goal->width, goal->speed, goal->force, goal->epsilon);
389 
390  if (not this->action_grasp_->isActive()) {
391  // Grasping Action was interrupted from another action goal callback and already preempted.
392  // Don't try to resend result now
393  return;
394  }
395 
396  franka_gripper::GraspResult result;
397  if (this->state_ != State::HOLDING) {
398  result.success = static_cast<decltype(result.success)>(false);
399  result.error = "Unexpected state transistion: The gripper not in HOLDING as expected";
400  action_grasp_->setAborted(result, result.error);
401  return;
402  }
403 
404  result.success = static_cast<decltype(result.success)>(grasp_succeeded);
405  if (not grasp_succeeded) {
406  double current_width = this->finger1_.getPosition() + this->finger2_.getPosition();
407  result.error =
408  "When the gripper stopped (below speed of " + std::to_string(this->speed_threshold_) +
409  " m/s the width between the fingers was not at " + std::to_string(goal->width) + "m (-" +
410  std::to_string(goal->epsilon.inner) + "m/+" + std::to_string(goal->epsilon.outer) +
411  "m) but at " + std::to_string(current_width) + "m";
412  setState(State::IDLE);
413  }
414 
415  action_grasp_->setSucceeded(result);
416 }
417 
418 void FrankaGripperSim::onGripperActionGoal(const control_msgs::GripperCommandGoalConstPtr& goal) {
419  control_msgs::GripperCommandResult result;
420  // HACK: As one gripper finger is <mimic>, MoveIt!'s trajectory execution manager
421  // only sends us the width of one finger. Multiply by 2 to get the intended width.
422  double width_d = goal->command.position * 2.0;
423 
424  ROS_INFO_STREAM_NAMED("FrankaGripperSim", "New Gripper Command Action Goal received: "
425  << goal->command.position << "m, "
426  << goal->command.max_effort << "N");
427 
428  if (width_d > kMaxFingerWidth || width_d < 0.0) {
429  std::string error =
430  "Commanding out of range position! max_position = " + std::to_string(kMaxFingerWidth / 2) +
431  ", commanded position = " + std::to_string(goal->command.position) +
432  ". Be aware that you command the position of"
433  " each finger which is half of the total opening width!";
434  ROS_ERROR_STREAM_NAMED("FrankaGripperSim", error);
435  result.reached_goal = static_cast<decltype(result.reached_goal)>(false);
436  action_gc_->setAborted(result, error);
437  return;
438  }
439 
440  franka_gripper::GraspEpsilon eps;
441  eps.inner = this->tolerance_gripper_action_;
442  eps.outer = this->tolerance_gripper_action_;
443 
444  double current_width = this->finger1_.getPosition() + this->finger2_.getPosition();
445  constexpr double kMinimumGraspForce = 1e-4;
446  bool succeeded = false;
447 
448  if (std::abs(goal->command.max_effort) < kMinimumGraspForce or width_d > current_width) {
449  succeeded = move(width_d, this->speed_default_);
450  if (not this->action_gc_->isActive()) {
451  // Gripper Action was interrupted from another action goal callback and already preempted.
452  // Don't try to resend result now
453  return;
454  }
455  } else {
456  succeeded = grasp(width_d, this->speed_default_, goal->command.max_effort, eps);
457  if (not this->action_gc_->isActive()) {
458  // Gripper Action was interrupted from another action goal callback and already preempted.
459  // Don't try to resend result now
460  return;
461  }
462  if (this->state_ != State::HOLDING) {
463  result.reached_goal = static_cast<decltype(result.reached_goal)>(false);
464  std::string error = "Unexpected state transition: The gripper not in HOLDING as expected";
465  action_gc_->setAborted(result, error);
466  return;
467  }
468  }
469 
470  result.position = this->finger1_.getPosition() + this->finger2_.getPosition();
471  result.effort = 0;
472  result.stalled = static_cast<decltype(result.stalled)>(false);
473  result.reached_goal = static_cast<decltype(result.reached_goal)>(succeeded);
474  if (not succeeded) {
475  setState(State::IDLE);
476  }
477  action_gc_->setSucceeded(result);
478 }
479 
480 bool FrankaGripperSim::move(double width, double speed) {
481  franka_gripper::GraspEpsilon eps;
482  eps.inner = this->tolerance_move_;
483  eps.outer = this->tolerance_move_;
484  transition(
485  State::MOVING,
486  Config{.width_desired = width, .speed_desired = speed, .force_desired = 0, .tolerance = eps});
487 
489  return this->state_ == State::IDLE;
490 }
491 
492 bool FrankaGripperSim::grasp(double width,
493  double speed,
494  double force,
495  const franka_gripper::GraspEpsilon& epsilon) {
496  double current_width = this->finger1_.getPosition() + this->finger2_.getPosition();
497  double direction = std::copysign(1.0, width - current_width);
498  transition(State::GRASPING, Config{.width_desired = width < current_width ? 0 : kMaxFingerWidth,
499  .speed_desired = speed,
500  .force_desired = direction * force,
501  .tolerance = epsilon});
502 
504  current_width = this->finger1_.getPosition() + this->finger2_.getPosition(); // recalculate
505  return width - epsilon.inner < current_width and current_width < width + epsilon.outer;
506 }
507 
508 } // namespace franka_gazebo
509 
void setConfig(const Config &&config)
void starting(const ros::Time &) override
bool initParam(const std::string &prefix, const bool quiet=false)
std::unique_ptr< actionlib::SimpleActionServer< franka_gripper::HomingAction > > action_homing_
double force_desired
Desired force with which to grasp objects, if grasps succeed [N].
std::string arm_id
#define ROS_ERROR_STREAM_NAMED(name, args)
void control(hardware_interface::JointHandle &joint, control_toolbox::Pid &, double q_d, double dq_d, double f_d, const ros::Duration &period)
const double kDefaultGripperActionWidthTolerance
When width between fingers is below this, the gripper action succeeds [m].
const int kGraspConsecutiveSamples
How many times the speed has to drop below resting threshold before the grasping will be checked...
Simulate the franka_gripper_node.
void onGraspGoal(const franka_gripper::GraspGoalConstPtr &goal)
void onMoveGoal(const franka_gripper::MoveGoalConstPtr &goal)
bool move(double width, double speed)
libfranka-like method to move the gripper to a certain position
std::unique_ptr< actionlib::SimpleActionServer< franka_gripper::GraspAction > > action_grasp_
#define ROS_INFO_STREAM_NAMED(name, args)
const double kDefaultGripperActionSpeed
How fast shall the gripper execute gripper command actions? [m/s].
void onHomingGoal(const franka_gripper::HomingGoalConstPtr &goal)
result
franka_gripper::GraspEpsilon tolerance
Tolerance range to check if grasping succeeds.
realtime_tools::RealtimePublisher< sensor_msgs::JointState > pub_
double width_desired
Desired width between both fingers [m].
bool param(const std::string &param_name, T &param_val, const T &default_val) const
const char * what() const noexcept override
double tolerance_gripper_action_
[m] inner + outer position tolerances used during gripper action
PLUGINLIB_EXPORT_CLASS(franka_gazebo::FrankaGripperSim, controller_interface::ControllerBase)
bool init(hardware_interface::EffortJointInterface *hw, ros::NodeHandle &nh) override
double computeCommand(double error, ros::Duration dt)
def error(args, kwargs)
bool getParam(const std::string &key, std::string &s) const
void update(const ros::Time &now, const ros::Duration &period) override
franka_hw::TriggerRate rate_trigger_
hardware_interface::JointHandle finger1_
ROSCPP_DECL bool ok()
void transition(const State &&state, const Config &&config)
double tolerance_move_
[m] inner + outer position tolerances used during grasp
bool grasp(double width, double speed, double force, const franka_gripper::GraspEpsilon &epsilon)
libfranka-like method to grasp an object with the gripper
std::unique_ptr< actionlib::SimpleActionServer< franka_gripper::MoveAction > > action_move_
std::unique_ptr< actionlib::SimpleActionServer< control_msgs::GripperCommandAction > > action_gc_
bool sleep()
const double kGraspRestingThreshold
Below which speed the target width should be checked to abort or succeed the grasp action [m/s]...
const double kMaxFingerWidth
void setState(const State &&state)
JointHandle getHandle(const std::string &name)
void init(const ros::NodeHandle &node, const std::string &topic, int queue_size, bool latched=false)
void setCommand(double command)
const std::string & getNamespace() const
void interrupt(const std::string &message, const State &except)
Interrupt any running action server unless the gripper is currently in a specific state...
const double kDefaultMoveWidthTolerance
When width between fingers is below this, the move action succeeds [m].
double speed_desired
Desired magnitude of the speed with which fingers should move [m/s].
void onGripperActionGoal(const control_msgs::GripperCommandGoalConstPtr &goal)
void onStopGoal(const franka_gripper::StopGoalConstPtr &goal)
hardware_interface::JointHandle finger2_
std::unique_ptr< actionlib::SimpleActionServer< franka_gripper::StopAction > > action_stop_


franka_gazebo
Author(s): Franka Emika GmbH
autogenerated on Mon Sep 19 2022 03:06:05