33Path tracking simulation with pure pursuit steering control and PID speed control.
44
55author: Atsushi Sakai (@Atsushi_twi)
6+ Guillaume Jacquenot (@Gjacquenot)
67
78"""
89import numpy as np
1718L = 2.9 # [m] wheel base of vehicle
1819
1920
20- old_nearest_point_index = None
2121show_animation = True
2222
2323
@@ -31,89 +31,104 @@ def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
3131 self .rear_x = self .x - ((L / 2 ) * math .cos (self .yaw ))
3232 self .rear_y = self .y - ((L / 2 ) * math .sin (self .yaw ))
3333
34+ def update (self , a , delta ):
3435
35- def update (state , a , delta ):
36+ self .x += self .v * math .cos (self .yaw ) * dt
37+ self .y += self .v * math .sin (self .yaw ) * dt
38+ self .yaw += self .v / L * math .tan (delta ) * dt
39+ self .v += a * dt
40+ self .rear_x = self .x - ((L / 2 ) * math .cos (self .yaw ))
41+ self .rear_y = self .y - ((L / 2 ) * math .sin (self .yaw ))
3642
37- state .x = state .x + state .v * math .cos (state .yaw ) * dt
38- state .y = state .y + state .v * math .sin (state .yaw ) * dt
39- state .yaw = state .yaw + state .v / L * math .tan (delta ) * dt
40- state .v = state .v + a * dt
41- state .rear_x = state .x - ((L / 2 ) * math .cos (state .yaw ))
42- state .rear_y = state .y - ((L / 2 ) * math .sin (state .yaw ))
43+ def calc_distance (self , point_x , point_y ):
4344
44- return state
45+ dx = self .rear_x - point_x
46+ dy = self .rear_y - point_y
47+ return math .hypot (dx , dy )
4548
4649
47- def PIDControl (target , current ):
48- a = Kp * (target - current )
50+ class States :
4951
50- return a
52+ def __init__ (self ):
53+ self .x = []
54+ self .y = []
55+ self .yaw = []
56+ self .v = []
57+ self .t = []
5158
59+ def append (self , t , state ):
60+ self .x .append (state .x )
61+ self .y .append (state .y )
62+ self .yaw .append (state .yaw )
63+ self .v .append (state .v )
64+ self .t .append (t )
5265
53- def pure_pursuit_control (state , cx , cy , pind ):
5466
55- ind = calc_target_index (state , cx , cy )
67+ def PIDControl (target , current ):
68+ a = Kp * (target - current )
5669
57- if pind >= ind :
58- ind = pind
70+ return a
5971
60- if ind < len (cx ):
61- tx = cx [ind ]
62- ty = cy [ind ]
63- else :
64- tx = cx [- 1 ]
65- ty = cy [- 1 ]
66- ind = len (cx ) - 1
6772
68- alpha = math .atan2 (ty - state .rear_y , tx - state .rear_x ) - state .yaw
73+ class Trajectory :
74+ def __init__ (self , cx , cy ):
75+ self .cx = cx
76+ self .cy = cy
77+ self .old_nearest_point_index = None
6978
70- Lf = k * state .v + Lfc
79+ def search_target_index (self , state ):
80+ if self .old_nearest_point_index is None :
81+ # search nearest point index
82+ dx = [state .rear_x - icx for icx in self .cx ]
83+ dy = [state .rear_y - icy for icy in self .cy ]
84+ d = np .hypot (dx , dy )
85+ ind = np .argmin (d )
86+ self .old_nearest_point_index = ind
87+ else :
88+ ind = self .old_nearest_point_index
89+ distance_this_index = state .calc_distance (self .cx [ind ], self .cy [ind ])
90+ while True :
91+ ind = ind + 1 if (ind + 1 ) < len (self .cx ) else ind
92+ distance_next_index = state .calc_distance (self .cx [ind ], self .cy [ind ])
93+ if distance_this_index < distance_next_index :
94+ break
95+ distance_this_index = distance_next_index
96+ self .old_nearest_point_index = ind
7197
72- delta = math . atan2 ( 2.0 * L * math . sin ( alpha ) / Lf , 1.0 )
98+ L = 0.0
7399
74- return delta , ind
100+ Lf = k * state . v + Lfc
75101
76- def calc_distance (state , point_x , point_y ):
102+ # search look ahead target point index
103+ while Lf > L and (ind + 1 ) < len (self .cx ):
104+ L = state .calc_distance (self .cx [ind ], self .cy [ind ])
105+ ind += 1
77106
78- dx = state .rear_x - point_x
79- dy = state .rear_y - point_y
80- return math .hypot (dx , dy )
107+ return ind
81108
82109
83- def calc_target_index (state , cx , cy ):
110+ def pure_pursuit_control (state , trajectory , pind ):
84111
85- global old_nearest_point_index
112+ ind = trajectory .search_target_index (state )
113+
114+ if pind >= ind :
115+ ind = pind
86116
87- if old_nearest_point_index is None :
88- # search nearest point index
89- dx = [state .rear_x - icx for icx in cx ]
90- dy = [state .rear_y - icy for icy in cy ]
91- d = [idx ** 2 + idy ** 2 for (idx , idy ) in zip (dx , dy )]
92- ind = d .index (min (d ))
93- old_nearest_point_index = ind
117+ if ind < len (trajectory .cx ):
118+ tx = trajectory .cx [ind ]
119+ ty = trajectory .cy [ind ]
94120 else :
95- ind = old_nearest_point_index
96- distance_this_index = calc_distance (state , cx [ind ], cy [ind ])
97- while True :
98- ind = ind + 1 if (ind + 1 ) < len (cx ) else ind
99- distance_next_index = calc_distance (state , cx [ind ], cy [ind ])
100- if distance_this_index < distance_next_index :
101- break
102- distance_this_index = distance_next_index
103- old_nearest_point_index = ind
104-
105- L = 0.0
121+ tx = trajectory .cx [- 1 ]
122+ ty = trajectory .cy [- 1 ]
123+ ind = len (trajectory .cx ) - 1
124+
125+ alpha = math .atan2 (ty - state .rear_y , tx - state .rear_x ) - state .yaw
106126
107127 Lf = k * state .v + Lfc
108128
109- # search look ahead target point index
110- while Lf > L and (ind + 1 ) < len (cx ):
111- dx = cx [ind ] - state .rear_x
112- dy = cy [ind ] - state .rear_y
113- L = math .hypot (dx , dy )
114- ind += 1
129+ delta = math .atan2 (2.0 * L * math .sin (alpha ) / Lf , 1.0 )
115130
116- return ind
131+ return delta , ind
117132
118133
119134def plot_arrow (x , y , yaw , length = 1.0 , width = 0.5 , fc = "r" , ec = "k" ):
@@ -122,7 +137,7 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
122137 """
123138
124139 if not isinstance (x , float ):
125- for ( ix , iy , iyaw ) in zip (x , y , yaw ):
140+ for ix , iy , iyaw in zip (x , y , yaw ):
126141 plot_arrow (ix , iy , iyaw )
127142 else :
128143 plt .arrow (x , y , length * math .cos (yaw ), length * math .sin (yaw ),
@@ -144,25 +159,18 @@ def main():
144159
145160 lastIndex = len (cx ) - 1
146161 time = 0.0
147- x = [state .x ]
148- y = [state .y ]
149- yaw = [state .yaw ]
150- v = [state .v ]
151- t = [0.0 ]
152- target_ind = calc_target_index (state , cx , cy )
162+ states = States ()
163+ states .append (time , state )
164+ trajectory = Trajectory (cx , cy )
165+ target_ind = trajectory .search_target_index (state )
153166
154167 while T >= time and lastIndex > target_ind :
155168 ai = PIDControl (target_speed , state .v )
156- di , target_ind = pure_pursuit_control (state , cx , cy , target_ind )
157- state = update (state , ai , di )
158-
159- time = time + dt
169+ di , target_ind = pure_pursuit_control (state , trajectory , target_ind )
170+ state .update (ai , di )
160171
161- x .append (state .x )
162- y .append (state .y )
163- yaw .append (state .yaw )
164- v .append (state .v )
165- t .append (time )
172+ time += dt
173+ states .append (time , state )
166174
167175 if show_animation : # pragma: no cover
168176 plt .cla ()
@@ -171,7 +179,7 @@ def main():
171179 lambda event : [exit (0 ) if event .key == 'escape' else None ])
172180 plot_arrow (state .x , state .y , state .yaw )
173181 plt .plot (cx , cy , "-r" , label = "course" )
174- plt .plot (x , y , "-b" , label = "trajectory" )
182+ plt .plot (states . x , states . y , "-b" , label = "trajectory" )
175183 plt .plot (cx [target_ind ], cy [target_ind ], "xg" , label = "target" )
176184 plt .axis ("equal" )
177185 plt .grid (True )
@@ -184,15 +192,15 @@ def main():
184192 if show_animation : # pragma: no cover
185193 plt .cla ()
186194 plt .plot (cx , cy , ".r" , label = "course" )
187- plt .plot (x , y , "-b" , label = "trajectory" )
195+ plt .plot (states . x , states . y , "-b" , label = "trajectory" )
188196 plt .legend ()
189197 plt .xlabel ("x[m]" )
190198 plt .ylabel ("y[m]" )
191199 plt .axis ("equal" )
192200 plt .grid (True )
193201
194202 plt .subplots (1 )
195- plt .plot (t , [iv * 3.6 for iv in v ], "-r" )
203+ plt .plot (states . t , [iv * 3.6 for iv in states . v ], "-r" )
196204 plt .xlabel ("Time[s]" )
197205 plt .ylabel ("Speed[km/h]" )
198206 plt .grid (True )
0 commit comments