jog_arm_server.cpp
Go to the documentation of this file.
1 // Title : jog_arm_server.cpp
3 // Project : jog_arm
4 // Created : 3/9/2017
5 // Author : Brian O'Neil, Andy Zelenak, Blake Anderson
6 //
7 // BSD 3-Clause License
8 //
9 // Copyright (c) 2018, Los Alamos National Security, LLC
10 // All rights reserved.
11 //
12 // Redistribution and use in source and binary forms, with or without
13 // modification, are permitted provided that the following conditions are met:
14 //
15 // * Redistributions of source code must retain the above copyright notice, this
16 // list of conditions and the following disclaimer.
17 //
18 // * Redistributions in binary form must reproduce the above copyright notice,
19 // this list of conditions and the following disclaimer in the documentation
20 // and/or other materials provided with the distribution.
21 //
22 // * Neither the name of the copyright holder nor the names of its
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 // ARE
30 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
31 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
39 
40 // Server node for arm jogging with MoveIt.
41 
42 #include <jog_arm/jog_arm_server.h>
43 #include <memory>
44 
45 // Initialize these static struct to hold ROS parameters.
46 // They must be static because they are used as arguments in thread creation.
49 std::unique_ptr<robot_model_loader::RobotModelLoader> jog_arm::JogROSInterface::model_loader_ptr_ = NULL;
50 
52 // JogROSInterface handles ROS subscriptions and instantiates the worker
53 // threads.
54 // One worker thread does the jogging calculations.
55 // Another worker thread does collision checking.
57 
58 static const char* const NODE_NAME = "jog_arm_server";
59 static const int GAZEBO_REDUNTANT_MESSAGE_COUNT = 30;
60 
61 // MAIN
62 int main(int argc, char** argv)
63 {
64  ros::init(argc, argv, NODE_NAME);
65 
66  jog_arm::JogROSInterface ros_interface;
67 
68  return 0;
69 }
70 
71 namespace jog_arm
72 {
73 // Constructor for the main ROS interface node
75 {
77 
78  // Read ROS parameters, typically from YAML file
79  if (!readParameters(n))
80  exit(EXIT_FAILURE);
81 
82  // Load the robot model. This is needed by the worker threads.
83  model_loader_ptr_ = std::unique_ptr<robot_model_loader::RobotModelLoader>(new robot_model_loader::RobotModelLoader);
84 
85  // Crunch the numbers in this thread
86  pthread_t joggingThread;
87  int rc = pthread_create(&joggingThread, nullptr, jog_arm::JogROSInterface::jogCalcThread, this);
88  if (rc)
89  {
90  ROS_FATAL_STREAM_NAMED(NODE_NAME, "Creating jog calculation thread failed");
91  return;
92  }
93 
94  // Check collisions in this thread
95  pthread_t collisionThread;
96  rc = pthread_create(&collisionThread, nullptr, jog_arm::JogROSInterface::CollisionCheckThread, this);
97  if (rc)
98  {
99  ROS_FATAL_STREAM_NAMED(NODE_NAME, "Creating collision check thread failed");
100  return;
101  }
102 
103  // ROS subscriptions. Share the data with the worker threads
104  ros::Subscriber cmd_sub =
107  ros::Subscriber joint_jog_cmd_sub =
109  ros::topic::waitForMessage<sensor_msgs::JointState>(ros_parameters_.joint_topic);
110  ros::topic::waitForMessage<geometry_msgs::TwistStamped>(ros_parameters_.cartesian_command_in_topic);
111 
112  // Publish freshly-calculated joints to the robot
113  // Put the outgoing msg in the right format (trajectory_msgs/JointTrajectory
114  // or std_msgs/Float64MultiArray).
115  ros::Publisher outgoing_cmd_pub;
116  if (ros_parameters_.command_out_type == "trajectory_msgs/JointTrajectory")
117  outgoing_cmd_pub = n.advertise<trajectory_msgs::JointTrajectory>(ros_parameters_.command_out_topic, 1);
118  else if (ros_parameters_.command_out_type == "std_msgs/Float64MultiArray")
119  outgoing_cmd_pub = n.advertise<std_msgs::Float64MultiArray>(ros_parameters_.command_out_topic, 1);
120 
121  // Wait for low pass filters to stabilize
123 
124  ros::Rate main_rate(1. / ros_parameters_.publish_period);
125 
126  while (ros::ok())
127  {
128  ros::spinOnce();
129 
130  pthread_mutex_lock(&shared_variables_.new_traj_mutex);
131  trajectory_msgs::JointTrajectory new_traj = shared_variables_.new_traj;
132  pthread_mutex_unlock(&shared_variables_.new_traj_mutex);
133 
134  // Check for stale cmds
135  pthread_mutex_lock(&shared_variables_.incoming_cmd_stamp_mutex);
138  {
139  // Mark that incoming commands are not stale
140  pthread_mutex_lock(&shared_variables_.command_is_stale_mutex);
142  pthread_mutex_unlock(&shared_variables_.command_is_stale_mutex);
143  }
144  else
145  {
146  pthread_mutex_lock(&shared_variables_.command_is_stale_mutex);
148  pthread_mutex_unlock(&shared_variables_.command_is_stale_mutex);
149  }
150  pthread_mutex_unlock(&shared_variables_.incoming_cmd_stamp_mutex);
151 
152  // Publish the most recent trajectory, unless the jogging calculation thread
153  // tells not to
154  pthread_mutex_lock(&shared_variables_.ok_to_publish_mutex);
156  {
157  // Put the outgoing msg in the right format
158  // (trajectory_msgs/JointTrajectory or std_msgs/Float64MultiArray).
159  if (ros_parameters_.command_out_type == "trajectory_msgs/JointTrajectory")
160  {
161  new_traj.header.stamp = ros::Time::now();
162  outgoing_cmd_pub.publish(new_traj);
163  }
164  else if (ros_parameters_.command_out_type == "std_msgs/Float64MultiArray")
165  {
166  std_msgs::Float64MultiArray joints;
168  joints.data = new_traj.points[0].positions;
170  joints.data = new_traj.points[0].velocities;
171  outgoing_cmd_pub.publish(joints);
172  }
173  }
174  else
175  {
176  ROS_WARN_STREAM_THROTTLE_NAMED(2, NODE_NAME, "Stale or zero command. "
177  "Try a larger 'incoming_command_timeout' parameter?");
178  }
179  pthread_mutex_unlock(&shared_variables_.ok_to_publish_mutex);
180 
181  main_rate.sleep();
182  }
183 
184  (void)pthread_join(joggingThread, nullptr);
185  (void)pthread_join(collisionThread, nullptr);
186 }
187 
188 // A separate thread for the heavy jogging calculations.
190 {
192  return nullptr;
193 }
194 
195 // A separate thread for collision checking.
197 {
199  return nullptr;
200 }
201 
202 // Constructor for the class that handles collision checking
204  const jog_arm_parameters& parameters, jog_arm_shared& shared_variables,
205  const std::unique_ptr<robot_model_loader::RobotModelLoader>& model_loader_ptr)
206 {
207  // If user specified true in yaml file
208  if (parameters.collision_check)
209  {
210  // MoveIt Setup
211  // Wait for model_loader_ptr to be non-null.
212  while (ros::ok() && !model_loader_ptr)
213  {
214  ROS_WARN_THROTTLE_NAMED(5, NODE_NAME, "Waiting for a non-null robot_model_loader pointer");
215  ros::Duration(0.1).sleep();
216  }
217  const robot_model::RobotModelPtr& kinematic_model = model_loader_ptr->getModel();
219  collision_detection::CollisionRequest collision_request;
220  collision_request.group_name = parameters.move_group_name;
221  collision_request.distance = true;
222  collision_detection::CollisionResult collision_result;
223  robot_state::RobotState& current_state = planning_scene.getCurrentStateNonConst();
225 
226  // Wait for initial messages
227  ROS_INFO_NAMED(NODE_NAME, "Waiting for first joint msg.");
228  ros::topic::waitForMessage<sensor_msgs::JointState>(parameters.joint_topic);
229  ROS_INFO_NAMED(NODE_NAME, "Received first joint msg.");
230 
231  ROS_INFO_NAMED(NODE_NAME, "Waiting for first command msg.");
232  ros::topic::waitForMessage<geometry_msgs::TwistStamped>(parameters.cartesian_command_in_topic);
233  ROS_INFO_NAMED(NODE_NAME, "Received first command msg.");
234 
235  // A very low cutoff frequency
236  jog_arm::LowPassFilter velocity_scale_filter(20);
237  // Assume no scaling, initially
238  velocity_scale_filter.reset(1);
239  ros::Rate collision_rate(parameters.collision_check_rate);
240 
242  // Spin while checking collisions
244  while (ros::ok())
245  {
246  sensor_msgs::JointState jts = shared_variables.joints;
247 
248  for (std::size_t i = 0; i < jts.position.size(); ++i)
249  current_state.setJointPositions(jts.name[i], &jts.position[i]);
250 
251  // process collision objects in scene
252  std::map<std::string, moveit_msgs::CollisionObject> c_objects_map = planning_scene_interface.getObjects();
253  for (auto& kv : c_objects_map)
254  {
255  planning_scene.processCollisionObjectMsg(kv.second);
256  }
257 
258  collision_result.clear();
259  planning_scene.checkCollision(collision_request, collision_result);
260 
261  // Scale robot velocity according to collision proximity and user-defined
262  // thresholds.
263  // I scaled exponentially (cubic power) so velocity drops off quickly
264  // after the threshold.
265  // Proximity decreasing --> decelerate
266  double velocity_scale = 1;
267 
268  // Ramp velocity down linearly when collision proximity is between
269  // lower_collision_proximity_threshold and
270  // hard_stop_collision_proximity_threshold
271  if ((collision_result.distance > parameters.hard_stop_collision_proximity_threshold) &&
272  (collision_result.distance < parameters.lower_collision_proximity_threshold))
273  {
274  // scale = k*(proximity-hard_stop_threshold)^3
275  velocity_scale =
276  64000. * pow(collision_result.distance - parameters.hard_stop_collision_proximity_threshold, 3);
277  }
278  //else if (collision_result.distance < parameters.hard_stop_collision_proximity_threshold)
279  // velocity_scale = 0;
280 
281  velocity_scale = velocity_scale_filter.filter(velocity_scale);
282  // Put a ceiling and a floor on velocity_scale
283  if (velocity_scale > 1)
284  velocity_scale = 1;
285  else if (velocity_scale < 0.05)
286  velocity_scale = 0.05;
287 
288  // Very slow if actually in collision
289  if (collision_result.collision)
290  velocity_scale = 0.02;
291 
292  pthread_mutex_lock(&shared_variables.collision_velocity_scale_mutex);
293  shared_variables.collision_velocity_scale = velocity_scale;
294  pthread_mutex_unlock(&shared_variables.collision_velocity_scale_mutex);
295 
296  collision_rate.sleep();
297  }
298  }
299 }
300 
301 // Constructor for the class that handles jogging calculations
302 JogCalcs::JogCalcs(const jog_arm_parameters& parameters, jog_arm_shared& shared_variables,
303  const std::unique_ptr<robot_model_loader::RobotModelLoader>& model_loader_ptr)
304  : move_group_(parameters.move_group_name)
305 {
306  parameters_ = parameters;
307 
308  // Publish collision status
309  warning_pub_ = nh_.advertise<std_msgs::Bool>(parameters_.warning_topic, 1);
310 
311  // MoveIt Setup
312  // Wait for model_loader_ptr to be non-null.
313  while (ros::ok() && !model_loader_ptr)
314  {
315  ROS_WARN_THROTTLE_NAMED(5, NODE_NAME, "Waiting for a non-null robot_model_loader pointer");
316  ros::Duration(0.1).sleep();
317  }
318  const robot_model::RobotModelPtr& kinematic_model = model_loader_ptr->getModel();
319  kinematic_state_ = std::make_shared<robot_state::RobotState>(kinematic_model);
320  kinematic_state_->setToDefaultValues();
321 
322  joint_model_group_ = kinematic_model->getJointModelGroup(parameters_.move_group_name);
323 
324  std::vector<double> dummy_joint_values;
325  kinematic_state_->copyJointGroupPositions(joint_model_group_, dummy_joint_values);
326 
327  // Wait for initial messages
328  ROS_INFO_NAMED(NODE_NAME, "Waiting for first joint msg.");
329  ros::topic::waitForMessage<sensor_msgs::JointState>(parameters_.joint_topic);
330  ROS_INFO_NAMED(NODE_NAME, "Received first joint msg.");
331 
332  ROS_INFO_NAMED(NODE_NAME, "Waiting for first command msg.");
333  ros::topic::waitForMessage<geometry_msgs::TwistStamped>(parameters_.cartesian_command_in_topic);
334  ROS_INFO_NAMED(NODE_NAME, "Received first command msg.");
335 
337 
339  jt_state_.position.resize(jt_state_.name.size());
340  jt_state_.velocity.resize(jt_state_.name.size());
341  jt_state_.effort.resize(jt_state_.name.size());
342 
343  // Low-pass filters for the joint positions & velocities
344  for (size_t i = 0; i < jt_state_.name.size(); ++i)
345  {
348  }
349 
350  // Initialize the position filters to initial robot joints
351  while (!updateJoints() && ros::ok())
352  {
353  incoming_jts_ = shared_variables.joints;
354  ros::Duration(0.001).sleep();
355  }
356  for (std::size_t i = 0; i < jt_state_.name.size(); ++i)
357  position_filters_[i].reset(jt_state_.position[i]);
358 
359  // Wait for the first jogging cmd.
360  // Store it in a class member for further calcs.
361  // Then free up the shared variable again.
362  geometry_msgs::TwistStamped cartesian_deltas;
363  jog_msgs::JogJoint joint_deltas;
364 
365  while (ros::ok() && cartesian_deltas.header.stamp == ros::Time(0.) && joint_deltas.header.stamp == ros::Time(0.))
366  {
367  ros::Duration(0.05).sleep();
368 
369  cartesian_deltas = shared_variables.command_deltas;
370  joint_deltas = shared_variables.joint_command_deltas;
371  }
372 
373  // Track the number of cycles during which motion has not occurred.
374  // Will avoid re-publishing zero velocities endlessly.
375  int zero_velocity_count = 0;
376  int num_zero_cycles_to_publish = 4;
377 
378  // Now do jogging calcs
379  while (ros::ok())
380  {
381  // If user commands are all zero, reset the low-pass filters
382  // when commands resume
383  bool zero_cartesian_traj_flag = shared_variables.zero_cartesian_cmd_flag;
384  bool zero_joint_traj_flag = shared_variables.zero_joint_cmd_flag;
385 
386  if (zero_cartesian_traj_flag && zero_joint_traj_flag)
387  // Reset low-pass filters
389 
390  // Pull data from the shared variables.
391  incoming_jts_ = shared_variables.joints;
392 
393  // Initialize the position filters to initial robot joints
394  while (!updateJoints() && ros::ok())
395  {
396  incoming_jts_ = shared_variables.joints;
397  ros::Duration(0.001).sleep();
398  }
399 
400  // If there have not been several consecutive cycles of all zeros and joint
401  // jogging commands are empty
402  if ((zero_velocity_count <= num_zero_cycles_to_publish) && zero_joint_traj_flag)
403  {
404  cartesian_deltas = shared_variables.command_deltas;
405 
406  if (!cartesianJogCalcs(cartesian_deltas, shared_variables))
407  continue;
408  }
409  // If there have not been several consecutive cycles of all zeros and joint
410  // jogging commands are not empty
411  else if ((zero_velocity_count <= num_zero_cycles_to_publish) && !zero_joint_traj_flag)
412  {
413  joint_deltas = shared_variables.joint_command_deltas;
414  ;
415 
416  if (!jointJogCalcs(joint_deltas, shared_variables))
417  continue;
418  }
419 
420  // Halt if the command is stale or inputs are all zero, or commands were
421  // zero
422  if (shared_variables.command_is_stale || (zero_cartesian_traj_flag && zero_joint_traj_flag))
423  {
424  halt(new_traj_);
425  zero_cartesian_traj_flag = true;
426  zero_joint_traj_flag = true;
427  }
428 
429  // Has the velocity command been zero for several cycles in a row?
430  // If so, stop publishing so other controllers can take over
431  bool valid_nonzero_trajectory =
432  !((zero_velocity_count > num_zero_cycles_to_publish) && zero_cartesian_traj_flag && zero_joint_traj_flag);
433 
434  // Send the newest target joints
435  if (!new_traj_.joint_names.empty())
436  {
437  // If everything normal, share the new traj to be published
438  if (valid_nonzero_trajectory)
439  {
440  pthread_mutex_lock(&shared_variables.new_traj_mutex);
441  pthread_mutex_lock(&shared_variables.ok_to_publish_mutex);
442  shared_variables.new_traj = new_traj_;
443  shared_variables.ok_to_publish = true;
444  pthread_mutex_unlock(&shared_variables.new_traj_mutex);
445  pthread_mutex_unlock(&shared_variables.ok_to_publish_mutex);
446  }
447  // Skip the jogging publication if all inputs have been zero for several
448  // cycles in a row
449  else if (zero_velocity_count > num_zero_cycles_to_publish)
450  {
451  pthread_mutex_lock(&shared_variables.ok_to_publish_mutex);
452  shared_variables.ok_to_publish = false;
453  pthread_mutex_unlock(&shared_variables.ok_to_publish_mutex);
454  }
455 
456  // Store last zero-velocity message flag to prevent superfluous warnings.
457  // Cartesian and joint commands must both be zero.
458  if (zero_cartesian_traj_flag && zero_joint_traj_flag)
459  zero_velocity_count += 1;
460  else
461  zero_velocity_count = 0;
462  }
463 
464  // Add a small sleep to avoid 100% CPU usage
465  ros::Duration(0.005).sleep();
466  }
467 }
468 
469 // Perform the jogging calculations
470 bool JogCalcs::cartesianJogCalcs(const geometry_msgs::TwistStamped& cmd, jog_arm_shared& shared_variables)
471 {
472  // Check for nan's in the incoming command
473  if (std::isnan(cmd.twist.linear.x) || std::isnan(cmd.twist.linear.y) || std::isnan(cmd.twist.linear.z) ||
474  std::isnan(cmd.twist.angular.x) || std::isnan(cmd.twist.angular.y) || std::isnan(cmd.twist.angular.z))
475  {
476  ROS_WARN_STREAM_NAMED(NODE_NAME, "nan in incoming command. Skipping this datapoint.");
477  return 0;
478  }
479 
480  // If incoming commands should be in the range [-1:1], check for |delta|>1
481  if (parameters_.command_in_type == "unitless")
482  {
483  if ((fabs(cmd.twist.linear.x) > 1) || (fabs(cmd.twist.linear.y) > 1) || (fabs(cmd.twist.linear.z) > 1) ||
484  (fabs(cmd.twist.angular.x) > 1) || (fabs(cmd.twist.angular.y) > 1) || (fabs(cmd.twist.angular.z) > 1))
485  {
486  ROS_WARN_STREAM_NAMED(NODE_NAME, "Component of incoming command is >1. Skipping this datapoint.");
487  return 0;
488  }
489  }
490 
491  // Convert the cmd to the MoveGroup planning frame.
492  try
493  {
495  }
496  catch (const tf::TransformException& ex)
497  {
499  return 0;
500  }
501  // To transform, these vectors need to be stamped. See answers.ros.org
502  // Q#199376
503  // Transform the linear component of the cmd message
504  geometry_msgs::Vector3Stamped lin_vector;
505  lin_vector.vector = cmd.twist.linear;
506  lin_vector.header.frame_id = cmd.header.frame_id;
507  try
508  {
509  listener_.transformVector(parameters_.planning_frame, lin_vector, lin_vector);
510  }
511  catch (const tf::TransformException& ex)
512  {
514  return 0;
515  }
516 
517  geometry_msgs::Vector3Stamped rot_vector;
518  rot_vector.vector = cmd.twist.angular;
519  rot_vector.header.frame_id = cmd.header.frame_id;
520  try
521  {
522  listener_.transformVector(parameters_.planning_frame, rot_vector, rot_vector);
523  }
524  catch (const tf::TransformException& ex)
525  {
527  return 0;
528  }
529 
530  // Put these components back into a TwistStamped
531  geometry_msgs::TwistStamped twist_cmd;
532  twist_cmd.header.stamp = cmd.header.stamp;
533  twist_cmd.header.frame_id = parameters_.planning_frame;
534  twist_cmd.twist.linear = lin_vector.vector;
535  twist_cmd.twist.angular = rot_vector.vector;
536 
537  const Eigen::VectorXd delta_x = scaleCartesianCommand(twist_cmd);
538 
539  kinematic_state_->setVariableValues(jt_state_);
541 
542  // Convert from cartesian commands to joint commands
543  Eigen::MatrixXd jacobian = kinematic_state_->getJacobian(joint_model_group_);
544  Eigen::JacobiSVD<Eigen::MatrixXd> svd(jacobian, Eigen::ComputeThinU | Eigen::ComputeThinV);
545  Eigen::VectorXd delta_theta = pseudoInverse(svd.matrixU(), svd.matrixV(), svd.singularValues().asDiagonal()) * delta_x;
546 
547  enforceJointVelocityLimits(delta_theta);
548  if (!addJointIncrements(jt_state_, delta_theta))
549  return 0;
550 
551  // Include a velocity estimate for velocity-controlled robots
552  Eigen::VectorXd joint_vel(delta_theta / parameters_.publish_period);
553 
554  lowPassFilterVelocities(joint_vel);
556 
559 
560  // If close to a collision or a singularity, decelerate
561  applyVelocityScaling(shared_variables, new_traj_, delta_theta, decelerateForSingularity(jacobian, delta_x));
562 
564  {
565  halt(new_traj_);
566  publishWarning(true);
567  }
568  else
569  publishWarning(false);
570 
571  // If using Gazebo simulator, insert redundant points
572  if (parameters_.gazebo)
573  {
575  }
576 
577  return 1;
578 }
579 
580 bool JogCalcs::jointJogCalcs(const jog_msgs::JogJoint& cmd, jog_arm_shared& shared_variables)
581 {
582  // Check for nan's or |delta|>1 in the incoming command
583  for (std::size_t i = 0; i < cmd.deltas.size(); ++i)
584  {
585  if (std::isnan(cmd.deltas[i]) || (fabs(cmd.deltas[i]) > 1))
586  {
587  ROS_WARN_STREAM_NAMED(NODE_NAME, "nan in incoming command. Skipping this datapoint.");
588  return 0;
589  }
590  }
591 
592  // Apply user-defined scaling
593  const Eigen::VectorXd delta = scaleJointCommand(cmd);
594 
595  kinematic_state_->setVariableValues(jt_state_);
597 
598  if (!addJointIncrements(jt_state_, delta))
599  return 0;
600 
601  // Include a velocity estimate for velocity-controlled robots
602  Eigen::VectorXd joint_vel(delta / parameters_.publish_period);
603 
604  lowPassFilterVelocities(joint_vel);
606 
607  // update joint state with new values
608  kinematic_state_->setVariableValues(jt_state_);
609 
612 
613  // check if new joint state is valid
615  {
616  halt(new_traj_);
617  publishWarning(true);
618  }
619  else
620  {
621  publishWarning(false);
622  }
623 
624  // done with calculations
625  if (parameters_.gazebo)
626  {
628  }
629 
630  return 1;
631 }
632 
633 // Spam several redundant points into the trajectory. The first few may be
634 // skipped if the
635 // time stamp is in the past when it reaches the client. Needed for gazebo
636 // simulation.
637 // Start from 2 because the first point's timestamp is already
638 // 1*parameters_.publish_period
639 void JogCalcs::insertRedundantPointsIntoTrajectory(trajectory_msgs::JointTrajectory& trajectory, int count) const
640 {
641  auto point = trajectory.points[0];
642  // Start from 2 because we already have the first point. End at count+1 so
643  // total # == count
644  for (int i = 2; i < count + 1; ++i)
645  {
646  point.time_from_start = ros::Duration(i * parameters_.publish_period);
647  trajectory.points.push_back(point);
648  }
649 }
650 
652 {
653  for (size_t i = 0; i < jt_state_.name.size(); ++i)
654  {
655  jt_state_.position[i] = position_filters_[i].filter(jt_state_.position[i]);
656 
657  // Check for nan's
658  if (std::isnan(jt_state_.position[i]))
659  {
660  jt_state_.position[i] = original_jts_.position[i];
661  jt_state_.velocity[i] = 0.;
662  }
663  }
664 }
665 
666 void JogCalcs::lowPassFilterVelocities(const Eigen::VectorXd& joint_vel)
667 {
668  for (size_t i = 0; i < jt_state_.name.size(); ++i)
669  {
670  jt_state_.velocity[i] = velocity_filters_[i].filter(joint_vel[static_cast<long>(i)]);
671 
672  // Check for nan's
673  if (std::isnan(jt_state_.velocity[static_cast<long>(i)]))
674  {
675  jt_state_.position[i] = original_jts_.position[i];
676  jt_state_.velocity[i] = 0.;
677  ROS_WARN_STREAM_NAMED(NODE_NAME, "nan in velocity filter");
678  }
679  }
680 }
681 
682 trajectory_msgs::JointTrajectory JogCalcs::composeOutgoingMessage(sensor_msgs::JointState& joint_state,
683  const ros::Time& stamp) const
684 {
685  trajectory_msgs::JointTrajectory new_jt_traj;
686  new_jt_traj.header.frame_id = parameters_.planning_frame;
687  new_jt_traj.header.stamp = stamp;
688  new_jt_traj.joint_names = joint_state.name;
689 
690  trajectory_msgs::JointTrajectoryPoint point;
691  point.time_from_start = ros::Duration(parameters_.publish_period);
693  point.positions = joint_state.position;
695  point.velocities = joint_state.velocity;
697  {
698  // I do not know of a robot that takes acceleration commands.
699  // However, some controllers check that this data is non-empty.
700  // Send all zeros, for now.
701  std::vector<double> acceleration(joint_state.velocity.size());
702  point.accelerations = acceleration;
703  }
704  new_jt_traj.points.push_back(point);
705 
706  return new_jt_traj;
707 }
708 
709 // Apply velocity scaling for proximity of collisions and singularities.
710 // Scale for collisions is read from a shared variable.
711 // Key equation: new_velocity =
712 // collision_scale*singularity_scale*previous_velocity
713 bool JogCalcs::applyVelocityScaling(jog_arm_shared& shared_variables, trajectory_msgs::JointTrajectory& new_jt_traj,
714  const Eigen::VectorXd& delta_theta, double singularity_scale)
715 {
716  double collision_scale = shared_variables.collision_velocity_scale;
717 
718  for (size_t i = 0; i < jt_state_.velocity.size(); ++i)
719  {
721  {
722  // If close to a singularity or joint limit, undo any change to the joint
723  // angles
724  new_jt_traj.points[0].positions[i] =
725  new_jt_traj.points[0].positions[i] -
726  (1. - singularity_scale * collision_scale) * delta_theta[static_cast<long>(i)];
727  }
729  new_jt_traj.points[0].velocities[i] *= singularity_scale * collision_scale;
730  }
731 
732  return 1;
733 }
734 
735 
736 void JogCalcs::enforceJointVelocityLimits(Eigen::VectorXd& calculated_joint_vel)
737 {
738  double maximum_joint_vel = calculated_joint_vel.cwiseAbs().maxCoeff();
739  if(maximum_joint_vel > parameters_.joint_scale)
740  {
741  // Scale the entire joint velocity vector so that each joint velocity is below min, and the output movement is scaled uniformly to match expected motion
742  calculated_joint_vel = calculated_joint_vel * parameters_.joint_scale / maximum_joint_vel;
743  }
744 }
745 
746 // Possibly calculate a velocity scaling factor, due to proximity of singularity
747 // and direction of motion
748 double JogCalcs::decelerateForSingularity(Eigen::MatrixXd jacobian, const Eigen::VectorXd commanded_velocity)
749 {
750  double velocity_scale = 1;
751 
752  // Find the direction away from nearest singularity.
753  // The last column of U from the SVD of the Jacobian points away from the
754  // singularity
755  Eigen::JacobiSVD<Eigen::MatrixXd> svd(jacobian, Eigen::ComputeThinU | Eigen::ComputeThinV);
756  Eigen::VectorXd vector_toward_singularity = svd.matrixU().col(5);
757 
758  double ini_condition = svd.singularValues()(0) / svd.singularValues()(svd.singularValues().size() - 1);
759 
760  // This singular vector tends to flip direction unpredictably. See R. Bro,
761  // "Resolving the Sign Ambiguity
762  // in the Singular Value Decomposition"
763  // Look ahead to see if the Jacobian's condition will decrease in this
764  // direction.
765  // Start with a scaled version of the singular vector
766  Eigen::VectorXd delta_x(6);
767  double scale = 100;
768  delta_x[0] = vector_toward_singularity[0] / scale;
769  delta_x[1] = vector_toward_singularity[1] / scale;
770  delta_x[2] = vector_toward_singularity[2] / scale;
771  delta_x[3] = vector_toward_singularity[3] / scale;
772  delta_x[4] = vector_toward_singularity[4] / scale;
773  delta_x[5] = vector_toward_singularity[5] / scale;
774 
775  // Calculate a small change in joints
776  Eigen::VectorXd delta_theta = pseudoInverse(jacobian) * delta_x;
777 
778  double theta[6];
779  const double* prev_joints = kinematic_state_->getVariablePositions();
780  for (std::size_t i = 0, size = static_cast<std::size_t>(delta_theta.size()); i < size; ++i)
781  theta[i] = prev_joints[i] + delta_theta(i);
782 
783  kinematic_state_->setVariablePositions(theta);
784  jacobian = kinematic_state_->getJacobian(joint_model_group_);
785  svd = Eigen::JacobiSVD<Eigen::MatrixXd>(jacobian);
786  double new_condition = svd.singularValues()(0) / svd.singularValues()(svd.singularValues().size() - 1);
787  // If new_condition < ini_condition, the singular vector does point towards a
788  // singularity.
789  // Otherwise, flip its direction.
790  if (ini_condition >= new_condition)
791  {
792  vector_toward_singularity[0] *= -1;
793  vector_toward_singularity[1] *= -1;
794  vector_toward_singularity[2] *= -1;
795  vector_toward_singularity[3] *= -1;
796  vector_toward_singularity[4] *= -1;
797  vector_toward_singularity[5] *= -1;
798  }
799 
800  // If this dot product is positive, we're moving toward singularity ==>
801  // decelerate
802  double dot = vector_toward_singularity.dot(commanded_velocity);
803  if (dot > 0)
804  {
805  // Ramp velocity down linearly when the Jacobian condition is between
806  // lower_singularity_threshold and
807  // hard_stop_singularity_threshold, and we're moving towards the singularity
808  if ((ini_condition > parameters_.lower_singularity_threshold) &&
810  {
811  velocity_scale = 1. -
812  (ini_condition - parameters_.lower_singularity_threshold) /
814  }
815 
816  // Very close to singularity, so halt.
817  else if (ini_condition > parameters_.hard_stop_singularity_threshold)
818  {
819  velocity_scale = 0;
820  ROS_WARN_NAMED(NODE_NAME, "Close to a singularity. Halting.");
821  }
822  }
823 
824  return velocity_scale;
825 }
826 
827 bool JogCalcs::checkIfJointsWithinBounds(trajectory_msgs::JointTrajectory& new_jt_traj)
828 {
829  bool halting = false;
830  for (auto joint : joint_model_group_->getJointModels())
831  {
832  if (!kinematic_state_->satisfiesVelocityBounds(joint))
833  {
834  ROS_WARN_STREAM_THROTTLE_NAMED(2, NODE_NAME, ros::this_node::getName() << " " << joint->getName() << " "
835  << " close to a "
836  " velocity limit. Enforcing limit.");
837  kinematic_state_->enforceVelocityBounds(joint);
838  for (std::size_t c = 0; c < new_jt_traj.joint_names.size(); ++c)
839  {
840  if (new_jt_traj.joint_names[c] == joint->getName())
841  {
842  new_jt_traj.points[0].velocities[c] = kinematic_state_->getJointVelocities(joint)[0];
843  break;
844  }
845  }
846  }
847 
848  // Halt if we're past a joint margin and joint velocity is moving even
849  // farther past
850  double joint_angle = 0;
851  for (std::size_t c = 0; c < new_jt_traj.joint_names.size(); ++c)
852  {
853  if (original_jts_.name[c] == joint->getName())
854  {
855  joint_angle = original_jts_.position.at(c);
856  break;
857  }
858  }
859 
860  if (!kinematic_state_->satisfiesPositionBounds(joint,
861  -jog_arm::JogROSInterface::ros_parameters_.joint_limit_margin))
862  {
863  const std::vector<moveit_msgs::JointLimits> limits = joint->getVariableBoundsMsg();
864 
865  // Joint limits are not defined for some joints. Skip them.
866  if (limits.size() > 0)
867  {
868  if ((kinematic_state_->getJointVelocities(joint)[0] < 0 &&
869  (joint_angle < (limits[0].min_position + jog_arm::JogROSInterface::ros_parameters_.joint_limit_margin))) ||
870  (kinematic_state_->getJointVelocities(joint)[0] > 0 &&
871  (joint_angle > (limits[0].max_position - jog_arm::JogROSInterface::ros_parameters_.joint_limit_margin))))
872  {
873  ROS_WARN_STREAM_THROTTLE_NAMED(2, NODE_NAME, ros::this_node::getName() << " " << joint->getName()
874  << " close to a "
875  " position limit. Halting.");
876  halting = true;
877  }
878  }
879  }
880  }
881 
882  return !halting;
883 }
884 
885 void JogCalcs::publishWarning(const bool active) const
886 {
887  std_msgs::Bool status;
888  status.data = static_cast<std_msgs::Bool::_data_type>(active);
889  warning_pub_.publish(status);
890 }
891 
892 // Avoid a singularity or other issue.
893 // Needs to be handled differently for position vs. velocity control
894 void JogCalcs::halt(trajectory_msgs::JointTrajectory& jt_traj)
895 {
896  for (std::size_t i = 0; i < jt_state_.velocity.size(); ++i)
897  {
898  // For position-controlled robots, can reset the joints to a known, good
899  // state
901  jt_traj.points[0].positions[i] = original_jts_.position[i];
902 
903  // For velocity-controlled robots, stop
905  jt_traj.points[0].velocities[i] = 0;
906  }
907 }
908 
909 // Reset the data stored in filters so the trajectory won't jump when jogging is
910 // resumed.
912 {
913  for (std::size_t i = 0; i < jt_state_.name.size(); ++i)
914  velocity_filters_[i].reset(0); // Zero velocity
915 }
916 
917 // Parse the incoming joint msg for the joints of our MoveGroup
919 {
920  // Check if every joint was zero. Sometimes an issue.
921  bool all_zeros = true;
922 
923  // Check that the msg contains enough joints
924  if (incoming_jts_.name.size() < jt_state_.name.size())
925  return 0;
926 
927  // Store joints in a member variable
928  for (std::size_t m = 0; m < incoming_jts_.name.size(); ++m)
929  {
930  for (std::size_t c = 0; c < jt_state_.name.size(); ++c)
931  {
932  if (incoming_jts_.name[m] == jt_state_.name[c])
933  {
934  jt_state_.position[c] = incoming_jts_.position[m];
935  // Make sure there was at least one nonzero value
936  if (incoming_jts_.position[m] != 0.)
937  all_zeros = false;
938  goto NEXT_JOINT;
939  }
940  }
941  NEXT_JOINT:;
942  }
943 
944  return !all_zeros;
945 }
946 
947 // Scale the incoming jog command
948 Eigen::VectorXd JogCalcs::scaleCartesianCommand(const geometry_msgs::TwistStamped& command) const
949 {
950  Eigen::VectorXd result(6);
951 
952  // Apply user-defined scaling if inputs are unitless [-1:1]
953  if (parameters_.command_in_type == "unitless")
954  {
955  result[0] = parameters_.linear_scale * command.twist.linear.x;
956  result[1] = parameters_.linear_scale * command.twist.linear.y;
957  result[2] = parameters_.linear_scale * command.twist.linear.z;
958  result[3] = parameters_.rotational_scale * command.twist.angular.x;
959  result[4] = parameters_.rotational_scale * command.twist.angular.y;
960  result[5] = parameters_.rotational_scale * command.twist.angular.z;
961  }
962  // Otherwise, commands are in m/s and rad/s
963  else if (parameters_.command_in_type == "speed_units")
964  {
965  result[0] = command.twist.linear.x * parameters_.publish_period;
966  result[1] = command.twist.linear.y * parameters_.publish_period;
967  result[2] = command.twist.linear.z * parameters_.publish_period;
968  result[3] = command.twist.angular.x * parameters_.publish_period;
969  result[4] = command.twist.angular.y * parameters_.publish_period;
970  result[5] = command.twist.angular.z * parameters_.publish_period;
971  }
972  else
973  ROS_ERROR_STREAM_NAMED(NODE_NAME, "Unexpected command_in_type");
974 
975  return result;
976 }
977 
978 Eigen::VectorXd JogCalcs::scaleJointCommand(const jog_msgs::JogJoint& command) const
979 {
980  Eigen::VectorXd result(jt_state_.name.size());
981 
982  for (std::size_t i = 0; i < jt_state_.name.size(); ++i)
983  {
984  result[i] = 0.0;
985  }
986 
987  // Store joints in a member variable
988  for (std::size_t m = 0; m < command.joint_names.size(); ++m)
989  {
990  for (std::size_t c = 0; c < jt_state_.name.size(); ++c)
991  {
992  if (command.joint_names[m] == jt_state_.name[c])
993  {
994  // Apply user-defined scaling if inputs are unitless [-1:1]
995  if (parameters_.command_in_type == "unitless")
996  result[c] = command.deltas[m] * parameters_.joint_scale;
997  // Otherwise, commands are in m/s and rad/s
998  else if (parameters_.command_in_type == "speed_units")
999  result[c] = command.deltas[m] * parameters_.publish_period;
1000  else
1001  ROS_ERROR_STREAM_NAMED(NODE_NAME, "Unexpected command_in_type");
1002  goto NEXT_JOINT;
1003  }
1004  }
1005  NEXT_JOINT:;
1006  }
1007 
1008  return result;
1009 }
1010 
1011 // Calculate a pseudo-inverse.
1012 Eigen::MatrixXd JogCalcs::pseudoInverse(const Eigen::MatrixXd& J) const
1013 {
1014  return J.transpose() * (J * J.transpose()).inverse();
1015 }
1016 
1017 Eigen::MatrixXd JogCalcs::pseudoInverse(const Eigen::MatrixXd& u_matrix, const Eigen::MatrixXd& v_matrix, const Eigen::MatrixXd& s_diagonals) const
1018 {
1019  return v_matrix * s_diagonals.inverse() * u_matrix.transpose();
1020 }
1021 
1022 // Add the deltas to each joint
1023 bool JogCalcs::addJointIncrements(sensor_msgs::JointState& output, const Eigen::VectorXd& increments) const
1024 {
1025  for (std::size_t i = 0, size = static_cast<std::size_t>(increments.size()); i < size; ++i)
1026  {
1027  try
1028  {
1029  output.position[i] += increments[static_cast<long>(i)];
1030  }
1031  catch (const std::out_of_range& e)
1032  {
1033  ROS_ERROR_STREAM_NAMED(NODE_NAME, ros::this_node::getName() << " Lengths of output and "
1034  "increments do not match.");
1035  return 0;
1036  }
1037  }
1038 
1039  return 1;
1040 }
1041 
1042 // Listen to cartesian delta commands.
1043 // Store them in a shared variable.
1044 void JogROSInterface::deltaCartesianCmdCB(const geometry_msgs::TwistStampedConstPtr& msg)
1045 {
1046  pthread_mutex_lock(&shared_variables_.command_deltas_mutex);
1047 
1048  // Copy everything but the frame name. The frame name is set by yaml file at startup.
1049  // (so it isn't copied over and over)
1050  shared_variables_.command_deltas.twist = msg->twist;
1051  shared_variables_.command_deltas.header.stamp = msg->header.stamp;
1052 
1053  // Check if input is all zeros. Flag it if so to skip calculations/publication
1054  pthread_mutex_lock(&shared_variables_.zero_cartesian_cmd_flag_mutex);
1055  shared_variables_.zero_cartesian_cmd_flag = shared_variables_.command_deltas.twist.linear.x == 0.0 &&
1056  shared_variables_.command_deltas.twist.linear.y == 0.0 &&
1057  shared_variables_.command_deltas.twist.linear.z == 0.0 &&
1058  shared_variables_.command_deltas.twist.angular.x == 0.0 &&
1059  shared_variables_.command_deltas.twist.angular.y == 0.0 &&
1060  shared_variables_.command_deltas.twist.angular.z == 0.0;
1061  pthread_mutex_unlock(&shared_variables_.zero_cartesian_cmd_flag_mutex);
1062 
1063  pthread_mutex_unlock(&shared_variables_.command_deltas_mutex);
1064 
1065  pthread_mutex_lock(&shared_variables_.incoming_cmd_stamp_mutex);
1066  shared_variables_.incoming_cmd_stamp = msg->header.stamp;
1067  pthread_mutex_unlock(&shared_variables_.incoming_cmd_stamp_mutex);
1068 }
1069 
1070 // Listen to joint delta commands.
1071 // Store them in a shared variable.
1072 void JogROSInterface::deltaJointCmdCB(const jog_msgs::JogJointConstPtr& msg)
1073 {
1074  pthread_mutex_lock(&shared_variables_.joint_command_deltas_mutex);
1075  shared_variables_.joint_command_deltas = *msg;
1076 
1077  // Check if joint inputs is all zeros. Flag it if so to skip
1078  // calculations/publication
1079  bool all_zeros = true;
1080  for (double delta : shared_variables_.joint_command_deltas.deltas)
1081  {
1082  all_zeros &= (delta == 0.0);
1083  };
1084  pthread_mutex_unlock(&shared_variables_.joint_command_deltas_mutex);
1085 
1086  pthread_mutex_lock(&shared_variables_.zero_joint_cmd_flag_mutex);
1087  shared_variables_.zero_joint_cmd_flag = all_zeros;
1088  pthread_mutex_unlock(&shared_variables_.zero_joint_cmd_flag_mutex);
1089 
1090  pthread_mutex_lock(&shared_variables_.incoming_cmd_stamp_mutex);
1091  shared_variables_.incoming_cmd_stamp = msg->header.stamp;
1092  pthread_mutex_unlock(&shared_variables_.incoming_cmd_stamp_mutex);
1093 }
1094 
1095 // Listen to joint angles.
1096 // Store them in a shared variable.
1097 void JogROSInterface::jointsCB(const sensor_msgs::JointStateConstPtr& msg)
1098 {
1099  pthread_mutex_lock(&shared_variables_.joints_mutex);
1100  shared_variables_.joints = *msg;
1101  pthread_mutex_unlock(&shared_variables_.joints_mutex);
1102 }
1103 
1104 // Read ROS parameters, typically from YAML file
1106 {
1107  std::size_t error = 0;
1108 
1109  // Specified in the launch file. All other parameters will be read
1110  // from this namespace.
1111  std::string parameter_ns;
1112  ros::param::get("~parameter_ns", parameter_ns);
1113  if (parameter_ns == "")
1114  {
1115  ROS_ERROR_STREAM_NAMED(NODE_NAME, "A namespace must be specified in the launch file, like:");
1116  ROS_ERROR_STREAM_NAMED(NODE_NAME, "<param name=\"parameter_ns\" "
1117  "type=\"string\" "
1118  "value=\"left_jog_arm_server\" />");
1119  return 0;
1120  }
1121 
1122  error += !rosparam_shortcuts::get("", n, parameter_ns + "/publish_period", ros_parameters_.publish_period);
1123  error += !rosparam_shortcuts::get("", n, parameter_ns + "/publish_delay", ros_parameters_.publish_delay);
1124  error += !rosparam_shortcuts::get("", n, parameter_ns + "/collision_check_rate", ros_parameters_.collision_check_rate);
1125  error += !rosparam_shortcuts::get("", n, parameter_ns + "/scale/linear", ros_parameters_.linear_scale);
1126  error += !rosparam_shortcuts::get("", n, parameter_ns + "/scale/rotational", ros_parameters_.rotational_scale);
1127  error += !rosparam_shortcuts::get("", n, parameter_ns + "/scale/joint", ros_parameters_.joint_scale);
1128  error +=
1129  !rosparam_shortcuts::get("", n, parameter_ns + "/low_pass_filter_coeff", ros_parameters_.low_pass_filter_coeff);
1130  error += !rosparam_shortcuts::get("", n, parameter_ns + "/joint_topic", ros_parameters_.joint_topic);
1131  error += !rosparam_shortcuts::get("", n, parameter_ns + "/command_in_type", ros_parameters_.command_in_type);
1132  error += !rosparam_shortcuts::get("", n, parameter_ns + "/cartesian_command_in_topic",
1133  ros_parameters_.cartesian_command_in_topic);
1134  error +=
1135  !rosparam_shortcuts::get("", n, parameter_ns + "/joint_command_in_topic", ros_parameters_.joint_command_in_topic);
1136  error += !rosparam_shortcuts::get("", n, parameter_ns + "/command_frame", ros_parameters_.command_frame);
1137  error += !rosparam_shortcuts::get("", n, parameter_ns + "/incoming_command_timeout",
1138  ros_parameters_.incoming_command_timeout);
1139  error += !rosparam_shortcuts::get("", n, parameter_ns + "/lower_singularity_threshold",
1140  ros_parameters_.lower_singularity_threshold);
1141  error += !rosparam_shortcuts::get("", n, parameter_ns + "/hard_stop_singularity_threshold",
1142  ros_parameters_.hard_stop_singularity_threshold);
1143  error += !rosparam_shortcuts::get("", n, parameter_ns + "/lower_collision_proximity_threshold",
1144  ros_parameters_.lower_collision_proximity_threshold);
1145  error += !rosparam_shortcuts::get("", n, parameter_ns + "/hard_stop_collision_proximity_threshold",
1146  ros_parameters_.hard_stop_collision_proximity_threshold);
1147  error += !rosparam_shortcuts::get("", n, parameter_ns + "/move_group_name", ros_parameters_.move_group_name);
1148  error += !rosparam_shortcuts::get("", n, parameter_ns + "/planning_frame", ros_parameters_.planning_frame);
1149  error += !rosparam_shortcuts::get("", n, parameter_ns + "/gazebo", ros_parameters_.gazebo);
1150  error += !rosparam_shortcuts::get("", n, parameter_ns + "/collision_check", ros_parameters_.collision_check);
1151  error += !rosparam_shortcuts::get("", n, parameter_ns + "/warning_topic", ros_parameters_.warning_topic);
1152  error += !rosparam_shortcuts::get("", n, parameter_ns + "/joint_limit_margin", ros_parameters_.joint_limit_margin);
1153  error += !rosparam_shortcuts::get("", n, parameter_ns + "/command_out_topic", ros_parameters_.command_out_topic);
1154  error += !rosparam_shortcuts::get("", n, parameter_ns + "/command_out_type", ros_parameters_.command_out_type);
1155  error += !rosparam_shortcuts::get("", n, parameter_ns + "/publish_joint_positions",
1156  ros_parameters_.publish_joint_positions);
1157  error += !rosparam_shortcuts::get("", n, parameter_ns + "/publish_joint_velocities",
1158  ros_parameters_.publish_joint_velocities);
1159  error += !rosparam_shortcuts::get("", n, parameter_ns + "/publish_joint_accelerations",
1160  ros_parameters_.publish_joint_accelerations);
1161 
1162  rosparam_shortcuts::shutdownIfError(parameter_ns, error);
1163 
1164  // Set the input frame, as determined by YAML file:
1165  pthread_mutex_lock(&shared_variables_.command_deltas_mutex);
1166  shared_variables_.command_deltas.header.frame_id = ros_parameters_.command_frame;
1167  pthread_mutex_unlock(&shared_variables_.command_deltas_mutex);
1168 
1169  // Input checking
1170  if (ros_parameters_.hard_stop_singularity_threshold < ros_parameters_.lower_singularity_threshold)
1171  {
1172  ROS_WARN_NAMED(NODE_NAME, "Parameter 'hard_stop_singularity_threshold' "
1173  "should be greater than 'lower_singularity_threshold.' "
1174  "Check yaml file.");
1175  return 0;
1176  }
1177  if ((ros_parameters_.hard_stop_singularity_threshold < 0.) || (ros_parameters_.lower_singularity_threshold < 0.))
1178  {
1179  ROS_WARN_NAMED(NODE_NAME, "Parameters 'hard_stop_singularity_threshold' "
1180  "and 'lower_singularity_threshold' should be "
1181  "greater than zero. Check yaml file.");
1182  return 0;
1183  }
1184  if (ros_parameters_.hard_stop_collision_proximity_threshold >= ros_parameters_.lower_collision_proximity_threshold)
1185  {
1186  ROS_WARN_NAMED(NODE_NAME, "Parameter 'hard_stop_collision_proximity_threshold' "
1187  "should be less than 'lower_collision_proximity_threshold.' "
1188  "Check yaml file.");
1189  return 0;
1190  }
1191  if ((ros_parameters_.hard_stop_collision_proximity_threshold < 0.) ||
1192  (ros_parameters_.lower_collision_proximity_threshold < 0.))
1193  {
1194  ROS_WARN_NAMED(NODE_NAME, "Parameters 'hard_stop_collision_proximity_threshold' "
1195  "and 'lower_collision_proximity_threshold' should be "
1196  "greater than zero. Check yaml file.");
1197  return 0;
1198  }
1199  if (ros_parameters_.low_pass_filter_coeff < 0.)
1200  {
1201  ROS_WARN_NAMED(NODE_NAME, "Parameter 'low_pass_filter_coeff' should be "
1202  "greater than zero. Check yaml file.");
1203  return 0;
1204  }
1205  if (ros_parameters_.joint_limit_margin < 0.)
1206  {
1207  ROS_WARN_NAMED(NODE_NAME, "Parameter 'joint_limit_margin' should be "
1208  "greater than zero. Check yaml file.");
1209  return 0;
1210  }
1211  if (ros_parameters_.command_in_type != "unitless" && ros_parameters_.command_in_type != "speed_units")
1212  {
1213  ROS_WARN_NAMED(NODE_NAME, "command_in_type should be 'unitless' or "
1214  "'speed_units'. Check yaml file.");
1215  return 0;
1216  }
1217  if (ros_parameters_.command_out_type != "trajectory_msgs/JointTrajectory" &&
1218  ros_parameters_.command_out_type != "std_msgs/Float64MultiArray")
1219  {
1220  ROS_WARN_NAMED(NODE_NAME, "Parameter command_out_type should be "
1221  "'trajectory_msgs/JointTrajectory' or "
1222  "'std_msgs/Float64MultiArray'. Check yaml file.");
1223  return 0;
1224  }
1225  if (!ros_parameters_.publish_joint_positions && !ros_parameters_.publish_joint_velocities &&
1226  !ros_parameters_.publish_joint_accelerations)
1227  {
1228  ROS_WARN_NAMED(NODE_NAME, "At least one of publish_joint_positions / "
1229  "publish_joint_velocities / "
1230  "publish_joint_accelerations must be true. Check "
1231  "yaml file.");
1232  return 0;
1233  }
1234  if ((ros_parameters_.command_out_type == "std_msgs/Float64MultiArray") && ros_parameters_.publish_joint_positions &&
1235  ros_parameters_.publish_joint_velocities)
1236  {
1237  ROS_WARN_NAMED(NODE_NAME, "When publishing a std_msgs/Float64MultiArray, "
1238  "you must select positions OR velocities.");
1239  return 0;
1240  }
1241  if (ros_parameters_.collision_check_rate < 0)
1242  {
1243  ROS_WARN_NAMED(NODE_NAME, "Parameter 'collision_check_rate' should be "
1244  "greater than zero. Check yaml file.");
1245  return 0;
1246  }
1247 
1248  return 1;
1249 }
1250 } // namespace jog_arm
tf::TransformListener listener_
bool checkIfJointsWithinBounds(trajectory_msgs::JointTrajectory_< std::allocator< void >> &new_jt_traj)
robot_state::RobotState & getCurrentStateNonConst()
double filter(double new_msrmt)
#define ROS_INFO_NAMED(name,...)
#define ROS_WARN_THROTTLE_NAMED(rate, name,...)
pthread_mutex_t collision_velocity_scale_mutex
void lowPassFilterVelocities(const Eigen::VectorXd &joint_vel)
sensor_msgs::JointState incoming_jts_
void publish(const boost::shared_ptr< M > &message) const
bool cartesianJogCalcs(const geometry_msgs::TwistStamped &cmd, jog_arm_shared &shared_variables)
ros::NodeHandle nh_
#define ROS_ERROR_STREAM_NAMED(name, args)
void enforceJointVelocityLimits(Eigen::VectorXd &calculated_joint_vel)
std::vector< jog_arm::LowPassFilter > position_filters_
#define ROS_WARN_NAMED(name,...)
pthread_mutex_t incoming_cmd_stamp_mutex
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
trajectory_msgs::JointTrajectory new_traj_
bool sleep() const
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
void deltaCartesianCmdCB(const geometry_msgs::TwistStampedConstPtr &msg)
sensor_msgs::JointState jt_state_
trajectory_msgs::JointTrajectory composeOutgoingMessage(sensor_msgs::JointState &joint_state, const ros::Time &stamp) const
ROSCPP_DECL const std::string & getName()
JogCalcs(const jog_arm_parameters &parameters, jog_arm_shared &shared_variables, const std::unique_ptr< robot_model_loader::RobotModelLoader > &model_loader_ptr)
void halt(trajectory_msgs::JointTrajectory &jt_traj)
std::map< std::string, moveit_msgs::CollisionObject > getObjects(const std::vector< std::string > &object_ids=std::vector< std::string >())
pthread_mutex_t new_traj_mutex
std::string cartesian_command_in_topic
bool processCollisionObjectMsg(const moveit_msgs::CollisionObject &object)
Eigen::VectorXd scaleCartesianCommand(const geometry_msgs::TwistStamped &command) const
bool readParameters(ros::NodeHandle &n)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW void clear()
geometry_msgs::TwistStamped command_deltas
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
jog_msgs::JogJoint joint_command_deltas
ros::Publisher warning_pub_
jog_arm_parameters parameters_
sensor_msgs::JointState joints
Eigen::MatrixXd pseudoInverse(const Eigen::MatrixXd &J) const
void insertRedundantPointsIntoTrajectory(trajectory_msgs::JointTrajectory &trajectory, int count) const
ROSCPP_DECL bool get(const std::string &key, std::string &s)
#define ROS_FATAL_STREAM_NAMED(name, args)
void jointsCB(const sensor_msgs::JointStateConstPtr &msg)
#define ROS_WARN_STREAM_THROTTLE_NAMED(rate, name, args)
void publishWarning(bool active) const
bool addJointIncrements(sensor_msgs::JointState &output, const Eigen::VectorXd &increments) const
static struct jog_arm_shared shared_variables_
void checkCollision(const collision_detection::CollisionRequest &req, collision_detection::CollisionResult &res)
static std::unique_ptr< robot_model_loader::RobotModelLoader > model_loader_ptr_
moveit::planning_interface::MoveGroupInterface move_group_
ROSCPP_DECL bool ok()
static const char *const NODE_NAME
CollisionCheckThread(const jog_arm_parameters &parameters, jog_arm_shared &shared_variables, const std::unique_ptr< robot_model_loader::RobotModelLoader > &model_loader_ptr)
pthread_mutex_t command_is_stale_mutex
double decelerateForSingularity(Eigen::MatrixXd jacobian, const Eigen::VectorXd commanded_velocity)
bool applyVelocityScaling(jog_arm_shared &shared_variables, trajectory_msgs::JointTrajectory &new_jt_traj, const Eigen::VectorXd &delta_theta, double singularity_scale)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::vector< jog_arm::LowPassFilter > velocity_filters_
Eigen::VectorXd scaleJointCommand(const jog_msgs::JogJoint &command) const
int main(int argc, char **argv)
bool sleep()
static void * CollisionCheckThread(void *thread_id)
static const int GAZEBO_REDUNTANT_MESSAGE_COUNT
void shutdownIfError(const std::string &parent_name, std::size_t error_count)
trajectory_msgs::JointTrajectory new_traj
const robot_state::JointModelGroup * joint_model_group_
const std::vector< std::string > & getJointNames()
bool jointJogCalcs(const jog_msgs::JogJoint &cmd, jog_arm_shared &shared_variables)
static Time now()
sensor_msgs::JointState original_jts_
pthread_mutex_t ok_to_publish_mutex
void reset(double data)
robot_state::RobotStatePtr kinematic_state_
static struct jog_arm_parameters ros_parameters_
ROSCPP_DECL void spinOnce()
void transformVector(const std::string &target_frame, const geometry_msgs::Vector3Stamped &stamped_in, geometry_msgs::Vector3Stamped &stamped_out) const
bool get(const std::string &parent_name, const ros::NodeHandle &nh, const std::string &param_name, bool &value)
void deltaJointCmdCB(const jog_msgs::JogJointConstPtr &msg)
static void * jogCalcThread(void *thread_id)
#define ROS_WARN_STREAM_NAMED(name, args)


jog_arm
Author(s): Brian O'Neil, Andy Zelenak , Blake Anderson, Nitish Sharma, Alexander Rössler
autogenerated on Mon Jun 10 2019 13:47:53