88
99import math
1010import numpy as np
11- import cv2
1211import matplotlib .pyplot as plt
12+ from collections import deque
1313
1414EXTEND_AREA = 1.0
1515
16+
1617def file_read (f ):
1718 """
1819 Reading LIDAR laser beams (angles and corresponding distance data)
@@ -27,6 +28,7 @@ def file_read(f):
2728 distances = np .array (distances )
2829 return angles , distances
2930
31+
3032def bresenham (start , end ):
3133 """
3234 Implementation of Bresenham's line drawing algorithm
@@ -70,6 +72,7 @@ def bresenham(start, end):
7072 points = np .array (points )
7173 return points
7274
75+
7376def calc_grid_map_config (ox , oy , xyreso ):
7477 """
7578 Calculates the size, and the maximum distances according to the the measurement center
@@ -83,28 +86,84 @@ def calc_grid_map_config(ox, oy, xyreso):
8386 print ("The grid map is " , xw , "x" , yw , "." )
8487 return minx , miny , maxx , maxy , xw , yw
8588
89+
8690def atan_zero_to_twopi (y , x ):
8791 angle = math .atan2 (y , x )
8892 if angle < 0.0 :
8993 angle += math .pi * 2.0
9094 return angle
9195
92- def generate_ray_casting_grid_map (ox , oy , xyreso , yawreso , breshen = True ):
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 len (fringe )> 0 :
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 ):
93155 """
94156 The breshen boolean tells if it's computed with bresenham ray casting (True) or with flood fill (False)
95157 """
96158 minx , miny , maxx , maxy , xw , yw = calc_grid_map_config (ox , oy , xyreso )
97159 pmap = np .ones ((xw , yw ))/ 2 # default 0.5 -- [[0.5 for i in range(yw)] for i in range(xw)]
98160 centix = int (round (- minx / xyreso )) # center x coordinate of the grid map
99161 centiy = int (round (- miny / xyreso )) # center y coordinate of the grid map
100- #print(centix, centiy)
101- prev_ix , prev_iy = centix - 1 , centiy
102162 # occupancy grid computed with bresenham ray casting
103163 if breshen :
104164 for (x , y ) in zip (ox , oy ):
105- angle = atan_zero_to_twopi (y , x )
106- ix = int (round ((x - minx ) / xyreso )) # x coordinte of the the occupied area
107- iy = int (round ((y - miny ) / xyreso )) # y coordinte of the the occupied area
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
108167 laser_beams = bresenham ((centix , centiy ), (ix , iy )) # line form the lidar to the cooupied point
109168 for laser_beam in laser_beams :
110169 pmap [laser_beam [0 ]][laser_beam [1 ]] = 0.0 # free area 0.0
@@ -114,19 +173,10 @@ def generate_ray_casting_grid_map(ox, oy, xyreso, yawreso, breshen = True):
114173 pmap [ix + 1 ][iy + 1 ] = 1.0 # extend the occupied area
115174 # occupancy grid computed with with flood fill
116175 else :
117- pmap = (np .ones ((xw , yw ), dtype = np .uint8 )) * 5 # food fill does not work with float numbers such as 0.5; so 5 is the default and later it is divided by 10
118- for (x , y ) in zip (ox , oy ):
119- ix = int (round ((x - minx ) / xyreso )) # x coordinte of the the occupied area
120- iy = int (round ((y - miny ) / xyreso )) # y coordinte of the the occupied area
121- free_area = bresenham ((prev_ix , prev_iy ), (ix , iy ))
122- for fa in free_area :
123- pmap [fa [0 ]][fa [1 ]] = 0 # free area 0.0
124- prev_ix = ix
125- prev_iy = iy
126- cv2 .floodFill (pmap , None , (centix , centiy ), 0 ) # filling the free spaces with 0, stating from the center
176+ pmap = init_floodfill ((centix , centiy ), (ox , oy ), (xw , yw ), (minx , miny ), xyreso )
177+ flood_fill ((centix , centiy ), pmap )
127178 pmap = np .array (pmap , dtype = np .float )
128- pmap /= 10
129- for (x , y ) in zip (ox , oy ):
179+ for (x , y ) in zip (ox , oy ):
130180 ix = int (round ((x - minx ) / xyreso ))
131181 iy = int (round ((y - miny ) / xyreso ))
132182 pmap [ix ][iy ] = 1.0 # occupied area 1.0
@@ -135,25 +185,25 @@ def generate_ray_casting_grid_map(ox, oy, xyreso, yawreso, breshen = True):
135185 pmap [ix + 1 ][iy + 1 ] = 1.0 # extend the occupied area
136186 return pmap , minx , maxx , miny , maxy , xyreso
137187
188+
138189def main ():
139190 """
140191 Example usage
141192 """
142193 print (__file__ , "start" )
143194 xyreso = 0.02 # x-y grid resolution
144- yawreso = math .radians (3.1 ) # yaw angle resolution [rad]
145195 ang , dist = file_read ("lidar01.csv" )
146196 ox = np .sin (ang ) * dist
147197 oy = np .cos (ang ) * dist
148- pmap , minx , maxx , miny , maxy , xyreso = generate_ray_casting_grid_map (ox , oy , xyreso , yawreso , True )
198+ pmap , minx , maxx , miny , maxy , xyreso = generate_ray_casting_grid_map (ox , oy , xyreso , True )
149199 xyres = np .array (pmap ).shape
150200 plt .figure (1 , figsize = (10 ,4 ))
151201 plt .subplot (122 )
152- plt .imshow (pmap , cmap = "PiYG_r" ) # cmap = "binary" "PiYG_r" "PiYG_r" "bone" "bone_r" "RdYlGn_r"
202+ plt .imshow (pmap , cmap = "PiYG_r" ) # cmap = "binary" "PiYG_r" "PiYG_r" "bone" "bone_r" "RdYlGn_r"
153203 plt .clim (- 0.4 , 1.4 )
154- plt .gca ().set_xticks (np .arange (- .5 , xyres [1 ], 1 ), minor = True )
155- plt .gca ().set_yticks (np .arange (- .5 , xyres [0 ], 1 ), minor = True )
156- plt .grid (True , which = "minor" , color = "w" , linewidth = .6 , alpha = 0.5 )
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 )
157207 plt .colorbar ()
158208 plt .subplot (121 )
159209 plt .plot ([oy , np .zeros (np .size (oy ))], [ox , np .zeros (np .size (oy ))], "ro-" )
@@ -164,7 +214,7 @@ def main():
164214 plt .ylim ((top , bottom )) # rescale y axis, to match the grid orientation
165215 plt .grid (True )
166216 plt .show ()
217+
167218
168219if __name__ == '__main__' :
169220 main ()
170-
0 commit comments