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 Baxter RSDK Joint Trajectory Example: file playback
00031 """
00032
00033 import argparse
00034 import operator
00035 import sys
00036 import threading
00037
00038 from bisect import bisect
00039 from copy import copy
00040 from os import path
00041
00042 import rospy
00043
00044 import actionlib
00045
00046 from control_msgs.msg import (
00047 FollowJointTrajectoryAction,
00048 FollowJointTrajectoryGoal,
00049 )
00050 from trajectory_msgs.msg import (
00051 JointTrajectoryPoint,
00052 )
00053
00054 import baxter_interface
00055
00056 from baxter_interface import CHECK_VERSION
00057
00058
00059 class Trajectory(object):
00060 def __init__(self):
00061
00062 self._left_client = actionlib.SimpleActionClient(
00063 'robot/limb/left/follow_joint_trajectory',
00064 FollowJointTrajectoryAction,
00065 )
00066 self._right_client = actionlib.SimpleActionClient(
00067 'robot/limb/right/follow_joint_trajectory',
00068 FollowJointTrajectoryAction,
00069 )
00070
00071
00072 l_server_up = self._left_client.wait_for_server(rospy.Duration(10.0))
00073 r_server_up = self._right_client.wait_for_server(rospy.Duration(10.0))
00074 if not l_server_up or not r_server_up:
00075 msg = ("Action server not available."
00076 " Verify action server availability.")
00077 rospy.logerr(msg)
00078 rospy.signal_shutdown(msg)
00079 sys.exit(1)
00080
00081 self._l_goal = FollowJointTrajectoryGoal()
00082 self._r_goal = FollowJointTrajectoryGoal()
00083
00084
00085 self._l_arm = baxter_interface.Limb('left')
00086 self._r_arm = baxter_interface.Limb('right')
00087
00088
00089 self._l_gripper = baxter_interface.Gripper('left', CHECK_VERSION)
00090 self._r_gripper = baxter_interface.Gripper('right', CHECK_VERSION)
00091
00092
00093 self._arm_trajectory_started = False
00094
00095 self._lock = threading.RLock()
00096
00097
00098 if self._l_gripper.error():
00099 self._l_gripper.reset()
00100 if self._r_gripper.error():
00101 self._r_gripper.reset()
00102 if (not self._l_gripper.calibrated() and
00103 self._l_gripper.type() != 'custom'):
00104 self._l_gripper.calibrate()
00105 if (not self._r_gripper.calibrated() and
00106 self._r_gripper.type() != 'custom'):
00107 self._r_gripper.calibrate()
00108
00109
00110 self._l_grip = FollowJointTrajectoryGoal()
00111 self._r_grip = FollowJointTrajectoryGoal()
00112
00113
00114 self._slow_move_offset = 0.0
00115 self._trajectory_start_offset = rospy.Duration(0.0)
00116 self._trajectory_actual_offset = rospy.Duration(0.0)
00117
00118
00119 self._param_ns = '/rsdk_joint_trajectory_action_server/'
00120
00121
00122 self._gripper_rate = 20.0
00123
00124 def _execute_gripper_commands(self):
00125 start_time = rospy.get_time() - self._trajectory_actual_offset.to_sec()
00126 r_cmd = self._r_grip.trajectory.points
00127 l_cmd = self._l_grip.trajectory.points
00128 pnt_times = [pnt.time_from_start.to_sec() for pnt in r_cmd]
00129 end_time = pnt_times[-1]
00130 rate = rospy.Rate(self._gripper_rate)
00131 now_from_start = rospy.get_time() - start_time
00132 while(now_from_start < end_time + (1.0 / self._gripper_rate) and
00133 not rospy.is_shutdown()):
00134 idx = bisect(pnt_times, now_from_start) - 1
00135 if self._r_gripper.type() != 'custom':
00136 self._r_gripper.command_position(r_cmd[idx].positions[0])
00137 if self._l_gripper.type() != 'custom':
00138 self._l_gripper.command_position(l_cmd[idx].positions[0])
00139 rate.sleep()
00140 now_from_start = rospy.get_time() - start_time
00141
00142 def _clean_line(self, line, joint_names):
00143 """
00144 Cleans a single line of recorded joint positions
00145
00146 @param line: the line described in a list to process
00147 @param joint_names: joint name keys
00148
00149 @return command: returns dictionary {joint: value} of valid commands
00150 @return line: returns list of current line values stripped of commas
00151 """
00152 def try_float(x):
00153 try:
00154 return float(x)
00155 except ValueError:
00156 return None
00157
00158 line = [try_float(x) for x in line.rstrip().split(',')]
00159
00160 combined = zip(joint_names[1:], line[1:])
00161
00162 cleaned = [x for x in combined if x[1] is not None]
00163
00164 command = dict(cleaned)
00165 return (command, line,)
00166
00167 def _add_point(self, positions, side, time):
00168 """
00169 Appends trajectory with new point
00170
00171 @param positions: joint positions
00172 @param side: limb to command point
00173 @param time: time from start for point in seconds
00174 """
00175
00176 point = JointTrajectoryPoint()
00177 point.positions = copy(positions)
00178 point.time_from_start = rospy.Duration(time)
00179 if side == 'left':
00180 self._l_goal.trajectory.points.append(point)
00181 elif side == 'right':
00182 self._r_goal.trajectory.points.append(point)
00183 elif side == 'left_gripper':
00184 self._l_grip.trajectory.points.append(point)
00185 elif side == 'right_gripper':
00186 self._r_grip.trajectory.points.append(point)
00187
00188 def parse_file(self, filename):
00189 """
00190 Parses input file into FollowJointTrajectoryGoal format
00191
00192 @param filename: input filename
00193 """
00194
00195 with open(filename, 'r') as f:
00196 lines = f.readlines()
00197
00198 joint_names = lines[0].rstrip().split(',')
00199
00200 for name in joint_names:
00201 if 'left' == name[:-3]:
00202 self._l_goal.trajectory.joint_names.append(name)
00203 elif 'right' == name[:-3]:
00204 self._r_goal.trajectory.joint_names.append(name)
00205
00206 def find_start_offset(pos):
00207
00208 cur = []
00209 cmd = []
00210 dflt_vel = []
00211 vel_param = self._param_ns + "%s_default_velocity"
00212
00213
00214 for name in joint_names:
00215 if 'left' == name[:-3]:
00216 cmd.append(pos[name])
00217 cur.append(self._l_arm.joint_angle(name))
00218 prm = rospy.get_param(vel_param % name, 0.25)
00219 dflt_vel.append(prm)
00220 elif 'right' == name[:-3]:
00221 cmd.append(pos[name])
00222 cur.append(self._r_arm.joint_angle(name))
00223 prm = rospy.get_param(vel_param % name, 0.25)
00224 dflt_vel.append(prm)
00225 diffs = map(operator.sub, cmd, cur)
00226 diffs = map(operator.abs, diffs)
00227
00228 offset = max(map(operator.div, diffs, dflt_vel))
00229 return offset
00230
00231 for idx, values in enumerate(lines[1:]):
00232
00233 cmd, values = self._clean_line(values, joint_names)
00234
00235 if idx == 0:
00236
00237
00238
00239
00240 cur_cmd = [self._l_arm.joint_angle(jnt) for jnt in self._l_goal.trajectory.joint_names]
00241 self._add_point(cur_cmd, 'left', 0.0)
00242 cur_cmd = [self._r_arm.joint_angle(jnt) for jnt in self._r_goal.trajectory.joint_names]
00243 self._add_point(cur_cmd, 'right', 0.0)
00244 start_offset = find_start_offset(cmd)
00245
00246
00247 self._slow_move_offset = start_offset
00248 self._trajectory_start_offset = rospy.Duration(start_offset + values[0])
00249
00250 cur_cmd = [cmd[jnt] for jnt in self._l_goal.trajectory.joint_names]
00251 self._add_point(cur_cmd, 'left', values[0] + start_offset)
00252 cur_cmd = [cmd[jnt] for jnt in self._r_goal.trajectory.joint_names]
00253 self._add_point(cur_cmd, 'right', values[0] + start_offset)
00254 cur_cmd = [cmd['left_gripper']]
00255 self._add_point(cur_cmd, 'left_gripper', values[0] + start_offset)
00256 cur_cmd = [cmd['right_gripper']]
00257 self._add_point(cur_cmd, 'right_gripper', values[0] + start_offset)
00258
00259 def _feedback(self, data):
00260
00261
00262 if (not self._get_trajectory_flag() and
00263 data.actual.time_from_start >= self._trajectory_start_offset):
00264 self._set_trajectory_flag(value=True)
00265 self._trajectory_actual_offset = data.actual.time_from_start
00266
00267 def _set_trajectory_flag(self, value=False):
00268 with self._lock:
00269
00270 self._arm_trajectory_started = value
00271
00272 def _get_trajectory_flag(self):
00273 temp_flag = False
00274 with self._lock:
00275
00276 temp_flag = self._arm_trajectory_started
00277 return temp_flag
00278
00279 def start(self):
00280 """
00281 Sends FollowJointTrajectoryAction request
00282 """
00283 self._left_client.send_goal(self._l_goal, feedback_cb=self._feedback)
00284 self._right_client.send_goal(self._r_goal, feedback_cb=self._feedback)
00285
00286 while not rospy.is_shutdown() and not self._get_trajectory_flag():
00287 rospy.sleep(0.05)
00288 self._execute_gripper_commands()
00289
00290 def stop(self):
00291 """
00292 Preempts trajectory execution by sending cancel goals
00293 """
00294 if (self._left_client.gh is not None and
00295 self._left_client.get_state() == actionlib.GoalStatus.ACTIVE):
00296 self._left_client.cancel_goal()
00297
00298 if (self._right_client.gh is not None and
00299 self._right_client.get_state() == actionlib.GoalStatus.ACTIVE):
00300 self._right_client.cancel_goal()
00301
00302
00303 rospy.sleep(0.1)
00304
00305 def wait(self):
00306 """
00307 Waits for and verifies trajectory execution result
00308 """
00309
00310
00311 last_time = self._r_goal.trajectory.points[-1].time_from_start.to_sec()
00312 time_buffer = rospy.get_param(self._param_ns + 'goal_time', 0.0) + 1.5
00313 timeout = rospy.Duration(self._slow_move_offset +
00314 last_time +
00315 time_buffer)
00316
00317 l_finish = self._left_client.wait_for_result(timeout)
00318 r_finish = self._right_client.wait_for_result(timeout)
00319 l_result = (self._left_client.get_result().error_code == 0)
00320 r_result = (self._right_client.get_result().error_code == 0)
00321
00322
00323 if all([l_finish, r_finish, l_result, r_result]):
00324 return True
00325 else:
00326 msg = ("Trajectory action failed or did not finish before "
00327 "timeout/interrupt.")
00328 rospy.logwarn(msg)
00329 return False
00330
00331
00332 def main():
00333 """RSDK Joint Trajectory Example: File Playback
00334
00335 Plays back joint positions honoring timestamps recorded
00336 via the joint_recorder example.
00337
00338 Run the joint_recorder.py example first to create a recording
00339 file for use with this example. Then make sure to start the
00340 joint_trajectory_action_server before running this example.
00341
00342 This example will use the joint trajectory action server
00343 with velocity control to follow the positions and times of
00344 the recorded motion, accurately replicating movement speed
00345 necessary to hit each trajectory point on time.
00346 """
00347 epilog = """
00348 Related examples:
00349 joint_recorder.py; joint_position_file_playback.py.
00350 """
00351 arg_fmt = argparse.RawDescriptionHelpFormatter
00352 parser = argparse.ArgumentParser(formatter_class=arg_fmt,
00353 description=main.__doc__,
00354 epilog=epilog)
00355 parser.add_argument(
00356 '-f', '--file', metavar='PATH', required=True,
00357 help='path to input file'
00358 )
00359 parser.add_argument(
00360 '-l', '--loops', type=int, default=1,
00361 help='number of playback loops. 0=infinite.'
00362 )
00363
00364 args = parser.parse_args(rospy.myargv()[1:])
00365
00366 print("Initializing node... ")
00367 rospy.init_node("rsdk_joint_trajectory_file_playback")
00368 print("Getting robot state... ")
00369 rs = baxter_interface.RobotEnable(CHECK_VERSION)
00370 print("Enabling robot... ")
00371 rs.enable()
00372 print("Running. Ctrl-c to quit")
00373
00374 traj = Trajectory()
00375 traj.parse_file(path.expanduser(args.file))
00376
00377 rospy.on_shutdown(traj.stop)
00378 result = True
00379 loop_cnt = 1
00380 loopstr = str(args.loops)
00381 if args.loops == 0:
00382 args.loops = float('inf')
00383 loopstr = "forever"
00384 while (result == True and loop_cnt <= args.loops
00385 and not rospy.is_shutdown()):
00386 print("Playback loop %d of %s" % (loop_cnt, loopstr,))
00387 traj.start()
00388 result = traj.wait()
00389 loop_cnt = loop_cnt + 1
00390 print("Exiting - File Playback Complete")
00391
00392 if __name__ == "__main__":
00393 main()