pose_converter.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Provides scripts for automatically converting from different pose types 
00004 # to others.
00005 #
00006 # Copyright (c) 2012, Georgia Tech Research Corporation
00007 # All rights reserved.
00008 # 
00009 # Redistribution and use in source and binary forms, with or without
00010 # modification, are permitted provided that the following conditions are met:
00011 #     * Redistributions of source code must retain the above copyright
00012 #       notice, this list of conditions and the following disclaimer.
00013 #     * Redistributions in binary form must reproduce the above copyright
00014 #       notice, this list of conditions and the following disclaimer in the
00015 #       documentation and/or other materials provided with the distribution.
00016 #     * Neither the name of the Georgia Tech Research Corporation nor the
00017 #       names of its contributors may be used to endorse or promote products
00018 #       derived from this software without specific prior written permission.
00019 # 
00020 # THIS SOFTWARE IS PROVIDED BY GEORGIA TECH RESEARCH CORPORATION ''AS IS'' AND
00021 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00022 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00023 # DISCLAIMED. IN NO EVENT SHALL GEORGIA TECH BE LIABLE FOR ANY DIRECT, INDIRECT,
00024 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00025 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00026 # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00027 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
00028 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00029 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 #
00031 # Author: Kelsey Hawkins
00032 
00033 import numpy as np
00034 import copy
00035 
00036 import roslib; roslib.load_manifest('hrl_geom')
00037 import rospy
00038 from std_msgs.msg import Header
00039 from geometry_msgs.msg import PoseStamped, Pose, Point, Quaternion, PointStamped
00040 from geometry_msgs.msg import Transform, TransformStamped, Vector3
00041 from geometry_msgs.msg import Twist, TwistStamped
00042 import hrl_geom.transformations as trans
00043 
00044 def rot_mat_to_axis_angle(R):
00045     ang = np.arccos((np.trace(R) - 1.0)/2.0)
00046     axis = np.array([R[2,1]-R[1,2], R[0,2]-R[2,0], R[1,0]-R[0,1]])
00047     axis = axis / np.linalg.norm(axis)
00048     return (axis, ang)
00049 
00050 def axis_angle_to_rot_mat(axis, ang):
00051     axis = axis / np.linalg.norm(axis)
00052     K = np.mat([[0.0, -axis[2], axis[1]],
00053                 [axis[2], 0.0, -axis[0]],
00054                 [-axis[1], axis[0], 0.0]])
00055     I = np.mat(np.eye(3))
00056     return I + np.sin(ang)*K + (1.0 - np.cos(ang))*K*K
00057 
00058 ##
00059 # Static class with a set of conversion functions for converting any of the supported
00060 # pose types into any of the others without having to provide the type explicity.
00061 class PoseConv(object):
00062     POSE_TYPES = [
00063         'pose_msg', 
00064         'pose_stamped_msg', 
00065         'point_msg', 
00066         'point_stamped_msg',
00067         'tf_msg',
00068         'tf_stamped_msg',
00069         'twist_msg',
00070         'twist_stamped_msg',
00071         'homo_mat',
00072         'pos_rot',
00073         'pos_quat',
00074         'pos_euler',
00075         'pos_axis_angle']
00076 
00077     ##
00078     # Returns a string describing the type of the given pose as
00079     # listed in PoseConv.POSE_TYPES. Returns None if unrecognized.
00080     @staticmethod
00081     def get_type(*args):
00082         try:
00083             if type(args[0]) is str:
00084                 return PoseConv.get_type(*args[1:])
00085             if len(args) == 1:
00086                 if type(args[0]) is Pose:
00087                     return 'pose_msg'
00088                 elif type(args[0]) is PoseStamped:
00089                     return 'pose_stamped_msg'
00090                 elif type(args[0]) is Transform:
00091                     return 'tf_msg'
00092                 elif type(args[0]) is TransformStamped:
00093                     return 'tf_stamped_msg'
00094                 elif type(args[0]) is Twist:
00095                     return 'twist_msg'
00096                 elif type(args[0]) is TwistStamped:
00097                     return 'twist_stamped_msg'
00098                 elif type(args[0]) is Point:
00099                     return 'point_msg'
00100                 elif type(args[0]) is PointStamped:
00101                     return 'point_stamped_msg'
00102                 elif isinstance(args[0], (np.matrix, np.ndarray)) and np.shape(args[0]) == (4, 4):
00103                     return 'homo_mat'
00104                 elif isinstance(args[0], (tuple, list)) and len(args[0]) == 2:
00105                     pos_arg = np.mat(args[0][0])
00106                     if pos_arg.shape != (1, 3) and pos_arg.shape != (3, 1):
00107                         return None
00108                     if isinstance(args[0][1], (tuple, list)) and len(args[0][1]) == 2:
00109                         if len(args[0][1][0]) == 3 and np.array(args[0][1][1]).size == 1:
00110                             return 'pos_axis_angle'
00111                         else:
00112                             return None
00113                     rot_arg = np.mat(args[0][1])
00114                     if rot_arg.shape == (3, 3):
00115                         return 'pos_rot'
00116                     if 1 not in rot_arg.shape:
00117                         return None
00118                     rot_arg = rot_arg.tolist()[0]
00119                     if len(rot_arg) == 3:
00120                         return 'pos_euler'
00121                     elif len(rot_arg) == 4:
00122                         return 'pos_quat'
00123                     else:
00124                         return None
00125             elif len(args) == 2:
00126                 return PoseConv.get_type(((args[0], args[1]),))
00127         except:
00128             pass
00129         return None
00130 
00131     ##
00132     # @return geometry_msgs.Pose
00133     @staticmethod
00134     def to_pose_msg(*args):
00135         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00136         if homo_mat is None:
00137             rospy.logwarn("[pose_converter] Unknown pose type.")
00138             return None
00139         else:
00140             return Pose(Point(*homo_mat[:3,3].T.A[0]), Quaternion(*quat_rot))
00141 
00142     ##
00143     # @return geometry_msgs.PoseStamped
00144     @staticmethod
00145     def to_pose_stamped_msg(*args):
00146         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00147         if homo_mat is None:
00148             rospy.logwarn("[pose_converter] Unknown pose type.")
00149             return None
00150         ps = PoseStamped()
00151         if header is None:
00152             ps.header.stamp = rospy.Time.now()
00153         else:
00154             ps.header.seq = header[0]
00155             ps.header.stamp = header[1]
00156             ps.header.frame_id = header[2]
00157         ps.pose = Pose(Point(*homo_mat[:3,3].T.A[0]), Quaternion(*quat_rot))
00158         return ps
00159 
00160     ##
00161     # @return geometry_msgs.Point
00162     @staticmethod
00163     def to_point_msg(*args):
00164         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00165         if homo_mat is None:
00166             rospy.logwarn("[pose_converter] Unknown pose type.")
00167             return None
00168         return Point(*homo_mat[:3,3].T.A[0])
00169 
00170     ##
00171     # @return geometry_msgs.PointStamped
00172     @staticmethod
00173     def to_point_stamped_msg(*args):
00174         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00175         if homo_mat is None:
00176             rospy.logwarn("[pose_converter] Unknown pose type.")
00177             return None
00178         ps = PointStamped()
00179         if header is None:
00180             ps.header.stamp = rospy.Time.now()
00181         else:
00182             ps.header.seq = header[0]
00183             ps.header.stamp = header[1]
00184             ps.header.frame_id = header[2]
00185         ps.point = Point(*homo_mat[:3,3].T.A[0])
00186         return ps
00187 
00188     ##
00189     # @return geometry_msgs.Transform
00190     @staticmethod
00191     def to_tf_msg(*args):
00192         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00193         if homo_mat is None:
00194             rospy.logwarn("[pose_converter] Unknown pose type.")
00195             return None
00196         else:
00197             return Transform(Vector3(*homo_mat[:3,3].T.A[0]), Quaternion(*quat_rot))
00198 
00199     ##
00200     # @return geometry_msgs.TransformStamped
00201     @staticmethod
00202     def to_tf_stamped_msg(*args):
00203         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00204         if homo_mat is None:
00205             rospy.logwarn("[pose_converter] Unknown pose type.")
00206             return None
00207         tf_stamped = TransformStamped()
00208         if header is None:
00209             tf_stamped.header.stamp = rospy.Time.now()
00210         else:
00211             tf_stamped.header.seq = header[0]
00212             tf_stamped.header.stamp = header[1]
00213             tf_stamped.header.frame_id = header[2]
00214         tf_stamped.transform = Transform(Vector3(*homo_mat[:3,3].T.A[0]), Quaternion(*quat_rot))
00215         return tf_stamped
00216 
00217     ##
00218     # @return geometry_msgs.Twist
00219     @staticmethod
00220     def to_twist_msg(*args):
00221         _, homo_mat, _, euler_rot = PoseConv._make_generic(args)
00222         if homo_mat is None:
00223             rospy.logwarn("[pose_converter] Unknown pose type.")
00224             return None
00225         else:
00226             return Twist(Vector3(*homo_mat[:3,3].T.A[0]), Vector3(*euler_rot))
00227 
00228     ##
00229     # @return geometry_msgs.TwistStamped
00230     @staticmethod
00231     def to_twist_stamped_msg(*args):
00232         header, homo_mat, _, euler_rot = PoseConv._make_generic(args)
00233         if homo_mat is None:
00234             rospy.logwarn("[pose_converter] Unknown pose type.")
00235             return None
00236         twist_stamped = TwistStamped()
00237         header_msg = Header()
00238         if header is None:
00239             header_msg.stamp = rospy.Time.now()
00240         else:
00241             header_msg.seq = header[0]
00242             header_msg.stamp = header[1]
00243             header_msg.frame_id = header[2]
00244         return TwistStamped(header_msg, Twist(Vector3(*homo_mat[:3,3].T.A[0]), Vector3(*euler_rot)))
00245 
00246     ##
00247     # @return 4x4 numpy mat
00248     @staticmethod
00249     def to_homo_mat(*args):
00250         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00251         if homo_mat is None:
00252             rospy.logwarn("[pose_converter] Unknown pose type.")
00253             return None
00254         else:
00255             return homo_mat.copy()
00256 
00257     ##
00258     # @return (3x1 numpy mat, 3x3 numpy mat)
00259     @staticmethod
00260     def to_pos_rot(*args):
00261         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00262         if homo_mat is None:
00263             rospy.logwarn("[pose_converter] Unknown pose type.")
00264             return None, None
00265         else:
00266             return homo_mat[:3,3].copy(), homo_mat[:3,:3].copy()
00267 
00268     ##
00269     # @return (3 list, 4 list)
00270     @staticmethod
00271     def to_pos_quat(*args):
00272         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00273         if homo_mat is None:
00274             rospy.logwarn("[pose_converter] Unknown pose type.")
00275             return None, None
00276         else:
00277             return copy.copy(list(homo_mat[:3,3].T.A[0])), copy.copy(quat_rot)
00278 
00279     ##
00280     # @return (3 list, 3 list)
00281     @staticmethod
00282     def to_pos_euler(*args):
00283         header, homo_mat, quat_rot, euler_rot = PoseConv._make_generic(args)
00284         if homo_mat is None:
00285             rospy.logwarn("[pose_converter] Unknown pose type.")
00286             return None, None
00287         else:
00288             return copy.copy(list(homo_mat[:3,3].T.A[0])), copy.copy(euler_rot)
00289 
00290     ##
00291     # @return (3 list, (3 list, float))
00292     @staticmethod
00293     def to_pos_axis_angle(*args):
00294         header, homo_mat, quat_rot, _ = PoseConv._make_generic(args)
00295         if homo_mat is None:
00296             rospy.logwarn("[pose_converter] Unknown pose type.")
00297             return None, None
00298         else:
00299             return copy.copy(list(homo_mat[:3,3].T.A[0])), rot_mat_to_axis_angle(homo_mat[:3,:3])
00300 
00301     @staticmethod
00302     def _make_generic(args):
00303         try:
00304             if type(args[0]) == str:
00305                 frame_id = args[0]
00306                 header, homo_mat, rot_quat, rot_euler = PoseConv._make_generic(args[1:])
00307                 if homo_mat is None:
00308                     return None, None, None, None
00309                 if header is None:
00310                     header = [0, rospy.Time.now(), '']
00311                 header[2] = frame_id
00312                 return header, homo_mat, rot_quat, rot_euler
00313 
00314             if len(args) == 2:
00315                 return PoseConv._make_generic(((args[0], args[1]),))
00316             elif len(args) == 1:
00317                 pose_type = PoseConv.get_type(*args)
00318                 if pose_type is None:
00319                     return None, None, None, None
00320                 if pose_type == 'pose_msg':
00321                     homo_mat, rot_quat, rot_euler = PoseConv._extract_pose_msg(args[0])
00322                     return None, homo_mat, rot_quat, rot_euler
00323                 elif pose_type == 'pose_stamped_msg':
00324                     homo_mat, rot_quat, rot_euler = PoseConv._extract_pose_msg(args[0].pose)
00325                     seq = args[0].header.seq
00326                     stamp = args[0].header.stamp
00327                     frame_id = args[0].header.frame_id
00328                     return [seq, stamp, frame_id], homo_mat, rot_quat, rot_euler
00329                 elif pose_type == 'tf_msg':
00330                     homo_mat, rot_quat, rot_euler = PoseConv._extract_tf_msg(args[0])
00331                     return None, homo_mat, rot_quat, rot_euler
00332                 elif pose_type == 'tf_stamped_msg':
00333                     homo_mat, rot_quat, rot_euler = PoseConv._extract_tf_msg(args[0].transform)
00334                     seq = args[0].header.seq
00335                     stamp = args[0].header.stamp
00336                     frame_id = args[0].header.frame_id
00337                     return [seq, stamp, frame_id], homo_mat, rot_quat, rot_euler
00338                 elif pose_type == 'point_msg':
00339                     homo_mat, rot_quat, rot_euler = PoseConv._extract_point_msg(args[0])
00340                     return None, homo_mat, rot_quat, rot_euler
00341                 elif pose_type == 'point_stamped_msg':
00342                     homo_mat, rot_quat, rot_euler = PoseConv._extract_point_msg(args[0].point)
00343                     seq = args[0].header.seq
00344                     stamp = args[0].header.stamp
00345                     frame_id = args[0].header.frame_id
00346                     return [seq, stamp, frame_id], homo_mat, rot_quat, rot_euler
00347                 elif pose_type == 'twist_msg':
00348                     homo_mat, rot_quat, rot_euler = PoseConv._extract_twist_msg(args[0])
00349                     return None, homo_mat, rot_quat, rot_euler
00350                 elif pose_type == 'twist_stamped_msg':
00351                     homo_mat, rot_quat, rot_euler = PoseConv._extract_twist_msg(args[0].twist)
00352                     seq = args[0].header.seq
00353                     stamp = args[0].header.stamp
00354                     frame_id = args[0].header.frame_id
00355                     return [seq, stamp, frame_id], homo_mat, rot_quat, rot_euler
00356                 elif pose_type == 'homo_mat':
00357                     return (None, np.mat(args[0]), trans.quaternion_from_matrix(args[0]).tolist(),
00358                             trans.euler_from_matrix(args[0]))
00359                 elif pose_type in ['pos_rot', 'pos_euler', 'pos_quat', 'pos_axis_angle']:
00360                     pos_arg = np.mat(args[0][0])
00361                     if pos_arg.shape == (1, 3):
00362                         # matrix is row, convert to column
00363                         pos = pos_arg.T
00364                     elif pos_arg.shape == (3, 1):
00365                         pos = pos_arg
00366 
00367                     if pose_type == 'pos_axis_angle':
00368                         homo_mat = np.mat(np.eye(4))
00369                         homo_mat[:3,:3] = axis_angle_to_rot_mat(args[0][1][0], args[0][1][1])
00370                         quat = trans.quaternion_from_matrix(homo_mat)
00371                         rot_euler = trans.euler_from_matrix(homo_mat)
00372                     elif pose_type == 'pos_rot':
00373                         # rotation matrix
00374                         homo_mat = np.mat(np.eye(4))
00375                         homo_mat[:3,:3] = np.mat(args[0][1])
00376                         quat = trans.quaternion_from_matrix(homo_mat)
00377                         rot_euler = trans.euler_from_matrix(homo_mat)
00378                     else:
00379                         rot_arg = np.mat(args[0][1])
00380                         if rot_arg.shape[1] == 1:
00381                             rot_arg = rot_arg.T
00382                         rot_list = rot_arg.tolist()[0]
00383                         if pose_type == 'pos_euler':
00384                             # Euler angles rotation
00385                             homo_mat = np.mat(trans.euler_matrix(*rot_list))
00386                             quat = trans.quaternion_from_euler(*rot_list)
00387                             rot_euler = rot_list
00388                         elif pose_type == 'pos_quat':
00389                             # quaternion rotation
00390                             homo_mat = np.mat(trans.quaternion_matrix(rot_list))
00391                             quat = rot_list
00392                             rot_euler = trans.euler_from_quaternion(quat)
00393 
00394                     homo_mat[:3, 3] = pos
00395                     return None, homo_mat, np.array(quat), rot_euler
00396         except:
00397             pass
00398 
00399         return None, None, None, None
00400 
00401     @staticmethod
00402     def _extract_pose_msg(pose):
00403         px = pose.position.x; py = pose.position.y; pz = pose.position.z
00404         ox = pose.orientation.x; oy = pose.orientation.y
00405         oz = pose.orientation.z; ow = pose.orientation.w
00406         quat = [ox, oy, oz, ow]
00407         rot_euler = trans.euler_from_quaternion(quat)
00408         homo_mat = np.mat(trans.quaternion_matrix(quat))
00409         homo_mat[:3,3] = np.mat([[px, py, pz]]).T
00410         return homo_mat, quat, rot_euler
00411 
00412     @staticmethod
00413     def _extract_tf_msg(tf_msg):
00414         px = tf_msg.translation.x; py = tf_msg.translation.y; pz = tf_msg.translation.z 
00415         ox = tf_msg.rotation.x; oy = tf_msg.rotation.y
00416         oz = tf_msg.rotation.z; ow = tf_msg.rotation.w
00417         quat = [ox, oy, oz, ow]
00418         rot_euler = trans.euler_from_quaternion(quat)
00419         homo_mat = np.mat(trans.quaternion_matrix(quat))
00420         homo_mat[:3,3] = np.mat([[px, py, pz]]).T
00421         return homo_mat, quat, rot_euler
00422 
00423     @staticmethod
00424     def _extract_twist_msg(twist_msg):
00425         pos = [twist_msg.linear.x, twist_msg.linear.y, twist_msg.linear.z]
00426         rot_euler = [twist_msg.angular.x, twist_msg.angular.y, twist_msg.angular.z]
00427         quat = trans.quaternion_from_euler(*rot_euler, axes='sxyz')
00428         homo_mat = np.mat(trans.euler_matrix(*rot_euler))
00429         homo_mat[:3,3] = np.mat([pos]).T
00430         return homo_mat, quat, rot_euler
00431 
00432     @staticmethod
00433     def _extract_point_msg(point_msg):
00434         pos = [point_msg.x, point_msg.y, point_msg.z]
00435         homo_mat = np.mat(np.eye(4))
00436         homo_mat[:3,3] = np.mat([pos]).T
00437         return homo_mat, [0., 0., 0., 1.], [0., 0., 0.]
00438 
00439 
00440 def main():
00441     rospy.init_node("pose_converter")
00442     pose = ([0.1, -0.5, 1.0], [0.04842544,  0.01236504,  0.24709477,  0.96770153])
00443     errors = 0
00444     for type_from in PoseConv.POSE_TYPES:
00445         for type_to in PoseConv.POSE_TYPES:
00446             print 
00447             print "Types: FROM %s, TO %s" % (type_from, type_to)
00448             exec("from_pose = PoseConv.to_%s(pose)" % type_from)
00449             if from_pose is None or (type(from_pose) is tuple and from_pose[0] is None):
00450                 print "from_pose ERROR\n" * 5
00451                 errors += 1
00452                 continue
00453             exec("to_pose = PoseConv.to_%s('base_link', from_pose)" % type_to)
00454             if to_pose is None or (type(to_pose) is tuple and to_pose[0] is None):
00455                 print "to_pose ERROR\n" * 5
00456                 errors += 1
00457                 continue
00458             exec("back_pose = PoseConv.to_%s(to_pose)" % type_from)
00459             if back_pose is None or (type(back_pose) is tuple and back_pose[0] is None):
00460                 print "back_pose ERROR\n" * 5
00461                 errors += 1
00462                 continue
00463             exec("orig_pose = PoseConv.to_pos_quat(back_pose)")
00464             if orig_pose is None or (type(orig_pose) is tuple and orig_pose[0] is None):
00465                 print "orig_pose ERROR\n" * 5
00466                 print pose
00467                 print orig_pose
00468                 errors += 1
00469                 continue
00470             if not np.allclose(orig_pose[0], pose[0]):
00471                 print "orig_pose pos ERROR\n" * 5
00472                 print pose
00473                 print orig_pose
00474                 errors += 1
00475                 continue
00476             if 'point' not in type_to + type_from and not np.allclose(orig_pose[1], pose[1]):
00477                 print "orig_pose rot ERROR\n" * 5
00478                 print pose
00479                 print orig_pose
00480                 errors += 1
00481                 continue
00482             print "-" * 50
00483             if type_from != PoseConv.get_type(from_pose) or type_to != PoseConv.get_type(to_pose):
00484                 print "get_type ERROR\n" * 5
00485                 errors += 1
00486                 continue
00487             print from_pose
00488             print "-" * 20
00489             print to_pose
00490     print "\n\nErrors: %d" % errors
00491 
00492 if __name__ == "__main__":
00493     main()


hrl_geom
Author(s): Kelsey Hawkins
autogenerated on Fri Aug 28 2015 11:05:32