joint_trajectory_streamer.cpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Southwest Research Institute
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
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  * * Neither the name of the Southwest Research Institute, nor the names
16  * of its contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
38 #include <map>
39 #include <vector>
40 #include <string>
41 
50 
54 
55 namespace motoman
56 {
57 namespace joint_trajectory_streamer
58 {
59 
60 namespace
61 {
62  const double pos_stale_time_ = 1.0; // max time since last "current position" update, for validation (sec)
63  const double start_pos_tol_ = 1e-4; // max difference btwn start & current position, for validation (rad)
64 }
65 
66 #define ROS_ERROR_RETURN(rtn, ...) do {ROS_ERROR(__VA_ARGS__); return(rtn);} while (0) // NOLINT(whitespace/braces)
67 
68 // override init() to read "robot_id" parameter and subscribe to joint_states
69 bool MotomanJointTrajectoryStreamer::init(SmplMsgConnection* connection, const std::map<int, RobotGroup> &robot_groups,
70  const std::map<std::string, double> &velocity_limits)
71 {
72  bool rtn = true;
73 
74  ROS_INFO("MotomanJointTrajectoryStreamer: init");
75 
76  this->robot_groups_ = robot_groups;
77  rtn &= JointTrajectoryStreamer::init(connection, robot_groups, velocity_limits);
78 
79  motion_ctrl_.init(connection, 0);
80  for (size_t i = 0; i < robot_groups_.size(); i++)
81  {
82  MotomanMotionCtrl motion_ctrl;
83 
84  int robot_id = robot_groups_[i].get_group_id();
85  rtn &= motion_ctrl.init(connection, robot_id);
86 
87  motion_ctrl_map_[robot_id] = motion_ctrl;
88  }
89 
91 
93 
94  return rtn;
95 }
96 
97 // override init() to read "robot_id" parameter and subscribe to joint_states
98 bool MotomanJointTrajectoryStreamer::init(SmplMsgConnection* connection, const std::vector<std::string> &joint_names,
99  const std::map<std::string, double> &velocity_limits)
100 {
101  bool rtn = true;
102 
103  ROS_INFO("MotomanJointTrajectoryStreamer: init");
104 
105  rtn &= JointTrajectoryStreamer::init(connection, joint_names, velocity_limits);
106 
107  // try to read robot_id parameter, if none specified
108  if ((robot_id_ < 0))
109  node_.param("robot_id", robot_id_, 0);
110 
111  rtn &= motion_ctrl_.init(connection, robot_id_);
112 
114 
116 
117  return rtn;
118 }
119 
121 {
122  // TODO( ): Find better place to call StopTrajMode
123  motion_ctrl_.setTrajMode(false); // release TrajMode, so INFORM jobs can run
124 }
125 
126 bool MotomanJointTrajectoryStreamer::disableRobotCB(std_srvs::Trigger::Request &req,
127  std_srvs::Trigger::Response &res)
128 {
129  trajectoryStop();
130 
131  bool ret = motion_ctrl_.setTrajMode(false);
132  res.success = ret;
133 
134  if (!res.success)
135  {
136  res.message = "Motoman robot was NOT disabled. Please re-examine and retry.";
137  ROS_ERROR_STREAM(res.message);
138  }
139  else
140  {
141  res.message = "Motoman robot is now disabled and will NOT accept motion commands.";
142  ROS_WARN_STREAM(res.message);
143  }
144 
145  return true;
146 }
147 
148 bool MotomanJointTrajectoryStreamer::enableRobotCB(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
149 {
150  bool ret = motion_ctrl_.setTrajMode(true);
151  res.success = ret;
152 
153  if (!res.success)
154  {
155  res.message = "Motoman robot was NOT enabled. Please re-examine and retry.";
156  ROS_ERROR_STREAM(res.message);
157  }
158  else
159  {
160  res.message = "Motoman robot is now enabled and will accept motion commands.";
161  ROS_WARN_STREAM(res.message);
162  }
163 
164  return true;
165 }
166 
167 // override create_message to generate JointTrajPtFull message (instead of default JointTrajPt)
168 bool MotomanJointTrajectoryStreamer::create_message(int seq, const trajectory_msgs::JointTrajectoryPoint& pt,
169  SimpleMessage* msg)
170 {
171  JointTrajPtFull msg_data;
173 
174  // copy position data
175  if (!pt.positions.empty())
176  {
177  if (VectorToJointData(pt.positions, values))
178  msg_data.setPositions(values);
179  else
180  ROS_ERROR_RETURN(false, "Failed to copy position data to JointTrajPtFullMessage");
181  }
182  else
183  msg_data.clearPositions();
184 
185  // copy velocity data
186  if (!pt.velocities.empty())
187  {
188  if (VectorToJointData(pt.velocities, values))
189  msg_data.setVelocities(values);
190  else
191  ROS_ERROR_RETURN(false, "Failed to copy velocity data to JointTrajPtFullMessage");
192  }
193  else
194  msg_data.clearVelocities();
195 
196  // copy acceleration data
197  if (!pt.accelerations.empty())
198  {
199  if (VectorToJointData(pt.accelerations, values))
200  msg_data.setAccelerations(values);
201  else
202  ROS_ERROR_RETURN(false, "Failed to copy acceleration data to JointTrajPtFullMessage");
203  }
204  else
205  msg_data.clearAccelerations();
206 
207  // copy scalar data
208  msg_data.setRobotID(robot_id_);
209  msg_data.setSequence(seq);
210  msg_data.setTime(pt.time_from_start.toSec());
211 
212 
213  // convert to message
214  JointTrajPtFullMessage jtpf_msg;
215  jtpf_msg.init(msg_data);
216 
217  return jtpf_msg.toRequest(*msg); // assume "request" COMM_TYPE for now
218 }
219 
220 bool MotomanJointTrajectoryStreamer::create_message_ex(int seq, const motoman_msgs::DynamicJointPoint& point,
221  SimpleMessage* msg)
222 {
223  JointTrajPtFullEx msg_data_ex;
224  JointTrajPtFullExMessage jtpf_msg_ex;
225  std::vector<industrial::joint_traj_pt_full::JointTrajPtFull> msg_data_vector;
226 
228 
229  int num_groups = point.num_groups;
230 
231  for (int i = 0; i < num_groups; i++)
232  {
233  JointTrajPtFull msg_data;
234 
235  motoman_msgs::DynamicJointsGroup pt;
236 
237  motoman_msgs::DynamicJointPoint dpoint;
238 
239  pt = point.groups[i];
240 
241  if (pt.positions.size() < 10)
242  {
243  int size_to_complete = 10 - pt.positions.size();
244 
245  std::vector<double> positions(size_to_complete, 0.0);
246  std::vector<double> velocities(size_to_complete, 0.0);
247  std::vector<double> accelerations(size_to_complete, 0.0);
248 
249  pt.positions.insert(pt.positions.end(), positions.begin(), positions.end());
250  pt.velocities.insert(pt.velocities.end(), velocities.begin(), velocities.end());
251  pt.accelerations.insert(pt.accelerations.end(), accelerations.begin(), accelerations.end());
252  }
253 
254  // copy position data
255  if (!pt.positions.empty())
256  {
257  if (VectorToJointData(pt.positions, values))
258  msg_data.setPositions(values);
259  else
260  ROS_ERROR_RETURN(false, "Failed to copy position data to JointTrajPtFullMessage");
261  }
262  else
263  msg_data.clearPositions();
264  // copy velocity data
265  if (!pt.velocities.empty())
266  {
267  if (VectorToJointData(pt.velocities, values))
268  msg_data.setVelocities(values);
269  else
270  ROS_ERROR_RETURN(false, "Failed to copy velocity data to JointTrajPtFullMessage");
271  }
272  else
273  msg_data.clearVelocities();
274 
275  // copy acceleration data
276  if (!pt.accelerations.empty())
277  {
278  if (VectorToJointData(pt.accelerations, values))
279  msg_data.setAccelerations(values);
280  else
281  ROS_ERROR_RETURN(false, "Failed to copy acceleration data to JointTrajPtFullMessage");
282  }
283  else
284  msg_data.clearAccelerations();
285 
286  // copy scalar data
287  msg_data.setRobotID(pt.group_number);
288  msg_data.setSequence(seq);
289  msg_data.setTime(pt.time_from_start.toSec());
290 
291  // convert to message
292  msg_data_vector.push_back(msg_data);
293  }
294 
295  msg_data_ex.setMultiJointTrajPtData(msg_data_vector);
296  msg_data_ex.setNumGroups(num_groups);
297  msg_data_ex.setSequence(seq);
298  jtpf_msg_ex.init(msg_data_ex);
299 
300  return jtpf_msg_ex.toRequest(*msg); // assume "request" COMM_TYPE for now
301 }
302 
303 bool MotomanJointTrajectoryStreamer::create_message(int seq, const motoman_msgs::DynamicJointsGroup& pt,
304  SimpleMessage* msg)
305 {
306  JointTrajPtFull msg_data;
308  // copy position data
309  if (!pt.positions.empty())
310  {
311  if (VectorToJointData(pt.positions, values))
312  msg_data.setPositions(values);
313  else
314  ROS_ERROR_RETURN(false, "Failed to copy position data to JointTrajPtFullMessage");
315  }
316  else
317  msg_data.clearPositions();
318 
319  // copy velocity data
320  if (!pt.velocities.empty())
321  {
322  if (VectorToJointData(pt.velocities, values))
323  msg_data.setVelocities(values);
324  else
325  ROS_ERROR_RETURN(false, "Failed to copy velocity data to JointTrajPtFullMessage");
326  }
327  else
328  msg_data.clearVelocities();
329 
330  // copy acceleration data
331  if (!pt.accelerations.empty())
332  {
333  if (VectorToJointData(pt.accelerations, values))
334  msg_data.setAccelerations(values);
335  else
336  ROS_ERROR_RETURN(false, "Failed to copy acceleration data to JointTrajPtFullMessage");
337  }
338  else
339  msg_data.clearAccelerations();
340 
341  // copy scalar data
342  msg_data.setRobotID(pt.group_number);
343 
344  msg_data.setSequence(seq);
345  msg_data.setTime(pt.time_from_start.toSec());
346 
347  // convert to message
348  JointTrajPtFullMessage jtpf_msg;
349  jtpf_msg.init(msg_data);
350 
351  return jtpf_msg.toRequest(*msg); // assume "request" COMM_TYPE for now
352 }
353 
354 bool MotomanJointTrajectoryStreamer::VectorToJointData(const std::vector<double> &vec,
355  JointData &joints)
356 {
357  if (static_cast<int>(vec.size()) > joints.getMaxNumJoints())
358  ROS_ERROR_RETURN(false, "Failed to copy to JointData. Len (%d) out of range (0 to %d)",
359  (int)vec.size(), joints.getMaxNumJoints());
360 
361  joints.init();
362  for (size_t i = 0; i < vec.size(); ++i)
363  {
364  joints.setJoint(i, vec[i]);
365  }
366  return true;
367 }
368 
369 // override send_to_robot to provide controllerReady() and setTrajMode() calls
370 bool MotomanJointTrajectoryStreamer::send_to_robot(const std::vector<SimpleMessage>& messages)
371 {
373  ROS_ERROR_RETURN(false, "Failed to initialize MotoRos motion, trajectory execution ABORTED. If safe, call the "
374  "'robot_enable' service to (re-)enable Motoplus motion and retry.");
375 
377 }
378 
379 // override streamingThread, to provide check/retry of MotionReply.result=BUSY
381 {
382  int connectRetryCount = 1;
383 
384  ROS_INFO("Starting Motoman joint trajectory streamer thread");
385  while (ros::ok())
386  {
387  ros::Duration(0.005).sleep();
388 
389  // automatically re-establish connection, if required
390  if (connectRetryCount-- > 0)
391  {
392  ROS_INFO("Connecting to robot motion server");
393  this->connection_->makeConnect();
394  ros::Duration(0.250).sleep(); // wait for connection
395 
396  if (this->connection_->isConnected())
397  connectRetryCount = 0;
398  else if (connectRetryCount <= 0)
399  {
400  ROS_ERROR("Timeout connecting to robot controller. Send new motion command to retry.");
401  this->state_ = TransferStates::IDLE;
402  }
403  continue;
404  }
405 
406  this->mutex_.lock();
407 
408  SimpleMessage msg, tmpMsg, reply;
409 
410  switch (this->state_)
411  {
412  case TransferStates::IDLE:
413  ros::Duration(0.250).sleep(); // slower loop while waiting for new trajectory
414  break;
415 
416  case TransferStates::STREAMING:
417  if (this->current_point_ >= static_cast<int>(this->current_traj_.size()))
418  {
419  ROS_INFO("Trajectory streaming complete, setting state to IDLE");
420  this->state_ = TransferStates::IDLE;
421  break;
422  }
423 
424  if (!this->connection_->isConnected())
425  {
426  ROS_DEBUG("Robot disconnected. Attempting reconnect...");
427  connectRetryCount = 5;
428  break;
429  }
430 
431  tmpMsg = this->current_traj_[this->current_point_];
432  msg.init(tmpMsg.getMessageType(), CommTypes::SERVICE_REQUEST,
433  ReplyTypes::INVALID, tmpMsg.getData()); // set commType=REQUEST
434 
435  if (!this->connection_->sendAndReceiveMsg(msg, reply, false))
436  ROS_WARN("Failed sent joint point, will try again");
437  else
438  {
439  MotionReplyMessage reply_status;
440  if (!reply_status.init(reply))
441  {
442  ROS_ERROR("Aborting trajectory: Unable to parse JointTrajectoryPoint reply");
443  this->state_ = TransferStates::IDLE;
444  break;
445  }
446 
447  if (reply_status.reply_.getResult() == MotionReplyResults::SUCCESS)
448  {
449  ROS_DEBUG("Point[%d of %d] sent to controller",
450  this->current_point_, static_cast<int>(this->current_traj_.size()));
451  this->current_point_++;
452  }
453  else if (reply_status.reply_.getResult() == MotionReplyResults::BUSY)
454  break; // silently retry sending this point
455  else
456  {
457  ROS_ERROR_STREAM("Aborting Trajectory. Failed to send point"
458  << " (#" << this->current_point_ << "): "
459  << MotomanMotionCtrl::getErrorString(reply_status.reply_));
460  this->state_ = TransferStates::IDLE;
461  break;
462  }
463  }
464  break;
465  default:
466  ROS_ERROR("Joint trajectory streamer: unknown state");
467  this->state_ = TransferStates::IDLE;
468  break;
469  }
470  this->mutex_.unlock();
471  }
472  ROS_WARN("Exiting trajectory streamer thread");
473 }
474 
475 // override trajectoryStop to send MotionCtrl message
477 {
478  this->state_ = TransferStates::IDLE; // stop sending trajectory points
480 }
481 
482 // override is_valid to include FS100-specific checks
483 bool MotomanJointTrajectoryStreamer::is_valid(const trajectory_msgs::JointTrajectory &traj)
484 {
485  if (!JointTrajectoryInterface::is_valid(traj))
486  return false;
487 
488  for (size_t i = 0; i < traj.points.size(); ++i)
489  {
490  const trajectory_msgs::JointTrajectoryPoint &pt = traj.points[i];
491 
492  // FS100 requires valid velocity data
493  if (pt.velocities.empty())
494  ROS_ERROR_RETURN(false, "Validation failed: Missing velocity data for trajectory pt %lu", i);
495  }
496 
497  if ((cur_joint_pos_.header.stamp - ros::Time::now()).toSec() > pos_stale_time_)
498  ROS_ERROR_RETURN(false, "Validation failed: Can't get current robot position.");
499 
500  // FS100 requires trajectory start at current position
501  namespace IRC_utils = industrial_robot_client::utils;
502  if (!IRC_utils::isWithinRange(cur_joint_pos_.name, cur_joint_pos_.position,
503  traj.joint_names, traj.points[0].positions,
504  start_pos_tol_))
505  {
506  ROS_ERROR_RETURN(false, "Validation failed: Trajectory doesn't start at current position.");
507  }
508  return true;
509 }
510 
511 bool MotomanJointTrajectoryStreamer::is_valid(const motoman_msgs::DynamicJointTrajectory &traj)
512 {
513  if (!JointTrajectoryInterface::is_valid(traj))
514  return false;
515  ros::Time time_stamp;
516  int group_number;
517  for (size_t i = 0; i < traj.points.size(); ++i)
518  {
519  for (int gr = 0; gr < traj.points[i].num_groups; gr++)
520  {
521  const motoman_msgs::DynamicJointsGroup &pt = traj.points[i].groups[gr];
522  time_stamp = cur_joint_pos_map_[pt.group_number].header.stamp;
523  // TODO( ): adjust for more joints
524  group_number = pt.group_number;
525  // FS100 requires valid velocity data
526  if (pt.velocities.empty())
527  ROS_ERROR_RETURN(false, "Validation failed: Missing velocity data for trajectory pt %lu", i);
528 
529  // FS100 requires trajectory start at current position
530  namespace IRC_utils = industrial_robot_client::utils;
531 
532  if (!IRC_utils::isWithinRange(cur_joint_pos_map_[group_number].name, cur_joint_pos_map_[group_number].position,
533  traj.joint_names, traj.points[0].groups[gr].positions,
534  start_pos_tol_))
535  {
536  ROS_ERROR_RETURN(false, "Validation failed: Trajectory doesn't start at current position.");
537  }
538  }
539  }
540 
541  if ((time_stamp - ros::Time::now()).toSec() > pos_stale_time_)
542  ROS_ERROR_RETURN(false, "Validation failed: Can't get current robot position.");
543 
544  return true;
545 }
546 
547 } // namespace joint_trajectory_streamer
548 } // namespace motoman
549 
Class encapsulated joint trajectory point data. The point data serves as a waypoint along a trajector...
void setSequence(industrial::shared_types::shared_int sequence)
std::vector< double > values
virtual bool send_to_robot(const std::vector< SimpleMessage > &messages)
Send trajectory to robot, using this node&#39;s robot-connection. Specific method must be implemented in ...
ros::ServiceServer disabler_
Service used to disable the robot controller. When disabled, all incoming goals are ignored...
void setTime(industrial::shared_types::shared_real time)
virtual bool create_message(int seq, const trajectory_msgs::JointTrajectoryPoint &pt, SimpleMessage *msg)
Create SimpleMessage for sending to the robot.
bool sleep() const
Wrapper class around Motoman-specific motion control commands.
Definition: motion_ctrl.h:51
void setVelocities(industrial::joint_data::JointData &velocities)
void setSequence(industrial::shared_types::shared_int sequence)
Sets joint trajectory point sequence number.
bool init(industrial::simple_message::SimpleMessage &msg)
Initializes message from a simple message.
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
#define ROS_WARN(...)
virtual bool toRequest(industrial::simple_message::SimpleMessage &msg)
static bool VectorToJointData(const std::vector< double > &vec, industrial::joint_data::JointData &joints)
bool disableRobotCB(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
Disable the robot. Response is true if the state was flipped or false if the state has not changed...
bool sendAndReceiveMsg(industrial::simple_message::SimpleMessage &send, industrial::simple_message::SimpleMessage &recv, bool verbose=false)
#define ROS_ERROR_RETURN(rtn,...)
void setNumGroups(industrial::shared_types::shared_int num_groups)
Sets num_groups Number of groups attached to the controller.
bool init(SmplMsgConnection *connection, int robot_id)
Definition: motion_ctrl.cpp:51
bool init(industrial::simple_message::SimpleMessage &msg)
bool send_to_robot(const std::vector< JointTrajPtMessage > &messages)
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool setJoint(industrial::shared_types::shared_int index, industrial::shared_types::shared_real value)
industrial::shared_types::shared_int getResult() const
Returns motion-control result code.
ROSCPP_DECL bool ok()
bool enableRobotCB(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
Enable the robot. Response is true if the state was flipped or false if the state has not changed...
static std::string getErrorString(const MotionReply &reply)
virtual bool create_message_ex(int seq, const motoman_msgs::DynamicJointPoint &point, SimpleMessage *msg)
#define ROS_WARN_STREAM(args)
Class encapsulated joint trajectory extended point message generation methods (either to or from a in...
virtual bool init(SmplMsgConnection *connection, const std::vector< std::string > &joint_names, const std::map< std::string, double > &velocity_limits=std::map< std::string, double >())
void setRobotID(industrial::shared_types::shared_int robot_id)
void setPositions(industrial::joint_data::JointData &positions)
bool init(int msgType, int commType, int replyCode, industrial::byte_array::ByteArray &data)
void setMultiJointTrajPtData(std::vector< industrial::joint_traj_pt_full::JointTrajPtFull > joint_trajectory_points)
Class encapsulated motoman motion reply message generation methods (either to or from a industrial::s...
industrial::byte_array::ByteArray & getData()
static Time now()
#define ROS_ERROR_STREAM(args)
ros::ServiceServer enabler_
Service used to enable the robot controller. When disabled, all incoming goals are ignored...
bool init(industrial::simple_message::SimpleMessage &msg)
Initializes message from a simple message.
virtual bool init(SmplMsgConnection *connection, const std::vector< std::string > &joint_names, const std::map< std::string, double > &velocity_limits=std::map< std::string, double >())
Class initializer.
#define ROS_ERROR(...)
void setAccelerations(industrial::joint_data::JointData &accelerations)
#define ROS_DEBUG(...)


motoman_driver
Author(s): Jeremy Zoss (Southwest Research Institute), Ted Miller (MotoROS) (Yaskawa Motoman), Eric Marcil (MotoROS) (Yaskawa Motoman)
autogenerated on Sat May 8 2021 02:27:43