EKFGPFOdometry.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
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)) # execute sampling every time topic is subscribed when this value is not larger than 0
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     # EKF
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             # update current_odom (only update pose, stamp and twist are copied from source_odom)
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     ## particle filter functions
00058     # sampling poses from EKF result (current_pose_with_covariance)
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                 # sampling
00066                 self.particles = self.sampling(self.odom.pose)
00067                 # weighting
00068                 self.weights = self.weighting(self.particles, self.min_weight)
00069                 # resampling
00070                 self.particles = self.resampling(self.particles, self.weights)
00071                 # estimate new pdf 
00072                 self.approximate_odometry(self.particles, self.weights)
00073                 # wait next measurement
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         # use only important particels
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)]) # [(p0, w0), (p1, w1), ..., (pN, wN)] -> [(sorted_p0, sorted_w0), (sorted_p1, sorted_w1), ..., (sorted_pN', sorted_wN')] -> [(sorted_p0, ..., sorted_pN'), (sorted_w0, ..., sorted_wN')]
00085         # estimate gaussian distribution for Odometry msg 
00086         mean, cov = self.guess_normal_distribution(selected_prt_weight[0], selected_prt_weight[1])
00087         # overwrite pose pdf
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         # refrect source_odom informations
00093         self.pub.publish(self.odom)
00094         if self.publish_tf:
00095             broadcast_transform(self.broadcast, self.odom, self.invert_tf)
00096         # diagnostics
00097         self.update_diagnostics(self.particles, self.weights, self.odom.header.stamp)
00098         # update prev_rpy to prevent jump of angles
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     # main functions
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() # call update() when control input is subscribed
00116             self.r.sleep()


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