@@ -301,21 +301,91 @@ def _new_goal(self):
301
301
302
302
303
303
class PurePursuit (VehicleDriverBase ):
304
- def __init__ (self , speed = 1 , radius = 1 ):
305
- pass
304
+ def __init__ (self , path , lookahead = 1 , linemarkerstyle = None , ** kwargs ):
305
+ """
306
+ Driving agent for pure pursuit
307
+
308
+ :param path: path as a set of waypoints
309
+ :type path: array_like(2,n) one column per point, optional
310
+ :param lookahead: lookahead distance, defaults to 1
311
+ :type lookahead: float, optional
312
+ """
313
+ super ().__init__ (** kwargs )
314
+ self ._path = path
315
+ self ._lookahead = lookahead
316
+ self ._linemarkerstyle = linemarkerstyle
306
317
307
318
def __str__ (self ):
308
- pass
319
+ """Convert to string
309
320
310
- def init (self ):
311
- pass
321
+ :return: driver parameters and state in in a compact human readable format
322
+ :rtype: str
323
+ """
324
+
325
+ s = "PurePursuit driver object\n "
326
+ s += (
327
+ f" X { self ._workspace [0 ]} : { self ._workspace [1 ]} ; Y { self ._workspace [0 ]} :"
328
+ f" { self ._workspace [1 ]} , lookahead={ self ._lookahead } \n "
329
+ )
330
+ return s
331
+
332
+ def init (self , ax = None ):
333
+ if ax is not None :
334
+ h = plt .plot (
335
+ self ._path [0 , :],
336
+ self ._path [1 , :],
337
+ ** self ._linemarkerstyle ,
338
+ label = "waypoints" ,
339
+ )[0 ]
340
+ self ._current_waypint = None
312
341
313
342
def demand (self ):
314
- pass
343
+
344
+ cp = self ._veh ._x [0 :2 ] # current point
345
+
346
+ # find closest point on the path to current point
347
+ d = np .linalg .norm (self ._path .T - cp , axis = 1 ) # rely on implicit expansion
348
+ i = np .argmin (d )
349
+
350
+ # find all points on the path at least lookahead distance away
351
+ (k ,) = np .where (d [i + 1 :] >= self ._lookahead ) # find all points beyond horizon
352
+ if len (k ) == 0 :
353
+ # no such points, we must be near the end, goal is the end
354
+ goal = self ._path [:, - 1 ]
355
+ k = - 1
356
+ else :
357
+ # many such points, take the first one
358
+ k = k [0 ] + 1 # first point beyond look ahead distance
359
+ goal = self ._path [:, k + i ]
360
+ k += i
361
+
362
+ if self ._waypoint_marker is not None :
363
+ pass
364
+ for marker in self ._waypoint_marker :
365
+ marker .set_color ("blue" )
366
+ self ._waypoint_marker [k ].set_color ("red" )
367
+
368
+ return self .driveto (goal )
315
369
316
370
317
371
# ========================================================================= #
318
372
319
373
if __name__ == "__main__" :
320
374
321
- import unittest
375
+ # import unittest
376
+
377
+ import roboticstoolbox as rtb
378
+
379
+ # veh = rtb.Bicycle(control=rtb.RandomPath(workspace=10), animation="car")
380
+ # print(veh.control)
381
+ # veh.run(20, animate=True)
382
+
383
+ # create the path
384
+ path = np .array ([[10.0 , 10 ], [10 , 60 ], [80 , 80 ], [50 , 10 ]]).T
385
+ veh = rtb .Bicycle (
386
+ control = rtb .PurePursuit (path , speed = 4 , lookahead = 5 , workspace = [0 , 100 ]),
387
+ animation = "car" ,
388
+ )
389
+ print (veh )
390
+ print (veh .control )
391
+ veh .run (60 , animate = True )
0 commit comments