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 import coefficients as coeff
00013
00014 from geometry_msgs.msg import PoseStamped
00015 from geometry_msgs.msg import Wrench
00016 from phantom_omni.msg import OmniFeedback
00017 from geometry_msgs.msg import Twist
00018 from phantom_omni.msg import PhantomButtonEvent
00019 from teleop_controllers.msg import JTTeleopControllerState
00020 from actionlib_msgs.msg import *
00021 from pr2_controllers_msgs.msg import *
00022 from pr2_msgs.msg import AccelerometerState
00023
00024
00025 class ForceFeedbackFilter:
00026
00027 def __init__(self, wrench_topic, dest_frame, wrench_frame, force_feedback_topic, tflistener, kp_name, kd_name):
00028 self.wrench_frame = wrench_frame
00029 self.dest_frame = dest_frame
00030 self.tflistener = tflistener
00031 self.omni_fb = rospy.Publisher(force_feedback_topic, OmniFeedback)
00032 rospy.Subscriber(wrench_topic, JTTeleopControllerState, self.wrench_callback)
00033 self.enable = False
00034 self.IIR_num = coeff.num
00035 self.IIR_den = coeff.den
00036 self.input_his = np.matrix(np.zeros((3,coeff.num.size)))
00037 self.output_his = np.matrix(np.zeros((3,coeff.den.size)))
00038
00039
00040 self.omni_max_limit = np.array([7., 7., 7.])
00041 self.omni_min_limit = np.array([-7., -7., -7.])
00042 self.kp = rospy.get_param(kp_name)
00043 self.kd = rospy.get_param(kd_name)
00044 self.force_scaling = -0.04
00045 self.force_old = np.zeros(3)
00046
00047 def set_force_scaling(self, scalar):
00048 self.force_scaling = -1.*scalar
00049
00050 def set_enable(self, v):
00051 self.enable = v
00052
00053 def wrench_callback(self, state):
00054
00055 x_err = np.matrix([state.x_err.linear.x, state.x_err.linear.y, state.x_err.linear.z]).T
00056 x_dot = np.matrix([state.xd.linear.x, state.xd.linear.y, state.xd.linear.z]).T
00057 feedback = -1.0*self.kp*x_err-self.kd*x_dot
00058
00059
00060 wr_ee = [feedback[0,0], feedback[1,0], feedback[2,0]]
00061
00062
00063 self.input_his = np.matrix(np.hstack((np.array(wr_ee).reshape((3,1)), np.array(self.input_his[:,0:-1]))))
00064 wr_ee_filt = self.input_his*self.IIR_num - self.output_his[:,:-1]*self.IIR_den[1:]
00065 self.output_his = np.matrix(np.hstack((np.array(wr_ee_filt).reshape((3,1)), np.array(self.output_his[:,0:-1]))))
00066
00067
00068 df_R_ee = tfu.rotate(self.dest_frame, 'torso_lift_link', self.tflistener) * tfu.rotate('torso_lift_link', self.wrench_frame, self.tflistener)
00069
00070 wr_df = self.force_scaling*np.array(tr.translation_from_matrix(df_R_ee * tfu.translation_matrix([feedback[0,0], feedback[1,0], feedback[2,0]])))
00071
00072
00073 wr_df = np.where(wr_df>self.omni_max_limit, self.omni_max_limit, wr_df)
00074 wr_df = np.where(wr_df<self.omni_min_limit, self.omni_min_limit, wr_df)
00075
00076 wr = OmniFeedback()
00077 wr.force.x = wr_df[0]
00078 wr.force.y = wr_df[1]
00079 wr.force.z = wr_df[2]
00080 if self.enable == True:
00081 self.omni_fb.publish(wr)
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092 def limit_range(numb, lower, upper):
00093 if lower > upper:
00094 raise RuntimeError('lower bound > upper bound, wa?')
00095 return max(min(numb, upper), lower)
00096
00097 class GripperOmniHandler:
00098 def __init__(self, topic, controller):
00099 rospy.Subscriber(topic, PhantomButtonEvent, self.button_event)
00100 self.gripper_server = actionlib.SimpleActionClient(controller + '/gripper_action', Pr2GripperCommandAction)
00101 self.gripper_server.wait_for_server()
00102 self.gripper_pose = None
00103 self.gripper_open = False
00104 self.gripper_close = False
00105 self.MAX_EFFORT = 40
00106 self.enabled = True
00107
00108 def set_enabled(self, v):
00109 self.enabled = v
00110
00111 def button_event(self, msg):
00112 if not (msg.grey_button == 1 and msg.white_button == 1):
00113
00114 if msg.grey_button == 1:
00115 if self.enabled:
00116 self.gripper_server.send_goal(Pr2GripperCommandGoal(
00117 Pr2GripperCommand(position = 0.0, max_effort = self.MAX_EFFORT)),
00118 done_cb = self.gripper_done_cb,
00119 feedback_cb=self.gripper_fb_cb)
00120 self.gripper_close = True
00121 elif msg.grey_button == 0 and self.gripper_pose != None and self.gripper_close:
00122 self.gripper_server.send_goal(Pr2GripperCommandGoal(
00123 Pr2GripperCommand(position = self.gripper_pose-.01, max_effort = self.MAX_EFFORT))
00124 )
00125 self.gripper_close = False
00126 self.gripper_pose = None
00127
00128
00129 if msg.white_button == 1:
00130 if self.enabled:
00131 self.gripper_server.send_goal(Pr2GripperCommandGoal(
00132 Pr2GripperCommand(position = 0.09, max_effort = self.MAX_EFFORT)),
00133 done_cb = self.gripper_done_cb,
00134 feedback_cb=self.gripper_fb_cb)
00135 self.l_gripper_open = True
00136 elif msg.white_button == 0 and self.gripper_pose != None and self.gripper_open:
00137 self.gripper_server.send_goal(Pr2GripperCommandGoal(
00138 Pr2GripperCommand(position = self.gripper_pose+.01, max_effort = self.MAX_EFFORT)))
00139 self.l_gripper_open = False
00140 self.gripper_pose = None
00141
00142 def gripper_done_cb(self, state, msg):
00143 self.gripper_pose = msg.position
00144
00145 def gripper_fb_cb(self, msg):
00146 self.gripper_pose = msg.position
00147 self.effort = msg.effort
00148
00149 class AccelerometerFeedback:
00150
00151 def __init__(self, dest_frame, accel_topic, force_topic, tflistener):
00152 rospy.Subscriber(accel_topic, AccelerometerState, self.cb)
00153 self.viz_pub = rospy.Publisher('omni1_force', PoseStamped)
00154 self.tflistener = tflistener
00155 self.dest_frame = dest_frame
00156 self.force_pub = rospy.Publisher(force_topic, OmniFeedback)
00157 self.gain = .5
00158
00159 def cb(self, msg):
00160 raw_read = [msg.samples[0].x, msg.samples[0].y, msg.samples[0].z]
00161 g = tr.translation_from_matrix(
00162 tfu.rotate(msg.header.frame_id, 'base_footprint', self.tflistener) * \
00163 tr.translation_matrix([0, 0, 9.665]))
00164 grav_adjusted = np.array(raw_read) - np.array(g)
00165
00166
00167 d_R_m = tfu.rotate(self.dest_frame, 'torso_lift_link', self.tflistener) * \
00168 tfu.rotate('torso_lift_link', msg.header.frame_id, self.tflistener)
00169
00170 t_sensable = self.gain * np.array(tr.translation_from_matrix(d_R_m * np.matrix(tr.translation_matrix(grav_adjusted))))
00171 w = OmniFeedback()
00172 w.force.x = t_sensable[0]
00173 w.force.y = t_sensable[1]
00174 w.force.z = t_sensable[2]
00175 self.force_pub.publish(w)
00176
00177 ps = PoseStamped()
00178 ps.header.frame_id = self.dest_frame
00179 ps.header.stamp = rospy.get_rostime()
00180 ps.pose.position.x = t_sensable[0]
00181 ps.pose.position.y = t_sensable[1]
00182 ps.pose.position.z = t_sensable[2]
00183 ps.pose.orientation.w = 1
00184 self.viz_pub.publish(ps)
00185
00186 class ControlPR2Arm:
00187
00188 def __init__(self, omni_name,
00189 pr2_control_topic,
00190 gripper_control_topic,
00191 gripper_tip_frame,
00192 center_in_torso_frame,
00193 scaling_in_base_frame,
00194 tfbroadcast,
00195 tflistener):
00196
00197
00198 self.enabled = False
00199 self.zero_out_forces = True
00200
00201 self.X_BOUNDS = [.3, 1.5]
00202 self.kPos = 15.
00203 self.kVel = 0.5
00204 self.kPos_close = 70.
00205 self.omni_name = omni_name
00206 self.center_in_torso_frame = center_in_torso_frame
00207 self.scaling_in_base_frame = scaling_in_base_frame
00208 self.tflistener = tflistener
00209 self.tfbroadcast = tfbroadcast
00210 self.gripper_tip_frame = gripper_tip_frame
00211 self.prev_dt = 0.0
00212 self.tip_tt = np.zeros((3,1))
00213 self.tip_tq = np.zeros((4,1))
00214 rate = rospy.Rate(100.0)
00215
00216 rospy.loginfo('Attempting to link ' + omni_name + ' to the PR2\'s torso frame.')
00217 success = False
00218 while not success and (not rospy.is_shutdown()):
00219 self.send_transform_to_link_omni_and_pr2_frame()
00220 rate.sleep()
00221 try:
00222 self.scale_omni_l0 = np.abs(self.l0_rotate_base(self.scaling_in_base_frame))
00223 success = True
00224 except tf.LookupException, e:
00225 pass
00226 except tf.ConnectivityException, e:
00227 pass
00228 rospy.loginfo('Finished linking frame for %s' % omni_name)
00229
00230 self.omni_fb = rospy.Publisher(self.omni_name + '_force_feedback', OmniFeedback)
00231 self.pr2_pub = rospy.Publisher(pr2_control_topic, PoseStamped)
00232 self.scale_omni_l0 = np.abs(self.l0_rotate_base(self.scaling_in_base_frame))
00233
00234 rospy.Subscriber(self.omni_name + '_pose', PoseStamped, self.omni_pose_cb)
00235 self.gripper_handler = GripperOmniHandler(self.omni_name + '_button', gripper_control_topic)
00236
00237
00238
00239 def send_transform_to_link_omni_and_pr2_frame(self):
00240 self.tfbroadcast.sendTransform(self.center_in_torso_frame,
00241 tuple(tf.transformations.quaternion_from_euler(0, 0, 0)),
00242 rospy.Time.now(),
00243 self.omni_name,
00244 "/torso_lift_link")
00245
00246
00247 def l0_rotate_base(self, vec_base):
00248 m = tfu.translation_matrix(vec_base)
00249 vec_l0 = tr.translation_from_matrix((tfu.rotate('/base_footprint', '/torso_lift_link', self.tflistener) * \
00250 tfu.rotate('/torso_lift_link', self.omni_name + '_link0', self.tflistener)).T * m)
00251 return np.array(vec_l0)
00252
00253
00254
00255 def torso_T_omni(self, omni_tip, msg_frame):
00256
00257
00258 z_T_6 = tfu.transform(self.omni_name + '_link0', msg_frame, self.tflistener)
00259 tip_0 = z_T_6 * omni_tip
00260 tip_0t = (np.array(tr.translation_from_matrix(tip_0)) * np.array(self.scale_omni_l0)).tolist()
00261 tip_0q = tr.quaternion_from_matrix(tip_0)
00262
00263
00264 tll_T_0 = tfu.transform('/torso_lift_link', self.omni_name + '_link0', self.tflistener)
00265 tip_torso_mat = tll_T_0 * tfu.tf_as_matrix([tip_0t, tip_0q])
00266 tip_tt, tip_tq = tfu.matrix_as_tf(tip_torso_mat)
00267 tip_tt[0] = limit_range(tip_tt[0], self.X_BOUNDS[0], self.X_BOUNDS[1])
00268 self.tip_tt = tip_tt
00269 self.tip_tq = tip_tq
00270
00271
00272
00273 def omni_T_torso(self, torso_mat):
00274 l0_mat = tfu.transform(self.omni_name + '_link0', '/torso_lift_link', self.tflistener) * torso_mat
00275 l0_t = (np.array(tr.translation_from_matrix(l0_mat)) / np.array(self.scale_omni_l0)).tolist()
00276 l0_q = tr.quaternion_from_matrix(l0_mat)
00277 omni_pt_mat = tfu.transform(self.omni_name, self.omni_name + '_link0', self.tflistener) * tfu.tf_as_matrix((l0_t, l0_q))
00278 return tfu.matrix_as_tf(omni_pt_mat)
00279
00280
00281
00282
00283 def set_control(self, s):
00284 self.enabled = s
00285 if self.enabled:
00286 self.gripper_handler.set_enabled(True)
00287 self.zero_out_forces = True
00288 else:
00289 self.gripper_handler.set_enabled(False)
00290
00291
00292
00293 def omni_pose_cb(self, msg):
00294 if self.enabled:
00295
00296 tip_omni, msg_frame = tfu.posestamped_as_matrix(msg)
00297 self.torso_T_omni(tip_omni, msg_frame)
00298 tip_tt = self.tip_tt
00299 tip_tq = self.tip_tq
00300
00301 ps = PoseStamped()
00302 ps.header.frame_id = '/torso_lift_link'
00303 ps.header.stamp = rospy.get_rostime()
00304 ps.pose.position.x = tip_tt[0]
00305 ps.pose.position.y = tip_tt[1]
00306 ps.pose.position.z = tip_tt[2]
00307 ps.pose.orientation.x = tip_tq[0]
00308 ps.pose.orientation.y = tip_tq[1]
00309 ps.pose.orientation.z = tip_tq[2]
00310 ps.pose.orientation.w = tip_tq[3]
00311 self.pr2_pub.publish(ps)
00312 if self.zero_out_forces:
00313 wr = OmniFeedback()
00314 wr.force.x = 0
00315 wr.force.y = 0
00316 wr.force.z = 0
00317 self.omni_fb.publish(wr)
00318 self.zero_out_forces = False
00319 else:
00320
00321 tip_tt = self.tip_tt
00322 tip_tq = self.tip_tq
00323 ps = PoseStamped()
00324 ps.header.frame_id = '/torso_lift_link'
00325 ps.header.stamp = rospy.get_rostime()
00326 ps.pose.position.x = tip_tt[0]
00327 ps.pose.position.y = tip_tt[1]
00328 ps.pose.position.z = tip_tt[2]
00329 ps.pose.orientation.x = tip_tq[0]
00330 ps.pose.orientation.y = tip_tq[1]
00331 ps.pose.orientation.z = tip_tq[2]
00332 ps.pose.orientation.w = tip_tq[3]
00333 self.pr2_pub.publish(ps)
00334
00335
00336
00337 tip_omni, msg_frame = tfu.posestamped_as_matrix(msg)
00338 m_o1 = tfu.transform(self.omni_name, msg_frame, self.tflistener) * tip_omni
00339 ee_point = np.matrix(tr.translation_from_matrix(m_o1)).T
00340 tip_torso = tfu.transform('/torso_lift_link', self.gripper_tip_frame, self.tflistener) \
00341 * tfu.tf_as_matrix(([0.,0.,0.], tr.quaternion_from_euler(0,0,0)))
00342 center_t, center_q = self.omni_T_torso(tip_torso)
00343 center_col_vec = np.matrix(center_t).T
00344
00345
00346 wr = OmniFeedback()
00347
00348
00349 lock_pos = tr.translation_matrix(np.matrix([0,-.268,-.150]))*tfu.transform(self.omni_name+'_sensable', self.omni_name, self.tflistener)*np.row_stack((center_col_vec, np.matrix([1.])))
00350 wr.position.x = (lock_pos[0,0])*1000.0
00351 wr.position.y = (lock_pos[1,0])*1000.0
00352 wr.position.z = (lock_pos[2,0])*1000.0
00353 self.omni_fb.publish(wr)
00354
00355
00356 class OmniPR2Teleop:
00357 def __init__(self, arm, ff):
00358 rospy.init_node('omni_frames')
00359 self.enabled = False
00360 self.tfbroadcast = tf.TransformBroadcaster()
00361 self.tflistener = tf.TransformListener()
00362 self.controller_list = []
00363 self.ff_list = []
00364 if arm == "l" or arm == "b":
00365 self.left_controller = ControlPR2Arm(
00366 omni_name ='omni2',
00367 pr2_control_topic = 'l_cart/command_pose',
00368 gripper_control_topic = 'l_gripper_controller',
00369 gripper_tip_frame = 'l_gripper_tool_frame',
00370 center_in_torso_frame = [1.2, .3, -1],
00371 scaling_in_base_frame = [3.5, 3., 5.],
00372 tfbroadcast=self.tfbroadcast,
00373 tflistener=self.tflistener)
00374 self.controller_list.append(self.left_controller)
00375 rospy.Subscriber('omni2_button', PhantomButtonEvent, self.omni_safety_lock_cb)
00376 if ff == True:
00377 self.left_feedback = ForceFeedbackFilter(wrench_topic = '/l_cart/state',
00378 dest_frame = '/omni2_sensable',
00379 wrench_frame = '/l_gripper_tool_frame',
00380 force_feedback_topic = 'omni2_force_feedback',
00381 tflistener = self.tflistener,
00382 kp_name = '/l_cart/cart_gains/trans/p',
00383 kd_name = '/l_cart/cart_gains/trans/d')
00384 self.ff_list.append(self.left_feedback)
00385 if arm == "r" or arm == "b":
00386 self.right_controller = ControlPR2Arm(
00387 omni_name ='omni1',
00388 pr2_control_topic = 'r_cart/command_pose',
00389 gripper_control_topic = 'r_gripper_controller',
00390 gripper_tip_frame = 'r_gripper_tool_frame',
00391 center_in_torso_frame = [1.2, -.3, -1],
00392 scaling_in_base_frame = [3.5, 3., 5.],
00393 tfbroadcast=self.tfbroadcast,
00394 tflistener=self.tflistener)
00395 rospy.Subscriber('omni1_button', PhantomButtonEvent, self.omni_safety_lock_cb)
00396 self.controller_list.append(self.right_controller)
00397 if ff == True:
00398 self.right_feedback = ForceFeedbackFilter(wrench_topic = '/r_cart/state',
00399 dest_frame = '/omni1_sensable',
00400 wrench_frame = '/r_gripper_tool_frame',
00401 force_feedback_topic = 'omni1_force_feedback',
00402 tflistener = self.tflistener,
00403 kp_name = '/r_cart/cart_gains/trans/p',
00404 kd_name = '/r_cart/cart_gains/trans/d')
00405 self.ff_list.append(self.right_feedback)
00406
00407 self.set_state(False)
00408
00409 def omni_safety_lock_cb(self, msg):
00410 if msg.grey_button == 1 and msg.white_button == 1:
00411 self.set_state(not self.enabled)
00412
00413 def set_state(self, s):
00414 self.enabled = s
00415 if self.enabled:
00416 rospy.loginfo('control ENABLED.')
00417 for cont in self.controller_list:
00418 cont.set_control(True)
00419 for f in self.ff_list:
00420 f.set_enable(True)
00421 else:
00422 rospy.loginfo('control disabled. Follow potential well to pose of arm.')
00423 for cont in self.controller_list:
00424 cont.set_control(False)
00425 for f in self.ff_list:
00426 f.set_enable(False)
00427
00428 def run(self):
00429 rate = rospy.Rate(100.0)
00430 rospy.loginfo('running...')
00431 while not rospy.is_shutdown():
00432 for cont in self.controller_list:
00433 cont.send_transform_to_link_omni_and_pr2_frame()
00434
00435
00436 if __name__ == '__main__':
00437 import optparse
00438 p = optparse.OptionParser()
00439
00440 p.add_option('--arms', action='store', type='string', dest='arm', default='r',
00441 help='this allows initialization of right, left or both arms with an input of r, l or b respectively')
00442 p.add_option('--ff', action='store', type='int', dest='ff', default='0',
00443 help='enter 1 to activate force feedback, 0 otherwise')
00444
00445 opt, args = p.parse_args()
00446
00447 o = OmniPR2Teleop(opt.arm, opt.ff)
00448 o.run()
00449
00450
00451