1
1
"""
2
2
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.
4
4
5
5
author: Atsushi Sakai (@Atsushi_twi)
6
6
Guillaume Jacquenot (@Gjacquenot)
10
10
import math
11
11
import matplotlib .pyplot as plt
12
12
13
-
13
+ # Parameters
14
14
k = 0.1 # look forward gain
15
- Lfc = 2.0 # look-ahead distance
15
+ Lfc = 2.0 # [m] look-ahead distance
16
16
Kp = 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
20
19
21
20
show_animation = True
22
21
@@ -28,20 +27,18 @@ def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
28
27
self .y = y
29
28
self .yaw = yaw
30
29
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 ))
33
32
34
33
def update (self , a , delta ):
35
-
36
34
self .x += self .v * math .cos (self .yaw ) * dt
37
35
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
39
37
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 ))
42
40
43
41
def calc_distance (self , point_x , point_y ):
44
-
45
42
dx = self .rear_x - point_x
46
43
dy = self .rear_y - point_y
47
44
return math .hypot (dx , dy )
@@ -56,27 +53,30 @@ def __init__(self):
56
53
self .v = []
57
54
self .t = []
58
55
59
- def append (self , t , state ):
56
+ def append (self , t , state ):
60
57
self .x .append (state .x )
61
58
self .y .append (state .y )
62
59
self .yaw .append (state .yaw )
63
60
self .v .append (state .v )
64
61
self .t .append (t )
65
62
66
63
67
- def PIDControl (target , current ):
64
+ def proportional_control (target , current ):
68
65
a = Kp * (target - current )
69
66
70
67
return a
71
68
72
69
73
- class Trajectory :
70
+ class TargetCourse :
71
+
74
72
def __init__ (self , cx , cy ):
75
73
self .cx = cx
76
74
self .cy = cy
77
75
self .old_nearest_point_index = None
78
76
79
77
def search_target_index (self , state ):
78
+
79
+ # To speed up nearest point search, doing it at only first time.
80
80
if self .old_nearest_point_index is None :
81
81
# search nearest point index
82
82
dx = [state .rear_x - icx for icx in self .cx ]
@@ -86,47 +86,45 @@ def search_target_index(self, state):
86
86
self .old_nearest_point_index = ind
87
87
else :
88
88
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 ])
90
91
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 ])
93
94
if distance_this_index < distance_next_index :
94
95
break
96
+ ind = ind + 1 if (ind + 1 ) < len (self .cx ) else ind
95
97
distance_this_index = distance_next_index
96
98
self .old_nearest_point_index = ind
97
99
98
- L = 0.0
99
-
100
- Lf = k * state .v + Lfc
100
+ Lf = k * state .v + Lfc # update look ahead distance
101
101
102
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 ])
103
+ while Lf > state .calc_distance (self .cx [ind ], self .cy [ind ]):
104
+ if (ind + 1 ) >= len (self .cx ):
105
+ break # not exceed goal
105
106
ind += 1
106
107
107
- return ind
108
+ return ind , Lf
108
109
109
110
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 )
113
113
114
114
if pind >= ind :
115
115
ind = pind
116
116
117
117
if ind < len (trajectory .cx ):
118
118
tx = trajectory .cx [ind ]
119
119
ty = trajectory .cy [ind ]
120
- else :
120
+ else : # toward goal
121
121
tx = trajectory .cx [- 1 ]
122
122
ty = trajectory .cy [- 1 ]
123
123
ind = len (trajectory .cx ) - 1
124
124
125
125
alpha = math .atan2 (ty - state .rear_y , tx - state .rear_x ) - state .yaw
126
126
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 )
130
128
131
129
return delta , ind
132
130
@@ -147,7 +145,7 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
147
145
148
146
def main ():
149
147
# target course
150
- cx = np .arange (0 , 50 , 0.1 )
148
+ cx = np .arange (0 , 50 , 0.5 )
151
149
cy = [math .sin (ix / 5.0 ) * ix / 2.0 for ix in cx ]
152
150
153
151
target_speed = 10.0 / 3.6 # [m/s]
@@ -161,22 +159,27 @@ def main():
161
159
time = 0.0
162
160
states = States ()
163
161
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 )
166
164
167
165
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
171
173
172
174
time += dt
173
175
states .append (time , state )
174
176
175
177
if show_animation : # pragma: no cover
176
178
plt .cla ()
177
179
# 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 ])
180
183
plot_arrow (state .x , state .y , state .yaw )
181
184
plt .plot (cx , cy , "-r" , label = "course" )
182
185
plt .plot (states .x , states .y , "-b" , label = "trajectory" )
0 commit comments