00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 from __future__ import division
00039 import roslib
00040 import rospy
00041 import scipy
00042 import pdb
00043 import random
00044 import math
00045 import scipy.linalg
00046 from geometry_msgs.msg import PoseStamped, Point, Pose, Vector3
00047 from convert_functions import *
00048 import geometry_msgs
00049
00050
00051
00052 class ClusterBoundingBoxFinder:
00053
00054 def __init__(self, tf_listener, tf_broadcaster, base_frame=""):
00055 assert(tf_listener)
00056 assert(tf_broadcaster)
00057 self.tf_listener = tf_listener
00058 self.tf_broadcaster = tf_broadcaster
00059 self.base_frame = base_frame
00060
00061
00062 if self.base_frame == "":
00063 self.base_frame = rospy.get_param("~z_up_frame", "")
00064
00065
00066
00067 def pca(self, points):
00068 cov_mat = points[0:3,:] * points[0:3,:].T/scipy.shape(points)[1]
00069 (values, vectors) = scipy.linalg.eig(cov_mat)
00070 if values[1] > values[0]:
00071 directions = [vectors[:,1], vectors[:,0]]
00072 else:
00073 directions = [vectors[:,0], vectors[:,1]]
00074
00075 return directions
00076
00077
00078
00079 def mean_shift_xy(self, points):
00080 mean = [0]*2
00081 shifted_points = points.copy()
00082 for i in range(2):
00083 mean[i] = scipy.mean(points[i,:])
00084 shifted_points[i,:] -= mean[i]
00085 return (shifted_points, mean)
00086
00087
00088
00089 def remove_outliers(self, points):
00090
00091 points_filtered = points[:]
00092 empty_space_width = .02
00093 check_percent = .02
00094 edge_width = .005
00095
00096
00097 for dim in range(3):
00098
00099
00100 num_points = scipy.shape(points_filtered)[1]
00101 points_sorted = points_filtered[:, points_filtered[dim, :].argsort().tolist()[0]]
00102
00103
00104 ind = int(math.floor(num_points*(1-check_percent)))
00105 if points_sorted[dim, -1] - points_sorted[dim, ind] > empty_space_width:
00106
00107
00108 searcharr = scipy.array(points_sorted[dim, :]).flatten()
00109 searchval = points_sorted[dim, ind] + edge_width
00110 thres_ind = scipy.searchsorted(searcharr, searchval)
00111 if thres_ind != 0 and thres_ind != len(searcharr):
00112 points_filtered = points_sorted[:, 0:thres_ind]
00113 rospy.loginfo("chopped points off of dim %d, highest val = %5.3f, searchval = %5.3f"%(dim, points_sorted[dim, -1], searchval))
00114 else:
00115 points_filtered = points_sorted
00116
00117
00118 if dim != 2:
00119 ind = int(math.floor(num_points*check_percent))
00120 if points_filtered[dim, ind] - points_filtered[dim, 0] > empty_space_width:
00121
00122
00123 searcharr = scipy.array(points_sorted[dim, :]).flatten()
00124 searchval = points_sorted[dim, ind] - edge_width
00125 thres_ind = scipy.searchsorted(searcharr, searchval)
00126 if thres_ind != 0 and thres_ind != len(searcharr):
00127 points_filtered = points_filtered[:, thres_ind:-1]
00128 rospy.loginfo("chopped points off of dim -%d, lowest val = %5.3f, searchval = %5.3f"%(dim, points_sorted[dim, 0], searchval))
00129 return points_filtered
00130
00131
00132
00133
00134
00135 def find_object_frame_and_bounding_box(self, point_cloud):
00136
00137
00138
00139 if self.base_frame == "":
00140 self.base_frame = rospy.get_param("~z_up_frame", point_cloud.header.frame_id)
00141
00142
00143 cluster_frame = point_cloud.header.frame_id
00144
00145 (points, cluster_to_base_frame) = transform_point_cloud(self.tf_listener, point_cloud, self.base_frame)
00146 if points == None:
00147 return (None, None, None)
00148
00149
00150
00151 table_height = points[2,:].min()
00152
00153
00154 (shifted_points, xy_mean) = self.mean_shift_xy(points)
00155 directions = self.pca(shifted_points[0:2, :])
00156
00157
00158
00159
00160 y_axis = scipy.mat([directions[1][0], directions[1][1], 0.])
00161 z_axis = scipy.mat([0.,0.,1.])
00162 x_axis = scipy.cross(y_axis, z_axis)
00163 rotmat = scipy.matrix(scipy.identity(4))
00164 rotmat[0:3, 0] = x_axis.T
00165 rotmat[0:3, 1] = y_axis.T
00166 rotmat[0:3, 2] = z_axis.T
00167 rotmat[2, 3] = table_height
00168 object_points = rotmat**-1 * shifted_points
00169
00170
00171 object_points = self.remove_outliers(object_points)
00172
00173
00174 object_bounding_box = [[0]*3 for i in range(2)]
00175 object_bounding_box_dims = [0]*3
00176 for dim in range(3):
00177 object_bounding_box[0][dim] = object_points[dim,:].min()
00178 object_bounding_box[1][dim] = object_points[dim,:].max()
00179 object_bounding_box_dims[dim] = object_bounding_box[1][dim] - object_bounding_box[0][dim]
00180
00181
00182 x_offset = object_bounding_box[1][0] - object_bounding_box_dims[0]/2.
00183 y_offset = object_bounding_box[1][1] - object_bounding_box_dims[1]/2.
00184 for i in range(2):
00185 object_bounding_box[i][0] -= x_offset
00186 object_bounding_box[i][1] -= y_offset
00187 object_points[0, :] -= x_offset
00188 object_points[1, :] -= y_offset
00189 offset_mat = scipy.mat(scipy.identity(4))
00190 offset_mat[0,3] = x_offset
00191 offset_mat[1,3] = y_offset
00192 rotmat = rotmat * offset_mat
00193
00194
00195
00196
00197 unshift_mean = scipy.identity(4)
00198 unshift_mean[0,3] = xy_mean[0]
00199 unshift_mean[1,3] = xy_mean[1]
00200 object_to_base_frame = unshift_mean*rotmat
00201 object_to_cluster_frame = cluster_to_base_frame**-1 * object_to_base_frame
00202
00203
00204 (object_frame_pos, object_frame_quat) = mat_to_pos_and_quat(object_to_cluster_frame)
00205
00206
00207 object_pose = PoseStamped()
00208 object_pose.pose.position.x = object_frame_pos[0]
00209 object_pose.pose.position.y = object_frame_pos[1]
00210 object_pose.pose.position.z = object_frame_pos[2]
00211 object_pose.pose.orientation.x = object_frame_quat[0]
00212 object_pose.pose.orientation.y = object_frame_quat[1]
00213 object_pose.pose.orientation.z = object_frame_quat[2]
00214 object_pose.pose.orientation.w = object_frame_quat[3]
00215 object_pose.header.frame_id = cluster_frame
00216
00217 return (object_points, object_bounding_box_dims, object_bounding_box, object_pose)
00218