jog_calcs.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * BSD 3-Clause License
3  *
4  * Copyright (c) 2019, Los Alamos National Security, LLC
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived from
19  * this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *******************************************************************************/
33 
34 /* Title : jog_calcs.cpp
35  * Project : moveit_jog_arm
36  * Created : 1/11/2019
37  * Author : Brian O'Neil, Andy Zelenak, Blake Anderson
38  */
39 
41 
42 static const std::string LOGNAME = "jog_calcs";
43 
44 namespace moveit_jog_arm
45 {
46 // Constructor for the class that handles jogging calculations
47 JogCalcs::JogCalcs(const JogArmParameters& parameters, const robot_model_loader::RobotModelLoaderPtr& model_loader_ptr)
48  : parameters_(parameters), default_sleep_rate_(1000)
49 {
50  // Publish jogger status
51  status_pub_ = nh_.advertise<std_msgs::Int8>(parameters_.status_topic, 1);
52 
53  // MoveIt Setup
54  while (ros::ok() && !model_loader_ptr)
55  {
56  ROS_WARN_THROTTLE_NAMED(5, LOGNAME, "Waiting for a non-null robot_model_loader pointer");
58  }
59  const moveit::core::RobotModelPtr& kinematic_model = model_loader_ptr->getModel();
60  kinematic_state_ = std::make_shared<moveit::core::RobotState>(kinematic_model);
61  kinematic_state_->setToDefaultValues();
62 
63  joint_model_group_ = kinematic_model->getJointModelGroup(parameters_.move_group_name);
64  prev_joint_velocity_ = Eigen::ArrayXd::Zero(joint_model_group_->getActiveJointModels().size());
65 }
66 
67 void JogCalcs::startMainLoop(JogArmShared& shared_variables)
68 {
69  // Reset flags
70  is_initialized_ = false;
71 
72  // Wait for initial messages
73  ROS_INFO_NAMED(LOGNAME, "jog_calcs_thread: Waiting for first joint msg.");
74  ros::topic::waitForMessage<sensor_msgs::JointState>(parameters_.joint_topic);
75  ROS_INFO_NAMED(LOGNAME, "jog_calcs_thread: Received first joint msg.");
76 
78  num_joints_ = internal_joint_state_.name.size();
79  internal_joint_state_.position.resize(num_joints_);
80  internal_joint_state_.velocity.resize(num_joints_);
81  // A map for the indices of incoming joint commands
82  for (std::size_t i = 0; i < internal_joint_state_.name.size(); ++i)
83  {
85  }
86 
87  // Low-pass filters for the joint positions & velocities
88  for (size_t i = 0; i < num_joints_; ++i)
89  {
91  }
92 
93  // Initialize the position filters to initial robot joints
94  while (!updateJoints(shared_variables) && ros::ok())
95  {
96  if (shared_variables.stop_requested)
97  return;
98 
99  shared_variables.lock();
100  incoming_joint_state_ = shared_variables.joints;
101  shared_variables.unlock();
103  }
104 
105  is_initialized_ = true;
106 
107  // Track the number of cycles during which motion has not occurred.
108  // Will avoid re-publishing zero velocities endlessly.
109  int zero_velocity_count = 0;
110 
111  ros::Rate loop_rate(1. / parameters_.publish_period);
112 
113  // Flag for staying inactive while there are no incoming commands
114  bool wait_for_jog_commands = true;
115 
116  // Incoming command messages
117  geometry_msgs::TwistStamped cartesian_deltas;
118  control_msgs::JointJog joint_deltas;
119 
120  // Do jogging calcs
121  while (ros::ok() && !shared_variables.stop_requested)
122  {
123  // Always update the joints and end-effector transform for 2 reasons:
124  // 1) in case the getCommandFrameTransform() method is being used
125  // 2) so the low-pass filters are up to date and don't cause a jump
126  while (!updateJoints(shared_variables) && ros::ok())
127  {
129  }
130 
131  // Get the transform from MoveIt planning frame to jogging command frame
132  // We solve (planning_frame -> base -> robot_link_command_frame)
133  // by computing (base->planning_frame)^-1 * (base->robot_link_command_frame)
134  kinematic_state_->setVariableValues(incoming_joint_state_);
135  tf_moveit_to_cmd_frame_ = kinematic_state_->getGlobalLinkTransform(parameters_.planning_frame).inverse() *
136  kinematic_state_->getGlobalLinkTransform(parameters_.robot_link_command_frame);
137  shared_variables.lock();
139  shared_variables.unlock();
140 
141  // If paused or while waiting for initial jog commands, just keep the low-pass filters up to date with current
142  // joints so a jump doesn't occur when restarting
143  if (wait_for_jog_commands || shared_variables.paused)
144  {
145  for (std::size_t i = 0; i < num_joints_; ++i)
146  position_filters_[i].reset(original_joint_state_.position[i]);
147 
148  shared_variables.lock();
149  // Check if there are any new commands with valid timestamp
150  wait_for_jog_commands = shared_variables.command_deltas.header.stamp == ros::Time(0.) &&
151  shared_variables.joint_command_deltas.header.stamp == ros::Time(0.);
152  shared_variables.unlock();
153  }
154  // If not waiting for initial command, and not paused.
155  // Do jogging calculations only if the robot should move, for efficiency
156  else
157  {
158  // Halt if the command is stale or inputs are all zero, or commands were zero
159  shared_variables.lock();
160  bool have_nonzero_cartesian_cmd = shared_variables.have_nonzero_cartesian_cmd;
161  bool have_nonzero_joint_cmd = shared_variables.have_nonzero_joint_cmd;
162  bool stale_command = shared_variables.command_is_stale;
163  shared_variables.unlock();
164 
165  bool valid_nonzero_command = false;
166  if (!stale_command)
167  {
168  // Prioritize cartesian jogging above joint jogging
169  if (have_nonzero_cartesian_cmd)
170  {
171  shared_variables.lock();
172  cartesian_deltas = shared_variables.command_deltas;
173  shared_variables.unlock();
174 
175  if (!cartesianJogCalcs(cartesian_deltas, shared_variables))
176  continue;
177  }
178  else if (have_nonzero_joint_cmd)
179  {
180  shared_variables.lock();
181  joint_deltas = shared_variables.joint_command_deltas;
182  shared_variables.unlock();
183 
184  if (!jointJogCalcs(joint_deltas, shared_variables))
185  continue;
186  }
187 
188  valid_nonzero_command = have_nonzero_cartesian_cmd || have_nonzero_joint_cmd;
189  }
190 
191  // If we should halt
192  if (!valid_nonzero_command)
193  {
194  // Keep the joint position filters up-to-date with current joints
195  for (std::size_t i = 0; i < num_joints_; ++i)
196  position_filters_[i].reset(original_joint_state_.position[i]);
197 
199  have_nonzero_cartesian_cmd = false;
200  have_nonzero_joint_cmd = false;
201  // Reset the valid command flag so jogging stops until a new command arrives
202  shared_variables.lock();
203  shared_variables.have_nonzero_cartesian_cmd = false;
204  shared_variables.have_nonzero_joint_cmd = false;
205  shared_variables.unlock();
206  }
207 
208  // Send the newest target joints
209  shared_variables.lock();
210  // If everything normal, share the new traj to be published
211  if (valid_nonzero_command)
212  {
213  shared_variables.outgoing_command = outgoing_command_;
214  shared_variables.ok_to_publish = true;
215  }
216  // Skip the jogging publication if all inputs have been zero for several cycles in a row.
217  // num_outgoing_halt_msgs_to_publish == 0 signifies that we should keep republishing forever.
219  (zero_velocity_count > parameters_.num_outgoing_halt_msgs_to_publish))
220  {
221  shared_variables.ok_to_publish = false;
222  }
223  // The command is invalid but we are publishing num_outgoing_halt_msgs_to_publish
224  else
225  {
226  shared_variables.outgoing_command = outgoing_command_;
227  shared_variables.ok_to_publish = true;
228  }
229  shared_variables.unlock();
230 
231  // Store last zero-velocity message flag to prevent superfluous warnings.
232  // Cartesian and joint commands must both be zero.
233  if (!have_nonzero_cartesian_cmd && !have_nonzero_joint_cmd)
234  {
235  // Avoid overflow
236  if (zero_velocity_count < std::numeric_limits<int>::max())
237  ++zero_velocity_count;
238  }
239  else
240  zero_velocity_count = 0;
241  }
242 
243  loop_rate.sleep();
244  }
245 }
246 
248 {
249  return is_initialized_;
250 }
251 
252 // Perform the jogging calculations
253 bool JogCalcs::cartesianJogCalcs(geometry_msgs::TwistStamped& cmd, JogArmShared& shared_variables)
254 {
255  // Check for nan's in the incoming command
256  if (std::isnan(cmd.twist.linear.x) || std::isnan(cmd.twist.linear.y) || std::isnan(cmd.twist.linear.z) ||
257  std::isnan(cmd.twist.angular.x) || std::isnan(cmd.twist.angular.y) || std::isnan(cmd.twist.angular.z))
258  {
259  ROS_WARN_STREAM_THROTTLE_NAMED(2, LOGNAME, "nan in incoming command. Skipping this datapoint.");
260  return false;
261  }
262 
263  // If incoming commands should be in the range [-1:1], check for |delta|>1
264  if (parameters_.command_in_type == "unitless")
265  {
266  if ((fabs(cmd.twist.linear.x) > 1) || (fabs(cmd.twist.linear.y) > 1) || (fabs(cmd.twist.linear.z) > 1) ||
267  (fabs(cmd.twist.angular.x) > 1) || (fabs(cmd.twist.angular.y) > 1) || (fabs(cmd.twist.angular.z) > 1))
268  {
269  ROS_WARN_STREAM_THROTTLE_NAMED(2, LOGNAME, "Component of incoming command is >1. Skipping this datapoint.");
270  return false;
271  }
272  }
273 
274  // Set uncontrolled dimensions to 0 in command frame
275  if (!shared_variables.control_dimensions[0])
276  cmd.twist.linear.x = 0;
277  if (!shared_variables.control_dimensions[1])
278  cmd.twist.linear.y = 0;
279  if (!shared_variables.control_dimensions[2])
280  cmd.twist.linear.z = 0;
281  if (!shared_variables.control_dimensions[3])
282  cmd.twist.angular.x = 0;
283  if (!shared_variables.control_dimensions[4])
284  cmd.twist.angular.y = 0;
285  if (!shared_variables.control_dimensions[5])
286  cmd.twist.angular.z = 0;
287 
288  // Transform the command to the MoveGroup planning frame
289  if (cmd.header.frame_id != parameters_.planning_frame)
290  {
291  Eigen::Vector3d translation_vector(cmd.twist.linear.x, cmd.twist.linear.y, cmd.twist.linear.z);
292  Eigen::Vector3d angular_vector(cmd.twist.angular.x, cmd.twist.angular.y, cmd.twist.angular.z);
293 
294  // We solve (planning_frame -> base -> cmd.header.frame_id)
295  // by computing (base->planning_frame)^-1 * (base->cmd.header.frame_id)
296  const auto tf_planning_to_cmd_frame =
297  kinematic_state_->getGlobalLinkTransform(parameters_.planning_frame).inverse() *
298  kinematic_state_->getGlobalLinkTransform(cmd.header.frame_id);
299 
300  translation_vector = tf_planning_to_cmd_frame.linear() * translation_vector;
301  angular_vector = tf_planning_to_cmd_frame.linear() * angular_vector;
302 
303  // Put these components back into a TwistStamped
304  cmd.header.frame_id = parameters_.planning_frame;
305  cmd.twist.linear.x = translation_vector(0);
306  cmd.twist.linear.y = translation_vector(1);
307  cmd.twist.linear.z = translation_vector(2);
308  cmd.twist.angular.x = angular_vector(0);
309  cmd.twist.angular.y = angular_vector(1);
310  cmd.twist.angular.z = angular_vector(2);
311  }
312 
313  Eigen::VectorXd delta_x = scaleCartesianCommand(cmd);
314 
315  // Convert from cartesian commands to joint commands
316  Eigen::MatrixXd jacobian = kinematic_state_->getJacobian(joint_model_group_);
317 
318  // May allow some dimensions to drift, based on shared_variables.drift_dimensions
319  // i.e. take advantage of task redundancy.
320  // Remove the Jacobian rows corresponding to True in the vector shared_variables.drift_dimensions
321  // Work backwards through the 6-vector so indices don't get out of order
322  for (auto dimension = jacobian.rows(); dimension >= 0; --dimension)
323  {
324  if (shared_variables.drift_dimensions[dimension] && jacobian.rows() > 1)
325  {
326  removeDimension(jacobian, delta_x, dimension);
327  }
328  }
329 
330  Eigen::JacobiSVD<Eigen::MatrixXd> svd =
331  Eigen::JacobiSVD<Eigen::MatrixXd>(jacobian, Eigen::ComputeThinU | Eigen::ComputeThinV);
332  Eigen::MatrixXd matrix_s = svd.singularValues().asDiagonal();
333  Eigen::MatrixXd pseudo_inverse = svd.matrixV() * matrix_s.inverse() * svd.matrixU().transpose();
334 
335  delta_theta_ = pseudo_inverse * delta_x;
336 
338 
339  // If close to a collision or a singularity, decelerate
340  applyVelocityScaling(shared_variables, delta_theta_,
341  velocityScalingFactorForSingularity(delta_x, svd, jacobian, pseudo_inverse));
343  {
344  ROS_ERROR_STREAM_THROTTLE_NAMED(5, LOGNAME, "Halting for collision!");
346  }
347 
349 
350  publishStatus();
351  // Cache the status so it can be retrieved asynchronously
352  updateCachedStatus(shared_variables);
353 
355 }
356 
357 bool JogCalcs::jointJogCalcs(const control_msgs::JointJog& cmd, JogArmShared& shared_variables)
358 {
359  // Check for nan's or |delta|>1 in the incoming command
360  for (double velocity : cmd.velocities)
361  {
362  if (std::isnan(velocity))
363  {
364  ROS_WARN_STREAM_THROTTLE_NAMED(2, LOGNAME, "nan in incoming command. Skipping this datapoint.");
365  return false;
366  }
367  }
368 
369  // Apply user-defined scaling
371 
373 
374  kinematic_state_->setVariableValues(internal_joint_state_);
375 
377 
378  publishStatus();
379  // Cache the status so it can be retrieved asynchronously
380  updateCachedStatus(shared_variables);
381 
383 }
384 
386 {
387  shared_variables.status = status_;
389 }
390 
392 {
395  return false;
396 
398 
399  // Calculate joint velocities here so that positions are filtered and SRDF bounds still get checked
401 
403 
405  {
408  }
409 
410  // done with calculations
412  {
414  }
415 
416  return true;
417 }
418 
419 // Spam several redundant points into the trajectory. The first few may be skipped if the
420 // time stamp is in the past when it reaches the client. Needed for gazebo simulation.
421 // Start from 2 because the first point's timestamp is already 1*parameters_.publish_period
422 void JogCalcs::insertRedundantPointsIntoTrajectory(trajectory_msgs::JointTrajectory& trajectory, int count) const
423 {
424  auto point = trajectory.points[0];
425  // Start from 2 because we already have the first point. End at count+1 so (total #) == count
426  for (int i = 2; i < count + 1; ++i)
427  {
428  point.time_from_start = ros::Duration(i * parameters_.publish_period);
429  trajectory.points.push_back(point);
430  }
431 }
432 
433 void JogCalcs::lowPassFilterPositions(sensor_msgs::JointState& joint_state)
434 {
435  for (size_t i = 0; i < position_filters_.size(); ++i)
436  {
437  joint_state.position[i] = position_filters_[i].filter(joint_state.position[i]);
438  }
439 }
440 
441 void JogCalcs::calculateJointVelocities(sensor_msgs::JointState& joint_state, const Eigen::ArrayXd& delta_theta)
442 {
443  for (int i = 0; i < delta_theta.size(); ++i)
444  {
445  joint_state.velocity[i] = delta_theta[i] / parameters_.publish_period;
446  }
447 }
448 
449 trajectory_msgs::JointTrajectory JogCalcs::composeJointTrajMessage(sensor_msgs::JointState& joint_state) const
450 {
451  trajectory_msgs::JointTrajectory new_joint_traj;
452  new_joint_traj.header.frame_id = parameters_.planning_frame;
453  new_joint_traj.header.stamp = ros::Time::now();
454  new_joint_traj.joint_names = joint_state.name;
455 
456  trajectory_msgs::JointTrajectoryPoint point;
457  point.time_from_start = ros::Duration(parameters_.publish_period);
459  point.positions = joint_state.position;
461  point.velocities = joint_state.velocity;
463  {
464  // I do not know of a robot that takes acceleration commands.
465  // However, some controllers check that this data is non-empty.
466  // Send all zeros, for now.
467  std::vector<double> acceleration(num_joints_);
468  point.accelerations = acceleration;
469  }
470  new_joint_traj.points.push_back(point);
471 
472  return new_joint_traj;
473 }
474 
475 // Apply velocity scaling for proximity of collisions and singularities.
476 // Scale for collisions is read from a shared variable.
477 void JogCalcs::applyVelocityScaling(JogArmShared& shared_variables, Eigen::ArrayXd& delta_theta,
478  double singularity_scale)
479 {
480  shared_variables.lock();
481  double collision_scale = shared_variables.collision_velocity_scale;
482  shared_variables.unlock();
483 
484  if (collision_scale > 0 && collision_scale < 1)
485  {
488  }
489  else if (collision_scale == 0)
490  {
492  }
493 
494  delta_theta = collision_scale * singularity_scale * delta_theta;
495 }
496 
497 // Possibly calculate a velocity scaling factor, due to proximity of singularity and direction of motion
498 double JogCalcs::velocityScalingFactorForSingularity(const Eigen::VectorXd& commanded_velocity,
499  const Eigen::JacobiSVD<Eigen::MatrixXd>& svd,
500  const Eigen::MatrixXd& jacobian,
501  const Eigen::MatrixXd& pseudo_inverse)
502 {
503  double velocity_scale = 1;
504  std::size_t num_dimensions = jacobian.rows();
505 
506  // Find the direction away from nearest singularity.
507  // The last column of U from the SVD of the Jacobian points directly toward or away from the singularity.
508  // The sign can flip at any time, so we have to do some extra checking.
509  // Look ahead to see if the Jacobian's condition will decrease.
510  Eigen::VectorXd vector_toward_singularity = svd.matrixU().col(num_dimensions - 1);
511 
512  double ini_condition = svd.singularValues()(0) / svd.singularValues()(svd.singularValues().size() - 1);
513 
514  // This singular vector tends to flip direction unpredictably. See R. Bro,
515  // "Resolving the Sign Ambiguity in the Singular Value Decomposition".
516  // Look ahead to see if the Jacobian's condition will decrease in this
517  // direction. Start with a scaled version of the singular vector
518  Eigen::VectorXd delta_x(num_dimensions);
519  double scale = 100;
520  delta_x = vector_toward_singularity / scale;
521 
522  // Calculate a small change in joints
523  Eigen::VectorXd new_theta;
524  kinematic_state_->copyJointGroupPositions(joint_model_group_, new_theta);
525  new_theta += pseudo_inverse * delta_x;
526  kinematic_state_->setJointGroupPositions(joint_model_group_, new_theta);
527 
528  Eigen::JacobiSVD<Eigen::MatrixXd> new_svd(jacobian);
529  double new_condition = new_svd.singularValues()(0) / new_svd.singularValues()(new_svd.singularValues().size() - 1);
530  // If new_condition < ini_condition, the singular vector does point towards a
531  // singularity. Otherwise, flip its direction.
532  if (ini_condition >= new_condition)
533  {
534  vector_toward_singularity *= -1;
535  }
536 
537  // If this dot product is positive, we're moving toward singularity ==> decelerate
538  double dot = vector_toward_singularity.dot(commanded_velocity);
539  if (dot > 0)
540  {
541  // Ramp velocity down linearly when the Jacobian condition is between lower_singularity_threshold and
542  // hard_stop_singularity_threshold, and we're moving towards the singularity
543  if ((ini_condition > parameters_.lower_singularity_threshold) &&
545  {
546  velocity_scale = 1. -
547  (ini_condition - parameters_.lower_singularity_threshold) /
551  }
552 
553  // Very close to singularity, so halt.
554  else if (ini_condition > parameters_.hard_stop_singularity_threshold)
555  {
556  velocity_scale = 0;
559  }
560  }
561 
562  return velocity_scale;
563 }
564 
565 void JogCalcs::enforceSRDFAccelVelLimits(Eigen::ArrayXd& delta_theta)
566 {
567  Eigen::ArrayXd velocity = delta_theta / parameters_.publish_period;
568  const Eigen::ArrayXd acceleration = (velocity - prev_joint_velocity_) / parameters_.publish_period;
569 
570  std::size_t joint_delta_index = 0;
571  for (auto joint : joint_model_group_->getActiveJointModels())
572  {
573  // Some joints do not have bounds defined
574  const auto bounds = joint->getVariableBounds(joint->getName());
575  if (bounds.acceleration_bounded_)
576  {
577  bool clip_acceleration = false;
578  double acceleration_limit = 0.0;
579  if (acceleration(joint_delta_index) < bounds.min_acceleration_)
580  {
581  clip_acceleration = true;
582  acceleration_limit = bounds.min_acceleration_;
583  }
584  else if (acceleration(joint_delta_index) > bounds.max_acceleration_)
585  {
586  clip_acceleration = true;
587  acceleration_limit = bounds.max_acceleration_;
588  }
589 
590  // Apply acceleration bounds
591  if (clip_acceleration)
592  {
593  // accel = (vel - vel_prev) / delta_t = ((delta_theta / delta_t) - vel_prev) / delta_t
594  // --> delta_theta = (accel * delta_t _ + vel_prev) * delta_t
595  const double relative_change =
596  ((acceleration_limit * parameters_.publish_period + prev_joint_velocity_(joint_delta_index)) *
598  delta_theta(joint_delta_index);
599  // Avoid nan
600  if (fabs(relative_change) < 1)
601  delta_theta(joint_delta_index) = relative_change * delta_theta(joint_delta_index);
602  }
603  }
604 
605  if (bounds.velocity_bounded_)
606  {
607  velocity(joint_delta_index) = delta_theta(joint_delta_index) / parameters_.publish_period;
608 
609  bool clip_velocity = false;
610  double velocity_limit = 0.0;
611  if (velocity(joint_delta_index) < bounds.min_velocity_)
612  {
613  clip_velocity = true;
614  velocity_limit = bounds.min_velocity_;
615  }
616  else if (velocity(joint_delta_index) > bounds.max_velocity_)
617  {
618  clip_velocity = true;
619  velocity_limit = bounds.max_velocity_;
620  }
621 
622  // Apply velocity bounds
623  if (clip_velocity)
624  {
625  // delta_theta = joint_velocity * delta_t
626  const double relative_change = (velocity_limit * parameters_.publish_period) / delta_theta(joint_delta_index);
627  // Avoid nan
628  if (fabs(relative_change) < 1)
629  {
630  delta_theta(joint_delta_index) = relative_change * delta_theta(joint_delta_index);
631  velocity(joint_delta_index) = relative_change * velocity(joint_delta_index);
632  }
633  }
634  ++joint_delta_index;
635  }
636  }
637 }
638 
639 bool JogCalcs::enforceSRDFPositionLimits(trajectory_msgs::JointTrajectory& new_joint_traj)
640 {
641  bool halting = false;
642 
643  for (auto joint : joint_model_group_->getActiveJointModels())
644  {
645  // Halt if we're past a joint margin and joint velocity is moving even farther past
646  double joint_angle = 0;
647  for (std::size_t c = 0; c < original_joint_state_.name.size(); ++c)
648  {
649  if (original_joint_state_.name[c] == joint->getName())
650  {
651  joint_angle = original_joint_state_.position.at(c);
652  break;
653  }
654  }
655  if (!kinematic_state_->satisfiesPositionBounds(joint, -parameters_.joint_limit_margin))
656  {
657  const std::vector<moveit_msgs::JointLimits> limits = joint->getVariableBoundsMsg();
658 
659  // Joint limits are not defined for some joints. Skip them.
660  if (!limits.empty())
661  {
662  if ((kinematic_state_->getJointVelocities(joint)[0] < 0 &&
663  (joint_angle < (limits[0].min_position + parameters_.joint_limit_margin))) ||
664  (kinematic_state_->getJointVelocities(joint)[0] > 0 &&
665  (joint_angle > (limits[0].max_position - parameters_.joint_limit_margin))))
666  {
667  ROS_WARN_STREAM_THROTTLE_NAMED(2, LOGNAME, ros::this_node::getName() << " " << joint->getName()
668  << " close to a "
669  " position limit. Halting.");
670  halting = true;
671  }
672  }
673  }
674  }
675  return !halting;
676 }
677 
679 {
680  std_msgs::Int8 status_msg;
681  status_msg.data = status_;
682  status_pub_.publish(status_msg);
683 }
684 
685 // Suddenly halt for a joint limit or other critical issue.
686 // Is handled differently for position vs. velocity control.
687 void JogCalcs::suddenHalt(Eigen::ArrayXd& delta_theta)
688 {
689  delta_theta = Eigen::ArrayXd::Zero(delta_theta.rows());
690 }
691 
692 // Suddenly halt for a joint limit or other critical issue.
693 // Is handled differently for position vs. velocity control.
694 void JogCalcs::suddenHalt(trajectory_msgs::JointTrajectory& joint_traj)
695 {
696  if (joint_traj.points.empty())
697  {
698  joint_traj.points.push_back(trajectory_msgs::JointTrajectoryPoint());
699  joint_traj.points[0].positions.resize(num_joints_);
700  joint_traj.points[0].velocities.resize(num_joints_);
701  }
702 
703  for (std::size_t i = 0; i < num_joints_; ++i)
704  {
705  // For position-controlled robots, can reset the joints to a known, good state
707  joint_traj.points[0].positions[i] = original_joint_state_.position[i];
708 
709  // For velocity-controlled robots, stop
711  joint_traj.points[0].velocities[i] = 0;
712  }
713 }
714 
715 // Parse the incoming joint msg for the joints of our MoveGroup
716 bool JogCalcs::updateJoints(JogArmShared& shared_variables)
717 {
718  shared_variables.lock();
719  incoming_joint_state_ = shared_variables.joints;
720  shared_variables.unlock();
721 
722  // Check that the msg contains enough joints
723  if (incoming_joint_state_.name.size() < num_joints_)
724  return false;
725 
726  // Store joints in a member variable
727  for (std::size_t m = 0; m < incoming_joint_state_.name.size(); ++m)
728  {
729  std::size_t c;
730  try
731  {
733  }
734  catch (const std::out_of_range& e)
735  {
736  ROS_DEBUG_STREAM_THROTTLE_NAMED(5, LOGNAME, "Ignoring joint " << incoming_joint_state_.name[m]);
737  continue;
738  }
739 
740  internal_joint_state_.position[c] = incoming_joint_state_.position[m];
741  }
742 
743  // Cache the original joints in case they need to be reset
745 
746  return true;
747 }
748 
749 // Scale the incoming jog command
750 Eigen::VectorXd JogCalcs::scaleCartesianCommand(const geometry_msgs::TwistStamped& command) const
751 {
752  Eigen::VectorXd result(6);
753 
754  // Apply user-defined scaling if inputs are unitless [-1:1]
755  if (parameters_.command_in_type == "unitless")
756  {
757  result[0] = parameters_.linear_scale * parameters_.publish_period * command.twist.linear.x;
758  result[1] = parameters_.linear_scale * parameters_.publish_period * command.twist.linear.y;
759  result[2] = parameters_.linear_scale * parameters_.publish_period * command.twist.linear.z;
760  result[3] = parameters_.rotational_scale * parameters_.publish_period * command.twist.angular.x;
761  result[4] = parameters_.rotational_scale * parameters_.publish_period * command.twist.angular.y;
762  result[5] = parameters_.rotational_scale * parameters_.publish_period * command.twist.angular.z;
763  }
764  // Otherwise, commands are in m/s and rad/s
765  else if (parameters_.command_in_type == "speed_units")
766  {
767  result[0] = command.twist.linear.x * parameters_.publish_period;
768  result[1] = command.twist.linear.y * parameters_.publish_period;
769  result[2] = command.twist.linear.z * parameters_.publish_period;
770  result[3] = command.twist.angular.x * parameters_.publish_period;
771  result[4] = command.twist.angular.y * parameters_.publish_period;
772  result[5] = command.twist.angular.z * parameters_.publish_period;
773  }
774  else
775  ROS_ERROR_STREAM_NAMED(LOGNAME, "Unexpected command_in_type");
776 
777  return result;
778 }
779 
780 Eigen::VectorXd JogCalcs::scaleJointCommand(const control_msgs::JointJog& command) const
781 {
782  Eigen::VectorXd result(num_joints_);
783 
784  for (std::size_t i = 0; i < num_joints_; ++i)
785  {
786  result[i] = 0.0;
787  }
788 
789  std::size_t c;
790  for (std::size_t m = 0; m < command.joint_names.size(); ++m)
791  {
792  try
793  {
794  c = joint_state_name_map_.at(command.joint_names[m]);
795  }
796  catch (const std::out_of_range& e)
797  {
798  ROS_WARN_STREAM_THROTTLE_NAMED(5, LOGNAME, "Ignoring joint " << incoming_joint_state_.name[m]);
799  continue;
800  }
801  // Apply user-defined scaling if inputs are unitless [-1:1]
802  if (parameters_.command_in_type == "unitless")
803  result[c] = command.velocities[m] * parameters_.joint_scale * parameters_.publish_period;
804  // Otherwise, commands are in m/s and rad/s
805  else if (parameters_.command_in_type == "speed_units")
806  result[c] = command.velocities[m] * parameters_.publish_period;
807  else
808  ROS_ERROR_STREAM_NAMED(LOGNAME, "Unexpected command_in_type, check yaml file.");
809  }
810 
811  return result;
812 }
813 
814 // Add the deltas to each joint
815 bool JogCalcs::addJointIncrements(sensor_msgs::JointState& output, const Eigen::VectorXd& increments) const
816 {
817  for (std::size_t i = 0, size = static_cast<std::size_t>(increments.size()); i < size; ++i)
818  {
819  try
820  {
821  output.position[i] += increments[static_cast<long>(i)];
822  }
823  catch (const std::out_of_range& e)
824  {
825  ROS_ERROR_STREAM_NAMED(LOGNAME, ros::this_node::getName() << " Lengths of output and "
826  "increments do not match.");
827  return false;
828  }
829  }
830 
831  return true;
832 }
833 
834 void JogCalcs::removeDimension(Eigen::MatrixXd& jacobian, Eigen::VectorXd& delta_x, unsigned int row_to_remove)
835 {
836  unsigned int num_rows = jacobian.rows() - 1;
837  unsigned int num_cols = jacobian.cols();
838 
839  if (row_to_remove < num_rows)
840  {
841  jacobian.block(row_to_remove, 0, num_rows - row_to_remove, num_cols) =
842  jacobian.block(row_to_remove + 1, 0, num_rows - row_to_remove, num_cols);
843  delta_x.segment(row_to_remove, num_rows - row_to_remove) =
844  delta_x.segment(row_to_remove + 1, num_rows - row_to_remove);
845  }
846  jacobian.conservativeResize(num_rows, num_cols);
847  delta_x.conservativeResize(num_rows);
848 }
849 } // namespace moveit_jog_arm
void enforceSRDFAccelVelLimits(Eigen::ArrayXd &delta_theta)
Scale the delta theta to match joint velocity/acceleration limits.
Definition: jog_calcs.cpp:565
Eigen::ArrayXd prev_joint_velocity_
Definition: jog_calcs.h:177
std::atomic< bool > stop_requested
Definition: jog_arm_data.h:103
ros::Publisher status_pub_
Definition: jog_calcs.h:169
moveit::core::RobotStatePtr kinematic_state_
Definition: jog_calcs.h:158
#define ROS_INFO_NAMED(name,...)
static const std::string LOGNAME
Definition: jog_calcs.cpp:42
#define ROS_WARN_THROTTLE_NAMED(period, name,...)
#define ROS_ERROR_STREAM_NAMED(name, args)
void lowPassFilterPositions(sensor_msgs::JointState &joint_state)
Smooth position commands with a lowpass filter.
Definition: jog_calcs.cpp:433
std::vector< LowPassFilter > position_filters_
Definition: jog_calcs.h:167
trajectory_msgs::JointTrajectory composeJointTrajMessage(sensor_msgs::JointState &joint_state) const
Compose the outgoing JointTrajectory message.
Definition: jog_calcs.cpp:449
double velocityScalingFactorForSingularity(const Eigen::VectorXd &commanded_velocity, const Eigen::JacobiSVD< Eigen::MatrixXd > &svd, const Eigen::MatrixXd &jacobian, const Eigen::MatrixXd &pseudo_inverse)
Possibly calculate a velocity scaling factor, due to proximity of singularity and direction of motion...
Definition: jog_calcs.cpp:498
void insertRedundantPointsIntoTrajectory(trajectory_msgs::JointTrajectory &trajectory, int count) const
Gazebo simulations have very strict message timestamp requirements. Satisfy Gazebo by stuffing multip...
Definition: jog_calcs.cpp:422
std::size_t size(custom_string const &s)
void updateCachedStatus(JogArmShared &shared_variables)
Update the stashed status so it can be retrieved asynchronously.
Definition: jog_calcs.cpp:385
std::atomic< bool > paused
Definition: jog_arm_data.h:100
ROSCPP_DECL const std::string & getName()
Eigen::Isometry3d tf_moveit_to_cmd_frame
Definition: jog_arm_data.h:90
bool addJointIncrements(sensor_msgs::JointState &output, const Eigen::VectorXd &increments) const
Definition: jog_calcs.cpp:815
JogCalcs(const JogArmParameters &parameters, const robot_model_loader::RobotModelLoaderPtr &model_loader_ptr)
Definition: jog_calcs.cpp:47
#define ROS_ERROR_STREAM_THROTTLE_NAMED(period, name, args)
doubleAcc dot(const VectorAcc &lhs, const VectorAcc &rhs)
ros::NodeHandle nh_
Definition: jog_calcs.h:69
sensor_msgs::JointState joints
Definition: jog_arm_data.h:67
bool enforceSRDFPositionLimits(trajectory_msgs::JointTrajectory &new_joint_traj)
Avoid overshooting joint limits.
Definition: jog_calcs.cpp:639
bool jointJogCalcs(const control_msgs::JointJog &cmd, JogArmShared &shared_variables)
Do jogging calculations for direct commands to a joint.
Definition: jog_calcs.cpp:357
bool convertDeltasToOutgoingCmd()
Convert joint deltas to an outgoing JointTrajectory command. This happens for joint commands and Cart...
Definition: jog_calcs.cpp:391
std::atomic_bool drift_dimensions[6]
Definition: jog_arm_data.h:93
ros::Rate default_sleep_rate_
Definition: jog_calcs.h:185
trajectory_msgs::JointTrajectory outgoing_command
Definition: jog_arm_data.h:81
bool isInitialized()
Check if the robot state is being updated and the end effector transform is known.
Definition: jog_calcs.cpp:247
geometry_msgs::TwistStamped command_deltas
Definition: jog_arm_data.h:63
void publish(const boost::shared_ptr< M > &message) const
bool cartesianJogCalcs(geometry_msgs::TwistStamped &cmd, JogArmShared &shared_variables)
Do jogging calculations for Cartesian twist commands.
Definition: jog_calcs.cpp:253
std::atomic_bool control_dimensions[6]
Definition: jog_arm_data.h:106
void applyVelocityScaling(JogArmShared &shared_variables, Eigen::ArrayXd &delta_theta, double singularity_scale)
Definition: jog_calcs.cpp:477
#define ROS_WARN_STREAM_THROTTLE_NAMED(period, name, args)
JogArmParameters parameters_
Definition: jog_calcs.h:173
sensor_msgs::JointState incoming_joint_state_
Definition: jog_calcs.h:163
Eigen::VectorXd scaleJointCommand(const control_msgs::JointJog &command) const
If incoming velocity commands are from a unitless joystick, scale them to physical units...
Definition: jog_calcs.cpp:780
const std::unordered_map< uint, std::string > JOG_ARM_STATUS_CODE_MAP({ { NO_WARNING, "No warnings" }, { DECELERATE_FOR_SINGULARITY, "Close to a singularity, decelerating" }, { HALT_FOR_SINGULARITY, "Very close to a singularity, emergency stop" }, { DECELERATE_FOR_COLLISION, "Close to a collision, decelerating" }, { HALT_FOR_COLLISION, "Collision detected, emergency stop" }, { JOINT_BOUND, "Close to a joint bound (position or velocity), halting" } })
std::map< std::string, std::size_t > joint_state_name_map_
Definition: jog_calcs.h:164
ROSCPP_DECL bool ok()
sensor_msgs::JointState internal_joint_state_
Definition: jog_calcs.h:163
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void calculateJointVelocities(sensor_msgs::JointState &joint_state, const Eigen::ArrayXd &delta_theta)
Convert an incremental position command to joint velocity message.
Definition: jog_calcs.cpp:441
const std::vector< std::string > & getActiveJointModelNames() const
void publishStatus() const
Publish the status of the jogger to a ROS topic.
Definition: jog_calcs.cpp:678
std::atomic< bool > is_initialized_
Definition: jog_calcs.h:72
control_msgs::JointJog joint_command_deltas
Definition: jog_arm_data.h:65
void suddenHalt(trajectory_msgs::JointTrajectory &joint_traj)
Suddenly halt for a joint limit or other critical issue. Is handled differently for position vs...
Definition: jog_calcs.cpp:694
const std::vector< const JointModel *> & getActiveJointModels() const
bool sleep()
static Time now()
const int gazebo_redundant_message_count_
Definition: jog_calcs.h:181
bool updateJoints(JogArmShared &shared_variables)
Parse the incoming joint msg for the joints of our MoveGroup.
Definition: jog_calcs.cpp:716
const moveit::core::JointModelGroup * joint_model_group_
Definition: jog_calcs.h:156
Eigen::VectorXd scaleCartesianCommand(const geometry_msgs::TwistStamped &command) const
If incoming velocity commands are from a unitless joystick, scale them to physical units...
Definition: jog_calcs.cpp:750
sensor_msgs::JointState original_joint_state_
Definition: jog_calcs.h:163
#define ROS_DEBUG_STREAM_THROTTLE_NAMED(period, name, args)
Eigen::Isometry3d tf_moveit_to_cmd_frame_
Definition: jog_calcs.h:179
void removeDimension(Eigen::MatrixXd &matrix, Eigen::VectorXd &delta_x, unsigned int row_to_remove)
Definition: jog_calcs.cpp:834
std::atomic< StatusCode > status
Definition: jog_arm_data.h:97
Eigen::ArrayXd delta_theta_
Definition: jog_calcs.h:176
void startMainLoop(JogArmShared &shared_variables)
Definition: jog_calcs.cpp:67
trajectory_msgs::JointTrajectory outgoing_command_
Definition: jog_calcs.h:165


moveit_jog_arm
Author(s): Brian O'Neil, Andy Zelenak , Blake Anderson, Alexander Rössler
autogenerated on Fri Jun 5 2020 03:53:46