-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathlocalisation.py
More file actions
153 lines (127 loc) · 5.09 KB
/
localisation.py
File metadata and controls
153 lines (127 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python
import math
class SimpleOdometry():
def __init__( self, pose = (0,0,0) ):
self.x = pose[0]
self.y = pose[1]
self.heading = pose[2]
def setPxPa( self, dist, angle ):
# advance robot by given distance and angle
if math.fabs(dist) > 1.0:
print dist, angle
if angle == 0.:
# Straight movement - a special case
self.x += dist * math.cos(self.heading)
self.y += dist * math.sin(self.heading)
#Not needed: self.heading += angle
else:
# Arc
r = dist / angle
self.x += -r * math.sin(self.heading) + r * math.sin(self.heading + angle)
self.y += +r * math.cos(self.heading) - r * math.cos(self.heading + angle)
self.heading += angle # not normalized
def pose( self ):
return (self.x, self.y, self.heading)
def setPose( self, pose ):
(self.x, self.y, self.heading) = pose
def updateCompass( self, compassData ):
pass
def updateGPS( self, gpsData ):
pass
def updateSharps( self, sharps ):
pass
class KalmanFilter( SimpleOdometry ):
"""
Simple KF version without correlations in x, y, heading variables.
http://en.wikipedia.org/wiki/Normal_distribution
f = a * exp( -(x-mean)**2/2*sigma**2 )
- sigma is called standard deviation
- sigma**2 is the variance of the distribution
About 68% of values drawn from a normal distribution are within one standard
deviation sigma > 0 away from the mean sigma; about 95% of the values are
within two standard deviations and about 99.7% lie within three standard
deviations.
varX ... variance of the X distribution (None = undefined)
kf.conv needs to be set to a route.Convertor instance in order to use the GPS
"""
#TODO: Configurable parameters (e.g. accuracy of odometry, compass accuracy, ...)
#TODO: Calibrate the parameters
#TODO: A proper EKF, UKF or even GraphMCL
def __init__( self, pose = (0,0,0), compassOffset = math.radians(90) ):
SimpleOdometry.__init__( self, pose )
self.varXY = None # unknown position
self.varHeading = None # unknown heading
self.compassOffset = compassOffset
self.conv = None
def setPxPa( self, dist, angle ):
SimpleOdometry.setPxPa( self, dist, angle )
if self.varXY != None:
self.varXY = self.varXY + (dist * 0.1)**2 # 10% error
if self.varHeading != None:
self.varHeading = self.varHeading + (angle * 0.1)**2
def updateCompass( self, compassData ):
varCompass = 0.1**2
angle = self.compassOffset - math.radians( compassData/10.0 ) # there could be some other North offset
if self.varHeading is None:
self.heading = angle
self.varHeading = varCompass
else:
totalVar = self.varHeading + varCompass
c = (math.cos(self.heading) * varCompass + math.cos(angle) * self.varHeading) / totalVar
s = (math.sin(self.heading) * varCompass + math.sin(angle) * self.varHeading) / totalVar
self.heading = math.atan2( s, c )
self.varHeading = (varCompass * self.varHeading) / totalVar # Equivalent to 1/(1/self.varHeading + 1/varCompass)
def updateGPS( self, gpsData ):
if self.conv is None:
return # cannot convert GPS coordinates to the local metric system
lat,lon,sat,hdop = gpsData
devGPS = 4.0 * hdop # http://users.erols.com/dlwilson/gpshdop.htm
varGPS = devGPS**2
x, y = self.conv((lon, lat))
if self.varXY is None:
self.x = x
self.y = y
self.varXY = varGPS
else:
K = self.varXY/(self.varXY + varGPS)
self.x += K * (x-self.x)
self.y += K * (y-self.y)
self.varXY *= (1.0 - K) # see http://robotika.cz/guide/filtering/cs
# it is the same as for compass
class Multilocalisation():
'''
Multilocalisation binds multiple localizations into a single one.
This allows multiple localizations (e.g. a local and a global one)
to be attached to the robot at the same time and being updated at the same
time.
'''
def __init__( self, basicLocalizations, primaryLocalization=None ):
'''
Parameters:
basicLocalizations ... List of basicLocalizations bound together
by this Multilocalisation.
primaryLocalization ... A localization which is used to provide
pose in the pose() function. If set to None,
the first of basicLocalizations is used.
'''
self.basicLocalizations = basicLocalizations
if primaryLocalization is None:
primaryLocalization = basicLocalizations[0]
self.primaryLocalization = primaryLocalization
def setPxPa( self, dist, angle ):
for loc in self.basicLocalizations:
loc.setPxPa(dist, angle)
def pose( self ):
return self.primaryLocalization.pose()
def setPose( self, pose ):
for loc in self.basicLocalizations:
loc.setPose(pose)
def updateCompass( self, compassData ):
for loc in self.basicLocalizations:
loc.updateCompass(compassData)
def updateGPS( self, gpsData ):
for loc in self.basicLocalizations:
loc.updateGPS(gpsData)
def updateSharps( self, sharps ):
for loc in self.basicLocalizations:
loc.updateSharps(sharps)