odometry_utils.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 
00003 import rospy
00004 import tf
00005 import numpy
00006 import scipy.stats
00007 import math
00008 from geometry_msgs.msg import PoseWithCovariance, TwistWithCovariance, Twist, Pose, Point, Quaternion, Vector3
00009 
00010 # twist transformation
00011 def transform_local_twist_to_global(pose, local_twist):
00012     trans = [pose.position.x, pose.position.y, pose.position.z]
00013     rot = [pose.orientation.x, pose.orientation.y,
00014            pose.orientation.z, pose.orientation.w]
00015     rotation_matrix = tf.transformations.quaternion_matrix(rot)[:3, :3]
00016     global_velocity = numpy.dot(rotation_matrix, numpy.array([[local_twist.linear.x],
00017                                                               [local_twist.linear.y],
00018                                                               [local_twist.linear.z]]))
00019     global_omega = numpy.dot(rotation_matrix, numpy.array([[local_twist.angular.x],
00020                                                            [local_twist.angular.y],
00021                                                            [local_twist.angular.z]]))
00022     return Twist(Vector3(*global_velocity[:, 0]), Vector3(*global_omega[:, 0]))
00023 
00024 def transform_local_twist_covariance_to_global(pose, local_twist_with_covariance):
00025     trans = [pose.position.x, pose.position.y, pose.position.z]
00026     rot = [pose.orientation.x, pose.orientation.y,
00027            pose.orientation.z, pose.orientation.w]
00028     rotation_matrix = tf.transformations.quaternion_matrix(rot)[:3, :3]
00029     twist_cov_matrix = numpy.matrix(local_twist_with_covariance).reshape(6, 6)
00030     global_twist_cov_matrix = numpy.zeros((6, 6))
00031     global_twist_cov_matrix[:3, :3] = (rotation_matrix.T).dot(twist_cov_matrix[:3, :3].dot(rotation_matrix))
00032     global_twist_cov_matrix[3:6, 3:6] = (rotation_matrix.T).dot(twist_cov_matrix[3:6, 3:6].dot(rotation_matrix))
00033     return global_twist_cov_matrix.reshape(-1,).tolist()
00034 
00035 # pose calculation
00036 def update_pose(pose, global_twist, dt):
00037     ret_pose = Pose()
00038     # calculate current pose as integration
00039     ret_pose.position.x = pose.position.x + global_twist.linear.x * dt
00040     ret_pose.position.y = pose.position.y + global_twist.linear.y * dt
00041     ret_pose.position.z = pose.position.z + global_twist.linear.z * dt
00042     ret_pose.orientation = update_quaternion(pose.orientation, global_twist.angular, dt)
00043     return ret_pose
00044 
00045 def update_quaternion(orientation, angular, dt): # angular is assumed to be global
00046     # quaternion calculation
00047     quat_vec = numpy.array([[orientation.x],
00048                             [orientation.y],
00049                             [orientation.z],
00050                             [orientation.w]])
00051     skew_omega = numpy.matrix([[0, -angular.z, angular.y, angular.x],
00052                                [angular.z, 0, -angular.x, angular.y],
00053                                [-angular.y, angular.x, 0, angular.z],
00054                                [-angular.x, -angular.y, -angular.z, 0]])
00055     new_quat_vec = quat_vec + 0.5 * numpy.dot(skew_omega, quat_vec) * dt
00056     norm = numpy.linalg.norm(new_quat_vec)
00057     if norm == 0:
00058         rospy.logwarn("norm of quaternion is zero")
00059     else:
00060         new_quat_vec = new_quat_vec / norm # normalize
00061     return Quaternion(*numpy.array(new_quat_vec).reshape(-1,).tolist())
00062 
00063 # covariance calculation
00064 def update_twist_covariance(twist, v_sigma, min_sigma = 1e-3): # twist is assumed to be local
00065     twist_list = [twist.twist.linear.x, twist.twist.linear.y, twist.twist.linear.z, twist.twist.angular.x, twist.twist.angular.y, twist.twist.angular.z]
00066     if all([abs(x) < 1e-6 for x in twist_list]):
00067         current_sigma = [min_sigma] * 6 # trust "completely stopping" state
00068     else:
00069         current_sigma = v_sigma
00070     return numpy.diag([max(x ** 2, min_sigma ** 2) for x in current_sigma]).reshape(-1,).tolist() # covariance should be singular
00071 
00072 def update_pose_covariance(pose_cov, global_twist_cov, dt):
00073     ret_pose_cov = []
00074     # make matirx from covariance array
00075     prev_pose_cov_matrix = numpy.matrix(pose_cov).reshape(6, 6)
00076     global_twist_cov_matrix = numpy.matrix(global_twist_cov).reshape(6, 6)
00077     # jacobian matrix
00078     # elements in pose and twist are assumed to be independent on global coordinates
00079     jacobi_pose = numpy.diag([1.0] * 6)
00080     jacobi_twist = numpy.diag([dt] * 6)
00081     # covariance calculation
00082     pose_cov_matrix = jacobi_pose.dot(prev_pose_cov_matrix.dot(jacobi_pose.T)) + jacobi_twist.dot(global_twist_cov_matrix.dot(jacobi_twist.T))
00083     # update covariances as array type (twist is same as before)
00084     ret_pose_cov = numpy.array(pose_cov_matrix).reshape(-1,).tolist()
00085     return ret_pose_cov
00086 
00087 # tf broadcast
00088 def broadcast_transform(broadcast, odom, invert_tf):
00089     if not odom or not broadcast:
00090         return
00091     position = [odom.pose.pose.position.x, odom.pose.pose.position.y, odom.pose.pose.position.z]
00092     orientation = [odom.pose.pose.orientation.x, odom.pose.pose.orientation.y, odom.pose.pose.orientation.z, odom.pose.pose.orientation.w]
00093     if invert_tf:
00094         homogeneous_matrix = make_homogeneous_matrix(position, orientation)
00095         homogeneous_matrix_inv = numpy.linalg.inv(homogeneous_matrix)
00096         position = list(homogeneous_matrix_inv[:3, 3])
00097         orientation = list(tf.transformations.quaternion_from_matrix(homogeneous_matrix_inv))
00098         parent_frame = odom.child_frame_id
00099         target_frame = odom.header.frame_id
00100     else:
00101         parent_frame = odom.header.frame_id
00102         target_frame = odom.child_frame_id
00103     broadcast.sendTransform(position, orientation, odom.header.stamp, target_frame, parent_frame)
00104 
00105 # mathmatical tools    
00106 def make_homogeneous_matrix(trans, rot):
00107     homogeneous_matrix = tf.transformations.quaternion_matrix(rot)
00108     homogeneous_matrix[:3, 3] = numpy.array(trans).reshape(1, 3)
00109     return homogeneous_matrix
00110     
00111 # scipy.stats.multivariate_normal only can be used after SciPy 0.14.0
00112 # input: x(array), mean(array), cov_inv(matrix) output: probability of x
00113 # covariance has to be inverted to reduce calculation time
00114 def norm_pdf_multivariate(x, mean, cov_inv):
00115     size = len(x)
00116     if size == len(mean) and (size, size) == cov_inv.shape:
00117         inv_det = numpy.linalg.det(cov_inv)
00118         if not inv_det > 0:
00119             rospy.logwarn("Determinant of inverse cov matrix {0} is equal or smaller than zero".format(inv_det))
00120             return 0.0
00121         norm_const = math.pow((2 * numpy.pi), float(size) / 2) * math.pow(1 / inv_det, 1.0 / 2) # determinant of inverse matrix is reciprocal
00122         if not norm_const > 0 :
00123             rospy.logwarn("Norm const {0} is equal or smaller than zero".format(norm_const))
00124             return 0.0
00125         x_mean = numpy.matrix(x - mean)
00126         exponent = -0.5 * (x_mean * cov_inv * x_mean.T)
00127         if exponent > 0:
00128             rospy.logwarn("Exponent {0} is larger than zero".format(exponent))
00129             exponent = 0
00130         result = math.pow(math.e, exponent)
00131         return result / norm_const
00132     else:
00133         rospy.logwarn("The dimensions of the input don't match")
00134         return 0.0
00135 
00136 # tf.transformations.euler_from_quaternion is slow because the function calculates matrix inside.
00137 # prev_euler expects previous [roll, pitch, yaw] angle list and fix ret_euler 
00138 # cf. https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
00139 def transform_quaternion_to_euler(quat, prev_euler = None):
00140     # singularity check
00141     sin_half_pitch = quat[3] * quat[1] - quat[2] * quat[0]
00142     if abs(sin_half_pitch) > 0.499:
00143         # use tf.transformations.euler_from_quaternion only at singularity points
00144         ret_euler = list(tf.transformations.euler_from_quaternion(quat))
00145     else:
00146         # zero check
00147         zero_thre = numpy.finfo(float).eps * 4.0 # epsilon for testing whether a number is close to zero
00148         roll_numerator = 2 * (quat[3] * quat[0] + quat[1] * quat[2])
00149         if abs(roll_numerator) < zero_thre:
00150             roll_numerator = numpy.sign(roll_numerator) * 0.0
00151         yaw_numerator = 2 * (quat[3] * quat[2] + quat[0] * quat[1])
00152         if abs(yaw_numerator) < zero_thre:
00153             yaw_numerator = numpy.sign(yaw_numerator) * 0.0
00154         ret_euler = [numpy.arctan2(roll_numerator, 1 - 2 * (quat[0] ** 2 + quat[1] ** 2)),
00155                      numpy.arcsin(2 * sin_half_pitch),
00156                      numpy.arctan2(yaw_numerator, 1 - 2 * (quat[1] ** 2 + quat[2] ** 2))]
00157     # consider arctan/arcsin range
00158     # TODO: This solution is not fundamental because it does not consider ununiqueness of euler angles
00159     if prev_euler != None:
00160         # roll: arctan2 is in range of [-pi, pi]
00161         for i in range(3):
00162             while abs(prev_euler[i] - ret_euler[i]) > numpy.pi:
00163                 ret_euler[i] += numpy.sign(prev_euler[i] - ret_euler[i]) * 2 * numpy.pi
00164     return ret_euler


jsk_robot_startup
Author(s):
autogenerated on Sat Jul 1 2017 02:42:18