00001
00002
00003 import numpy as np
00004
00005 import roslib
00006 roslib.load_manifest('hrl_ellipsoidal_control')
00007 import rospy
00008 from tf import TransformBroadcaster
00009
00010 import hrl_geom.transformations as trans
00011 from hrl_geom.pose_converter import PoseConv
00012 from hrl_geom import transformations as trans
00013 from hrl_ellipsoidal_control.controller_base import min_jerk_traj
00014
00015 class EllipsoidSpace(object):
00016 def __init__(self, E=1, is_oblate=False):
00017 self.A = 1
00018 self.E = E
00019
00020 self.a = self.A * self.E
00021 self.is_oblate = is_oblate
00022 self.center = None
00023 self.frame_broadcaster = TransformBroadcaster()
00024 self.center_tf_timer = None
00025
00026 def load_ell_params(self, ell_pose, E, is_oblate=False, height=1):
00027 rospy.loginfo("Loading Ellipsoid Parameters")
00028 self.set_center(ell_pose)
00029 self.E = E
00030 self.a = self.A * self.E
00031 self.is_oblate = is_oblate
00032 self.height = height
00033
00034 def set_center(self, pose_stamped):
00035 rospy.loginfo("[ellipsoid_space] Setting center to:\r\n %s" %pose_stamped)
00036 if self.center_tf_timer is not None:
00037 self.center_tf_timer.shutdown()
00038 self.center = pose_stamped
00039 def broadcast_ell_center(event):
00040 tr = (pose_stamped.pose.position.x,
00041 pose_stamped.pose.position.y,
00042 pose_stamped.pose.position.z)
00043 quat = (pose_stamped.pose.orientation.x,
00044 pose_stamped.pose.orientation.y,
00045 pose_stamped.pose.orientation.z,
00046 pose_stamped.pose.orientation.w)
00047 self.frame_broadcaster.sendTransform(tr, quat, rospy.Time.now(),
00048 '/ellipse_frame',
00049 self.center.header.frame_id)
00050 self.center_tf_timer = rospy.Timer(rospy.Duration(0.01), broadcast_ell_center)
00051
00052 def set_bounds(self, lat_bounds=None, lon_bounds=None, height_bounds=None):
00053 assert lon_bounds[1] >= 0
00054 self._lat_bounds = lat_bounds
00055 self._lon_bounds = lon_bounds
00056 self._height_bounds = height_bounds
00057
00058 def enforce_bounds(self, ell_pos):
00059 lat = np.clip(ell_pos[0], self._lat_bounds[0], self._lat_bounds[1])
00060 if self._lon_bounds[0] >= 0:
00061 lon = np.clip(ell_pos[1], self._lon_bounds[0], self._lon_bounds[1])
00062 else:
00063 ell_lon_1 = np.mod(ell_pos[1], 2 * np.pi)
00064 min_lon = np.mod(self._lon_bounds[0], 2 * np.pi)
00065 if (ell_lon_1 >= min_lon) or (ell_lon_1 <= self._lon_bounds[1]):
00066 lon = ell_pos[1]
00067 else:
00068 if min_lon - ell_lon_1 < ell_lon_1 - self._lon_bounds[1]:
00069 lon = min_lon
00070 else:
00071 lon = self._lon_bounds[1]
00072 height = np.clip(ell_pos[2], self._height_bounds[0], self._height_bounds[1])
00073 return np.array([lat, lon, height])
00074
00075 def ellipsoidal_to_cart(self, lat, lon, height):
00076
00077 if not self.is_oblate:
00078 x = self.a * np.sinh(height) * np.sin(lat) * np.cos(lon)
00079 y = self.a * np.sinh(height) * np.sin(lat) * np.sin(lon)
00080 z = self.a * np.cosh(height) * np.cos(lat)
00081 else:
00082 x = self.a * np.cosh(height) * np.cos(lat-np.pi/2) * np.cos(lon)
00083 y = self.a * np.cosh(height) * np.cos(lat-np.pi/2) * np.sin(lon)
00084 z = self.a * np.sinh(height) * np.sin(lat-np.pi/2)
00085 pos_local = np.mat([x, y, z]).T
00086 return pos_local
00087
00088 def partial_height(self, lat, lon, height):
00089
00090 if not self.is_oblate:
00091 x = self.a * np.cosh(height) * np.sin(lat) * np.cos(lon)
00092 y = self.a * np.cosh(height) * np.sin(lat) * np.sin(lon)
00093 z = self.a * np.sinh(height) * np.cos(lat)
00094 else:
00095 x = self.a * np.sinh(height) * np.sin(lat-np.pi/2) * np.cos(lon)
00096 y = self.a * np.sinh(height) * np.sin(lat-np.pi/2) * np.sin(lon)
00097 z = self.a * np.cosh(height) * np.cos(lat-np.pi/2)
00098 return np.mat([x, y, z]).T
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113 def ellipsoidal_to_pose(self, pose):
00114 ell_pos, ell_quat = PoseConv.to_pos_quat(pose)
00115 if not self.is_oblate:
00116 return self._ellipsoidal_to_pose_prolate(ell_pos, ell_quat)
00117 else:
00118 return self._ellipsoidal_to_pose_oblate(ell_pos, ell_quat)
00119
00120 def _ellipsoidal_to_pose_prolate(self, ell_pos, ell_quat):
00121 pos = self.ellipsoidal_to_cart(ell_pos[0], ell_pos[1], ell_pos[2])
00122 df_du = self.partial_height(ell_pos[0], ell_pos[1], ell_pos[2])
00123 nx, ny, nz = df_du.T.A[0] / np.linalg.norm(df_du)
00124 j = np.sqrt(1./(1.+ny*ny/(nz*nz)))
00125 k = -ny*j/nz
00126 norm_rot = np.mat([[-nx, ny*k - nz*j, 0],
00127 [-ny, -nx*k, j],
00128 [-nz, nx*j, k]])
00129 _, norm_quat = PoseConv.to_pos_quat(np.mat([0, 0, 0]).T, norm_rot)
00130 rot_angle = np.arctan(-norm_rot[2,1] / norm_rot[2,2])
00131
00132 quat_ortho_rot = trans.quaternion_from_euler(rot_angle + np.pi, 0.0, 0.0)
00133 norm_quat_ortho = trans.quaternion_multiply(norm_quat, quat_ortho_rot)
00134 norm_rot_ortho = np.mat(trans.quaternion_matrix(norm_quat_ortho)[:3,:3])
00135 if norm_rot_ortho[2, 2] > 0:
00136 flip_axis_ang = 0
00137 else:
00138 flip_axis_ang = np.pi
00139 quat_flip = trans.quaternion_from_euler(flip_axis_ang, 0.0, 0.0)
00140 norm_quat_ortho_flipped = trans.quaternion_multiply(norm_quat_ortho, quat_flip)
00141 ell_frame_quat = trans.quaternion_multiply(norm_quat_ortho_flipped, ell_quat)
00142 pose = PoseConv.to_pos_quat(pos, ell_frame_quat)
00143
00144
00145
00146
00147 return pose
00148
00149 def _ellipsoidal_to_pose_oblate(self, ell_pos, ell_quat):
00150 pos = self.ellipsoidal_to_cart(ell_pos[0], ell_pos[1], ell_pos[2])
00151 df_du = self.partial_height(-ell_pos[0], ell_pos[1], ell_pos[2])
00152 nx, ny, nz = df_du.T.A[0] / np.linalg.norm(df_du)
00153 j = np.sqrt(1./(1.+ny*ny/(nz*nz)))
00154 k = -ny*j/nz
00155 norm_rot = np.mat([[-nx, ny*k - nz*j, 0],
00156 [-ny, -nx*k, j],
00157 [-nz, nx*j, k]])
00158 _, norm_quat = PoseConv.to_pos_quat(np.mat([0, 0, 0]).T, norm_rot)
00159 rot_angle = np.arctan(-norm_rot[2,1] / norm_rot[2,2])
00160
00161 quat_ortho_rot = trans.quaternion_from_euler(rot_angle, 0.0, 0.0)
00162 norm_quat_ortho = trans.quaternion_multiply(norm_quat, quat_ortho_rot)
00163 quat_ortho_rot2 = trans.quaternion_from_euler(0.0, np.pi/2, 0.0)
00164 norm_quat_ortho = trans.quaternion_multiply(norm_quat_ortho, quat_ortho_rot2)
00165 if lon >= np.pi:
00166 quat_flip = trans.quaternion_from_euler(0.0, 0.0, np.pi)
00167 norm_quat_ortho = trans.quaternion_multiply(norm_quat_ortho, quat_flip)
00168 ell_frame_quat = trans.quaternion_multiply(norm_quat_ortho, ell_quat)
00169
00170 pose = PoseConv.to_pos_quat(pos, norm_quat_ortho)
00171
00172
00173
00174 return pose
00175
00176 def normal_to_ellipse(self, lat, lon, height):
00177 print "Finding ell_to_pose"
00178 if not self.is_oblate:
00179 return self._normal_to_ellipse_prolate(lat, lon, height)
00180 else:
00181 return self._normal_to_ellipse_oblate(lat, lon, height)
00182
00183 def _normal_to_ellipse_prolate(self, lat, lon, height):
00184 pos = self.ellipsoidal_to_cart(lat, lon, height)
00185 df_du = self.partial_height(lat, lon, height)
00186 nx, ny, nz = df_du.T.A[0] / np.linalg.norm(df_du)
00187 j = np.sqrt(1./(1.+ny*ny/(nz*nz)))
00188 k = -ny*j/nz
00189 norm_rot = np.mat([[-nx, ny*k - nz*j, 0],
00190 [-ny, -nx*k, j],
00191 [-nz, nx*j, k]])
00192 _, norm_quat = PoseConv.to_pos_quat(np.mat([0, 0, 0]).T, norm_rot)
00193 rot_angle = np.arctan(-norm_rot[2,1] / norm_rot[2,2])
00194
00195 quat_ortho_rot = trans.quaternion_from_euler(rot_angle + np.pi, 0.0, 0.0)
00196 norm_quat_ortho = trans.quaternion_multiply(norm_quat, quat_ortho_rot)
00197 norm_rot_ortho = np.mat(trans.quaternion_matrix(norm_quat_ortho)[:3,:3])
00198 if norm_rot_ortho[2, 2] > 0:
00199 flip_axis_ang = 0
00200 else:
00201 flip_axis_ang = np.pi
00202 quat_flip = trans.quaternion_from_euler(flip_axis_ang, 0.0, 0.0)
00203 norm_quat_ortho_flipped = trans.quaternion_multiply(norm_quat_ortho, quat_flip)
00204
00205 pose = PoseConv.to_pos_quat(pos, norm_quat_ortho_flipped)
00206
00207
00208
00209 return pose
00210
00211 def _normal_to_ellipse_oblate(self, lat, lon, height):
00212 pos = self.ellipsoidal_to_cart(lat, lon, height)
00213 df_du = self.partial_height(-lat, lon, height)
00214 nx, ny, nz = df_du.T.A[0] / np.linalg.norm(df_du)
00215 j = np.sqrt(1./(1.+ny*ny/(nz*nz)))
00216 k = -ny*j/nz
00217 norm_rot = np.mat([[-nx, ny*k - nz*j, 0],
00218 [-ny, -nx*k, j],
00219 [-nz, nx*j, k]])
00220 _, norm_quat = PoseConv.to_pos_quat(np.mat([0, 0, 0]).T, norm_rot)
00221 rot_angle = np.arctan(-norm_rot[2,1] / norm_rot[2,2])
00222
00223 quat_ortho_rot = trans.quaternion_from_euler(rot_angle, 0.0, 0.0)
00224 norm_quat_ortho = trans.quaternion_multiply(norm_quat, quat_ortho_rot)
00225 quat_ortho_rot2 = trans.quaternion_from_euler(0.0, np.pi/2, 0.0)
00226 norm_quat_ortho = trans.quaternion_multiply(norm_quat_ortho, quat_ortho_rot2)
00227 if lon >= np.pi:
00228 quat_flip = trans.quaternion_from_euler(0.0, 0.0, np.pi)
00229 norm_quat_ortho = trans.quaternion_multiply(norm_quat_ortho, quat_flip)
00230
00231 pose = PoseConv.to_pos_quat(pos, norm_quat_ortho)
00232
00233
00234
00235 return pose
00236
00237 def pose_to_ellipsoidal(self, pose):
00238 pose_pos, pose_rot = PoseConv.to_pos_rot(pose)
00239 lat, lon, height = self.pos_to_ellipsoidal(pose_pos[0,0], pose_pos[1,0], pose_pos[2,0])
00240 _, ell_rot = PoseConv.to_pos_rot(self.normal_to_ellipse(lat, lon, height))
00241 _, quat_rot = PoseConv.to_pos_quat(np.mat([0]*3).T, ell_rot.T * pose_rot)
00242 return [lat, lon, height], quat_rot
00243
00244 def pos_to_ellipsoidal(self, x, y, z):
00245 if not self.is_oblate:
00246 return self._pos_to_ellipsoidal_prolate(x, y, z)
00247 else:
00248 return self._pos_to_ellipsoidal_oblate(x, y, z)
00249
00250 def _pos_to_ellipsoidal_prolate(self, x, y, z):
00251 lon = np.arctan2(y, x)
00252 if lon < 0.:
00253 lon += 2 * np.pi
00254 p = np.sqrt(x**2 + y**2)
00255 a = self.a
00256 inner = np.sqrt((np.sqrt((z**2 - a**2 + p**2)**2 + (2. * a * p)**2) / a**2 -
00257 (z / a)**2 - (p / a)**2 + 1) / 2.)
00258 assert inner < 1.0001
00259 if inner > 0.9999:
00260 lat = np.pi/2.
00261 else:
00262 lat = np.arcsin(inner)
00263 if z < 0.:
00264 lat = np.pi - np.fabs(lat)
00265 if np.fabs(np.sin(lat)) > 0.05:
00266 if np.fabs(np.cos(lon)) > 0.05:
00267 use_case = 'x'
00268 sinh_height = x / (a * np.sin(lat) * np.cos(lon))
00269 height = np.log(sinh_height + np.sqrt(sinh_height**2 + 1))
00270 else:
00271 use_case = 'y'
00272 sinh_height = y / (a * np.sin(lat) * np.sin(lon))
00273 height = np.log(sinh_height + np.sqrt(sinh_height**2 + 1))
00274 else:
00275 use_case = 'z'
00276 cosh_height = z / (a * np.cos(lat))
00277 assert np.fabs(cosh_height) >= 1, ("cosh_height %f, a %f, x %f, y %f, z %f, lat %f, lon %f" %
00278 (cosh_height, a, x, y, z, lat, lon))
00279 height = np.log(cosh_height + np.sqrt(cosh_height**2 - 1))
00280 print ("%s pos_to_ellipsoidal: xyz: %f, %f, %f; latlonheight: %f, %f, %f" %
00281 (use_case, x, y, z, lat, lon, height))
00282 assert not np.any(np.isnan([lat, lon, height])), ("cosh_height %f, a %f, x %f, y %f, z %f, lat %f, lon %f" %
00283 (cosh_height, a, x, y, z, lat, lon))
00284 return lat, lon, height
00285
00286 def _pos_to_ellipsoidal_oblate(self, x, y, z):
00287 lon = np.arctan2(y, x)
00288 if lon < 0.:
00289 lon += 2 * np.pi
00290 p = np.sqrt(x**2 + y**2)
00291 a = self.a
00292 d_1 = np.sqrt((p + a)**2 + z**2)
00293 d_2 = np.sqrt((p - a)**2 + z**2)
00294 cosh_height = (d_1 + d_2) / (2 * a)
00295 assert np.fabs(cosh_height) >= 1, ("cosh_height %f, a %f, x %f, y %f, z %f, lat %f, lon %f" %
00296 (cosh_height, a, x, y, z, lat, lon))
00297 height = np.log(cosh_height + np.sqrt(cosh_height**2 - 1))
00298 cos_lat = (d_1 - d_2) / (2 * a)
00299 lat = np.arccos(cos_lat)
00300 if z < 0.:
00301 lat *= -1
00302
00303
00304 lat += np.pi / 2.
00305
00306 return lat, lon, height
00307
00308
00309 def create_ellipsoidal_path(self, start_ell_pos, start_ell_quat,
00310 end_ell_pos, end_ell_quat,
00311 velocity=0.001, min_jerk=True):
00312
00313 print "Start rot (%s):\r\n%s" %(type(start_ell_quat),start_ell_quat)
00314 print "End rot (%s):\r\n%s" %(type(end_ell_quat),end_ell_quat)
00315
00316 _, start_ell_rot = PoseConv.to_pos_rot((start_ell_pos,start_ell_quat))
00317 _, end_ell_rot = PoseConv.to_pos_rot((end_ell_pos,end_ell_quat))
00318 rpy = trans.euler_from_matrix(start_ell_rot.T * end_ell_rot)
00319 end_ell_pos[1] = np.mod(end_ell_pos[1], 2 * np.pi)
00320 ell_init = np.mat(start_ell_pos).T
00321 ell_final = np.mat(end_ell_pos).T
00322
00323
00324 if np.fabs(2 * np.pi + ell_final[1,0] - ell_init[1,0]) < np.pi:
00325 ell_final[1,0] += 2 * np.pi
00326 elif np.fabs(-2 * np.pi + ell_final[1,0] - ell_init[1,0]) < np.pi:
00327 ell_final[1,0] -= 2 * np.pi
00328 if np.any(np.isnan(ell_init)) or np.any(np.isnan(ell_final)):
00329 rospy.logerr("[ellipsoid_space] Nan values in ellipsoid. " +
00330 "ell_init: %f, %f, %f; " % (ell_init[0,0], ell_init[1,0], ell_init[2,0]) +
00331 "ell_final: %f, %f, %f; " % (ell_final[0,0], ell_final[1,0], ell_final[2,0]))
00332 return None
00333
00334 num_samps = np.max([2, int(np.linalg.norm(ell_final - ell_init) / velocity),
00335 int(np.linalg.norm(rpy) / velocity)])
00336 if min_jerk:
00337 t_vals = min_jerk_traj(num_samps)
00338 else:
00339 t_vals = np.linspace(0,1,num_samps)
00340
00341
00342 ell_lat_traj = np.interp(t_vals, (0,1),(start_ell_pos[0], end_ell_pos[0]))
00343 ell_lon_traj = np.interp(t_vals, (0,1),(start_ell_pos[1], end_ell_pos[1]))
00344 ell_height_traj = np.interp(t_vals, (0,1),(start_ell_pos[2], end_ell_pos[2]))
00345 ell_pos_traj = np.vstack((ell_lat_traj, ell_lon_traj, ell_height_traj))
00346
00347 ell_quat_traj = [trans.quaternion_slerp(start_ell_quat, end_ell_quat, t) for t in t_vals]
00348 return [(ell_pos_traj[:,i], ell_quat_traj[i]) for i in xrange(num_samps)]
00349
00350 def main():
00351 e_space = EllipsoidSpace(1)
00352
00353 for xm in range(2):
00354 for ym in range(2):
00355 for zm in range(2):
00356 for i in range(10000):
00357 x, y, z = np.random.uniform(-2.5, 2.5, 3)
00358 lat, lon, height = e_space.pos_to_ellipsoidal(xm*x, ym*y, zm*z)
00359 assert lat >= 0 and lat <= np.pi*1.0001, ("latlonheight: %f, %f, %f" %
00360 (lat, lon, height))
00361 assert lon >= 0 and lon < 2*np.pi*1.0001, ("latlonheight: %f, %f, %f" %
00362 (lat, lon, height))
00363 assert height >= 0, ("latlonheight: %f, %f, %f" %
00364 (lat, lon, height))
00365
00366
00367 if __name__ == "__main__":
00368 main()