Skip to content

Commit 7c77e01

Browse files
authored
Merge pull request AtsushiSakai#198 from horverno/master
Lidar measurement to grid map
2 parents a6aae71 + ce1fa0f commit 7c77e01

File tree

6 files changed

+699
-0
lines changed

6 files changed

+699
-0
lines changed
279 KB
Loading
372 KB
Loading
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
0.008450416037156572,0.5335
2+
0.046902201120156306,0.5345
3+
0.08508127850753233,0.537
4+
0.1979822644959155,0.2605
5+
0.21189035697274505,0.2625
6+
0.2587960806200922,0.26475
7+
0.3043382657893199,0.2675
8+
0.34660795861105775,0.27075
9+
0.43632879047139106,0.59
10+
0.4739624524675188,0.60025
11+
0.5137777760286397,0.611
12+
0.5492297764597742,0.6265
13+
0.5895905154121426,0.64
14+
0.6253152235389017,0.6565
15+
0.6645851317087743,0.676
16+
0.6997644244442851,0.6975
17+
0.7785769484796541,0.3345
18+
0.7772134100015329,0.74575
19+
0.8652979956881222,0.3315
20+
0.8996591653367609,0.31775
21+
0.9397471965935056,0.31275
22+
0.9847439663714841,0.31125
23+
1.0283771976713423,0.31325
24+
1.0641019057981014,0.31975
25+
1.1009174447073562,0.3335
26+
1.2012738766970301,0.92275
27+
1.2397256617800307,0.95325
28+
1.2779047391674068,0.9865
29+
1.316629231946031,1.01775
30+
1.3561718478115274,1.011
31+
1.3948963405901518,1.0055
32+
1.4330754179775278,1.00225
33+
1.4731634492342724,0.99975
34+
1.5113425266216485,0.9975
35+
1.5517032655740168,1.001
36+
1.5896096352657691,1.00275
37+
1.6288795434356418,1.008
38+
1.6684221593011381,1.0135
39+
1.7066012366885142,1.022
40+
1.7453257294671385,1.02875
41+
1.7862318838107551,0.9935
42+
1.8257744996762515,1.0025
43+
1.8661352386286207,0.96075
44+
1.9045870237116205,0.92125
45+
1.9465840088377355,0.8855
46+
1.986944747790103,0.85725
47+
2.025669240568728,0.832
48+
2.065757271825472,0.80675
49+
2.1066634261690886,0.78875
50+
2.1464787497302105,0.7705
51+
2.1865667809869542,0.75625
52+
2.2261093968524506,0.74475
53+
2.2683790896741876,0.68275
54+
2.3090125363221823,0.6375
55+
2.3510095214482956,0.59925
56+
2.3916429680962885,0.5665
57+
2.4330945378311526,0.538
58+
2.4783640153047557,0.50825
59+
2.5203610004308707,0.4875
60+
2.562903400948233,0.46825
61+
2.599173524466238,0.45
62+
2.642806755766097,0.4355
63+
2.685076448587836,0.42275
64+
2.722437402888339,0.4125
65+
2.766888757275069,0.40125
66+
2.8007045115324587,0.39525
67+
2.841883373571701,0.385
68+
2.8819714048284446,0.3805
69+
2.922332143780814,0.38575
70+
2.9637837135156797,0.38425
71+
3.0005992524249336,0.36575
72+
3.0401418682904318,0.3765
73+
3.079957191851552,0.3915
74+
3.115409192282687,0.408
75+
3.154679100452558,0.4265
76+
3.184949654666836,0.447
77+
3.2242195628367085,0.4715
78+
3.2574899017028507,0.49875
79+
3.2959416867858504,0.52875
80+
3.3292120256519926,0.564
81+
3.3665729799524957,0.6055
82+
3.4031158111661277,0.6515
83+
3.438022396206014,0.70675
84+
3.4756560582021407,0.771
85+
3.513562427893893,0.77075
86+
3.5522869206725183,0.7785
87+
3.621827383056667,0.79575
88+
3.65918833735717,0.8045
89+
3.697367414744546,0.81725
90+
3.7377281536969154,0.83325
91+
3.775634523388667,0.8415
92+
3.8135408930804187,0.85575
93+
3.8522653858590425,0.87325
94+
3.8898990478551703,0.88725
95+
3.9299870791119154,0.906
96+
3.9665299103255465,0.9265
97+
4.006072526191043,0.94575
98+
4.043978895882795,0.97175
99+
4.081885265574547,1.02075
100+
4.1206097583531704,1.046
101+
4.1587888357405465,1.07025
102+
4.196422497736674,1.097
103+
4.234874282819675,1.12575
104+
4.286688744988257,0.73475
105+
4.324322406984384,0.72225
106+
4.364410438241129,0.731
107+
4.405862007975994,0.7405
108+
4.44267754688525,0.749
109+
4.484129116620115,0.76025
110+
4.522853609398739,0.76825
111+
4.560759979090491,0.77125
112+
4.5989390564778665,0.77725
113+
4.640117918517108,0.782
114+
4.679115118991357,0.78425
115+
4.717294196378733,0.789
116+
4.757109519939853,0.78825
117+
4.796652135805349,0.7855
118+
4.8337403824102285,0.786
119+
4.871646752101981,0.78275
120+
4.9109166602718535,0.7785
121+
4.950186568441726,0.7635
122+
4.990274599698471,0.74725
123+
5.028180969390222,0.737
124+
5.0677235852557185,0.72575
125+
5.109720570381833,0.71525
126+
5.149263186247329,0.70625
127+
5.1863514328522085,0.69975
128+
5.230530079543315,0.693
129+
5.269799987713188,0.68925
130+
5.307979065100563,0.68425
131+
5.347248973270435,0.68275
132+
5.386518881440308,0.68075
133+
5.426606912697053,0.68175
134+
5.465604113171301,0.67825
135+
5.502419652080556,0.6835
136+
5.543871221815422,0.6885
137+
5.580959468420302,0.67925
138+
5.624319992024535,0.6555
139+
5.660044700151294,0.639
140+
5.700950854494911,0.623
141+
5.740220762664784,0.6075
142+
5.783581286269018,0.59475
143+
5.820124117482649,0.58475
144+
5.861848394913139,0.57325
145+
5.899209349213642,0.565
146+
5.938751965079138,0.55525
147+
5.9782945809446355,0.55175
148+
6.017564489114507,0.546
149+
6.059288766544997,0.5405
150+
6.097467843932373,0.53825
151+
6.139464829058487,0.534
152+
6.176825783358991,0.5325
153+
6.215822983833238,0.53125
154+
6.252911230438118,0.53075
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
"""
2+
3+
LIDAR to 2D grid map example
4+
5+
author: Erno Horvath, Csaba Hajdu based on Atsushi Sakai's scripts (@Atsushi_twi)
6+
7+
"""
8+
9+
import math
10+
import numpy as np
11+
import matplotlib.pyplot as plt
12+
from collections import deque
13+
14+
EXTEND_AREA = 1.0
15+
16+
17+
def file_read(f):
18+
"""
19+
Reading LIDAR laser beams (angles and corresponding distance data)
20+
"""
21+
measures = [line.split(",") for line in open(f)]
22+
angles = []
23+
distances = []
24+
for measure in measures:
25+
angles.append(float(measure[0]))
26+
distances.append(float(measure[1]))
27+
angles = np.array(angles)
28+
distances = np.array(distances)
29+
return angles, distances
30+
31+
32+
def bresenham(start, end):
33+
"""
34+
Implementation of Bresenham's line drawing algorithm
35+
See en.wikipedia.org/wiki/Bresenham's_line_algorithm
36+
Bresenham's Line Algorithm
37+
Produces a np.array from start and end (original from roguebasin.com)
38+
>>> points1 = bresenham((4, 4), (6, 10))
39+
>>> print(points1)
40+
np.array([[4,4], [4,5], [5,6], [5,7], [5,8], [6,9], [6,10]])
41+
"""
42+
# setup initial conditions
43+
x1, y1 = start
44+
x2, y2 = end
45+
dx = x2 - x1
46+
dy = y2 - y1
47+
is_steep = abs(dy) > abs(dx) # determine how steep the line is
48+
if is_steep: # rotate line
49+
x1, y1 = y1, x1
50+
x2, y2 = y2, x2
51+
swapped = False # swap start and end points if necessary and store swap state
52+
if x1 > x2:
53+
x1, x2 = x2, x1
54+
y1, y2 = y2, y1
55+
swapped = True
56+
dx = x2 - x1 # recalculate differentials
57+
dy = y2 - y1 # recalculate differentials
58+
error = int(dx / 2.0) # calculate error
59+
ystep = 1 if y1 < y2 else -1
60+
# iterate over bounding box generating points between start and end
61+
y = y1
62+
points = []
63+
for x in range(x1, x2 + 1):
64+
coord = [y, x] if is_steep else (x, y)
65+
points.append(coord)
66+
error -= abs(dy)
67+
if error < 0:
68+
y += ystep
69+
error += dx
70+
if swapped: # reverse the list if the coordinates were swapped
71+
points.reverse()
72+
points = np.array(points)
73+
return points
74+
75+
76+
def calc_grid_map_config(ox, oy, xyreso):
77+
"""
78+
Calculates the size, and the maximum distances according to the the measurement center
79+
"""
80+
minx = round(min(ox) - EXTEND_AREA / 2.0)
81+
miny = round(min(oy) - EXTEND_AREA / 2.0)
82+
maxx = round(max(ox) + EXTEND_AREA / 2.0)
83+
maxy = round(max(oy) + EXTEND_AREA / 2.0)
84+
xw = int(round((maxx - minx) / xyreso))
85+
yw = int(round((maxy - miny) / xyreso))
86+
print("The grid map is ", xw, "x", yw, ".")
87+
return minx, miny, maxx, maxy, xw, yw
88+
89+
90+
def atan_zero_to_twopi(y, x):
91+
angle = math.atan2(y, x)
92+
if angle < 0.0:
93+
angle += math.pi * 2.0
94+
return angle
95+
96+
97+
def init_floodfill(cpoint, opoints, xypoints, mincoord, xyreso):
98+
"""
99+
cpoint: center point
100+
opoints: detected obstacles points (x,y)
101+
xypoints: (x,y) point pairs
102+
"""
103+
centix, centiy = cpoint
104+
prev_ix, prev_iy = centix - 1, centiy
105+
ox, oy = opoints
106+
xw, yw = xypoints
107+
minx, miny = mincoord
108+
pmap = (np.ones((xw, yw))) * 0.5
109+
for (x, y) in zip(ox, oy):
110+
ix = int(round((x - minx) / xyreso)) # x coordinate of the the occupied area
111+
iy = int(round((y - miny) / xyreso)) # y coordinate of the the occupied area
112+
free_area = bresenham((prev_ix, prev_iy), (ix, iy))
113+
for fa in free_area:
114+
pmap[fa[0]][fa[1]] = 0 # free area 0.0
115+
prev_ix = ix
116+
prev_iy = iy
117+
return pmap
118+
119+
120+
def flood_fill(cpoint, pmap):
121+
"""
122+
cpoint: starting point (x,y) of fill
123+
pmap: occupancy map generated from Bresenham ray-tracing
124+
"""
125+
# Fill empty areas with queue method
126+
sx, sy = pmap.shape
127+
fringe = deque()
128+
fringe.appendleft(cpoint)
129+
while fringe:
130+
n = fringe.pop()
131+
nx, ny = n
132+
# West
133+
if nx > 0:
134+
if pmap[nx - 1, ny] == 0.5:
135+
pmap[nx - 1, ny] = 0.0
136+
fringe.appendleft((nx - 1, ny))
137+
# East
138+
if nx < sx - 1:
139+
if pmap[nx + 1, ny] == 0.5:
140+
pmap[nx + 1, ny] = 0.0
141+
fringe.appendleft((nx + 1, ny))
142+
# North
143+
if ny > 0:
144+
if pmap[nx, ny - 1] == 0.5:
145+
pmap[nx, ny - 1] = 0.0
146+
fringe.appendleft((nx, ny - 1))
147+
# South
148+
if ny < sy - 1:
149+
if pmap[nx, ny + 1] == 0.5:
150+
pmap[nx, ny + 1] = 0.0
151+
fringe.appendleft((nx, ny + 1))
152+
153+
154+
def generate_ray_casting_grid_map(ox, oy, xyreso, breshen=True):
155+
"""
156+
The breshen boolean tells if it's computed with bresenham ray casting (True) or with flood fill (False)
157+
"""
158+
minx, miny, maxx, maxy, xw, yw = calc_grid_map_config(ox, oy, xyreso)
159+
pmap = np.ones((xw, yw))/2 # default 0.5 -- [[0.5 for i in range(yw)] for i in range(xw)]
160+
centix = int(round(-minx / xyreso)) # center x coordinate of the grid map
161+
centiy = int(round(-miny / xyreso)) # center y coordinate of the grid map
162+
# occupancy grid computed with bresenham ray casting
163+
if breshen:
164+
for (x, y) in zip(ox, oy):
165+
ix = int(round((x - minx) / xyreso)) # x coordinate of the the occupied area
166+
iy = int(round((y - miny) / xyreso)) # y coordinate of the the occupied area
167+
laser_beams = bresenham((centix, centiy), (ix, iy)) # line form the lidar to the cooupied point
168+
for laser_beam in laser_beams:
169+
pmap[laser_beam[0]][laser_beam[1]] = 0.0 # free area 0.0
170+
pmap[ix][iy] = 1.0 # occupied area 1.0
171+
pmap[ix+1][iy] = 1.0 # extend the occupied area
172+
pmap[ix][iy+1] = 1.0 # extend the occupied area
173+
pmap[ix+1][iy+1] = 1.0 # extend the occupied area
174+
# occupancy grid computed with with flood fill
175+
else:
176+
pmap = init_floodfill((centix, centiy), (ox, oy), (xw, yw), (minx, miny), xyreso)
177+
flood_fill((centix, centiy), pmap)
178+
pmap = np.array(pmap, dtype=np.float)
179+
for (x, y) in zip(ox, oy):
180+
ix = int(round((x - minx) / xyreso))
181+
iy = int(round((y - miny) / xyreso))
182+
pmap[ix][iy] = 1.0 # occupied area 1.0
183+
pmap[ix+1][iy] = 1.0 # extend the occupied area
184+
pmap[ix][iy+1] = 1.0 # extend the occupied area
185+
pmap[ix+1][iy+1] = 1.0 # extend the occupied area
186+
return pmap, minx, maxx, miny, maxy, xyreso
187+
188+
189+
def main():
190+
"""
191+
Example usage
192+
"""
193+
print(__file__, "start")
194+
xyreso = 0.02 # x-y grid resolution
195+
ang, dist = file_read("lidar01.csv")
196+
ox = np.sin(ang) * dist
197+
oy = np.cos(ang) * dist
198+
pmap, minx, maxx, miny, maxy, xyreso = generate_ray_casting_grid_map(ox, oy, xyreso, True)
199+
xyres = np.array(pmap).shape
200+
plt.figure(1, figsize=(10,4))
201+
plt.subplot(122)
202+
plt.imshow(pmap, cmap="PiYG_r") # cmap = "binary" "PiYG_r" "PiYG_r" "bone" "bone_r" "RdYlGn_r"
203+
plt.clim(-0.4, 1.4)
204+
plt.gca().set_xticks(np.arange(-.5, xyres[1], 1), minor=True)
205+
plt.gca().set_yticks(np.arange(-.5, xyres[0], 1), minor=True)
206+
plt.grid(True, which="minor", color="w", linewidth=0.6, alpha=0.5)
207+
plt.colorbar()
208+
plt.subplot(121)
209+
plt.plot([oy, np.zeros(np.size(oy))], [ox, np.zeros(np.size(oy))], "ro-")
210+
plt.axis("equal")
211+
plt.plot(0.0, 0.0, "ob")
212+
plt.gca().set_aspect("equal", "box")
213+
bottom, top = plt.ylim() # return the current ylim
214+
plt.ylim((top, bottom)) # rescale y axis, to match the grid orientation
215+
plt.grid(True)
216+
plt.show()
217+
218+
219+
if __name__ == '__main__':
220+
main()

0 commit comments

Comments
 (0)