ellipsoid_space.py
Go to the documentation of this file.
00001 #! /usr/bin/python
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         #self.B = np.sqrt(1. - E**2)
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         #assert height > 0 and lat >= 0 and lat <= np.pi and lon >= 0 and lon < 2 * np.pi
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         #assert height > 0 and lat >= 0 and lat <= np.pi and lon >= 0 and lon < 2 * np.pi
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     #def partial_v(self, lat, lon, height):
00101     #    #assert height > 0 and lat >= 0 and lat <= np.pi and lon >= 0 and lon < 2 * np.pi
00102     #    x = self.a * np.sinh(height) * np.cos(lat) * np.cos(lon)
00103     #    y = self.a * np.sinh(height) * np.cos(lat) * np.sin(lon)
00104     #    z = self.a * np.cosh(height) * -np.sin(lat)
00105     #    return np.mat([x, y, z]).T
00106     #def partial_p(self, lat, lon, height):
00107     #    #assert height > 0 and lat >= 0 and lat <= np.pi and lon >= 0 and lon < 2 * np.pi
00108     #    x = self.a * np.sinh(height) * np.sin(lat) * -np.sin(lon)
00109     #    y = self.a * np.sinh(height) * np.sin(lat) * np.cos(lon)
00110     #    z = 0.
00111     #    return np.mat([x, y, z]).T
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         #print norm_rot
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         #print ("ellipsoidal_to_pose: latlonheight: %f, %f, %f" %
00145         #       (lat, lon, height) +
00146         #       str(PoseConv.to_homo_mat(pose)))
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         #print norm_rot
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         #print ("ellipsoidal_to_pose: latlonheight: %f, %f, %f" %
00172         #       (lat, lon, height) +
00173         #       str(PoseConv.to_homo_mat(pose)))
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         #print norm_rot
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         #print ("ellipsoidal_to_pose: latlonheight: %f, %f, %f" %
00207         #       (lat, lon, height) +
00208         #       str(PoseConv.to_homo_mat(pose)))
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         #print norm_rot
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         #print ("ellipsoidal_to_pose: latlonheight: %f, %f, %f" %
00233         #       (lat, lon, height) +
00234         #       str(PoseConv.to_homo_mat(pose)))
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         # we're going to convert the latitude coord so it is always positive:
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) # get roll, pitch, yaw of angle diff
00319         end_ell_pos[1] = np.mod(end_ell_pos[1], 2 * np.pi) # wrap longitude value
00320         ell_init = np.mat(start_ell_pos).T 
00321         ell_final = np.mat(end_ell_pos).T
00322 
00323         # find the closest longitude angle to interpolate to
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         # smoothly interpolate from init to final
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     # test pos_to_ellipsoidal
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                     #print lat, lon, height
00366 
00367 if __name__ == "__main__":
00368     main()


hrl_ellipsoidal_control
Author(s): Kelsey Hawkins
autogenerated on Wed Nov 27 2013 11:41:49