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=1)
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()
00150 res.error_code=FollowJointTrajectoryResult.INVALID_JOINTS
00151 msg = 'Incoming trajectory joints do not match the joints of the controller'
00152 rospy.logerr(msg)
00153 rospy.logerr(' self.joint_names={%s}' % (set(self.joint_names)))
00154 rospy.logerr(' traj.joint_names={%s}' % (set(traj.joint_names)))
00155 self.action_server.set_aborted(result=res, text=msg)
00156 return
00157
00158
00159 if num_points == 0:
00160 msg = 'Incoming trajectory is empty'
00161 rospy.logerr(msg)
00162 self.action_server.set_aborted(text=msg)
00163 return
00164
00165
00166
00167 lookup = [traj.joint_names.index(joint) for joint in self.joint_names]
00168 durations = [0.0] * num_points
00169
00170
00171 durations[0] = traj.points[0].time_from_start.to_sec()
00172
00173 for i in range(1, num_points):
00174 durations[i] = (traj.points[i].time_from_start - traj.points[i - 1].time_from_start).to_sec()
00175
00176 if not traj.points[0].positions:
00177 res = FollowJointTrajectoryResult()
00178 res.error_code=FollowJointTrajectoryResult.INVALID_GOAL
00179 msg = 'First point of trajectory has no positions'
00180 rospy.logerr(msg)
00181 self.action_server.set_aborted(result=res, text=msg)
00182 return
00183
00184 trajectory = []
00185 time = rospy.Time.now() + rospy.Duration(0.01)
00186
00187 for i in range(num_points):
00188 seg = Segment(self.num_joints)
00189
00190 if traj.header.stamp == rospy.Time(0.0):
00191 seg.start_time = (time + traj.points[i].time_from_start).to_sec() - durations[i]
00192 else:
00193 seg.start_time = (traj.header.stamp + traj.points[i].time_from_start).to_sec() - durations[i]
00194
00195 seg.duration = durations[i]
00196
00197
00198 if traj.points[i].velocities and len(traj.points[i].velocities) != self.num_joints:
00199 res = FollowJointTrajectoryResult()
00200 res.error_code=FollowJointTrajectoryResult.INVALID_GOAL
00201 msg = 'Command point %d has %d elements for the velocities' % (i, len(traj.points[i].velocities))
00202 rospy.logerr(msg)
00203 self.action_server.set_aborted(result=res, text=msg)
00204 return
00205
00206 if len(traj.points[i].positions) != self.num_joints:
00207 res = FollowJointTrajectoryResult()
00208 res.error_code=FollowJointTrajectoryResult.INVALID_GOAL
00209 msg = 'Command point %d has %d elements for the positions' % (i, len(traj.points[i].positions))
00210 rospy.logerr(msg)
00211 self.action_server.set_aborted(result=res, text=msg)
00212 return
00213
00214 for j in range(self.num_joints):
00215 if traj.points[i].velocities:
00216 seg.velocities[j] = traj.points[i].velocities[lookup[j]]
00217 if traj.points[i].positions:
00218 seg.positions[j] = traj.points[i].positions[lookup[j]]
00219
00220 trajectory.append(seg)
00221
00222 rospy.loginfo('Trajectory start requested at %.3lf, waiting...', traj.header.stamp.to_sec())
00223 rate = rospy.Rate(self.update_rate)
00224
00225 while traj.header.stamp > time:
00226 time = rospy.Time.now()
00227 rate.sleep()
00228
00229 end_time = traj.header.stamp + rospy.Duration(sum(durations))
00230 seg_end_times = [rospy.Time.from_sec(trajectory[seg].start_time + durations[seg]) for seg in range(len(trajectory))]
00231
00232 rospy.loginfo('Trajectory start time is %.3lf, end time is %.3lf, total duration is %.3lf', time.to_sec(), end_time.to_sec(), sum(durations))
00233
00234 self.trajectory = trajectory
00235 traj_start_time = rospy.Time.now()
00236
00237 for seg in range(len(trajectory)):
00238 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()))
00239 rospy.logdebug('goal positions are: %s' % str(trajectory[seg].positions))
00240
00241
00242 if durations[seg] == 0:
00243 rospy.logdebug('skipping segment %d with duration of 0 seconds' % seg)
00244 continue
00245
00246 multi_packet = {}
00247
00248 for port, joints in self.port_to_joints.items():
00249 vals = []
00250
00251 for joint in joints:
00252 j = self.joint_names.index(joint)
00253
00254 start_position = self.joint_states[joint].current_pos
00255 if seg != 0: start_position = trajectory[seg - 1].positions[j]
00256
00257 desired_position = trajectory[seg].positions[j]
00258 desired_velocity = max(self.min_velocity, abs(desired_position - start_position) / durations[seg])
00259
00260 self.msg.desired.positions[j] = desired_position
00261 self.msg.desired.velocities[j] = desired_velocity
00262
00263
00264 if hasattr(self.joint_to_controller[joint], "master_id"):
00265 master_id = self.joint_to_controller[joint].master_id
00266 slave_id = self.joint_to_controller[joint].slave_id
00267 master_pos, slave_pos = self.joint_to_controller[joint].pos_rad_to_raw(desired_position)
00268 spd = self.joint_to_controller[joint].spd_rad_to_raw(desired_velocity)
00269 vals.append((master_id, master_pos, spd))
00270 vals.append((slave_id, slave_pos, spd))
00271 else:
00272 motor_id = self.joint_to_controller[joint].motor_id
00273 pos = self.joint_to_controller[joint].pos_rad_to_raw(desired_position)
00274 spd = self.joint_to_controller[joint].spd_rad_to_raw(desired_velocity)
00275 vals.append((motor_id, pos, spd))
00276
00277 multi_packet[port] = vals
00278
00279 for port, vals in multi_packet.items():
00280 self.port_to_io[port].set_multi_position_and_speed(vals)
00281
00282 while time < seg_end_times[seg]:
00283
00284
00285 if self.action_server.is_preempt_requested():
00286 msg = 'New trajectory received. Aborting old trajectory.'
00287 multi_packet = {}
00288
00289 for port, joints in self.port_to_joints.items():
00290 vals = []
00291
00292 for joint in joints:
00293 cur_pos = self.joint_states[joint].current_pos
00294
00295 motor_id = self.joint_to_controller[joint].motor_id
00296 pos = self.joint_to_controller[joint].pos_rad_to_raw(cur_pos)
00297
00298 vals.append((motor_id, pos))
00299
00300 multi_packet[port] = vals
00301
00302 for port, vals in multi_packet.items():
00303 self.port_to_io[port].set_multi_position(vals)
00304
00305 self.action_server.set_preempted(text=msg)
00306 rospy.logwarn(msg)
00307 return
00308
00309 rate.sleep()
00310 time = rospy.Time.now()
00311
00312
00313 for j, joint in enumerate(self.joint_names):
00314 if self.trajectory_constraints[j] > 0 and self.msg.error.positions[j] > self.trajectory_constraints[j]:
00315 res = FollowJointTrajectoryResult()
00316 res.error_code=FollowJointTrajectoryResult.PATH_TOLERANCE_VIOLATED
00317 msg = 'Unsatisfied position constraint for %s, trajectory point %d, %f is larger than %f' % \
00318 (joint, seg, self.msg.error.positions[j], self.trajectory_constraints[j])
00319 rospy.logwarn(msg)
00320 self.action_server.set_aborted(result=res, text=msg)
00321 return
00322
00323
00324 rospy.sleep(self.goal_time_constraint)
00325
00326 for i, j in enumerate(self.joint_names):
00327 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]))
00328
00329
00330 for (joint, pos_error, pos_constraint) in zip(self.joint_names, self.msg.error.positions, self.goal_constraints):
00331 if pos_constraint > 0 and abs(pos_error) > pos_constraint:
00332 res = FollowJointTrajectoryResult()
00333 res.error_code=FollowJointTrajectoryResult.GOAL_TOLERANCE_VIOLATED
00334 msg = 'Aborting because %s joint wound up outside the goal constraints, %f is larger than %f' % \
00335 (joint, pos_error, pos_constraint)
00336 rospy.logwarn(msg)
00337 self.action_server.set_aborted(result=res, text=msg)
00338 break
00339 else:
00340 msg = 'Trajectory execution successfully completed'
00341 rospy.loginfo(msg)
00342 res = FollowJointTrajectoryResult()
00343 res.error_code=FollowJointTrajectoryResult.SUCCESSFUL
00344 self.action_server.set_succeeded(result=res, text=msg)
00345
00346 def update_state(self):
00347 rate = rospy.Rate(self.state_update_rate)
00348 while self.running and not rospy.is_shutdown():
00349 self.msg.header.stamp = rospy.Time.now()
00350
00351
00352 for i, joint in enumerate(self.joint_names):
00353 state = self.joint_states[joint]
00354 self.msg.actual.positions[i] = state.current_pos
00355 self.msg.actual.velocities[i] = abs(state.velocity)
00356 self.msg.error.positions[i] = self.msg.actual.positions[i] - self.msg.desired.positions[i]
00357 self.msg.error.velocities[i] = self.msg.actual.velocities[i] - self.msg.desired.velocities[i]
00358
00359 self.state_pub.publish(self.msg)
00360 rate.sleep()