14
14
from spatialmath .base .animate import *
15
15
from scipy .ndimage import *
16
16
from matplotlib import cm
17
- from roboticstoolbox .mobile .navigation import Navigation
17
+ import matplotlib .pyplot as plt
18
+ from matplotlib import animation
19
+ from roboticstoolbox .mobile .Planner import Planner
18
20
19
-
20
- class Bug2 (Navigation ):
21
+ class Bug2Planner (Planner ):
21
22
def __init__ (self , occ_grid , ** kwargs ):
22
23
23
24
"""
24
25
25
26
%Bug2.Bug2 Construct a Bug2 navigation object
26
27
%
27
- % B = Bug2(MAP, OPTIONS) is a bug2 navigation object, and MAP is an occupancy grid,
28
- % a representation of a planar world as a matrix whose elements are 0 (free
29
- % space) or 1 (occupied).
28
+ B = Bug2(MAP, OPTIONS) is a bug2 navigation object, and MAP is an occupancy grid,
29
+ a representation of a planar world as a matrix whose elements are 0 (free
30
+ space) or 1 (occupied).
30
31
%
31
- % Options::
32
- % 'goal',G Specify the goal point (1x2)
33
- % 'inflate',K Inflate all obstacles by K cells.
32
+ Options::
33
+ 'goal',G Specify the goal point (1x2)
34
+ 'inflate',K Inflate all obstacles by K cells.
34
35
%
35
- % See also Navigation.Navigation.
36
+ See also Navigation.Navigation.
36
37
37
38
%Navigation.Navigation Create a Navigation object
38
39
%
39
- % N = Navigation(OCCGRID, OPTIONS) is a Navigation object that holds an
40
- % occupancy grid OCCGRID. A number of options can be be passed.
40
+ N = Navigation(OCCGRID, OPTIONS) is a Navigation object that holds an
41
+ occupancy grid OCCGRID. A number of options can be be passed.
41
42
%
42
- % Options::
43
- % 'goal',G Specify the goal point (2x1)
44
- % 'inflate',K Inflate all obstacles by K cells.
45
- % 'private' Use private random number stream.
46
- % 'reset' Reset random number stream.
47
- % 'verbose' Display debugging information
48
- % 'seed',S Set the initial state of the random number stream. S must
49
- % be a proper random number generator state such as saved in
50
- % the seed0 property of an earlier run.
43
+ Options::
44
+ 'goal',G Specify the goal point (2x1)
45
+ 'inflate',K Inflate all obstacles by K cells.
46
+ 'private' Use private random number stream.
47
+ 'reset' Reset random number stream.
48
+ 'verbose' Display debugging information
49
+ 'seed',S Set the initial state of the random number stream. S must
50
+ be a proper random number generator state such as saved in
51
+ the seed0 property of an earlier run.
51
52
%
52
- % Notes::
53
- % - In the occupancy grid a value of zero means free space and non-zero means
54
- % occupied (not driveable).
55
- % - Obstacle inflation is performed with a round structuring element (kcircle)
56
- % with radius given by the 'inflate' option.
57
- % - Inflation requires either MVTB or IPT installed.
58
- % - The 'private' option creates a private random number stream for the methods
59
- % rand, randn and randi. If not given the global stream is used.
53
+ Notes::
54
+ - In the occupancy grid a value of zero means free space and non-zero means
55
+ occupied (not driveable).
56
+ - Obstacle inflation is performed with a round structuring element (kcircle)
57
+ with radius given by the 'inflate' option.
58
+ - Inflation requires either MVTB or IPT installed.
59
+ - The 'private' option creates a private random number stream for the methods
60
+ rand, randn and randi. If not given the global stream is used.
60
61
%
61
- % See also randstream.
62
+ See also randstream.
62
63
63
64
"""
64
65
super ().__init__ (occ_grid = occ_grid , ** kwargs )
@@ -100,102 +101,100 @@ def edge(self):
100
101
def k (self ):
101
102
return self ._k
102
103
103
- def query (self , start = None , goal = None , animate = False , movie = None , current = False ):
104
+ # override query method of base class
105
+ def query (self , start = None , goal = None , animate = False , movie = None , trail = True ):
104
106
"""
105
- [summary]
106
-
107
- :param start: [description], defaults to None
108
- :type start: [type], optional
109
- :param goal: [description], defaults to None
110
- :type goal: [type], optional
111
- :param animate: [description], defaults to False
107
+ Find a path using Bug2
108
+
109
+ :param start: starting position
110
+ :type start: array_like(2)
111
+ :param goal: goal position
112
+ :type goal: array_like(2)
113
+ :param animate: show animation of robot moving over the map,
114
+ defaults to False
112
115
:type animate: bool, optional
113
- :param movie: [description], defaults to None
114
- :type movie: [type], optional
115
- :param current: [description], defaults to False
116
+ :param movie: save animation as a movie, defaults to None. Is either
117
+ name of movie or a tuple (filename, frame interval)
118
+ :type movie: str or tuple(str, float), optional
119
+ :param trail: show the path followed by the robot, defaults to True
116
120
:type current: bool, optional
117
- :return: [description]
118
- :rtype: [type]
121
+ :return: path from ``start`` to ``goal``
122
+ :rtype: ndarray(2,N)
119
123
120
- %Bug2.query Find a path
121
- %
122
- % B.query(START, GOAL, OPTIONS) is the path (Nx2) from START (1x2) to GOAL
123
- % (1x2). Row are the coordinates of successive points along the path. If
124
- % either START or GOAL is [] the grid map is displayed and the user is
125
- % prompted to select a point by clicking on the plot.
126
- %
127
- % Options::
128
- % 'animate' show a simulation of the robot moving along the path
129
- % 'movie',M create a movie
130
- % 'current' show the current position as a black circle
131
- %
132
- % Notes::
133
- % - START and GOAL are given as X,Y coordinates in the grid map, not as
134
- % MATLAB row and column coordinates.
135
- % - START and GOAL are tested to ensure they lie in free space.
136
- % - The Bug2 algorithm is completely reactive so there is no planning
137
- % method.
138
- % - If the bug does a lot of back tracking it's hard to see the current
139
- % position, use the 'current' option.
140
- % - For the movie option if M contains an extension a movie file with that
141
- % extension is created. Otherwise a folder will be created containing
142
- % individual frames.
143
- %
144
- % See also Animate.
124
+ Compute the path from ``start`` to `goal` assuming the robot is capable
125
+ of 8-way motion from its current cell to the next.
126
+
127
+ .. note::
128
+
129
+ - ``start`` and `goal` are given as (x,y) coordinates in the grid map,
130
+ not as matrix row and column coordinates.
131
+ - ``start`` and ``goal`` are tested to ensure they lie in free space
132
+ - The Bug2 algorithm is completely reactive, there is no planning so
133
+ paths can be very inefficient.
134
+ - If the bug does a lot of back tracking it's hard to see the current
135
+ position, set ``trail=False``
136
+ - For the ``movie`` option specify a file or a tuple containing the
137
+ filename and the frame interval (1/fps) in seconds.
138
+
139
+ :seealso: :class:`MovieWriter`
145
140
"""
146
- anim = None
147
- if movie is not None :
148
- anim = Animate (movie )
149
- animate = True
150
141
151
142
# make sure start and goal are set and valid
152
- self .check_query (start , goal )
143
+ self .check_points (start , goal )
153
144
154
145
# compute the m-line
155
146
# create homogeneous representation of the line
156
147
# line*[x y 1]' = 0
157
- self ._m_line = hom_line (self ._start [0 ], self ._start [1 ],
158
- self ._goal [0 ], self ._goal [1 ])
148
+ self ._m_line = hom_line (self ._start , self ._goal )
149
+
150
+ if movie is not None :
151
+ animate = True
159
152
160
153
if animate :
161
154
self .plot ()
162
155
self .plot_m_line ()
156
+ plt .pause (0.05 )
157
+
158
+ movie = MovieWriter (movie )
163
159
164
160
robot = self ._start [:]
165
161
self ._step = 1
166
- path = [self ._start ]
162
+ path = np . r_ [self ._start ]
167
163
h = None
168
164
165
+ trail_line , = plt .plot (0 , 0 , 'y.' )
166
+ trail_head , = plt .plot (0 , 0 , 'ko' )
167
+
169
168
# iterate using the next() method until we reach the goal
170
169
while True :
171
170
if animate :
172
- plt .plot (robot [0 ], robot [1 ], 'y.' )
173
- plt .pause (0.1 )
174
- if current :
175
- h = self .plot (robot )
176
- plt .draw ()
177
- if movie is not None :
178
- anim .plot (h )
179
- if current :
180
- self .delete (h )
171
+ trail_head .set_data (robot [0 ], robot [1 ])
172
+ if trail :
173
+ trail_line .set_data (path .T )
174
+
175
+ plt .pause (0.001 )
176
+ # plt.draw()
177
+ # plt.show(block=False)
178
+ # plt.gcf().canvas.flush_events()
179
+
180
+ movie .add ()
181
181
182
182
# move to next point on path
183
183
robot = self .next (robot )
184
184
185
- # have we been here before, ie. in a loop
186
- if any ([all (robot == x ) for x in path ]):
187
- raise RuntimeError ('trapped' )
185
+ # # have we been here before, ie. in a loop
186
+ # if any([all(robot == x) for x in path]):
187
+ # raise RuntimeError('trapped')
188
188
189
189
# are we there yet?
190
190
if robot is None :
191
191
break
192
192
else :
193
- path . append ( robot )
193
+ path = np . vstack (( path , robot ) )
194
194
195
- if movie is not None :
196
- anim .done ()
195
+ movie .done ()
197
196
198
- return np . r_ [ path ]
197
+ return path
199
198
200
199
def plot_m_line (self , ls = None ):
201
200
if ls is None :
@@ -290,6 +289,7 @@ def plan(self):
290
289
# https://github.com/petercorke/toolbox-common-matlab/blob/master/edgelist.m
291
290
292
291
# these are directions of 8-neighbours in a clockwise direction
292
+ # fmt: off
293
293
_dirs = np .array ([
294
294
[- 1 , 0 ],
295
295
[- 1 , 1 ],
@@ -300,7 +300,7 @@ def plan(self):
300
300
[ 0 , - 1 ],
301
301
[- 1 , - 1 ],
302
302
])
303
-
303
+ # fmt: on
304
304
def edgelist (im , p , direction = 1 ):
305
305
"""
306
306
Find edge of a region
@@ -446,8 +446,8 @@ def adjacent_point(im, seed, pix0):
446
446
447
447
# Implementation of Peter Corke's matlab homline function from:
448
448
# https://github.com/petercorke/spatialmath-matlab/blob/master/homline.m
449
- def hom_line (x1 , y1 , x2 , y2 ):
450
- line = np .cross (np .r_ [x1 , y1 , 1 ], np .r_ [x2 , y2 , 1 ])
449
+ def hom_line (p1 , p2 ):
450
+ line = np .cross (np .r_ [p1 [ 0 ], p1 [ 1 ] , 1 ], np .r_ [p2 [ 0 ], p2 [ 1 ] , 1 ])
451
451
452
452
# normalize so that the result of x*l' is the pixel distance
453
453
# from the line
@@ -465,7 +465,7 @@ def hom_line(x1, y1, x2, y2):
465
465
# ind[ind < 0] = -1
466
466
# ind[ind >= array_shape[0]*array_shape[1]] = -1
467
467
# rows = (ind.astype('int') / array_shape[1])
468
- # cols = ind % array_shape[1]
468
+ # cols = ind array_shape[1]
469
469
# return rows, cols
470
470
471
471
# def col_norm(x):
0 commit comments