test_force.py
Go to the documentation of this file.
00001 #!/usr/bin/env python  
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 #import threading
00014 
00015 from geometry_msgs.msg import PoseStamped
00016 from geometry_msgs.msg import Wrench
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, Wrench)
00032 
00033 ##################NEED TO MODIFY WHERE IT IS PUBLISHING TO CHECK BOTH ARMS AT SAME TIME##########
00034         self.filtered_fb = rospy.Publisher('/test_filtered_fb', Wrench)
00035 ################################################################################################
00036         #REMOVE references to self.filtered_fb after check that everything is working
00037         rospy.Subscriber(wrench_topic, JTTeleopControllerState, self.wrench_callback)
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, scalar):
00051         self.force_scaling = -1.*scalar
00052 
00053     def set_enable(self, v):
00054         self.enable = v
00055 
00056     def wrench_callback(self, state):
00057         #calculating force estimate from position error (does not compensate for motion error due to dynamics)
00058         x_err = np.matrix([state.x_err.linear.x, state.x_err.linear.y, state.x_err.linear.z]).T
00059         x_dot = np.matrix([state.xd.linear.x, state.xd.linear.y, state.xd.linear.z]).T
00060         feedback = -1.0*self.kp*x_err-self.kd*x_dot
00061 
00062         #currently the calculated force feedback is published but not to omni, we use the velocity limited state from the controller
00063 #        wr_ee = [state.F.force.x, state.F.force.y, state.F.force.z]
00064         wr_ee = [feedback[0,0], feedback[1,0], feedback[2,0]]
00065        
00066         #this is a simple FIR filter designed in Matlab to smooth the force estimate
00067         shift_right = np.array(self.history[:,0:self.FIR.size-1])
00068         new_col = np.array(wr_ee).reshape((3,1))
00069         self.history = np.matrix(np.hstack((new_col, shift_right)))
00070         wr_ee_filt = self.history*self.FIR
00071 
00072         #find and use the rotation matrix from wrench to torso                                                                   
00073         df_R_ee = tfu.rotate(self.dest_frame, 'torso_lift_link', self.tflistener) * \
00074                 tfu.rotate('torso_lift_link', self.wrench_frame, self.tflistener)
00075         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]])))
00076 
00077         #limiting the max and min force feedback sent to omni                                                                    
00078         wr_df = np.where(wr_df>self.omni_max_limit, self.omni_max_limit, wr_df)
00079         wr_df = np.where(wr_df<self.omni_min_limit, self.omni_min_limit, wr_df)
00080 
00081         wr = Wrench()
00082         wr.force.x = wr_df[0,0]
00083         wr.force.y = wr_df[1,0]
00084         wr.force.z = wr_df[2,0]
00085              
00086         #publishing of two different wrenches calculated, DELETE first when all finished
00087         if self.enable == False:
00088             self.filtered_fb.publish(wr)
00089 
00090         if self.enable == True:
00091             self.omni_fb.publish(wr)
00092 
00093 #this could be used for trying to damp the force feedback, didn't work very well
00094 #         self.force_old[0] = wr.force.x
00095 #         self.force_old[1] = wr.force.y
00096 #         self.force_old[2] = wr.force.z
00097 #         dt = rospy.Time.now().nsecs*1e-9-self.prev_time
00098 #         self.prev_time = rospy.Time.now().nsecs*1e-9
00099 #         print "time step: ", dt
00100 
00101 
00102 
00103 class FF_PR2Teleop:
00104     def __init__(self):
00105         rospy.init_node('pr2_omni_force_feedback')
00106         self.tfbroadcast = tf.TransformBroadcaster()
00107 
00108         self.tflistener = tf.TransformListener()
00109 #         self.left_feedback = ForceFeedbackFilter(wrench_topic = '/l_cart/state', #'/l_cart/test/wrench_unfiltered', #
00110 #             dest_frame = '/omni1_sensable',
00111 #             wrench_frame = '/l_gripper_tool_frame', 
00112 #             force_feedback_topic = 'omni1_force_feedback',
00113 #             tflistener = self.tflistener,
00114 #             kp_name = '/l_cart/cart_gains/trans/p',
00115 #             kd_name = '/l_cart/cart_gains/trans/d')
00116         self.right_feedback = ForceFeedbackFilter(wrench_topic = '/r_cart/state', #'/l_cart/test/wrench_unfiltered', #
00117             dest_frame = '/omni2_sensable',
00118             wrench_frame = '/r_gripper_tool_frame', 
00119             force_feedback_topic = 'omni2_force_feedback',
00120             tflistener = self.tflistener,
00121             kp_name = '/r_cart/cart_gains/trans/p',
00122             kd_name = '/r_cart/cart_gains/trans/d')
00123         rospy.Subscriber('omni1_button', PhantomButtonEvent, self.omni_safety_lock_cb)
00124         rospy.Subscriber('omni2_button', PhantomButtonEvent, self.omni_safety_lock_cb)
00125 
00126     def omni_safety_lock_cb(self, msg):
00127         if msg.grey_button == 1 and msg.white_button == 1:
00128             self.set_state(not self.enabled)
00129 
00130     def set_state(self, s):
00131         self.enabled = s
00132         if self.enabled:
00133             rospy.loginfo('control ENABLED.')
00134 #            self.left_feedback.set_enable(True)
00135             self.right_feedback.set_enable(True)
00136         else:
00137             rospy.loginfo('control disabled.  Follow potential well to pose of arm.')
00138 #            self.left_feedback.set_enable(False)
00139             self.right_feedback.set_enable(False)
00140 
00141     def run(self):
00142         rate = rospy.Rate(100.0)
00143         rospy.loginfo('running...')
00144         rospy.spin()
00145  
00146 if __name__ == '__main__':
00147     #o = ForceFeedbackFilter()
00148     #rospy.spin()
00149     o = FF_PR2Teleop()
00150     o.run()
00151     #while not rospy.is_shutdown():
00152     #    time.sleep(1.0)
00153 
00154 


pr2_omni_teleop
Author(s): Hai Nguyen, Marc Killpack Advisor: Prof. Charlie Kemp, Lab: Healthcare Robotics Lab at Georgia Tech
autogenerated on Wed Nov 27 2013 11:51:39