-
Notifications
You must be signed in to change notification settings - Fork 0
/
slam.py
228 lines (195 loc) · 6.78 KB
/
slam.py
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import numpy as np
class Slam():
def __init__(self):
#parameters
self.rejection_distance = 0.2
self.sensors = ['Laser_Sensor','Laser_Sensor0','Laser_Sensor1','Laser_Sensor2']
print('init slam')
#initialisatie
self.old_map = False
def calculations(self,distance,oude_locatie):
#distance is de afstand gescand door de sensors
#oude_locatie is de positie van de robot [x,y,hoek(radialen)]
#variabele initialisatie
derivate_measured_distance = []
raycount = 0
totale_afstand = 0
totale_ray = 0
op_cilinder = False
measured_points_x = []
measured_points_y = []
measured_distance = []
i = 0
#alle afstanden van de sensoren achterelkaar plaatsen
while i < len(self.sensors):
measured_distance.extend(distance[self.sensors[i]])
i+=1
i = 0
#hoek per scan
self.angle_per_scan = 2 * np.pi / len(measured_distance)
#punten bepalen voor plot
heading_measured_points = np.multiply(self.angle_per_scan,list(range(0,len(measured_distance))))
i = 0
while i < len(measured_distance):
measured_points_x.append(np.cos(heading_measured_points[i]) * measured_distance[i])
measured_points_y.append(np.sin(heading_measured_points[i]) * measured_distance[i])
i += 1
#Bepalen van landmarks
landmark_ray_center,landmark_ray_distance = retrieve_landmarks(measured_distance)
#hoek van landmarks bepalen door vermenigvuldigen van hoek per scan en ray_center
heading_landmarks = np.multiply(self.angle_per_scan,landmark_ray_center)
#robot kaart maken
landmark_x = np.cos(heading_landmarks) * landmark_ray_distance
landmark_y = np.sin(heading_landmarks) * landmark_ray_distance
if self.old_map is False:
print('map maken')
#als self_map false is is er nog geen kaart aangemaakt (eerste cyclus).
#return de oude locatie omdat er nog geen betere berekend kon worden.
self.old_map = np.column_stack((landmark_x,landmark_y)).tolist()
self.old_map_x = landmark_x
self.old_map_y = landmark_y
return measured_points_x,measured_points_y,self.old_map_x,self.old_map_y,[],[],False
else:
print('map gebruiken')
#tweede cyclus, positie berekenen
#Eerst landmarks koppelen waarvoor de translatie nodig is van de robot nulpunt naar kaart nulpunt.
transformed_robot_map_x = np.cos(oude_locatie[2]+heading_landmarks) * landmark_ray_distance + oude_locatie[0]
transformed_robot_map_y = np.sin(oude_locatie[2]+heading_landmarks) * landmark_ray_distance - oude_locatie[1]
#Landmarks van robot en kaart bij elkaar brengen voor slam
transformed_robot_map = np.column_stack((landmark_x,landmark_y)).tolist()
connection = connect_landmark(self.old_map_x,self.old_map_y,transformed_robot_map_x,transformed_robot_map_y,self.rejection_distance)
#Voorbereiden van maps voor slam, de volgorde van punten goedzetten
robot_map_sorted = []
for item in connection:
robot_map_sorted.append([landmark_x[item[0]],landmark_y[item[1]]])
print(robot_map_sorted)
#slam tijd!!!
s,r,tx,ty = estimate(robot_map_sorted,self.old_map)
print(s,r,tx,ty)
#positie bepalen
nieuwe_positie = transform([0,0],s,r,tx,ty)
print(nieuwe_positie)
#for item in transformed_robot_map:
# transformed_robot_map_x.append(item[0])
# transformed_robot_map_y.append(item[1])
return measured_points_x,measured_points_y,self.old_map_x,self.old_map_y,transformed_robot_map_x,transformed_robot_map_y,nieuwe_positie
def transform(p,s,r,tx,ty):
'''
Parameter
p
point [x, y] or list of points [[x1,y1], [x2,y2], ...]
'''
def transform_one(q):
return [s * q[0] - r * q[1] + tx,
r * q[0] + s * q[1] + ty]
if not isinstance(p[0], list):
# # Single point
return transform_one(p)
# else
transformed = []
for item in p:
transformed.append(transform_one(item))
return transformed
def connect_landmark(map1x,map1y,map2x,map2y,rejection_distance):
#map1x,map1y are the map wich should be mapped to. map 2 will be mapped to map 1
#return [,][,]
connecting = []
i = 0
while i < len(map1x):
diff = []
n = 0
while n < len(map2x):
diff.append(np.sqrt(np.square(map1x[i]-map2x[n])+np.square(map1y[i]-map2y[n])))
n += 1
if rejection_distance > np.amin(diff):
connecting.append([i,np.argmin(diff)])
print(diff)
i += 1
print(connecting)
return connecting
def retrieve_landmarks(measured_distance):
#measured_distance is a list of points around the robot scanned by lidar scanner. list is pure distance
landmark_ray_distance, landmark_ray_center = [],[]
derivate_measured_distance = []
op_cilinder = False
raycount = 0
totale_afstand = 0
totale_ray = 0
i = 0
while i < len(measured_distance)-2:
derivate_measured_distance.append((measured_distance[(i-1)]-measured_distance[(i+1)])/2)
if derivate_measured_distance[i] > 0.4:
op_cilinder = True
if derivate_measured_distance[i] < -0.4:
if 5 < raycount:
landmark_ray_center.append(totale_ray/raycount + 1)
landmark_ray_distance.append(totale_afstand/raycount+0.25)
op_cilinder = False
raycount = 0
totale_afstand = 0
totale_ray = 0
if op_cilinder == True:
raycount += 1
totale_afstand += measured_distance[i+1]
totale_ray += i
i += 1
return landmark_ray_center, landmark_ray_distance
def robot_map_translation_calculation(locatie):
#input [x,y,hoek[radialen]]
#output s,r,tx,ty
robot_positie = [[locatie[0],locatie[1]],[np.cos(locatie[2]+locatie[0]),np.sin(locatie[2])+locatie[1]]]
#kaart positie 0 punt altijd op 0,0 met aftand 1 in y richting
map_positie = [[0,0],[0,1]]
#transformatie berekenen
s,r,tx,ty = estimate(map_positie,robot_positie)
return s,r,tx,ty
def estimate(domainpoints, rangepoints):
'''
Parameters
domainpoints
list of [x, y] 2D lists
rangepoints
list of [x, y] 2D lists
'''
# Alias
X = domainpoints
Y = rangepoints
# Allow arrays of different length but
# ignore the extra points.
N = min(len(X), len(Y))
a1 = b1 = c1 = d1 = 0.0
a2 = b2 = 0.0
ad = bc = ac = bd = 0.0
for i in range(N):
a = X[i][0]
b = X[i][1]
c = Y[i][0]
d = Y[i][1]
a1 += a
b1 += b
c1 += c
d1 += d
a2 += a * a
b2 += b * b
ad += a * d
bc += b * c
ac += a * c
bd += b * d
# Denominator.
# It is zero iff X[i] = X[j] for every i and j in [0, n).
# In other words, iff all the domain points are the same.
den = N * a2 + N * b2 - a1 * a1 - b1 * b1
if abs(den) < 1e-8:
# The domain points are the same.
# We assume the translation to the mean of the range
# to be the best guess. However if N=0, assume identity.
if N == 0:
return Transform(1.0, 0.0, 0.0, 0.0)
else:
return Transform(1.0, 0.0, (c1 / N) - a, (d1 / N) - b)
# Estimators
s = (N * (ac + bd) - a1 * c1 - b1 * d1) / den
r = (N * (ad - bc) + b1 * c1 - a1 * d1) / den
tx = (-a1 * (ac + bd) + b1 * (ad - bc) + a2 * c1 + b2 * c1) / den
ty = (-b1 * (ac + bd) - a1 * (ad - bc) + a2 * d1 + b2 * d1) / den
return s, r, tx, ty