00001
00002
00003 import rospy
00004 from nav_msgs.msg import Odometry
00005 from geometry_msgs.msg import Point, Quaternion, Vector3
00006 import numpy
00007 import tf
00008 import threading
00009 import sys
00010 import itertools
00011
00012
00013
00014
00015
00016
00017 class OdometryIntegrator:
00018 def __init__(self):
00019 rospy.init_node("OdometryIntegrator", anonymous=True)
00020 self.odoms = [None, None]
00021 self.result_odom = None
00022 self.lock = threading.Lock()
00023 self.pub = rospy.Publisher("~output", Odometry, queue_size=10)
00024 self.sub = rospy.Subscriber("~source_odom_0", Odometry, self.callback, 0)
00025 self.sub = rospy.Subscriber("~source_odom_1", Odometry, self.callback, 1)
00026 self.rate = rospy.get_param("~rate", 100)
00027 self.r = rospy.Rate(self.rate)
00028 self.publish_tf = rospy.get_param("~publish_tf", True)
00029 self.invert_tf = rospy.get_param("~invert_tf", True)
00030 self.odom_frame = rospy.get_param("~odom_frame", "integrated_odom")
00031 self.base_link_frame = rospy.get_param("~base_link_frame", "BODY")
00032 self.broadcast = tf.TransformBroadcaster()
00033 self.listener = tf.TransformListener()
00034 self.time_threshould = float(rospy.get_param("~time_threshould", 0.1))
00035
00036 def execute(self):
00037 while not rospy.is_shutdown():
00038 with self.lock:
00039 self.integrate_odom()
00040 if self.publish_tf:
00041 self.broadcast_transform()
00042 self.r.sleep()
00043
00044 def callback(self, msg, index):
00045 if index < len(self.odoms):
00046 with self.lock:
00047 self.odoms[index] = msg
00048
00049 def integrate_odom(self):
00050 poses = []
00051 pose_covs = []
00052 twists = []
00053 twist_covs = []
00054
00055
00056 for odom in self.odoms:
00057 if not odom:
00058 rospy.logwarn("odometry source cannot be subscribed")
00059 return
00060
00061
00062 for odom_pair in list(itertools.combinations(self.odoms, 2)):
00063 time_diff = abs((odom_pair[0].header.stamp - odom_pair[1].header.stamp).to_sec())
00064 if time_diff > self.time_threshould:
00065 rospy.logwarn("odometry source timestamp is too distant: %f [sec]", time_diff)
00066 return
00067
00068
00069
00070 current_time = rospy.Time.now()
00071 for odom in self.odoms:
00072 dt = (current_time - odom.header.stamp).to_sec()
00073 self.update_odom_pose(odom, dt)
00074
00075
00076 for i in range(len(self.odoms)):
00077 euler = tf.transformations.euler_from_quaternion([self.odoms[i].pose.pose.orientation.x, self.odoms[i].pose.pose.orientation.y,
00078 self.odoms[i].pose.pose.orientation.z, self.odoms[i].pose.pose.orientation.w])
00079 poses.append(numpy.array([[self.odoms[i].pose.pose.position.x],
00080 [self.odoms[i].pose.pose.position.y],
00081 [self.odoms[i].pose.pose.position.z],
00082 [euler[0]],
00083 [euler[1]],
00084 [euler[2]]]))
00085 pose_covs.append(numpy.mat(self.odoms[i].pose.covariance).reshape(6, 6))
00086 twists.append(numpy.array([[self.odoms[i].twist.twist.linear.x],
00087 [self.odoms[i].twist.twist.linear.y],
00088 [self.odoms[i].twist.twist.linear.z],
00089 [self.odoms[i].twist.twist.angular.x],
00090 [self.odoms[i].twist.twist.angular.y],
00091 [self.odoms[i].twist.twist.angular.z]]))
00092 twist_covs.append(numpy.mat(self.odoms[i].twist.covariance).reshape(6, 6))
00093
00094 new_pose, new_pose_cov = self.calculate_mean_and_covariance(poses, pose_covs)
00095 new_twist, new_twist_cov = self.calculate_mean_and_covariance(twists, twist_covs)
00096
00097
00098 self.result_odom = Odometry()
00099 self.result_odom.header.stamp = current_time
00100 self.result_odom.header.frame_id = self.odom_frame
00101 self.result_odom.child_frame_id = self.base_link_frame
00102 quat = tf.transformations.quaternion_from_euler(*new_pose[3:6])
00103 self.result_odom.pose.pose.position = Point(*new_pose[0:3])
00104 self.result_odom.pose.pose.orientation = Quaternion(*quat)
00105 self.result_odom.pose.covariance = numpy.array(new_pose_cov).reshape(-1,).tolist()
00106 self.result_odom.twist.twist.linear = Vector3(*new_twist[0:3])
00107 self.result_odom.twist.twist.angular = Vector3(*new_twist[3:6])
00108 self.result_odom.twist.covariance = numpy.array(new_twist_cov).reshape(-1,).tolist()
00109 self.pub.publish(self.result_odom)
00110
00111 def calculate_mean_and_covariance(self, means, covs):
00112
00113
00114
00115
00116
00117
00118
00119 cov0_inv_sum_covs = numpy.dot(covs[0], numpy.linalg.inv(covs[0] + covs[1]))
00120 new_cov = covs[0] - numpy.dot(cov0_inv_sum_covs, covs[0])
00121 new_mean = means[0] + numpy.dot(cov0_inv_sum_covs, means[1] - means[0])
00122 return new_mean, new_cov
00123
00124 def update_odom_pose(self, odom, dt):
00125 try:
00126 (trans,rot) = self.listener.lookupTransform(odom.header.frame_id, odom.child_frame_id, odom.header.stamp)
00127 except:
00128 try:
00129 rospy.logwarn("timestamp %f of tf (%s to %s) is not correct. use rospy.Time(0).", odom.header.stamp.to_sec(), odom.header.frame_id, odom.child_frame_id)
00130 (trans,rot) = self.listener.lookupTransform(odom.header.frame_id, odom.child_frame_id, rospy.Time(0))
00131 except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
00132 rospy.logwarn("failed to solve tf: %s to %s", odom.header.frame_id, odom.child_frame_id)
00133 return
00134 rotation_matrix = tf.transformations.quaternion_matrix(rot)[:3, :3]
00135 global_velocity = numpy.dot(rotation_matrix, numpy.array([[odom.twist.twist.linear.x],
00136 [odom.twist.twist.linear.y],
00137 [odom.twist.twist.linear.z]]))
00138 global_omega = numpy.dot(rotation_matrix, numpy.array([[odom.twist.twist.angular.x],
00139 [odom.twist.twist.angular.y],
00140 [odom.twist.twist.angular.z]]))
00141 odom.pose.pose.position.x += global_velocity[0, 0] * dt
00142 odom.pose.pose.position.y += global_velocity[1, 0] * dt
00143 odom.pose.pose.position.z += global_velocity[2, 0] * dt
00144 euler = list(tf.transformations.euler_from_quaternion((odom.pose.pose.orientation.x, odom.pose.pose.orientation.y,
00145 odom.pose.pose.orientation.z, odom.pose.pose.orientation.w)))
00146 euler[0] += global_omega[0, 0] * dt
00147 euler[1] += global_omega[1, 0] * dt
00148 euler[2] += global_omega[2, 0] * dt
00149 quat = tf.transformations.quaternion_from_euler(*euler)
00150 odom.pose.pose.orientation = Quaternion(*quat)
00151
00152 def broadcast_transform(self):
00153 if not self.result_odom:
00154 return
00155 position = [self.result_odom.pose.pose.position.x, self.result_odom.pose.pose.position.y, self.result_odom.pose.pose.position.z]
00156 orientation = [self.result_odom.pose.pose.orientation.x, self.result_odom.pose.pose.orientation.y, self.result_odom.pose.pose.orientation.z, self.result_odom.pose.pose.orientation.w]
00157 if self.invert_tf:
00158 homogeneous_matrix = tf.transformations.quaternion_matrix(orientation)
00159 homogeneous_matrix[:3, 3] = numpy.array(position).reshape(1, 3)
00160 homogeneous_matrix_inv = numpy.linalg.inv(homogeneous_matrix)
00161 position = list(homogeneous_matrix_inv[:3, 3])
00162 orientation = list(tf.transformations.quaternion_from_matrix(homogeneous_matrix_inv))
00163 parent_frame = self.base_link_frame
00164 target_frame = self.odom_frame
00165 else:
00166 parent_frame = self.odom_frame
00167 target_frame = self.base_link_frame
00168 self.broadcast.sendTransform(position, orientation, rospy.Time.now(), target_frame, parent_frame)
00169
00170 if __name__ == '__main__':
00171 try:
00172 node = OdometryIntegrator()
00173 node.execute()
00174 except rospy.ROSInterruptException: pass