00001 
00002 import roslib
00003 roslib.load_manifest('pr2_omni_teleop')
00004 
00005 import rospy
00006 import tf
00007 import math
00008 import actionlib
00009 import tf.transformations as tr
00010 import numpy as np
00011 import hrl_lib.tf_utils as tfu
00012 
00013 
00014 from geometry_msgs.msg import PoseStamped
00015 from geometry_msgs.msg import Wrench
00016 from geometry_msgs.msg import Twist
00017 from phantom_omni.msg import PhantomButtonEvent
00018 from teleop_controllers.msg import JTTeleopControllerState
00019 from actionlib_msgs.msg import *
00020 from pr2_controllers_msgs.msg import *
00021 from pr2_msgs.msg import AccelerometerState
00022 
00023 import coefficients as coeff
00024 
00025 class ForceFeedbackFilter:
00026 
00027     def __init__(self, wrench_topic, dest_frame, wrench_frame, force_feedback_topic, kp_name, kd_name):
00028 
00029         self.wrench_frame = wrench_frame
00030         self.dest_frame = dest_frame
00031         
00032         self.tflistener = tf.TransformListener()
00033 
00034         self.omni_fb = rospy.Publisher(force_feedback_topic, Wrench)
00035         self.filtered_fb = rospy.Publisher('/filtered_fb', Wrench)
00036         rospy.Subscriber(wrench_topic, JTTeleopControllerState, self.wrench_callback)
00037         
00038         self.enable = False
00039         self.FIR = coeff.coefficients
00040         self.history = np.matrix(np.zeros((3,17)))
00041         self.prev_time = rospy.Time.now().nsecs*1e-9
00042         self.prev_dt = 0.0
00043         self.omni_max_limit = np.array([5., 5., 5.])
00044         self.omni_min_limit = np.array([-5., -5., -5.])
00045         self.kp = rospy.get_param(kp_name)
00046         self.kd = rospy.get_param(kd_name)
00047         self.force_scaling = 0.025
00048         self.force_old = np.zeros(3)
00049 
00050     def set_force_scaling(self, force):
00051         self.force_scaling = force
00052 
00053     def set_enable(self, v):
00054         self.enable = v
00055 
00056     def wrench_callback(self, state):
00057         x_err = np.matrix([state.x_err.linear.x, state.x_err.linear.y, state.x_err.linear.z]).T
00058         x_dot = np.matrix([state.xd.linear.x, state.xd.linear.y, state.xd.linear.z]).T
00059         feedback = -1.0*self.kp*x_err-self.kd*x_dot
00060         wr_ee = [state.F.force.x, state.F.force.y, state.F.force.z]
00061         
00062         shift_right = np.array(self.history[:,0:self.FIR.size-1])
00063         new_col = np.array(wr_ee).reshape((3,1))
00064         self.history = np.matrix(np.hstack((new_col, shift_right)))
00065         wr_ee_filt = self.history*self.FIR
00066         df_R_ee = tfu.rotate(self.dest_frame, 'torso_lift_link', self.tflistener) * \
00067                 tfu.rotate('torso_lift_link', self.wrench_frame, self.tflistener)
00068         wr_df = self.force_scaling*np.array(tr.translation_from_matrix(df_R_ee * tfu.translation_matrix([wr_ee_filt[0,0], wr_ee_filt[1,0], wr_ee_filt[2,0]])))
00069         wr_df = np.where(wr_df>self.omni_max_limit, self.omni_max_limit, wr_df)
00070         wr_df = np.where(wr_df<self.omni_min_limit, self.omni_min_limit, wr_df)
00071 
00072 
00073         
00074         
00075         
00076         
00077         
00078         wr = Wrench()
00079         
00080         wr.force.x = -wr_df[0]
00081         wr.force.y = -wr_df[1]
00082         wr.force.z = -wr_df[2]
00083         
00084         test = Wrench()
00085         test.force.x = feedback[0,0] 
00086         test.force.y = feedback[1,0] 
00087         test.force.z = feedback[2,0] 
00088         
00089         self.force_old[0] = wr.force.x
00090         self.force_old[1] = wr.force.y
00091         self.force_old[2] = wr.force.z
00092 
00093                 
00094         self.filtered_fb.publish(test)
00095         if self.enable == True:
00096             self.omni_fb.publish(wr)
00097         
00098         
00099         
00100 
00101 
00102 
00103 
00104 
00105 
00106 
00107 
00108 
00109 
00110 
00111 
00112 
00113 
00114 
00115 
00116 def limit_range(numb, lower, upper):
00117     if lower > upper:
00118         raise RuntimeError('lower bound > upper bound, wa?')
00119     return max(min(numb, upper), lower)
00120 
00121 class GripperOmniHandler:
00122     def __init__(self, topic, controller):
00123         rospy.Subscriber(topic, PhantomButtonEvent, self.button_event)
00124         self.gripper_server = actionlib.SimpleActionClient(controller + '/gripper_action', Pr2GripperCommandAction)
00125         self.gripper_server.wait_for_server()
00126         self.gripper_pose = None
00127         self.gripper_open = False
00128         self.gripper_close = False
00129         self.MAX_EFFORT = 40
00130         self.enabled = True
00131 
00132     def set_enabled(self, v):
00133         self.enabled = v
00134 
00135     def button_event(self, msg):
00136         if not (msg.grey_button == 1 and msg.white_button == 1):
00137             
00138             if msg.grey_button == 1:
00139                 if self.enabled:
00140                     self.gripper_server.send_goal(Pr2GripperCommandGoal(
00141                                          Pr2GripperCommand(position = 0.0, max_effort = self.MAX_EFFORT)),
00142                                          done_cb = self.gripper_done_cb,
00143                                          feedback_cb=self.gripper_fb_cb)
00144                     self.gripper_close = True
00145             elif msg.grey_button == 0 and self.gripper_pose != None and self.gripper_close:
00146                 self.gripper_server.send_goal(Pr2GripperCommandGoal(
00147                                      Pr2GripperCommand(position = self.gripper_pose-.01, max_effort = self.MAX_EFFORT))
00148                                      )
00149                 self.gripper_close = False
00150                 self.gripper_pose = None
00151                 
00152             
00153             if msg.white_button == 1:
00154                 if self.enabled:
00155                     self.gripper_server.send_goal(Pr2GripperCommandGoal(
00156                                          Pr2GripperCommand(position = 0.09, max_effort = self.MAX_EFFORT)),
00157                                          done_cb = self.gripper_done_cb,
00158                                          feedback_cb=self.gripper_fb_cb)
00159                     self.l_gripper_open = True
00160             elif msg.white_button == 0 and self.gripper_pose != None and self.gripper_open:
00161                 self.gripper_server.send_goal(Pr2GripperCommandGoal(
00162                                      Pr2GripperCommand(position = self.gripper_pose+.01, max_effort = self.MAX_EFFORT)))
00163                 self.l_gripper_open = False
00164                 self.gripper_pose = None
00165 
00166     def gripper_done_cb(self, state, msg):
00167         self.gripper_pose = msg.position
00168 
00169     def gripper_fb_cb(self, msg):
00170         self.gripper_pose = msg.position
00171         self.effort = msg.effort
00172 
00173 class AccelerometerFeedback:
00174     
00175     def __init__(self, dest_frame, accel_topic, force_topic, tflistener):
00176         rospy.Subscriber(accel_topic, AccelerometerState, self.cb)
00177         self.viz_pub = rospy.Publisher('omni1_force', PoseStamped)
00178         self.tflistener = tflistener
00179         self.dest_frame = dest_frame
00180         self.force_pub = rospy.Publisher(force_topic, Wrench)
00181         self.gain = .5
00182 
00183     def cb(self, msg):
00184         raw_read = [msg.samples[0].x, msg.samples[0].y, msg.samples[0].z]
00185         g = tr.translation_from_matrix(
00186                 tfu.rotate(msg.header.frame_id, 'base_footprint', self.tflistener) * \
00187                 tr.translation_matrix([0, 0, 9.665])) 
00188         grav_adjusted = np.array(raw_read) - np.array(g)
00189         
00190 
00191         d_R_m = tfu.rotate(self.dest_frame, 'torso_lift_link', self.tflistener) * \
00192                 tfu.rotate('torso_lift_link', msg.header.frame_id, self.tflistener)
00193 
00194         t_sensable = self.gain * np.array(tr.translation_from_matrix(d_R_m * np.matrix(tr.translation_matrix(grav_adjusted))))
00195         w = Wrench()
00196         w.force.x = t_sensable[0]
00197         w.force.y = t_sensable[1]
00198         w.force.z = t_sensable[2]
00199         self.force_pub.publish(w)
00200 
00201         ps = PoseStamped()
00202         ps.header.frame_id = self.dest_frame
00203         ps.header.stamp = rospy.get_rostime()
00204         ps.pose.position.x = t_sensable[0]
00205         ps.pose.position.y = t_sensable[1]
00206         ps.pose.position.z = t_sensable[2]
00207         ps.pose.orientation.w = 1
00208         self.viz_pub.publish(ps)
00209 
00210 class JTWrenchFeedback:
00211     def __init__(self, 
00212             wrench_topic, 
00213             wrench_frame,
00214             dest_frame, 
00215             force_feedback_topic, 
00216             tflistener): 
00217         self.wrench_frame = wrench_frame
00218         self.dest_frame = dest_frame
00219         self.tflistener = tflistener
00220 
00221         self.omni_fb = rospy.Publisher(force_feedback_topic, Wrench)
00222         self.filtered_fb = rospy.Publisher('/filtered_fb', Wrench)
00223         rospy.Subscriber(wrench_topic, Twist, self.wrench_callback)
00224         self.enable = False
00225         self.history = np.zeros((3,4))
00226         self.prev_time = rospy.Time.now().nsecs*1e-9
00227         self.prev_dt = 0.0
00228 
00229     def set_enable(self, v):
00230         self.enable = v
00231 
00232     def wrench_callback(self, w):
00233         wr_tool = [w.linear.x, w.linear.y, w.linear.z]
00234         df_R_wf = tfu.rotate(self.dest_frame, 'torso_lift_link', self.tflistener) * \
00235                 tfu.rotate('torso_lift_link', self.wrench_frame, self.tflistener)
00236         wr_df = np.array(tr.translation_from_matrix(df_R_wf * tfu.translation_matrix(wr_tool)))
00237         
00238 
00239         wr_sensable = 0.2*wr_df+0.2*self.history[:,0]+0.2*self.history[:,1]+0.2*self.history[:,2]+0.2*self.history[:,3]
00240         self.history[:,1:] = self.history[:,0:3]
00241         self.history[:,0] = wr_sensable
00242         wr = Wrench()
00243         wr.force.x = wr_sensable[0]
00244         wr.force.y = wr_sensable[1]
00245         wr.force.z = wr_sensable[2]
00246         self.filtered_fb.publish(wr)
00247 
00248         dt = rospy.Time.now().nsecs*1e-9-self.prev_time
00249         self.prev_time = rospy.Time.now().nsecs*1e-9
00250 
00251         print "time_Step in force :", dt
00252 
00253         if self.enable:
00254             wr = Wrench()
00255             wr.force.x = wr_sensable[0]
00256             wr.force.y = wr_sensable[1]
00257             wr.force.z = wr_sensable[2]
00258             
00259 
00260 
00261 
00262 
00263 class ControlPR2Arm:
00264 
00265     def __init__(self, omni_name, 
00266             pr2_control_topic, 
00267             gripper_control_topic,  
00268             gripper_tip_frame, 
00269             center_in_torso_frame, 
00270             scaling_in_base_frame, 
00271             tfbroadcast, 
00272             tflistener): 
00273 
00274         
00275         self.enabled = False
00276         self.zero_out_forces = True
00277 
00278         self.X_BOUNDS = [.3, 1.5] 
00279         self.kPos = 15.
00280         self.kVel = 0.5
00281         self.kPos_close = 70.
00282         self.omni_name = omni_name
00283         self.center_in_torso_frame = center_in_torso_frame
00284         self.scaling_in_base_frame = scaling_in_base_frame
00285         self.tflistener = tflistener
00286         self.tfbroadcast = tfbroadcast
00287         self.gripper_tip_frame = gripper_tip_frame
00288         self.prev_time = rospy.Time.now().nsecs*1e-9
00289         self.prev_dt = 0.0
00290         
00291         rate = rospy.Rate(100.0)
00292         
00293         
00294         
00295 
00296         rospy.loginfo('Attempting to link ' + omni_name + ' to the PR2\'s torso frame.')
00297         success = False
00298         while not success and (not rospy.is_shutdown()):
00299             self.send_transform_to_link_omni_and_pr2_frame()
00300             rate.sleep()
00301             try:
00302                 self.scale_omni_l0 = np.abs(self.l0_rotate_base(self.scaling_in_base_frame))
00303                 success = True
00304             except tf.LookupException, e:
00305                 pass
00306                 
00307             except tf.ConnectivityException, e:
00308                 pass
00309                 
00310         rospy.loginfo('Finished linking frame for %s' % omni_name)
00311 
00312         self.omni_fb = rospy.Publisher(self.omni_name + '_force_feedback', Wrench)
00313         self.pr2_pub = rospy.Publisher(pr2_control_topic, PoseStamped)
00314         
00315         self.scale_omni_l0 = np.abs(self.l0_rotate_base(self.scaling_in_base_frame))
00316         
00317         
00318         
00319         
00320         
00321         
00322         
00323         
00324         
00325         
00326 
00327         rospy.Subscriber(self.omni_name + '_pose', PoseStamped, self.omni_pose_cb)
00328         self.gripper_handler = GripperOmniHandler(self.omni_name + '_button', gripper_control_topic)
00329 
00330     
00331     
00332     def send_transform_to_link_omni_and_pr2_frame(self):
00333         self.tfbroadcast.sendTransform(self.center_in_torso_frame,
00334                          tuple(tf.transformations.quaternion_from_euler(0, 0, 0)),
00335                          rospy.Time.now(),
00336                          self.omni_name,
00337                          "/torso_lift_link")
00338     
00339     
00340     
00341     
00342     
00343 
00344     
00345     
00346     def l0_rotate_base(self, vec_base):
00347         m = tfu.translation_matrix(vec_base)
00348         vec_l0 = tr.translation_from_matrix((tfu.rotate('/base_footprint', '/torso_lift_link', self.tflistener) * \
00349                                              tfu.rotate('/torso_lift_link', self.omni_name + '_link0', self.tflistener)).T * m)
00350         return np.array(vec_l0)
00351 
00352     
00353     
00354     def torso_T_omni(self, omni_tip, msg_frame):
00355         
00356         
00357         z_T_6 = tfu.transform(self.omni_name + '_link0', msg_frame, self.tflistener)
00358         tip_0 = z_T_6 * omni_tip
00359         tip_0t = (np.array(tr.translation_from_matrix(tip_0)) * np.array(self.scale_omni_l0)).tolist()
00360         tip_0q = tr.quaternion_from_matrix(tip_0)
00361 
00362         
00363         tll_T_0 = tfu.transform('/torso_lift_link', self.omni_name + '_link0', self.tflistener)
00364         tip_torso_mat = tll_T_0 * tfu.tf_as_matrix([tip_0t, tip_0q])
00365         tip_tt, tip_tq = tfu.matrix_as_tf(tip_torso_mat)
00366         tip_tt[0] = limit_range(tip_tt[0], self.X_BOUNDS[0], self.X_BOUNDS[1])
00367         return tip_tt, tip_tq
00368 
00369     
00370     
00371     def omni_T_torso(self, torso_mat):
00372         l0_mat = tfu.transform(self.omni_name + '_link0', '/torso_lift_link', self.tflistener) * torso_mat
00373         l0_t = (np.array(tr.translation_from_matrix(l0_mat)) / np.array(self.scale_omni_l0)).tolist()
00374         l0_q = tr.quaternion_from_matrix(l0_mat)
00375         omni_pt_mat = tfu.transform(self.omni_name, self.omni_name + '_link0', self.tflistener) * tfu.tf_as_matrix((l0_t, l0_q))
00376         return tfu.matrix_as_tf(omni_pt_mat)
00377 
00378 
00379     
00380     
00381     def set_control(self, s):
00382         self.enabled = s
00383         if self.enabled:
00384             self.gripper_handler.set_enabled(True)
00385             self.zero_out_forces = True
00386         else:
00387             self.gripper_handler.set_enabled(False)
00388 
00389     
00390     
00391     def omni_pose_cb(self, msg):
00392         if self.enabled:
00393             
00394             tip_omni, msg_frame = tfu.posestamped_as_matrix(msg)
00395             tip_tt, tip_tq = self.torso_T_omni(tip_omni, msg_frame)
00396 
00397             
00398             ps = PoseStamped()
00399             ps.header.frame_id = '/torso_lift_link'
00400             ps.header.stamp = rospy.get_rostime()
00401             ps.pose.position.x = tip_tt[0]
00402             ps.pose.position.y = tip_tt[1]
00403             ps.pose.position.z = tip_tt[2]
00404             ps.pose.orientation.x = tip_tq[0]
00405             ps.pose.orientation.y = tip_tq[1]
00406             ps.pose.orientation.z = tip_tq[2]
00407             ps.pose.orientation.w = tip_tq[3]
00408             self.pr2_pub.publish(ps)
00409             if self.zero_out_forces:
00410                 wr = Wrench()
00411                 wr.force.x = 0 
00412                 wr.force.y = 0 
00413                 wr.force.z = 0 
00414                 self.omni_fb.publish(wr)
00415                 self.zero_out_forces = False
00416         else:
00417             tip_omni, msg_frame = tfu.posestamped_as_matrix(msg)
00418             m_o1 = tfu.transform(self.omni_name, msg_frame, self.tflistener) * tip_omni
00419             ee_point = np.matrix(tr.translation_from_matrix(m_o1)).T
00420 
00421             
00422             tip_torso = tfu.transform('/torso_lift_link', self.gripper_tip_frame, self.tflistener) \
00423                                   * tfu.tf_as_matrix(([0.,0.,0.], tr.quaternion_from_euler(0,0,0)))
00424             center_t, center_q = self.omni_T_torso(tip_torso)
00425             center_col_vec = np.matrix(center_t).T
00426 
00427             
00428             tip_tt, tip_tq = self.torso_T_omni(tfu.tf_as_matrix((center_t, center_q)), self.omni_name)
00429             ps = PoseStamped()
00430             ps.header.frame_id = '/torso_lift_link'
00431             ps.header.stamp = rospy.get_rostime()
00432             ps.pose.position.x = tip_tt[0]
00433             ps.pose.position.y = tip_tt[1]
00434             ps.pose.position.z = tip_tt[2]
00435             ps.pose.orientation.x = tip_tq[0]
00436             ps.pose.orientation.y = tip_tq[1]
00437             ps.pose.orientation.z = tip_tq[2]
00438             ps.pose.orientation.w = tip_tq[3]
00439             self.pr2_pub.publish(ps)
00440 
00441             
00442             
00443             
00444             
00445             
00446             
00447             
00448             
00449             
00450             dt = rospy.Time.now().nsecs*1e-9-self.prev_time
00451             self.prev_time = rospy.Time.now().nsecs*1e-9
00452             if dt>0:
00453                 self.prev_dt = dt
00454             err_dir = center_col_vec - ee_point
00455             if np.linalg.norm(err_dir) < .15:
00456                 force_o1 = self.kPos*err_dir
00457             else:
00458                 force_o1 = 0.*err_dir
00459 
00460 
00461             
00462             force_s = tfu.transform(self.omni_name + '_sensable', self.omni_name, self.tflistener) * np.row_stack((force_o1, np.matrix([1.])))
00463             wr = Wrench()
00464             wr.force.x = force_s[0]
00465             wr.force.y = force_s[1]
00466             wr.force.z = force_s[2]
00467             self.omni_fb.publish(wr)
00468 
00469 
00470 class OmniPR2Teleop:
00471     def __init__(self):
00472         rospy.init_node('omni_frames')
00473         self.enabled = False
00474         self.tfbroadcast = tf.TransformBroadcaster()
00475         self.tflistener = tf.TransformListener()
00476 
00477         self.left_controller = ControlPR2Arm(
00478                                     omni_name ='omni1', 
00479                                     pr2_control_topic = 'l_cart/command_pose',
00480                                     gripper_control_topic = 'l_gripper_controller',
00481                                     gripper_tip_frame = 'l_gripper_tool_frame',
00482                                     center_in_torso_frame = [1.2, .3, -1], 
00483                                     scaling_in_base_frame = [3.5, 3., 5.],
00484                                     tfbroadcast=self.tfbroadcast,
00485                                     tflistener=self.tflistener)
00486 
00487         
00488         
00489         
00490         
00491         
00492         
00493         
00494         
00495         
00496         
00497         
00498         
00499         
00500         
00501         self.left_feedback = ForceFeedbackFilter(wrench_topic = '/l_cart/state', 
00502             dest_frame = '/omni1_sensable',
00503             wrench_frame = '/l_gripper_tool_frame', 
00504             force_feedback_topic = 'omni1_force_feedback',
00505             kp_name = '/l_cart/cart_gains/trans/p',
00506             kd_name = '/l_cart/cart_gains/trans/d')
00507 
00508 
00509 
00510 
00511         rospy.Subscriber('omni1_button', PhantomButtonEvent, self.omni_safety_lock_cb)
00512         rospy.Subscriber('omni2_button', PhantomButtonEvent, self.omni_safety_lock_cb)
00513         self.set_state(False)
00514 
00515     def omni_safety_lock_cb(self, msg):
00516         if msg.grey_button == 1 and msg.white_button == 1:
00517             self.set_state(not self.enabled)
00518 
00519     def set_state(self, s):
00520         self.enabled = s
00521         if self.enabled:
00522             rospy.loginfo('control ENABLED.')
00523             self.left_controller.set_control(True)
00524             self.left_feedback.set_enable(True)
00525             
00526         else:
00527             rospy.loginfo('control disabled.  Follow potential well to pose of arm.')
00528             self.left_controller.set_control(False)
00529             self.left_feedback.set_enable(False)
00530             
00531 
00532     def run(self):
00533         rate = rospy.Rate(100.0)
00534         rospy.loginfo('running...')
00535         while not rospy.is_shutdown():
00536             self.left_controller.send_transform_to_link_omni_and_pr2_frame()
00537             
00538             rate.sleep()
00539 
00540  
00541 if __name__ == '__main__':
00542     
00543     
00544     o = OmniPR2Teleop()
00545     o.run()
00546     
00547     
00548 
00549