00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 import os
00023
00024 import roslib; roslib.load_manifest('katana_kinect_calibration')
00025 import rospy
00026
00027 import tf
00028 from tf.transformations import quaternion_slerp, euler_from_quaternion
00029
00030 from geometry_msgs.msg import Point, Quaternion, Pose, PoseStamped
00031 from sensor_msgs.msg import JointState
00032 from katana_msgs.msg import JointMovementAction, JointMovementGoal
00033 from ar_pose.msg import ARMarkers
00034
00035 from actionlib import SimpleActionClient
00036
00037 from math import sqrt, acos
00038
00039
00040 class CalibrateException(Exception):
00041 pass
00042 class NoJointStatesFoundException(CalibrateException):
00043 pass
00044 class NoTransformCachedException(CalibrateException):
00045 pass
00046 class LastHopReachedException(CalibrateException):
00047 pass
00048
00049
00050 JOINTS=['katana_motor1_pan_joint', 'katana_motor2_lift_joint', 'katana_motor3_lift_joint', 'katana_motor4_lift_joint', 'katana_motor5_wrist_roll_joint', 'katana_r_finger_joint', 'katana_l_finger_joint']
00051
00052
00053 def averagePoses(pose1, pose2, t):
00054 """Average two poses with weighting t to 1
00055
00056 This computes the average of the kartesian positions using
00057 linear interpolation and of the rotation using SLERP"""
00058
00059 factor= float(t)/(t+1)
00060 translation= tuple(map(lambda x,y: factor*x + (1-factor)*y, pose1[0], pose2[0]))
00061 rotation= quaternion_slerp(pose1[1], pose2[1], 1.0/(t+1))
00062 return (translation, rotation)
00063
00064
00065 class Dance:
00066 """Move Katana arm around in a given fashion"""
00067
00068 def __init__(self):
00069 self.poses= [
00070 [-0.07, 0.21, -0.14, -1.03, -2.96, 0.27, 0.27],
00071 [-1.35, 0.15, -0.25, -0.89, -2.96, 0.27, 0.27],
00072 [-1.3, 0.96, -0.09, 0.33, -2.96, 0.27, 0.27],
00073 [-0.07, 0.92, 0.1, 0.16, -2.96, 0.27, 0.27],
00074 [1.13, 0.93, 0.02, 0.16, -2.96, 0.27, 0.27],
00075 [1.64, 0.05, 0.26, -0.19, -2.96, 0.27, 0.27],
00076
00077 ]
00078 self.i= -1
00079 self.hopping= False
00080 self.client= SimpleActionClient('katana_arm_controller/joint_movement_action', JointMovementAction)
00081 self.client.wait_for_server()
00082
00083 def hop(self, pose= None, noreset= False):
00084 """Hop to next stance of the dance
00085
00086 pose - JointState, optional. If given, this pose will be taken instead of the next one
00087 noreset - Boolean, optional. If True, don't reset the PoseBuffer during movement
00088 """
00089 if pose == None:
00090 if self.i+1 >= len(self.poses):
00091 self.i= -1
00092 raise LastHopReachedException()
00093
00094 self.i= (self.i+1) % len(self.poses)
00095 goal= JointMovementGoal( jointGoal= JointState(name=JOINTS, position=self.poses[self.i]) )
00096 else:
00097 goal= JointMovementGoal(jointGoal= pose)
00098
00099 self.hopping= True
00100 if not noreset:
00101 transform.reset()
00102 self.client.send_goal(goal)
00103 self.client.wait_for_result()
00104 self.hopping= False
00105 if not noreset:
00106 transform.reset()
00107 return self.client.get_result()
00108
00109 def setup(self):
00110 """Get as safe as possible to the first stance"""
00111
00112 try:
00113 jointmsg= rospy.wait_for_message('/katana_joint_states', JointState, 2.0)
00114 self.old_pose= []
00115 for n in JOINTS:
00116 self.old_pose.append(jointmsg.position[jointmsg.name.index(n)])
00117 except Exception, e:
00118 raise NoJointStatesFoundException(e)
00119
00120 self.hop(JointState(
00121 name= ['katana_motor3_lift_joint', 'katana_motor4_lift_joint', 'katana_motor5_wrist_roll_joint', 'katana_r_finger_joint', 'katana_l_finger_joint'],
00122 position= [-2.18, -2.02, -2.96, 0.28, 0.28]
00123 ), noreset= True)
00124 self.hop( JointState(name= ['katana_motor2_lift_joint'], position= [2.16]), noreset= True )
00125 self.hop( JointState(name= ['katana_motor1_pan_joint'], position= [0.00]), noreset= True )
00126 self.hop(noreset= True)
00127
00128 def restoreOldPose(self):
00129 """This restores the pose of the arm before setup() was called"""
00130
00131 self.hop( JointState(name= JOINTS, position= [0.00, 2.16, -2.18, -2.02, -2.96, 0.28, 0.28]), noreset= True )
00132 self.hop( JointState(name= [JOINTS[0]], position= [self.old_pose[0]]), noreset= True)
00133 self.hop( JointState(name= [JOINTS[1]], position= [self.old_pose[1]]), noreset= True)
00134 self.hop( JointState(name= JOINTS[2:], position= self.old_pose[2:]), noreset= True )
00135
00136
00137 class TransformBuffer:
00138 """Accumulate poses of the camera as average"""
00139
00140 def __init__(self):
00141 self.reset()
00142 self.listener= tf.TransformListener()
00143 rospy.Subscriber('/ar_pose_markers', ARMarkers, self.update_cb)
00144
00145 def addTransform(self, transform):
00146 """add new transform to current average"""
00147
00148 (self.translation, self.rotation)= averagePoses( (self.translation, self.rotation), transform, self.samples)
00149 self.samples+= 1
00150
00151 def getTransform(self):
00152 if self.samples == 0:
00153 raise NoTransformCachedException()
00154 return (self.translation, self.rotation)
00155
00156 def reset(self):
00157 self.translation= (0,0,0)
00158 self.rotation= (1,0,0,0)
00159 self.samples= 0
00160 self.last_reset= rospy.get_time()
00161
00162 def update_cb(self, msg):
00163 if len(msg.markers) > 0:
00164 if dance.hopping:
00165 self.reset()
00166 try:
00167 self.listener.waitForTransform('/katana_pattern_seen', '/kinect_link', msg.header.stamp, rospy.Duration(2.0))
00168 transform= self.listener.lookupTransform('/katana_pattern_seen', '/kinect_link', msg.header.stamp)
00169
00170
00171 pose= PoseStamped( msg.header, Pose(Point(*(transform[0])), Quaternion(*(transform[1]))) )
00172 pose.header.frame_id= '/katana_pattern'
00173 base_transform= self.listener.transformPose('/base_link', pose)
00174
00175 p= base_transform.pose.position
00176 r= base_transform.pose.orientation
00177 self.addTransform(((p.x,p.y,p.z),(r.x, r.y, r.z, r.w)))
00178 except tf.Exception, e:
00179 rospy.loginfo('no transform /katana_pattern_seen -> /kinect_link available')
00180
00181
00182 if __name__ == '__main__':
00183 rospy.init_node('kinect_transform')
00184
00185
00186 config_file= rospy.get_param('~config_file', os.getenv('HOME') + '/.ros/kinect_pose.urdf.xacro')
00187 timeout= max(1, rospy.get_param('~timeout', 20))
00188 samples_required= max(1, rospy.get_param('~samples_required', 300))
00189 runs= max(1, rospy.get_param('~runs', 1))
00190 write_config= rospy.get_param('~write_config', True)
00191 publish_tf= rospy.get_param('~publish_tf', False)
00192
00193 dance= Dance()
00194 transform= TransformBuffer()
00195
00196 broadcaster= tf.TransformBroadcaster()
00197
00198 r= rospy.Rate(10)
00199 dance.setup()
00200 poses= []
00201 current_run= 0
00202 while not rospy.is_shutdown():
00203 if publish_tf:
00204 try:
00205 t= transform.getTransform()
00206 broadcaster.sendTransform(t[0], t[1], rospy.Time.now(), '/kinect_link', '/base_link')
00207 except NoTransformCachedException:
00208 pass
00209
00210 try:
00211 if transform.samples >= samples_required:
00212 rospy.loginfo('finished pose with %d samples.' % samples_required)
00213 poses.append(transform.getTransform())
00214 dance.hop()
00215 elif rospy.get_time() - transform.last_reset > timeout:
00216 if transform.samples > 0:
00217 rospy.logwarn('found only %d samples, but %d are required. Ignoring pose!' % (transform.samples, samples_required) )
00218 else:
00219 rospy.logwarn('could not detect marker! Ignoring pose!')
00220 dance.hop()
00221 except LastHopReachedException:
00222 current_run+= 1
00223 if current_run >= runs:
00224 break
00225 else:
00226
00227
00228 dance.hop()
00229 r.sleep()
00230
00231 dance.restoreOldPose()
00232
00233 if len(poses) == 0:
00234 rospy.logfatal('Could not use any pose at all! There were not enough (any?) recognized samples before timeout.')
00235 exit(1)
00236
00237
00238 mean= ((0,0,0), (1,0,0,0))
00239 avgdev= [0,0,0,0]
00240 for i in xrange(len(poses)):
00241 mean= averagePoses(mean, poses[i], i)
00242 for p in poses:
00243
00244 avgdev[0]+= abs(p[0][0]-mean[0][0])/len(poses)
00245 avgdev[1]+= abs(p[0][1]-mean[0][1])/len(poses)
00246 avgdev[2]+= abs(p[0][2]-mean[0][2])/len(poses)
00247
00248 avgdev[3]+= acos(sum( map(lambda x,y: x*y, mean[1], p[1]) )) / len(poses)
00249
00250 rospy.logdebug('Dist: %f (%f, %f, %f) / Angle: %f' % (sqrt(sum( map(lambda x,y: (x-y)*(x-y), p[0], mean[0]) )), p[0][0]-mean[0][0], p[0][1]-mean[0][1], p[0][2]-mean[0][2], acos(sum( map(lambda x,y: x*y, mean[1], p[1])))) )
00251
00252 rospy.loginfo('Averaged over %d poses\nMean: %s\nAvg. Dev.: (x: %.4f, y: %.4f, z: %.4f), rotation: %.4f' % (len(poses), str(mean), avgdev[0], avgdev[1], avgdev[2], avgdev[3]))
00253
00254 xyz= mean[0]
00255 rpy= euler_from_quaternion(mean[1])
00256 if write_config:
00257 config= open( config_file, 'w' )
00258 config.write('<?xml version="1.0"?>\n<robot xmlns:xacro="http://ros.org/wiki/xacro">\n')
00259 config.write('<property name="kinect_xyz" value="%.3f %.3f %.3f"/>\n' % (xyz[0], xyz[1], xyz[2]))
00260 config.write('<property name="kinect_rpy" value="%.3f %.3f %.3f"/>\n' % (rpy[0], rpy[1], rpy[2]))
00261 config.write('</robot>')
00262 config.close()