skin_contact_to_resultant_force.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 #
00004 # April 17, 2012
00005 #
00006 # This node will take as input the SkinContact message from the
00007 # software simulation (which contains the contact locations and the
00008 # full contact force vector), and will output another SkinContact
00009 # message that has the force and contact location for the resultant
00010 # force.
00011 #
00012 # The hope is to feed this into MPC and compare what whole-arm skin
00013 # gives us relative to having a force-torque sensor at the base of
00014 # each link.
00015 #
00016 #
00017 
00018 import numpy as np, math
00019 import copy
00020 
00021 import roslib; roslib.load_manifest('hrl_common_code_darpa_m3')
00022 import hrl_lib.viz as hv
00023 import hrl_lib.util as ut
00024 import hrl_lib.transforms as tr
00025 
00026 import rospy
00027 import tf
00028 
00029 from visualization_msgs.msg import Marker
00030 from hrl_haptic_manipulation_in_clutter_msgs.msg import SkinContact
00031 
00032 from hrl_msgs.msg import FloatArrayBare
00033 from geometry_msgs.msg import Point
00034 from geometry_msgs.msg import Vector3
00035 
00036 
00037 #
00038 # Using Harvey Lipkin, Chapter 4, page 7, Poinsot's Wrench Theorem
00039 #
00040 # fc_mat - 3xN np matrix of contact forces
00041 # xc_mat - 3xN np matrix of contact locations (torso_lift_link)
00042 # o - 3x1 np matrix of point on the joint axis (torso_lift_link)
00043 # o_next - 3x1 np matrix of point on end of the link (torso_lift_link)
00044 def simulate_ft_sensor_on_link_base(fc_mat, xc_mat, o, o_next):
00045     f_res = fc_mat.sum(1)
00046     tau_res = np.matrix(np.cross((xc_mat-o).T.A, fc_mat.T.A)).T.sum(1)
00047 
00048     if np.linalg.norm(f_res) < 0.01:
00049         return np.matrix([0,0,0]).T, None
00050 
00051     poc = np.cross(f_res.A1, tau_res.A1) / (f_res.T * f_res)[0,0]
00052     nrml = np.cross(np.array([0.,0.,1.]), (o_next - o).A1)
00053     nrml = nrml / np.linalg.norm(nrml)
00054 
00055     # From Harvey Lipkin's notes, C is a point on the line of action
00056     # of the resultant force that is closest to O.
00057     # What I want is the point on the line of action that lies on the
00058     # link.
00059     # Let the vector from O to this desired point be Poc + l * f_res
00060     # where l is a scalar that I need to compute. For this vector to
00061     # be oriented along the link, it must be perpendicular to the
00062     # vector in the plane but normal to the link. This gives us:
00063     # (poc + l * f_res).T * nrml = 0
00064     # we can rearrange this equation to get:
00065     l = -np.dot(poc, nrml) / np.dot(nrml, f_res.A1)
00066 
00067     return f_res, o + np.matrix(poc).T + l * f_res
00068 
00069 def skin_contact_cb(sc, callback_args):
00070     sc_pub, kinematics = callback_args
00071     sf = SkinContact() # sf - single force
00072 
00073     sf.header.frame_id = '/torso_lift_link' # has to be this and no other coord frame.
00074     sf.header.stamp = sc.header.stamp
00075 
00076     if len(sc.link_names) == 0:
00077         sc_pub.publish(sc)
00078         return
00079 
00080     fc_l = []
00081     xc_l = []
00082     
00083     # no guarantee that contacts for a particular link will be
00084     # together in the list. so, i am sorting the list and preserving
00085     # the indices to get the appropriate forces and locations.
00086     link_names = sc.link_names
00087     idxs = range(len(link_names))
00088     sorted_list = zip(link_names, idxs)
00089     sorted_list.sort()
00090 
00091     i = 0
00092     for nm, idx in sorted_list:
00093         jt_num = int(nm[-1]) - 1 # assume that links are named link1 ...
00094 
00095         fc_l.append(np.matrix([sc.forces[idx].x, sc.forces[idx].y, sc.forces[idx].z]).T)
00096         xc_l.append(np.matrix([sc.locations[idx].x, sc.locations[idx].y, sc.locations[idx].z]).T) 
00097 
00098         if i == len(sorted_list)-1 or sorted_list[i+1][0] != nm:
00099             o, _ = kinematics.FK(q, jt_num)
00100             o_next, _ = kinematics.FK(q, jt_num+1)
00101             f_res, loc = simulate_ft_sensor_on_link_base(np.column_stack(fc_l),
00102                                                          np.column_stack(xc_l),
00103                                                          o, o_next)
00104             if loc == None:
00105                 continue
00106 
00107             nrml = np.cross(np.array([0.,0.,1.]), (o_next - o).A1)
00108             nrml = np.matrix(nrml / np.linalg.norm(nrml)).T
00109             if (nrml.T * f_res)[0,0] < 0.:
00110                 nrml = -1. * nrml
00111 
00112             f_res_direc = f_res / np.linalg.norm(f_res)
00113             link_length = np.linalg.norm(o-o_next)
00114             link_direc = (o_next - o) / link_length
00115             if (link_direc.T * f_res_direc)[0,0] > 0.93:
00116                 # here angle b/w nrml and f_res is < 20 degrees
00117                 loc = o_next
00118             elif (link_direc.T * f_res_direc)[0,0] < -0.93:
00119                 f_res = f_res * -1
00120                 loc = o_next
00121                 f_res_direc = -f_res_direc
00122 
00123             dist_from_o =  np.linalg.norm(loc-o)
00124             dist_from_o_next =  np.linalg.norm(loc-o_next)
00125             if dist_from_o > link_length or dist_from_o_next > link_length:
00126                 if dist_from_o > dist_from_o_next:
00127                     loc = o_next
00128                 else:
00129                     loc = o
00130 
00131             # contact close to the tip, assume circular tip and so
00132             # contact normal can be anything.
00133             if np.linalg.norm(loc-o_next) < 0.02:
00134                 nrml = f_res_direc
00135 
00136 
00137 
00138             sf.link_names.append(nm)
00139             sf.forces.append(Vector3(f_res[0,0], f_res[1,0], f_res[2,0]))
00140             sf.locations.append(Point(loc[0,0], loc[1,0], loc[2,0]))
00141             sf.normals.append(Vector3(nrml[0,0], nrml[1,0], nrml[2,0]))
00142             sf.pts_x.append(FloatArrayBare([loc[0,0]]))
00143             sf.pts_y.append(FloatArrayBare([loc[1,0]]))
00144             sf.pts_z.append(FloatArrayBare([loc[2,0]]))
00145 
00146             fc_l = []
00147             xc_l = []
00148 
00149         i+=1
00150     
00151     sc_pub.publish(sf)
00152 
00153 
00154 def joint_angles_cb(fab):
00155     global q
00156     q = copy.copy(fab.data)
00157 
00158 
00159 
00160 if __name__ == '__main__':
00161     import hrl_common_code_darpa_m3.robot_config.three_link_planar_capsule as d_robot
00162     roslib.load_manifest('hrl_software_simulation_darpa_m3')
00163     import hrl_software_simulation_darpa_m3.gen_sim_arms as gsa
00164 
00165     node_nm = 'skin_contact_to_resultant_force'
00166     rospy.init_node(node_nm)
00167 
00168     kinematics = gsa.RobotSimulatorKDL(d_robot)
00169     q = [0.,0.,0.]
00170 
00171     skin_topic = '/skin/contacts'
00172     sc_pub = rospy.Publisher(skin_topic, SkinContact)
00173 
00174     rospy.Subscriber('/skin/contacts_all', SkinContact,
00175                      skin_contact_cb,
00176                      callback_args = (sc_pub, kinematics))
00177     rospy.Subscriber('/sim_arm/joint_angles', FloatArrayBare,
00178                      joint_angles_cb)
00179 
00180     rospy.loginfo('Started skin_contact to resultant force!')
00181     rospy.spin()
00182 
00183 


hrl_common_code_darpa_m3
Author(s): Advait Jain, Marc Killpack. Advisor: Prof. Charlie Kemp. Healthcare Robotics Lab, Georgia Tech
autogenerated on Wed Nov 27 2013 11:34:42