11"""
22
3- Path tracking simulation with pure pursuit steering control and PID speed control.
3+ Path tracking simulation with pure pursuit steering and PID speed control.
44
55author: Atsushi Sakai (@Atsushi_twi)
66 Guillaume Jacquenot (@Gjacquenot)
1010import math
1111import matplotlib .pyplot as plt
1212
13-
13+ # Parameters
1414k = 0.1 # look forward gain
15- Lfc = 2.0 # look-ahead distance
15+ Lfc = 2.0 # [m] look-ahead distance
1616Kp = 1.0 # speed proportional gain
17- dt = 0.1 # [s]
18- L = 2.9 # [m] wheel base of vehicle
19-
17+ dt = 0.1 # [s] time tick
18+ WB = 2.9 # [m] wheel base of vehicle
2019
2120show_animation = True
2221
@@ -28,20 +27,18 @@ def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
2827 self .y = y
2928 self .yaw = yaw
3029 self .v = v
31- self .rear_x = self .x - ((L / 2 ) * math .cos (self .yaw ))
32- self .rear_y = self .y - ((L / 2 ) * math .sin (self .yaw ))
30+ self .rear_x = self .x - ((WB / 2 ) * math .cos (self .yaw ))
31+ self .rear_y = self .y - ((WB / 2 ) * math .sin (self .yaw ))
3332
3433 def update (self , a , delta ):
35-
3634 self .x += self .v * math .cos (self .yaw ) * dt
3735 self .y += self .v * math .sin (self .yaw ) * dt
38- self .yaw += self .v / L * math .tan (delta ) * dt
36+ self .yaw += self .v / WB * math .tan (delta ) * dt
3937 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 ))
38+ self .rear_x = self .x - ((WB / 2 ) * math .cos (self .yaw ))
39+ self .rear_y = self .y - ((WB / 2 ) * math .sin (self .yaw ))
4240
4341 def calc_distance (self , point_x , point_y ):
44-
4542 dx = self .rear_x - point_x
4643 dy = self .rear_y - point_y
4744 return math .hypot (dx , dy )
@@ -56,27 +53,30 @@ def __init__(self):
5653 self .v = []
5754 self .t = []
5855
59- def append (self , t , state ):
56+ def append (self , t , state ):
6057 self .x .append (state .x )
6158 self .y .append (state .y )
6259 self .yaw .append (state .yaw )
6360 self .v .append (state .v )
6461 self .t .append (t )
6562
6663
67- def PIDControl (target , current ):
64+ def proportional_control (target , current ):
6865 a = Kp * (target - current )
6966
7067 return a
7168
7269
73- class Trajectory :
70+ class TargetCourse :
71+
7472 def __init__ (self , cx , cy ):
7573 self .cx = cx
7674 self .cy = cy
7775 self .old_nearest_point_index = None
7876
7977 def search_target_index (self , state ):
78+
79+ # To speed up nearest point search, doing it at only first time.
8080 if self .old_nearest_point_index is None :
8181 # search nearest point index
8282 dx = [state .rear_x - icx for icx in self .cx ]
@@ -86,47 +86,45 @@ def search_target_index(self, state):
8686 self .old_nearest_point_index = ind
8787 else :
8888 ind = self .old_nearest_point_index
89- distance_this_index = state .calc_distance (self .cx [ind ], self .cy [ind ])
89+ distance_this_index = state .calc_distance (self .cx [ind ],
90+ self .cy [ind ])
9091 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 ])
92+ distance_next_index = state . calc_distance ( self .cx [ ind + 1 ],
93+ self .cy [ind + 1 ])
9394 if distance_this_index < distance_next_index :
9495 break
96+ ind = ind + 1 if (ind + 1 ) < len (self .cx ) else ind
9597 distance_this_index = distance_next_index
9698 self .old_nearest_point_index = ind
9799
98- L = 0.0
99-
100- Lf = k * state .v + Lfc
100+ Lf = k * state .v + Lfc # update look ahead distance
101101
102102 # 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 ])
103+ while Lf > state .calc_distance (self .cx [ind ], self .cy [ind ]):
104+ if (ind + 1 ) >= len (self .cx ):
105+ break # not exceed goal
105106 ind += 1
106107
107- return ind
108+ return ind , Lf
108109
109110
110- def pure_pursuit_control (state , trajectory , pind ):
111-
112- ind = trajectory .search_target_index (state )
111+ def pure_pursuit_steer_control (state , trajectory , pind ):
112+ ind , Lf = trajectory .search_target_index (state )
113113
114114 if pind >= ind :
115115 ind = pind
116116
117117 if ind < len (trajectory .cx ):
118118 tx = trajectory .cx [ind ]
119119 ty = trajectory .cy [ind ]
120- else :
120+ else : # toward goal
121121 tx = trajectory .cx [- 1 ]
122122 ty = trajectory .cy [- 1 ]
123123 ind = len (trajectory .cx ) - 1
124124
125125 alpha = math .atan2 (ty - state .rear_y , tx - state .rear_x ) - state .yaw
126126
127- Lf = k * state .v + Lfc
128-
129- delta = math .atan2 (2.0 * L * math .sin (alpha ) / Lf , 1.0 )
127+ delta = math .atan2 (2.0 * WB * math .sin (alpha ) / Lf , 1.0 )
130128
131129 return delta , ind
132130
@@ -147,7 +145,7 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
147145
148146def main ():
149147 # target course
150- cx = np .arange (0 , 50 , 0.1 )
148+ cx = np .arange (0 , 50 , 0.5 )
151149 cy = [math .sin (ix / 5.0 ) * ix / 2.0 for ix in cx ]
152150
153151 target_speed = 10.0 / 3.6 # [m/s]
@@ -161,22 +159,27 @@ def main():
161159 time = 0.0
162160 states = States ()
163161 states .append (time , state )
164- trajectory = Trajectory (cx , cy )
165- target_ind = trajectory .search_target_index (state )
162+ target_course = TargetCourse (cx , cy )
163+ target_ind , _ = target_course .search_target_index (state )
166164
167165 while T >= time and lastIndex > target_ind :
168- ai = PIDControl (target_speed , state .v )
169- di , target_ind = pure_pursuit_control (state , trajectory , target_ind )
170- state .update (ai , di )
166+
167+ # Calc control input
168+ ai = proportional_control (target_speed , state .v )
169+ di , target_ind = pure_pursuit_steer_control (
170+ state , target_course , target_ind )
171+
172+ state .update (ai , di ) # Control vehicle
171173
172174 time += dt
173175 states .append (time , state )
174176
175177 if show_animation : # pragma: no cover
176178 plt .cla ()
177179 # for stopping simulation with the esc key.
178- plt .gcf ().canvas .mpl_connect ('key_release_event' ,
179- lambda event : [exit (0 ) if event .key == 'escape' else None ])
180+ plt .gcf ().canvas .mpl_connect (
181+ 'key_release_event' ,
182+ lambda event : [exit (0 ) if event .key == 'escape' else None ])
180183 plot_arrow (state .x , state .y , state .yaw )
181184 plt .plot (cx , cy , "-r" , label = "course" )
182185 plt .plot (states .x , states .y , "-b" , label = "trajectory" )
0 commit comments