00001
00002
00003 import rospy
00004 import numpy
00005 import scipy.stats
00006 import random
00007 import threading
00008 import itertools
00009 import tf
00010 import time
00011 from operator import itemgetter
00012
00013 from nav_msgs.msg import Odometry
00014 from geometry_msgs.msg import PoseWithCovariance, TwistWithCovariance, Twist, Pose, Point, Quaternion, Vector3, TransformStamped
00015 from ParticleOdometry import ParticleOdometry
00016 from odometry_utils import norm_pdf_multivariate, transform_quaternion_to_euler, transform_local_twist_to_global, transform_local_twist_covariance_to_global, update_pose, update_pose_covariance, broadcast_transform
00017
00018 class EKFGPFOdometry(ParticleOdometry):
00019 def __init__(self):
00020 ParticleOdometry.__init__(self)
00021 self.min_sampling_rate = float(rospy.get_param("~min_sampling_rate", self.rate))
00022 self.last_sampling_time = rospy.Time.now()
00023
00024 def initialize_odometry(self, trans, rot):
00025 with self.lock:
00026 self.particles = None
00027 self.weights = []
00028 self.odom = Odometry()
00029 self.odom.pose.pose = Pose(Point(*trans), Quaternion(*rot))
00030 self.odom.pose.covariance = numpy.diag([x ** 2 for x in self.init_sigma]).reshape(-1,).tolist()
00031 self.odom.twist.twist = Twist(Vector3(0, 0, 0), Vector3(0, 0, 0))
00032 self.odom.twist.covariance = numpy.diag([0.001**2]*6).reshape(-1,).tolist()
00033 self.odom.header.stamp = rospy.Time.now()
00034 self.odom.header.frame_id = self.odom_frame
00035 self.odom.child_frame_id = self.base_link_frame
00036 self.source_odom = None
00037 self.measure_odom = None
00038 self.imu = None
00039 self.imu_rotation = None
00040 self.prev_rpy = None
00041
00042
00043 def source_odom_callback(self, msg):
00044 with self.lock:
00045 self.source_odom = msg
00046 self.ekf_update(self.odom, self.source_odom)
00047
00048 def ekf_update(self, current_odom, source_odom):
00049 dt = (source_odom.header.stamp - current_odom.header.stamp).to_sec()
00050 if dt > 0:
00051
00052 current_odom.header.stamp = source_odom.header.stamp
00053 current_odom.twist = source_odom.twist
00054 new_pose_with_covariance = self.update_pose_with_covariance(current_odom.pose, source_odom.twist, dt)
00055 current_odom.pose = new_pose_with_covariance
00056
00057
00058
00059 def measure_odom_callback(self, msg):
00060 with self.lock:
00061 self.measure_odom = msg
00062 if not self.odom or not self.source_odom:
00063 return
00064 elif self.min_sampling_rate <= 0 or (self.measure_odom.header.stamp - self.last_sampling_time).to_sec() > 1.0 / self.min_sampling_rate:
00065
00066 self.particles = self.sampling(self.odom.pose)
00067
00068 self.weights = self.weighting(self.particles, self.min_weight)
00069
00070 self.particles = self.resampling(self.particles, self.weights)
00071
00072 self.approximate_odometry(self.particles, self.weights)
00073
00074 self.last_sampling_time = self.measure_odom.header.stamp
00075
00076 def sampling(self, current_pose_with_covariance):
00077 pose_mean = self.convert_pose_to_list(current_pose_with_covariance.pose)
00078 pose_cov_matrix = zip(*[iter(current_pose_with_covariance.covariance)]*6)
00079 return [self.convert_list_to_pose(x) for x in numpy.random.multivariate_normal(pose_mean, pose_cov_matrix, int(self.particle_num)).tolist()]
00080
00081 def approximate_odometry(self, particles, weights):
00082
00083 combined_prt_weight = zip(self.particles, self.weights)
00084 selected_prt_weight = zip(*sorted(combined_prt_weight, key = itemgetter(1), reverse = True)[:int(self.valid_particle_num)])
00085
00086 mean, cov = self.guess_normal_distribution(selected_prt_weight[0], selected_prt_weight[1])
00087
00088 self.odom.pose.pose = self.convert_list_to_pose(mean)
00089 self.odom.pose.covariance = list(itertools.chain(*cov))
00090
00091 def publish_odometry(self):
00092
00093 self.pub.publish(self.odom)
00094 if self.publish_tf:
00095 broadcast_transform(self.broadcast, self.odom, self.invert_tf)
00096
00097 self.update_diagnostics(self.particles, self.weights, self.odom.header.stamp)
00098
00099 self.prev_rpy = transform_quaternion_to_euler([self.odom.pose.pose.orientation.x, self.odom.pose.pose.orientation.y, self.odom.pose.pose.orientation.z, self.odom.pose.pose.orientation.w], self.prev_rpy)
00100
00101
00102 def update(self):
00103 if not self.odom or not self.source_odom:
00104 rospy.logwarn("[%s]: odometry is not initialized", rospy.get_name())
00105 return
00106 else:
00107 self.publish_odometry()
00108 if self.publish_histogram:
00109 histgram_msg = self.make_histogram_array(self.particles, self.source_odom.header.stamp)
00110 self.pub_hist.publish(histgram_msg)
00111
00112 def execute(self):
00113 while not rospy.is_shutdown():
00114 with self.lock:
00115 self.update()
00116 self.r.sleep()