wrench_zeroing.py
Go to the documentation of this file.
00001 #! /usr/bin/python
00002 
00003 import cPickle as pickle
00004 import sys
00005 import copy
00006 import numpy as np
00007 from threading import Lock
00008 
00009 import roslib
00010 roslib.load_manifest("hrl_ft")
00011 import rospy
00012 import rosparam
00013 import tf
00014 import tf.transformations as tf_trans
00015 from geometry_msgs.msg import WrenchStamped, Wrench, Vector3, Point, Quaternion, Pose, PoseArray
00016 from std_msgs.msg import Float64, Bool, ColorRGBA
00017 from visualization_msgs.msg import Marker
00018 
00019 g = 9.80665
00020 
00021 class WrenchListener(object):
00022     def __init__(self, wrench_topic):
00023         self.cur_wrench = WrenchStamped()
00024         rospy.Subscriber(wrench_topic, WrenchStamped, self.record_wrench)
00025 
00026     def record_wrench(self, msg):
00027         self.cur_wrench = [msg.wrench.force.x,
00028                            msg.wrench.force.y,
00029                            msg.wrench.force.z,
00030                            msg.wrench.torque.x,
00031                            msg.wrench.torque.y,
00032                            msg.wrench.torque.z]
00033 
00034 def collect_data_pr2(n_4, n_5, n_6):
00035     roslib.load_manifest("hrl_pr2_arms")
00036     from hrl_pr2_arms.pr2_arm_joint_traj import PR2ArmJointTraj, create_ep_arm
00037     wrench_topic = rospy.get_param("~wrench_topic")
00038     gravity_frame = rospy.get_param("~gravity_frame")
00039     wrench_frame = rospy.get_param("~wrench_frame")
00040     jnt_arm = create_ep_arm('l', arm_type=PR2ArmJointTraj)
00041     wrench_list = WrenchListener(wrench_topic)
00042     tf_list = tf.TransformListener()
00043 
00044     q_setup = [0.4, 0.0, 1.0, -1.2, 0, -0.1, -3.14]
00045     q_4_vals = np.linspace(0, 4.7, n_4)
00046     q_5_vals = np.linspace(-0.1, -1.8, n_5)
00047     q_6_vals = np.linspace(-3.14, -0.2, n_6)
00048 
00049     jnt_arm.set_ep(q_setup, 8)
00050     rospy.sleep(8.)
00051 
00052     data = []
00053     pose_array_pub = rospy.Publisher('wrench_train_poses', PoseArray, latch=True)
00054 
00055     q_cur = q_setup
00056     for q_4 in q_4_vals:
00057         for q_5 in q_5_vals:
00058             for q_6 in q_6_vals:
00059                 q_cur[4] = q_4
00060                 q_cur[5] = q_5
00061                 q_cur[6] = q_6
00062                 jnt_arm.set_ep(q_cur, 2.)
00063                 rospy.sleep(4.)
00064                 (trans, rot) = tf_list.lookupTransform(gravity_frame, wrench_frame, rospy.Time(0))
00065                 datum = (copy.copy(wrench_list.cur_wrench), rot)
00066                 data.append(datum)
00067                 print  "Wrench: %s\r\nOrient: %s" %(datum[0], datum[1])
00068                 if rospy.is_shutdown():
00069                     return
00070     poses = []
00071     for datum in data:
00072         p = Pose(Point(0,0,0), Quaternion(*datum[1]))
00073         poses.append(copy.copy(p))
00074     pa = PoseArray()
00075     pa.header.frame_id = wrench_frame
00076     pa.header.stamp = rospy.Time.now()
00077     pa.poses = poses
00078     pose_array_pub.publish(pa)
00079     raw_input('holding to catch pose array. Press ENTER to continue.')
00080     return data
00081 
00082 def collect_data_tool():
00083     wrench_topic = rospy.get_param("~wrench_topic")
00084     gravity_frame = rospy.get_param("~gravity_frame")
00085     wrench_frame = rospy.get_param("~wrench_frame")
00086     wrench_list = WrenchListener(wrench_topic)
00087     tf_list = tf.TransformListener()
00088     data = []
00089     while not rospy.is_shutdown():
00090         done_query = raw_input("Enter 'd' when done")
00091         if done_query == 'd':
00092             break
00093         (trans, rot) = tf_list.lookupTransform(gravity_frame, wrench_frame, rospy.Time(0))
00094         data.append((copy.copy(wrench_list.cur_wrench), rot))
00095     print "Wrench, orientation data", data
00096     return data
00097 
00098 def process_data(data, is_backwards):
00099     wf_chain = []
00100     grav_chain = []
00101     if is_backwards:
00102         # account for the fact that the ft sensor is sensing forces
00103         # backwards and is measuring reaction forces as opposed to the appied forces
00104         react_mult = -1.
00105     else:
00106         react_mult = 1.
00107     for w, quat in data: #For wrench, wrist orientation in data list
00108         wf_chain.extend(w[:3]) #Add wrench force complnents to list
00109         rot_mat = np.mat(tf_trans.quaternion_matrix(quat))[:3,:3] #get rot matrix from quaternion from grav frame to wrench frame
00110         z_grav = react_mult * rot_mat.T * np.mat([0, 0, -1.]).T #Get Gravity unit vector in wrench frame
00111         z_x, z_y, z_z = z_grav.T.A[0] #Get components of gravity vector
00112         grav_chain.append([g * z_x, 1, 0, 0]) #add
00113         grav_chain.append([g * z_y, 0, 1, 0])
00114         grav_chain.append([g * z_z, 0, 0, 1])
00115 
00116     #############################################
00117     #Solve Ax=b for x. x=[x1, x2, x3, x4].T
00118     #For every 3 rows in A,b -> Ax=b gives:
00119     #grav_x*x1 + x2 = force_x
00120     #grav_y*x1 + x3 = force_y
00121     #grav_z*x1 + x4 = force_z
00122     #x1=mass
00123     #x2 = offset in force_x signal
00124     #x3 = offset in force_y signal
00125     #x4 = offset in force_z signal
00126     b_w = np.mat(wf_chain).T
00127     A_g = np.mat(grav_chain)
00128     x_p, res_p, _, _ = np.linalg.lstsq(A_g, b_w)
00129     ################################################
00130 
00131     mass = x_p[0,0]
00132 
00133     wt_chain = []
00134     torque_chain = []
00135     for w, quat in data:
00136         wt_chain.extend(w[3:]) #Add torque readings to 3*Nx1 matrix (N is number of data points)
00137         rot_mat = np.mat(tf_trans.quaternion_matrix(quat))[:3,:3]
00138         z_grav = react_mult * rot_mat.T * np.mat([0, 0, -1.]).T #Get vector for idealized gravity (-z in gravity frame) in wrench frame
00139         force_grav = mass * g * z_grav #Ideal Force of gravity along each axis
00140         f_x, f_y, f_z = force_grav.T.A[0]
00141         torque_chain.append([0, f_z, -f_y, 1, 0, 0]) #Build 3*nx6 matrix of torque components, offsets
00142         torque_chain.append([-f_z, 0, f_x, 0, 1, 0])
00143         torque_chain.append([f_y, -f_x, 0, 0, 0, 1])
00144 
00145     ##################################################
00146     #Solve Ax=b for x. x=[x1,x2,x3,x4,x5,c6].T
00147     #For every 3 rows in A,b -> Ax=b gives:
00148     #torque_x = fz*x2 - fy*x3 + x4 (force in +z x COM offset in y gives torque in +x, f in -y x COM offset z, plus signal offset)
00149     #torque_y = fz*x1 - fy*x3 + x5
00150     #torque_z = fz*x1 - fy*x2 + x6
00151     #x1 = COM offset in x
00152     #x2 = COM offset in y
00153     #x3 = COM offset in z
00154     #x4 = offset in x torque signal
00155     #x5 = offset in y torque signal
00156     #x6 = offset in z torque signal
00157     b_t = np.mat(wt_chain).T
00158     A_t = np.mat(torque_chain)
00159     x_t, res_t, _, _ = np.linalg.lstsq(A_t, b_t)
00160     ###################################################
00161 
00162     print "Force residues:", res_p
00163     print "Torque residues:", res_t
00164 
00165     return x_p.T.A[0].tolist() + x_t.T.A[0].tolist()
00166 
00167 def save_params(p, filename):
00168     ft_params = { "mass" : p[0],
00169                   "force_zero_x" : p[1],
00170                   "force_zero_y" : p[2],
00171                   "force_zero_z" : p[3],
00172                   "com_pos_x" : p[4],
00173                   "com_pos_y" : p[5],
00174                   "com_pos_z" : p[6],
00175                   "torque_zero_x" : p[7],
00176                   "torque_zero_y" : p[8],
00177                   "torque_zero_z" : p[9] }
00178     rosparam.upload_params(rospy.get_name(), ft_params)
00179     rospy.sleep(0.5)
00180     rosparam.dump_params(filename, rospy.get_name())
00181 
00182 class WrenchZeroer(object):
00183     def __init__(self, start_zero=True, is_backwards=True):
00184 
00185         wrench_topic = rospy.get_param("~wrench_topic")
00186         self.gravity_frame = rospy.get_param("~gravity_frame")
00187         self.wrench_frame = rospy.get_param("~wrench_frame")
00188         self.tf_list = tf.TransformListener()
00189         rospy.Subscriber("~rezero_wrench", Bool, self.rezero_wrench)
00190         self.zero_pub = rospy.Publisher("~wrench_zeroed", WrenchStamped)
00191         self.vis_pub = rospy.Publisher("~wrench_markers", Marker)
00192 
00193         self.mass = rospy.get_param("~mass")
00194         self.wrench_zero = np.mat(np.zeros((6, 1)))
00195         self.wrench_zero[0, 0] = rospy.get_param("~force_zero_x")
00196         self.wrench_zero[1, 0] = rospy.get_param("~force_zero_y")
00197         self.wrench_zero[2, 0] = rospy.get_param("~force_zero_z")
00198         self.wrench_zero[3, 0] = rospy.get_param("~torque_zero_x")
00199         self.wrench_zero[4, 0] = rospy.get_param("~torque_zero_y")
00200         self.wrench_zero[5, 0] = rospy.get_param("~torque_zero_z")
00201         self.com_pos = np.mat(np.zeros((3, 1)))
00202         self.com_pos[0, 0] = rospy.get_param("~com_pos_x")
00203         self.com_pos[1, 0] = rospy.get_param("~com_pos_y")
00204         self.com_pos[2, 0] = rospy.get_param("~com_pos_z")
00205 
00206         if start_zero:
00207             self.got_zero = False
00208         else:
00209             self.got_zero = True
00210 
00211         if is_backwards:
00212             self.react_mult = -1.
00213         else:
00214             self.react_mult = 1.
00215 
00216         self.wrench_location_frame = rospy.get_param("~wrench_location_frame")
00217         self.wrench_base_frame = rospy.get_param("~wrench_base_frame")
00218 
00219         self.colors = [ColorRGBA(1., 0., 0., 1.), ColorRGBA(0., 1., 0., 1.)]
00220 
00221         self.msg_lock = Lock()
00222         self.last_msg_time = 0.
00223         rospy.Subscriber(wrench_topic, WrenchStamped, self.save_cur_msg, queue_size=1)
00224         rospy.sleep(0.5)
00225 
00226     def save_cur_msg(self, msg):
00227         with self.msg_lock:
00228             self.cur_msg = msg
00229             self.last_msg_time = rospy.get_time()
00230 
00231     def _process_wrench(self, te):
00232         with self.msg_lock:
00233             #if rospy.get_time() - self.last_msg_time > 0.1:
00234                 #rospy.loginfo("[wrench_zeroing]: Msg data over 100ms old, not publishing.");
00235             #    return
00236             cur_wrench = np.mat([self.cur_msg.wrench.force.x,
00237                                  self.cur_msg.wrench.force.y,
00238                                  self.cur_msg.wrench.force.z,
00239                                  self.cur_msg.wrench.torque.x,
00240                                  self.cur_msg.wrench.torque.y,
00241                                  self.cur_msg.wrench.torque.z]).T
00242             header = self.cur_msg.header
00243         try:
00244             (ft_pos, ft_quat) = self.tf_list.lookupTransform(self.gravity_frame,
00245                                                              self.wrench_frame,
00246                                                              rospy.Time(0))
00247         except tf.LookupException as le:
00248             #rospy.loginfo("[wrench zeroing] TF Failure: \r\n %s,\r\n %s" %(sys.exc_info()[0], le))
00249             return
00250         except tf.ExtrapolationException as ee:
00251         #    rospy.loginfo("[wrench zeroing] TF Failure:\r\n %s,\r\n%s" %(sys.exc_info()[0], ee))
00252             return
00253         except tf.Exception as tfe:
00254            # rospy.loginfo("[wrench_zeroing]: TF failure:\r\n%s, \r\n%s" %(sys.exc_info()[0], tfe))
00255             return
00256         except:
00257             rospy.loginfo("[wrench_zeroing]: Unknown TF failure:\r\n%s" %sys.exc_info()[0])
00258             return
00259         rot_mat = np.mat(tf_trans.quaternion_matrix(ft_quat))[:3,:3]
00260         z_grav = self.react_mult * rot_mat.T * np.mat([0, 0, -1.]).T
00261         force_grav = np.mat(np.zeros((6, 1)))
00262         force_grav[:3, 0] = self.mass * g * z_grav
00263         torque_grav = np.mat(np.zeros((6, 1)))
00264         torque_grav[3:, 0] = np.mat(np.cross(self.com_pos.T.A[0], force_grav[:3, 0].T.A[0])).T
00265         zeroing_wrench = force_grav + torque_grav + self.wrench_zero
00266         zeroed_wrench = self.react_mult * (cur_wrench - zeroing_wrench)
00267 
00268         if not self.got_zero:
00269             self.wrench_zero = (cur_wrench - (force_grav + torque_grav))
00270             self.got_zero = True
00271 
00272         if self.wrench_frame == self.wrench_location_frame:
00273             tf_zeroed_wrench = self.transform_wrench(zeroed_wrench)
00274             if tf_zeroed_wrench is None:
00275                 rospy.loginfo("Second TF Fail")
00276                 return
00277         else:
00278             tf_zeroed_wrench = zeroed_wrench
00279         zero_msg = WrenchStamped(header,
00280                                  Wrench(Vector3(*tf_zeroed_wrench[:3,0]),
00281                                         Vector3(*tf_zeroed_wrench[3:,0])))
00282         self.zero_pub.publish(zero_msg)
00283         self.visualize_wrench(tf_zeroed_wrench)
00284 
00285     def transform_wrench(self, wrench):
00286         try:
00287             ft_pos, ft_quat = self.tf_list.lookupTransform(self.gravity_frame,
00288                                                            self.wrench_frame,
00289                                                            rospy.Time(0))
00290             loc_pos, loc_quat = self.tf_list.lookupTransform(self.gravity_frame,
00291                                                              self.wrench_location_frame,
00292                                                              rospy.Time(0))
00293             base_pos, base_quat = self.tf_list.lookupTransform(self.wrench_base_frame,
00294                                                                self.wrench_frame,
00295                                                                rospy.Time(0))
00296         except:
00297             return None
00298         pos_diff = np.array(ft_pos) - np.array(loc_pos)
00299         loc_tf_wrench = wrench.copy()
00300         loc_tf_wrench[3:, 0] += np.mat(np.cross(pos_diff, loc_tf_wrench[:3, 0].T.A[0])).T
00301         base_rot_mat = np.mat(tf_trans.quaternion_matrix(base_quat))[:3,:3]
00302         zs = np.mat(np.zeros((3,3)))
00303         base_tf_wrench = np.bmat([[base_rot_mat, zs], [zs, base_rot_mat]]) * loc_tf_wrench
00304         return base_tf_wrench
00305 
00306     def rezero_wrench(self, msg):
00307         self.got_zero = False
00308 
00309     def visualize_wrench(self, wrench):
00310         try:
00311             loc_pos, loc_quat = self.tf_list.lookupTransform(self.wrench_base_frame,
00312                                                              self.wrench_location_frame,
00313                                                              rospy.Time(0))
00314         except:
00315             return
00316         self.publish_vector(self.wrench_base_frame, np.array(loc_pos), 0.05 * wrench[:3,0].A.T[0], 0)
00317         self.publish_vector(self.wrench_base_frame, np.array(loc_pos), 0.2 * wrench[3:,0].A.T[0], 1)
00318 
00319     def publish_vector(self, frame, loc, v, m_id):
00320         m = Marker()
00321         m.header.frame_id = frame
00322         m.header.stamp = rospy.Time.now()
00323         m.ns = "wrench_zeroing"
00324         m.id = m_id
00325         m.type = Marker.ARROW
00326         m.action = Marker.ADD
00327         m.points.append(Point(*loc))
00328         m.points.append(Point(*(loc + v)))
00329         m.scale = Vector3(0.01, 0.02, 0.01)
00330         m.color = self.colors[m_id]
00331         self.vis_pub.publish(m)
00332 
00333     def start_publisher(self, rate):
00334         self.timer_pub = rospy.Timer(rospy.Duration(1./rate), self._process_wrench)
00335 
00336 def main():
00337     rospy.init_node("wrench_zeroing")
00338 
00339     from optparse import OptionParser
00340     p = OptionParser()
00341     p.add_option('-f', '--file', dest="filename", default="",
00342                  help="YAML file to save parameters in.")
00343     p.add_option('-l', '--load', dest="is_load",
00344                  action="store_true", default=False,
00345                  help="Load parameters from file.")
00346     p.add_option('-r', '--run', dest="is_run",
00347                  action="store_true", default=False,
00348                  help="Publish a zeroed wrench.")
00349     p.add_option('-t', '--train', dest="is_train",
00350                  action="store_true", default=False,
00351                  help="Train by moving the gripper to different poses and measuring the wrenches.")
00352     p.add_option('-b', '--backwards', dest="is_backwards",
00353                  action="store_true", default=False,
00354                  help="Set if the sensor is mounted backwards.")
00355     p.add_option('-p', '--pr2', dest="is_pr2",
00356                  action="store_true", default=False,
00357                  help="Will run automated data collection if this is the PR2.")
00358     p.add_option('-z', '--start_zero', dest="start_zero",
00359                  action="store_true", default=False,
00360                  help="Use the first value in to zero the sensor.")
00361     p.add_option('-n', '--ntrials', dest="n_trials",
00362                  default="6,6,4",
00363                  help="Number of trials for each of the last 3 joint angles to move through. (default: 6,6,4)")
00364     p.add_option('-c', '--rate', dest="rate", type="int", default=100,
00365                  help="Rate at which to publish the output topics.")
00366     (opts, args) = p.parse_args()
00367 
00368     try:
00369         n_4, n_5, n_6 = [int(n_str) for n_str in opts.n_trials.split(',')]
00370     except:
00371         print "Bad --ntrials parameter (format: --ntrials 6,6,4)"
00372         p.print_help()
00373         return
00374 
00375     if opts.is_load:
00376         params = rosparam.load_file(opts.filename, rospy.get_name())[0][0]
00377         rosparam.upload_params(rospy.get_name(), params)
00378 
00379     if opts.is_run:
00380         rospy.sleep(0.1)
00381         nft_z = WrenchZeroer(start_zero=opts.start_zero, is_backwards=opts.is_backwards)
00382         nft_z.start_publisher(opts.rate)
00383         rospy.spin()
00384         return
00385 
00386     if opts.is_train:
00387         if opts.is_pr2:
00388             data = collect_data_pr2(n_4, n_5, n_6)
00389             param_vector = process_data(data, is_backwards=opts.is_backwards)
00390             save_params(param_vector, opts.filename)
00391             return
00392         else:
00393             data = collect_data_tool()
00394             param_vector = process_data(data, is_backwards=opts.is_backwards)
00395             save_params(param_vector, opts.filename)
00396             return
00397 
00398 if __name__ == "__main__":
00399     main()


hrl_ft
Author(s): Kelsey Hawkins / kphawkins@gatech.edu, Advisor: Prof. Charlie Kemp (Healthcare Robotics Lab at Georgia Tech)
autogenerated on Wed Nov 27 2013 11:54:03