traj_playback.py
Go to the documentation of this file.
00001 #! /usr/bin/python
00002 
00003 import numpy as np
00004 import cPickle as pickle
00005 from threading import Lock
00006 import copy
00007 
00008 import roslib
00009 roslib.load_manifest("hrl_pr2_traj_playback")
00010 import rospy
00011 from std_msgs.msg import String
00012 from std_srvs.srv import Empty, EmptyResponse
00013 import roslaunch.substitution_args
00014 import actionlib
00015 
00016 from hrl_pr2_arms.pr2_arm_joint_traj import create_ep_arm, PR2ArmJointTraj
00017 from hrl_pr2_arms.pr2_controller_switcher import ControllerSwitcher
00018 from msg import TrajectoryPlayAction, TrajectoryPlayGoal
00019 
00020 RATE = 20
00021 JOINT_TOLERANCES = [0.03, 0.1, 0.1, 0.1, 0.17, 0.15, 0.12]
00022 JOINT_VELOCITY_WEIGHT = [3.0, 1.7, 1.7, 1.0, 1.0, 1.0, 0.5]
00023 
00024 CTRL_NAME_LOW = '%s_joint_controller_low'
00025 PARAM_FILE_LOW = '$(find hrl_pr2_arms)/params/joint_traj_params_electric_low.yaml'
00026 CTRL_NAME_NONE = '%s_joint_controller_none'
00027 PARAM_FILE_NONE = '$(find hrl_pr2_arms)/params/joint_traj_params_electric_none.yaml'
00028 
00029 ##
00030 # Allows one to move the arm through a set of joint angles safely and controller
00031 # agnostic.  
00032 class TrajPlayback(object):
00033     ##
00034     # @param arm_char 'r' or 'l' to choose which arm to load
00035     # @param ctrl_name name of the controller to load from the param_file 
00036     # @param param_file Location of the parameter file which specifies the controller
00037     #        (e.g. "$(find hrl_pr2_arms)/params/joint_traj_params_electric_low.yaml")
00038     def __init__(self, arm_char, ctrl_name="%s_arm_controller", param_file=None):
00039         self.arm_char = arm_char
00040         self.ctrl_name = ctrl_name
00041         self.param_file = param_file
00042         self.cur_joint_traj = None
00043         self.running = False
00044         self.is_paused = False
00045         self.ctrl_switcher = ControllerSwitcher()
00046         rospy.loginfo('[trajectory_playback] Started Traj Playback for %s arm with %s controller.' %(self.arm_char, self.ctrl_name))
00047 
00048     ##
00049     # Switches controllers and returns an instance of the arm to control
00050     def load_arm(self):
00051         self.ctrl_switcher.carefree_switch(self.arm_char, self.ctrl_name, 
00052                                            self.param_file, reset=False)
00053         return create_ep_arm(self.arm_char, PR2ArmJointTraj, 
00054                              controller_name=self.ctrl_name, timeout=8)
00055 
00056     ##
00057     # Plays back the specified trajectory.  The arm must currently be at the first
00058     # joint configuration specified in the joint trajectory within a certain degree of
00059     # tolerance.
00060     # @param joint_trajectory List of lists of length 7 representing joint angles to move through.
00061     # @param rate Frequency with which to iterate through the list.
00062     # @param blocking If True, the function will wait until done, if False, it will return
00063     #                 immediately
00064     def execute(self, joint_trajectory, rate, blocking=True):
00065         if len(joint_trajectory) == 0:
00066             return True
00067         if self.running:
00068             rospy.logerr("[arm_pose_move_controller] Trajectory already in motion.")
00069             return False
00070         self.cur_arm = self.load_arm()
00071         if not self.can_exec_traj(joint_trajectory):
00072             rospy.logwarn("[arm_pose_move_controller] Arm not at trajectory start.")
00073             return False
00074         def exec_traj(te):
00075             self.cur_idx = 0
00076             self.stop_traj = False
00077             self.is_paused = False
00078             self.running = True
00079             rospy.loginfo("[arm_pose_move_controller] Starting trajectory.")
00080             r = rospy.Rate(rate)
00081             while (not rospy.is_shutdown() and not self.stop_traj and 
00082                    self.cur_idx < len(joint_trajectory)):
00083                 self.cur_arm.set_ep(joint_trajectory[self.cur_idx], 1./rate)
00084                 self.cur_idx += 1
00085                 r.sleep()
00086             self.cur_arm.set_ep(self.cur_arm.get_ep(), 0.3)
00087             if self.cur_idx < len(joint_trajectory):
00088                 self.cur_joint_traj = joint_trajectory[self.cur_idx:]
00089                 successful = False
00090             else:
00091                 self.cur_joint_traj = None
00092                 self.is_paused = False
00093                 successful = True
00094             self.last_rate = rate
00095             self.running = False
00096             return successful
00097         if blocking:
00098             return exec_traj(None)
00099         else:
00100             self.exec_traj_timer = rospy.Timer(rospy.Duration(0.1), exec_traj, oneshot=True)
00101         return True
00102 
00103     ##
00104     # Executes a linearly interpolated trajectory from the current joint angles to the
00105     # q_goal angles.
00106     # @param q_goal List of length 7 representing the desired end configuration.
00107     # @param rate Rate with which to execute trajectory.
00108     # @param velocity Speed (~rad/s) to move based on a heursitic which weighs relative
00109     #                 joint speeds.  The elbow will not move quicker than the velocity
00110     #                 in rad/s.
00111     # @param blocking If True, the function will wait until done, if False, it will return
00112     #                 immediately
00113     def move_to_angles(self, q_goal, rate=RATE, velocity=0.1, blocking=True):
00114         traj = self.get_angle_traj(q_goal, rate, velocity)
00115         if len(traj) == 0:
00116             return True
00117         return self.execute(traj, rate, blocking)
00118 
00119     def get_angle_traj(self, q_goal, rate=RATE, velocity=0.1):
00120         self.cur_arm = self.load_arm()
00121         q_cur = self.cur_arm.get_ep()
00122         diff = self.cur_arm.difference_joints(q_goal, q_cur)
00123         max_ang = np.max(np.fabs(diff) * JOINT_VELOCITY_WEIGHT)
00124         time_traj = max_ang / velocity
00125         steps = np.round(rate * time_traj)
00126         if steps == 0:
00127             return []
00128         t_vals = np.linspace(0., 1., steps)
00129         return [q_cur + diff * t for t in t_vals]
00130 
00131     ##
00132     # Determines whether or not the arm can execute the trajectory by checking the first
00133     # joint configuration and seeing whether or not it is within joint tolerances.
00134     # @param joint_trajectory List of lists of length 7 representing joint angles to move through.
00135     # @return True if the arm is at the beginning, False otherwise.
00136     def can_exec_traj(self, joint_trajectory):
00137         if len(joint_trajectory) == 0:
00138             return True
00139         q_cur = self.cur_arm.get_ep()
00140         q_init = joint_trajectory[0]
00141         diff = self.cur_arm.difference_joints(q_cur, q_init)
00142         return np.all(np.fabs(diff) < JOINT_TOLERANCES)
00143 
00144     def is_moving(self):
00145         return self.running
00146 
00147     ##
00148     # Pauses the movement of the trajectory but doesn't reset its position in the array.
00149     def pause_moving(self):
00150         if self.is_moving():
00151             self.stop_traj = True
00152             while not rospy.is_shutdown() and self.running:
00153                 rospy.sleep(0.01)
00154             self.is_paused = True
00155             return True
00156         return False
00157 
00158     ##
00159     # Restarts the currently running movement after being paused.
00160     def restart_moving(self, blocking=True):
00161         if not self.is_paused or self.cur_joint_traj is None:
00162             return False
00163         self.execute(self.cur_joint_traj, self.last_rate, blocking=blocking)
00164         return True
00165 
00166     ##
00167     # Stops the movement of the trajectory and resets the trajectory so it cannot restart.
00168     def stop_moving(self):
00169         self.stop_traj = True
00170         self.is_paused = False
00171         self.cur_joint_traj = None
00172         while not rospy.is_shutdown() and self.running:
00173             rospy.sleep(0.01)
00174 
00175     def preempt(self):
00176         self.stop_moving()
00177 
00178 def load_arm_file(filename):
00179     try:
00180         directory = rospy.get_param("~traj_directory", "")
00181         if directory[-1] != '/':
00182             directory = directory + "/"
00183         f = file(roslaunch.substitution_args.resolve_args(directory + filename), "r")
00184         traj, arm_char, rate = pickle.load(f)
00185         if arm_char not in ['r', 'l']:
00186             raise Exception("arm_char not r or l")
00187         return traj, arm_char, rate
00188     except Exception as e:
00189         print "Cannot open file."
00190         print "Error:", e
00191         return None, None, None
00192 
00193 def move_to_setup_from_file(filename, ctrl_name=CTRL_NAME_LOW, param_file=PARAM_FILE_LOW, 
00194                             velocity=0.1, rate=RATE, reverse=False, blocking=True):
00195     traj, arm_char, rate = load_arm_file(filename)
00196     if traj is None:
00197         return None
00198     if not reverse:
00199         q = traj[0]
00200     else:
00201         q = traj[-1]
00202     traj_ctrl = TrajPlayback(arm_char, ctrl_name, param_file)
00203     traj_ctrl.load_arm()
00204     rospy.sleep(0.1)
00205     traj_ctrl.move_to_angles(q, velocity=velocity, 
00206                              rate=rate, blocking=blocking)
00207 
00208 def can_exec_traj_from_file(filename, reverse=False):
00209     traj, arm_char, rate = load_arm_file(filename)
00210     if traj is None:
00211         return None
00212     traj_ctrl = TrajPlayback(arm_char)
00213     traj_ctrl.load_arm()
00214     rospy.sleep(0.1)
00215     return traj_ctrl.can_exec_traj(traj)
00216 
00217 def exec_traj_from_file(filename, ctrl_name=CTRL_NAME_LOW, param_file=PARAM_FILE_LOW, 
00218                         reverse=False, rate_mult=0.8, blocking=True):
00219     traj, arm_char, rate = load_arm_file(filename)
00220     if traj is None:
00221         return
00222     if reverse:
00223         traj.reverse()
00224     traj_ctrl = TrajPlayback(arm_char, ctrl_name, param_file)
00225     traj_ctrl.load_arm()
00226     rospy.sleep(0.1)
00227     return traj_ctrl.execute(traj, rate * rate_mult, blocking)
00228 
00229 class TrajectorySaver(object):
00230     def __init__(self, rate):
00231         self.rate = rate
00232 
00233     def record_trajectory(self, arm_char, blocking=True):
00234         self.traj = []
00235         self.stop_recording = False
00236         self.is_recording = True
00237         self.cur_arm = create_ep_arm(arm_char, timeout=5)
00238         def rec_traj(te):
00239             rospy.loginfo("[arm_pose_move_controller] Recording trajectory.")
00240             r = rospy.Rate(self.rate)
00241             while not rospy.is_shutdown() and not self.stop_recording:
00242                 q = self.cur_arm.get_joint_angles()
00243                 self.traj.append(q)
00244                 r.sleep()
00245             rospy.loginfo("[arm_pose_move_controller] Stopped recording trajectory.")
00246             self.is_recording = False
00247         if blocking:
00248             rec_traj(None)
00249         else:
00250             self.rec_traj_timer = rospy.Timer(rospy.Duration(0.1), rec_traj, oneshot=True)
00251 
00252     def stop_record(self, save_file):
00253         self.stop_recording = True
00254         while not rospy.is_shutdown() and self.is_recording:
00255             rospy.sleep(0.1)
00256         f = file(save_file, "w")
00257         pickle.dump((self.traj, self.cur_arm.arm_side, self.rate), f)
00258         f.close()
00259 
00260 class TrajectoryServer(object):
00261     def __init__(self, arm_char, as_name, ctrl_name, param_file):
00262         self.traj_ctrl = TrajPlayback(arm_char, ctrl_name, param_file)
00263         self.arm_dict = {'r' : "right", 'l' : "left"}
00264 
00265         def pause_cb(req):
00266             if not self.traj_ctrl.is_paused:
00267                 if self.traj_ctrl.pause_moving():
00268                     self.publish_feedback("Trajectory playback on the %s arm paused." % 
00269                                           self.arm_dict[self.traj_ctrl.arm_char])
00270             else:
00271                 self.traj_ctrl.restart_moving(blocking=False)
00272                 self.publish_feedback("Trajectory playback on the %s arm restarted." % 
00273                                       self.arm_dict[self.traj_ctrl.arm_char])
00274             return EmptyResponse()
00275         self.traj_pause_srv = rospy.Service(as_name + "_pause", Empty, pause_cb)
00276 
00277         def stop_cb(req):
00278             self.publish_feedback("Trajectory playback on the %s arm stopping." % 
00279                                   self.arm_dict[self.traj_ctrl.arm_char])
00280             self.traj_ctrl.stop_moving()
00281             return EmptyResponse()
00282         self.traj_stop_srv = rospy.Service(as_name + "_stop", Empty, stop_cb)
00283         self.feedback_pub = rospy.Publisher(as_name + "/feedback", String)
00284 
00285         self.traj_srv = actionlib.SimpleActionServer(as_name, TrajectoryPlayAction, 
00286                                                      self.traj_play_cb, False)
00287         self.traj_srv.register_preempt_callback(self.traj_cancel_cb)
00288         self.traj_srv.start()
00289         self.last_goal = None
00290 
00291     def same_goal_as_last(self, new_goal):
00292         if self.last_goal is None:
00293             self.last_goal = copy.copy(new_goal)
00294             return False
00295         ret_val = (new_goal.filepath == self.last_goal.filepath and
00296                    new_goal.reverse == self.last_goal.reverse and
00297                    new_goal.mode == self.last_goal.mode)
00298         self.last_goal = copy.copy(new_goal)
00299         return ret_val
00300 
00301     def publish_feedback(self, msg):
00302         rospy.loginfo("[arm_pose_move_controller] %s" % msg)
00303         self.feedback_pub.publish(msg)
00304 
00305     def traj_play_cb(self, goal):
00306         traj, arm_char, rate = load_arm_file(goal.filepath)
00307         if traj is None:
00308             self.traj_srv.set_aborted(text="Failed to open file.")
00309             return
00310         if arm_char != self.traj_ctrl.arm_char:
00311             self.traj_srv.set_aborted(text="File contains wrong arm.")
00312             return
00313         self.publish_feedback("Trajectory playback on the %s arm starting." % 
00314                               self.arm_dict[self.traj_ctrl.arm_char])
00315         if goal.reverse:
00316             traj.reverse()
00317         if self.same_goal_as_last(goal) and self.traj_ctrl.is_paused:
00318             self.publish_feedback("Trajectory playback on the %s arm restarted." % 
00319                                   self.arm_dict[self.traj_ctrl.arm_char])
00320             self.traj_ctrl.restart_moving(blocking=True)
00321         else:
00322             if goal.mode == goal.MOVE_SETUP or goal.mode == goal.SETUP_AND_TRAJ:
00323                 setup_traj = self.traj_ctrl.get_angle_traj(traj[0], velocity=goal.setup_velocity, 
00324                                                            rate=RATE)
00325                 if goal.mode == goal.MOVE_SETUP:
00326                     full_traj = setup_traj
00327                 else:
00328                     full_traj = setup_traj + traj
00329             elif goal.mode == goal.TRAJ_ONLY:
00330                 full_traj = traj
00331             else:
00332                 self.traj_srv.set_aborted(text="Unknown goal mode.")
00333                 return
00334             if self.traj_ctrl.execute(full_traj, rate * goal.traj_rate_mult, blocking=True):
00335                 self.publish_feedback("Trajectory playback on the %s arm successful." % 
00336                                       self.arm_dict[self.traj_ctrl.arm_char])
00337         self.traj_srv.set_succeeded()
00338 
00339     def traj_cancel_cb(self):
00340         self.traj_ctrl.stop_moving()
00341 
00342 def main():
00343 
00344     from optparse import OptionParser
00345     p = OptionParser()
00346     p.add_option('-f', '--filename', dest="filename", default="",
00347                  help="File to save trajectory to or load from.")
00348     p.add_option('-l', '--left_arm', dest="left_arm",
00349                  action="store_true", default=False,
00350                  help="Use left arm.")
00351     p.add_option('-r', '--right_arm', dest="right_arm",
00352                  action="store_true", default=False,
00353                  help="Use right arm.")
00354     p.add_option('-s', '--save_mode', dest="save_mode",
00355                  action="store_true", default=False,
00356                  help="Saving mode.")
00357     p.add_option('-t', '--traj_mode', dest="traj_mode",
00358                  action="store_true", default=False,
00359                  help="Trajectory mode.")
00360     p.add_option('-c', '--ctrl_name', dest="ctrl_name", default=None, #CTRL_NAME_LOW,
00361                  help="Controller to run the playback with.")
00362     p.add_option('-p', '--params', dest="param_file", default=None, #PARAM_FILE_LOW,
00363                  help="YAML file to save parameters in or load from.")
00364     p.add_option('-v', '--srv_mode', dest="srv_mode", 
00365                  action="store_true", default=False,
00366                  help="Server mode.")
00367     p.add_option('-y', '--playback_mode', dest="playback_mode", 
00368                  action="store_true", default=False,
00369                  help="Plays back trajectory immediately.")
00370     p.add_option('-z', '--reverse', dest="reverse", 
00371                  action="store_true", default=False,
00372                  help="Plays back trajectory in reverse.")
00373     (opts, args) = p.parse_args()
00374 
00375     if opts.right_arm:
00376         arm_char = 'r'
00377     elif opts.left_arm:
00378         arm_char = 'l'
00379     else:
00380         print "Must select arm with -r or -l."
00381         return
00382     filename = opts.filename
00383 
00384     rospy.init_node("arm_pose_move_controller_%s" % arm_char)
00385     if opts.save_mode:
00386         assert(opts.right_arm + opts.left_arm == 1)
00387         if opts.traj_mode:
00388             ctrl_switcher = ControllerSwitcher()
00389             ctrl_switcher.carefree_switch(arm_char, CTRL_NAME_NONE, PARAM_FILE_NONE, reset=False)
00390             traj_saver = TrajectorySaver(RATE)
00391             raw_input("Press enter to start recording")
00392             traj_saver.record_trajectory(arm_char, blocking=False)
00393             
00394             import curses
00395             def wait_for_key(stdscr):
00396                 curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
00397                 stdscr.addstr(0,0, "Press any key to stop!", curses.color_pair(1) )
00398                 stdscr.refresh()
00399                 c = 0
00400                 while not rospy.is_shutdown() and c == 0:
00401                     c = stdscr.getch()
00402             curses.wrapper(wait_for_key)
00403 
00404             traj_saver.stop_record(roslaunch.substitution_args.resolve_args(opts.filename))
00405             ctrl_switcher.carefree_switch(arm_char, opts.ctrl_name, PARAM_FILE_LOW, reset=False)
00406             return
00407         else:
00408             print "FIX"
00409             return
00410     elif opts.srv_mode:
00411         traj_srv = TrajectoryServer(arm_char, "/trajectory_playback_" + arm_char, 
00412                                     opts.ctrl_name, opts.param_file)
00413         rospy.spin()
00414         return
00415     elif opts.playback_mode:
00416         raw_input("Press enter to continue")
00417         if opts.traj_mode:
00418             exec_traj_from_file(opts.filename,
00419                                 ctrl_name=opts.ctrl_name,
00420                                 param_file=opts.param_file,
00421                                 reverse=opts.reverse,
00422                                 blocking=True)
00423         else:
00424             move_to_setup_from_file(opts.filename,
00425                                     ctrl_name=opts.ctrl_name,
00426                                     param_file=opts.param_file,
00427                                     reverse=opts.reverse,
00428                                     blocking=True)
00429 
00430 if __name__ == "__main__":
00431     main()


hrl_pr2_traj_playback
Author(s): Kelsey Hawkins
autogenerated on Wed Nov 27 2013 11:43:06