00001
00002
00003
00004 import roslib; roslib.load_manifest('husky_localization')
00005 import rospy
00006 from threading import RLock
00007
00008 from clearpath_base.msg import Encoders, RotateRate
00009 from clearpath_sensors.msg import GPSFix
00010 from geometry_msgs.msg import Vector3, Twist, Pose2d
00011
00012 from numpy import matrix
00013
00014 import os, sys, getopt, math
00015 from LatLongUTMconversion import LLtoUTM
00016
00017
00018
00019 class Kalman():
00020 def __init__(self, wheel_sep):
00021 self.wheel_sep = wheel_sep
00022 self.init(0.0, 0.0, 0.0)
00023
00024 def init(self, x, y, theta):
00025 self.active = False
00026 self.xyrot = matrix([[x],
00027 [y],
00028 [theta]])
00029 self.last_vel_time = rospy.get_time()
00030 self.error_covariance = matrix([[0.0, 0.0, 0.0],
00031 [0.0, 0.0, 0.0],
00032 [0.0, 0.0, 0.0],])
00033 self.kalman_gain = matrix([[0.0, 0.0, 0.0],
00034 [0.0, 0.0, 0.0],
00035 [0.0, 0.0, 0.0]])
00036 self.process_error_coeff = matrix([[0.0, 0.0, 0.0],
00037 [0.0, 0.0, 0.0],
00038 [0.0, 0.0, 0.0]])
00039 self.identity_mat = matrix([[1.0, 0.0, 0.0],
00040 [0.0, 1.0, 0.0],
00041 [0.0, 0.0, 1.0]])
00042 self.approx_vel = 0.0
00043 self.vel_dir = 0.0
00044
00045 def predict_from_encoder(self, delL, delR, left_err, right_err):
00046 if self.active == False:
00047 return
00048
00049 new_time = rospy.get_time()
00050 delta_t = new_time - self.last_vel_time
00051
00052 process_error = (math.fabs(left_err)+math.fabs(right_err))/2.0
00053 old_x, old_y, old_rot = self.xyrot
00054
00055 rough_vel = ((delR+delL)/2.0)/delta_t
00056 slipvmin = 0.2
00057 slipvmax = 1.0
00058 slipmin = 1.0
00059 slipmax = 0.7
00060 straight_slip = max(slipmin, min(slipmax, ((slipmax-slipmin)/(slipvmax-slipvmin))*rough_vel))
00061 slipvmin = 0.2
00062 slipvmax = 1.0
00063 slipmin = 0.7
00064 slipmax = 0.3
00065 turn_slip = max(slipmin, min(slipmax, ((slipmax-slipmin)/(slipvmax-slipvmin))*rough_vel))
00066
00067 avg_dist = ((delR+delL)/2.0)*straight_slip
00068 del_theta = ((delR-delL)/self.wheel_sep)*turn_slip
00069 avg_theta = old_rot + del_theta/2.0
00070
00071 self.xyrot[0] += avg_dist * math.cos(avg_theta)
00072 self.xyrot[1] += avg_dist * math.sin(avg_theta)
00073 self.xyrot[2] += del_theta
00074
00075 self.vel_dir = (delL + delR) / 2.0
00076
00077 self.approx_vel = math.sqrt(pow(self.xyrot[0]-old_x,2) + pow(self.xyrot[1]-old_y,2))/delta_t
00078 self.last_vel_time = new_time
00079
00080 k = avg_dist/(2.0*self.wheel_sep)
00081 heading_covar = (math.fabs(left_err)+math.fabs(right_err))/(self.wheel_sep)
00082 self.process_error_coeff[0] = [0.5*math.cos(avg_theta)-k*math.sin(avg_theta), 0.5*math.cos(avg_theta)+k*math.sin(avg_theta), 0]
00083 self.process_error_coeff[1] = [0.5*math.sin(avg_theta)+k*math.cos(avg_theta), 0.5*math.sin(avg_theta)-k*math.cos(avg_theta), 0]
00084 if process_error > 0.0:
00085 self.process_error_coeff[2] = [0, 0, heading_covar/process_error]
00086 else:
00087 self.process_error_coeff[2] = heading_covar
00088 self.error_covariance = self.error_covariance + (self.process_error_coeff*process_error)*self.process_error_coeff.T
00089
00090 def wrapAngle(self, data):
00091 if data < 0.0:
00092 data = data + math.pi*2.0
00093 if data > math.pi*2.0:
00094 data = data - math.pi*2.0
00095 return data
00096
00097 def measure_from_gps(self, x, y, gps_track, measure_error):
00098 if self.active == False:
00099 return
00100
00101 if self.vel_dir < 0.0:
00102 gps_track = gps_track + math.pi
00103
00104
00105 self.xyrot[2] = self.wrapAngle(self.xyrot[2])
00106 gps_track = self.wrapAngle(gps_track)
00107
00108
00109 angleBet = 0.0;
00110 if gps_track < self.xyrot[2]:
00111 angleBet = self.xyrot[2] - gps_track
00112 if angleBet > math.pi:
00113 gps_track = gps_track + 2.0*math.pi
00114 else:
00115 angleBet = gps_track - self.xyrot[2]
00116 if angleBet > math.pi:
00117 gps_track = gps_track - 2.0*math.pi
00118
00119
00120 gpsdata = matrix([[x],
00121 [y],
00122 [gps_track]])
00123
00124
00125 self.kalman_gain = self.error_covariance*((self.error_covariance + measure_error).I)
00126 self.xyrot = self.xyrot + self.kalman_gain*(gpsdata - self.xyrot)
00127 self.error_covariance = (self.identity_mat - self.kalman_gain)*self.error_covariance
00128
00129
00130 self.xyrot[2] = self.wrapAngle(self.xyrot[2])
00131
00132
00133 class Encoders():
00134 def __init__(self, lock, xyrot, variance):
00135 self.odompublisher = odompublisher
00136 self.lock = lock
00137 self.last_update = rospy.Time.now() - rospy.Duration(5,0)
00138 self.xyrot = xyrot
00139 self.last_encoder = None
00140 self.active = False
00141
00142
00143
00144 self.variance = variance
00145
00146 def callback(self, data):
00147 with self.lock:
00148 if self.last_encoder:
00149 left_delta = data.encoders[0].travel - self.last_encoder.encoders[0].travel
00150 right_delta = data.encoders[1].travel - self.last_encoder.encoders[1].travel
00151
00152 left_error = left_delta * self.variance
00153 right_error = right_delta * self.variance
00154 self.xyrot.predict_from_encoder(left_delta, right_delta, left_error, right_error)
00155
00156 self.last_encoder = data
00157 self.last_update = rospy.Time.now()
00158
00159
00160 class GPS():
00161 def __init__(self, lock, xyrot, variance):
00162 self.lock = lock
00163 self.last_update = rospy.Time.now() - rospy.Duration(5,0)
00164 self.xyrot = xyrot
00165 self.active = False
00166 self.reference_ellipsoid = 23
00167 self.last_gps_data = None
00168
00169
00170
00171 self.measure_error = matrix([[variance, 0.0, 0.0],
00172 [0.0, variance, 0.0],
00173 [0.0, 0.0, 0.0]])
00174
00175 def callback(self, data):
00176 with self.lock:
00177 try:
00178
00179 utm_zone, easting, northing = LLtoUTM(self.reference_ellipsoid, data.latitude, data.longitude)
00180 heading = (-data.track)*(math.pi/180.0) + (math.pi/2)
00181 except ValueError:
00182
00183 return
00184
00185
00186 if math.fabs(data.speed) < 0.4:
00187 self.measure_error[2,2] = 4.0*math.pi
00188 else:
00189 self.measure_error[2,2] = math.pi/8.0
00190
00191
00192 self.xyrot.measure_from_gps(easting, northing, heading, self.measure_error)
00193
00194 self.last_gps_data = (easting, northing, heading)
00195 self.last_update = rospy.Time.now()
00196
00197
00198
00199 class Localization():
00200 def __init__(self):
00201 rospy.init_node('localization_a100', anonymous=True)
00202 self.lock = RLock()
00203 self.wheel_separation = float(rospy.get_param('wheel_separation', 0.5))
00204
00205 self.xyrot = Kalman(self.wheel_separation)
00206
00207 self.encoders = EncoderDevice(self, self.lock, self.xyrot, 0.5)
00208 self.gps = GPS(self, self.lock, self.xyrot, 0.5)
00209
00210 rospy.Subscriber("data/encoders", Encoders, self.encoders.callback)
00211 rospy.Subscriber("fix", GPSFix, self.gps_callback)
00212
00213
00214 self.pose_pub = rospy.Publisher('pose', Pose2D, latch=True)
00215
00216 self.UPDATE_HZ = 20
00217 self.rate = rospy.Rate(self.UPDATE_HZ)
00218
00219 def is_gps_active(self):
00220 with self.lock:
00221 if (rospy.Time.now() - self.gps.last_update) < rospy.Duration(2,0):
00222 return True
00223 else:
00224 return False
00225
00226 def disable_KF(self):
00227 with self.lock:
00228 self.xyrot.active = False
00229 self.encoders.active = False
00230 self.gps.active = False
00231
00232 def enable_KF(self):
00233 with self.lock:
00234 self.xyrot.active = True
00235 self.encoders.active = True
00236 self.gps.active = True
00237
00238 def initialize_from_gps(self):
00239 with self.lock:
00240 x, y, heading = self.gps.last_gps_data
00241 self.xyrot.init(x, y, heading)
00242
00243 def run(self):
00244 rospy.sleep(0.5)
00245 self.lps_active = False
00246 self.gps_active = False
00247 while not rospy.is_shutdown():
00248 self.rate.sleep()
00249
00250
00251 if self.is_gps_active() == True:
00252 if self.gps_active == False:
00253
00254 self.initialize_from_gps()
00255 self.enable_KF()
00256 self.gps_active = True
00257 self.publish_odom_message_from_odom()
00258 else:
00259
00260 if self.gps_active == True:
00261 self.disable_KF()
00262 self.gps_active = False
00263 self.lps_active = False
00264
00265
00266 Localization().run()