00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 from __future__ import division
00036
00037
00038 __author__ = 'Antons Rebguns'
00039 __copyright__ = 'Copyright (c) 2010-2011 Antons Rebguns'
00040
00041 __license__ = 'BSD'
00042 __maintainer__ = 'Antons Rebguns'
00043 __email__ = 'anton@email.arizona.edu'
00044
00045
00046 from threading import Thread
00047
00048 import rospy
00049 import actionlib
00050
00051 from std_msgs.msg import Float64
00052 from trajectory_msgs.msg import JointTrajectory
00053 from control_msgs.msg import FollowJointTrajectoryAction
00054 from control_msgs.msg import FollowJointTrajectoryFeedback
00055 from control_msgs.msg import FollowJointTrajectoryResult
00056
00057
00058 class Segment():
00059 def __init__(self, num_joints):
00060 self.start_time = 0.0
00061 self.duration = 0.0
00062 self.positions = [0.0] * num_joints
00063 self.velocities = [0.0] * num_joints
00064
00065 class JointTrajectoryActionController():
00066 def __init__(self, controller_namespace, controllers):
00067 self.update_rate = 1000
00068 self.state_update_rate = 50
00069 self.trajectory = []
00070
00071 self.controller_namespace = controller_namespace
00072 self.joint_names = [c.joint_name for c in controllers]
00073
00074 self.joint_to_controller = {}
00075 for c in controllers:
00076 self.joint_to_controller[c.joint_name] = c
00077
00078 self.port_to_joints = {}
00079 for c in controllers:
00080 if c.port_namespace not in self.port_to_joints: self.port_to_joints[c.port_namespace] = []
00081 self.port_to_joints[c.port_namespace].append(c.joint_name)
00082
00083 self.port_to_io = {}
00084 for c in controllers:
00085 if c.port_namespace in self.port_to_io: continue
00086 self.port_to_io[c.port_namespace] = c.dxl_io
00087
00088 self.joint_states = dict(zip(self.joint_names, [c.joint_state for c in controllers]))
00089 self.num_joints = len(self.joint_names)
00090 self.joint_to_idx = dict(zip(self.joint_names, range(self.num_joints)))
00091
00092 def initialize(self):
00093 ns = self.controller_namespace + '/joint_trajectory_action_node/constraints'
00094 self.goal_time_constraint = rospy.get_param(ns + '/goal_time', 0.0)
00095 self.stopped_velocity_tolerance = rospy.get_param(ns + '/stopped_velocity_tolerance', 0.01)
00096 self.goal_constraints = []
00097 self.trajectory_constraints = []
00098 self.min_velocity = rospy.get_param(self.controller_namespace + '/joint_trajectory_action_node/min_velocity', 0.1)
00099
00100 for joint in self.joint_names:
00101 self.goal_constraints.append(rospy.get_param(ns + '/' + joint + '/goal', -1.0))
00102 self.trajectory_constraints.append(rospy.get_param(ns + '/' + joint + '/trajectory', -1.0))
00103
00104
00105 self.msg = FollowJointTrajectoryFeedback()
00106 self.msg.joint_names = self.joint_names
00107 self.msg.desired.positions = [0.0] * self.num_joints
00108 self.msg.desired.velocities = [0.0] * self.num_joints
00109 self.msg.desired.accelerations = [0.0] * self.num_joints
00110 self.msg.actual.positions = [0.0] * self.num_joints
00111 self.msg.actual.velocities = [0.0] * self.num_joints
00112 self.msg.error.positions = [0.0] * self.num_joints
00113 self.msg.error.velocities = [0.0] * self.num_joints
00114
00115 return True
00116
00117
00118 def start(self):
00119 self.running = True
00120
00121 self.command_sub = rospy.Subscriber(self.controller_namespace + '/command', JointTrajectory, self.process_command)
00122 self.state_pub = rospy.Publisher(self.controller_namespace + '/state', FollowJointTrajectoryFeedback, queue_size=None)
00123 self.action_server = actionlib.SimpleActionServer(self.controller_namespace + '/follow_joint_trajectory',
00124 FollowJointTrajectoryAction,
00125 execute_cb=self.process_follow_trajectory,
00126 auto_start=False)
00127 self.action_server.start()
00128 Thread(target=self.update_state).start()
00129
00130 def stop(self):
00131 self.running = False
00132
00133 def process_command(self, msg):
00134 if self.action_server.is_active(): self.action_server.set_preempted()
00135
00136 while self.action_server.is_active():
00137 rospy.sleep(0.01)
00138
00139 self.process_trajectory(msg)
00140
00141 def process_follow_trajectory(self, goal):
00142 self.process_trajectory(goal.trajectory)
00143
00144 def process_trajectory(self, traj):
00145 num_points = len(traj.points)
00146
00147
00148 if set(self.joint_names) != set(traj.joint_names):
00149 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.INVALID_JOINTS)
00150 msg = 'Incoming trajectory joints do not match the joints of the controller'
00151 rospy.logerr(msg)
00152 rospy.logerr(' self.joint_names={}' % (set(self.joint_names)))
00153 rospy.logerr(' traj.joint_names={}' % (set(traj.joint_names)))
00154 self.action_server.set_aborted(result=res, text=msg)
00155 return
00156
00157
00158 if num_points == 0:
00159 msg = 'Incoming trajectory is empty'
00160 rospy.logerr(msg)
00161 self.action_server.set_aborted(text=msg)
00162 return
00163
00164
00165
00166 lookup = [traj.joint_names.index(joint) for joint in self.joint_names]
00167 durations = [0.0] * num_points
00168
00169
00170 durations[0] = traj.points[0].time_from_start.to_sec()
00171
00172 for i in range(1, num_points):
00173 durations[i] = (traj.points[i].time_from_start - traj.points[i - 1].time_from_start).to_sec()
00174
00175 if not traj.points[0].positions:
00176 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.INVALID_GOAL)
00177 msg = 'First point of trajectory has no positions'
00178 rospy.logerr(msg)
00179 self.action_server.set_aborted(result=res, text=msg)
00180 return
00181
00182 trajectory = []
00183 time = rospy.Time.now() + rospy.Duration(0.01)
00184
00185 for i in range(num_points):
00186 seg = Segment(self.num_joints)
00187
00188 if traj.header.stamp == rospy.Time(0.0):
00189 seg.start_time = (time + traj.points[i].time_from_start).to_sec() - durations[i]
00190 else:
00191 seg.start_time = (traj.header.stamp + traj.points[i].time_from_start).to_sec() - durations[i]
00192
00193 seg.duration = durations[i]
00194
00195
00196 if traj.points[i].velocities and len(traj.points[i].velocities) != self.num_joints:
00197 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.INVALID_GOAL)
00198 msg = 'Command point %d has %d elements for the velocities' % (i, len(traj.points[i].velocities))
00199 rospy.logerr(msg)
00200 self.action_server.set_aborted(result=res, text=msg)
00201 return
00202
00203 if len(traj.points[i].positions) != self.num_joints:
00204 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.INVALID_GOAL)
00205 msg = 'Command point %d has %d elements for the positions' % (i, len(traj.points[i].positions))
00206 rospy.logerr(msg)
00207 self.action_server.set_aborted(result=res, text=msg)
00208 return
00209
00210 for j in range(self.num_joints):
00211 if traj.points[i].velocities:
00212 seg.velocities[j] = traj.points[i].velocities[lookup[j]]
00213 if traj.points[i].positions:
00214 seg.positions[j] = traj.points[i].positions[lookup[j]]
00215
00216 trajectory.append(seg)
00217
00218 rospy.loginfo('Trajectory start requested at %.3lf, waiting...', traj.header.stamp.to_sec())
00219 rate = rospy.Rate(self.update_rate)
00220
00221 while traj.header.stamp > time:
00222 time = rospy.Time.now()
00223 rate.sleep()
00224
00225 end_time = traj.header.stamp + rospy.Duration(sum(durations))
00226 seg_end_times = [rospy.Time.from_sec(trajectory[seg].start_time + durations[seg]) for seg in range(len(trajectory))]
00227
00228 rospy.loginfo('Trajectory start time is %.3lf, end time is %.3lf, total duration is %.3lf', time.to_sec(), end_time.to_sec(), sum(durations))
00229
00230 self.trajectory = trajectory
00231 traj_start_time = rospy.Time.now()
00232
00233 for seg in range(len(trajectory)):
00234 rospy.logdebug('current segment is %d time left %f cur time %f' % (seg, durations[seg] - (time.to_sec() - trajectory[seg].start_time), time.to_sec()))
00235 rospy.logdebug('goal positions are: %s' % str(trajectory[seg].positions))
00236
00237
00238 if durations[seg] == 0:
00239 rospy.logdebug('skipping segment %d with duration of 0 seconds' % seg)
00240 continue
00241
00242 multi_packet = {}
00243
00244 for port, joints in self.port_to_joints.items():
00245 vals = []
00246
00247 for joint in joints:
00248 j = self.joint_names.index(joint)
00249
00250 start_position = self.joint_states[joint].current_pos
00251 if seg != 0: start_position = trajectory[seg - 1].positions[j]
00252
00253 desired_position = trajectory[seg].positions[j]
00254 desired_velocity = max(self.min_velocity, abs(desired_position - start_position) / durations[seg])
00255
00256 self.msg.desired.positions[j] = desired_position
00257 self.msg.desired.velocities[j] = desired_velocity
00258
00259
00260 if hasattr(self.joint_to_controller[joint], "master_id"):
00261 master_id = self.joint_to_controller[joint].master_id
00262 slave_id = self.joint_to_controller[joint].slave_id
00263 master_pos, slave_pos = self.joint_to_controller[joint].pos_rad_to_raw(desired_position)
00264 spd = self.joint_to_controller[joint].spd_rad_to_raw(desired_velocity)
00265 vals.append((master_id, master_pos, spd))
00266 vals.append((slave_id, slave_pos, spd))
00267 else:
00268 motor_id = self.joint_to_controller[joint].motor_id
00269 pos = self.joint_to_controller[joint].pos_rad_to_raw(desired_position)
00270 spd = self.joint_to_controller[joint].spd_rad_to_raw(desired_velocity)
00271 vals.append((motor_id, pos, spd))
00272
00273 multi_packet[port] = vals
00274
00275 for port, vals in multi_packet.items():
00276 self.port_to_io[port].set_multi_position_and_speed(vals)
00277
00278 while time < seg_end_times[seg]:
00279
00280
00281 if self.action_server.is_preempt_requested():
00282 msg = 'New trajectory received. Aborting old trajectory.'
00283 multi_packet = {}
00284
00285 for port, joints in self.port_to_joints.items():
00286 vals = []
00287
00288 for joint in joints:
00289 cur_pos = self.joint_states[joint].current_pos
00290
00291 motor_id = self.joint_to_controller[joint].motor_id
00292 pos = self.joint_to_controller[joint].pos_rad_to_raw(cur_pos)
00293
00294 vals.append((motor_id, pos))
00295
00296 multi_packet[port] = vals
00297
00298 for port, vals in multi_packet.items():
00299 self.port_to_io[port].set_multi_position(vals)
00300
00301 self.action_server.set_preempted(text=msg)
00302 rospy.logwarn(msg)
00303 return
00304
00305 rate.sleep()
00306 time = rospy.Time.now()
00307
00308
00309 for j, joint in enumerate(self.joint_names):
00310 if self.trajectory_constraints[j] > 0 and self.msg.error.positions[j] > self.trajectory_constraints[j]:
00311 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.PATH_TOLERANCE_VIOLATED)
00312 msg = 'Unsatisfied position constraint for %s, trajectory point %d, %f is larger than %f' % \
00313 (joint, seg, self.msg.error.positions[j], self.trajectory_constraints[j])
00314 rospy.logwarn(msg)
00315 self.action_server.set_aborted(result=res, text=msg)
00316 return
00317
00318
00319 rospy.sleep(self.goal_time_constraint)
00320
00321 for i, j in enumerate(self.joint_names):
00322 rospy.logdebug('desired pos was %f, actual pos is %f, error is %f' % (trajectory[-1].positions[i], self.joint_states[j].current_pos, self.joint_states[j].current_pos - trajectory[-1].positions[i]))
00323
00324
00325 for (joint, pos_error, pos_constraint) in zip(self.joint_names, self.msg.error.positions, self.goal_constraints):
00326 if pos_constraint > 0 and abs(pos_error) > pos_constraint:
00327 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.GOAL_TOLERANCE_VIOLATED)
00328 msg = 'Aborting because %s joint wound up outside the goal constraints, %f is larger than %f' % \
00329 (joint, pos_error, pos_constraint)
00330 rospy.logwarn(msg)
00331 self.action_server.set_aborted(result=res, text=msg)
00332 break
00333 else:
00334 res = FollowJointTrajectoryResult(FollowJointTrajectoryResult.SUCCESSFUL)
00335 msg = 'Trajectory execution successfully completed'
00336 rospy.loginfo(msg)
00337 self.action_server.set_succeeded(result=res, text=msg)
00338
00339 def update_state(self):
00340 rate = rospy.Rate(self.state_update_rate)
00341 while self.running and not rospy.is_shutdown():
00342 self.msg.header.stamp = rospy.Time.now()
00343
00344
00345 for i, joint in enumerate(self.joint_names):
00346 state = self.joint_states[joint]
00347 self.msg.actual.positions[i] = state.current_pos
00348 self.msg.actual.velocities[i] = abs(state.velocity)
00349 self.msg.error.positions[i] = self.msg.actual.positions[i] - self.msg.desired.positions[i]
00350 self.msg.error.velocities[i] = self.msg.actual.velocities[i] - self.msg.desired.velocities[i]
00351
00352 self.state_pub.publish(self.msg)
00353 rate.sleep()