5
5
author: Atsushi Sakai (@Atsushi_twi)
6
6
7
7
"""
8
+ from __future__ import division , print_function
9
+
8
10
import sys
11
+
9
12
sys .path .append ("../../PathPlanning/CubicSpline/" )
10
13
11
- import math
12
14
import matplotlib .pyplot as plt
13
- import cubic_spline_planner
15
+ import numpy as np
14
16
17
+ import cubic_spline_planner
15
18
16
19
k = 0.5 # control gain
17
20
Kp = 1.0 # speed propotional gain
18
21
dt = 0.1 # [s] time difference
19
22
L = 2.9 # [m] Wheel base of vehicle
20
- max_steer = math .radians (30.0 ) # [rad] max steering angle
23
+ max_steer = np .radians (30.0 ) # [rad] max steering angle
21
24
22
25
show_animation = True
23
26
24
27
25
- class State :
28
+ class State (object ):
29
+ """
30
+ Class representing the state of a vehicle.
31
+
32
+ :param x: (float) x-coordinate
33
+ :param y: (float) y-coordinate
34
+ :param yaw: (float) yaw angle
35
+ :param v: (float) speed
36
+ """
26
37
27
38
def __init__ (self , x = 0.0 , y = 0.0 , yaw = 0.0 , v = 0.0 ):
39
+ """Instantiate the object."""
40
+ super (State , self ).__init__ ()
28
41
self .x = x
29
42
self .y = y
30
43
self .yaw = yaw
31
44
self .v = v
32
45
46
+ def update (self , acceleration , delta ):
47
+ """
48
+ Update the state of the vehicle.
33
49
34
- def update ( state , a , delta ):
50
+ Stanley Control uses bicycle model.
35
51
36
- if delta >= max_steer :
37
- delta = max_steer
38
- elif delta <= - max_steer :
39
- delta = - max_steer
52
+ :param acceleration: (float) Acceleration
53
+ :param delta: (float) Steering
54
+ """
55
+ delta = np . clip ( delta , - max_steer , max_steer )
40
56
41
- state . x = state .x + state .v * math .cos (state .yaw ) * dt
42
- state . y = state .y + state .v * math .sin (state .yaw ) * dt
43
- state . yaw = state .yaw + state .v / L * math .tan (delta ) * dt
44
- state .yaw = pi_2_pi ( state .yaw )
45
- state . v = state .v + a * dt
57
+ self .x += self .v * np .cos (self .yaw ) * dt
58
+ self .y += self .v * np .sin (self .yaw ) * dt
59
+ self .yaw += self .v / L * np .tan (delta ) * dt
60
+ self .yaw = normalize_angle ( self .yaw )
61
+ self .v += acceleration * dt
46
62
47
- return state
48
63
64
+ def pid_control (target , current ):
65
+ """
66
+ Proportional control for the speed.
49
67
50
- def PIDControl (target , current ):
51
- a = Kp * (target - current )
68
+ :param target: (float)
69
+ :param current: (float)
70
+ :return: (float)
71
+ """
72
+ return Kp * (target - current )
52
73
53
- return a
54
74
75
+ def stanley_control (state , cx , cy , cyaw , last_target_idx ):
76
+ """
77
+ Stanley steering control.
55
78
56
- def stanley_control (state , cx , cy , cyaw , pind ):
79
+ :param state: (State object)
80
+ :param cx: ([float])
81
+ :param cy: ([float])
82
+ :param cyaw: ([float])
83
+ :param last_target_idx: (int)
84
+ :return: (float, int)
85
+ """
86
+ current_target_idx , error_front_axle = calc_target_index (state , cx , cy )
57
87
58
- ind , efa = calc_target_index (state , cx , cy )
88
+ if last_target_idx >= current_target_idx :
89
+ current_target_idx = last_target_idx
59
90
60
- if pind >= ind :
61
- ind = pind
62
-
63
- theta_e = pi_2_pi ( cyaw [ ind ] - state .yaw )
64
- theta_d = math . atan2 ( k * efa , state . v )
91
+ # theta_e corrects the heading error
92
+ theta_e = normalize_angle ( cyaw [ current_target_idx ] - state . yaw )
93
+ # theta_d corrects the cross track error
94
+ theta_d = np . arctan2 ( k * error_front_axle , state .v )
95
+ # Steering control
65
96
delta = theta_e + theta_d
66
97
67
- return delta , ind
98
+ return delta , current_target_idx
99
+
68
100
101
+ def normalize_angle (angle ):
102
+ """
103
+ Normalize an angle to [-pi, pi].
69
104
70
- def pi_2_pi (angle ):
71
- while (angle > math .pi ):
72
- angle = angle - 2.0 * math .pi
105
+ :param angle: (float)
106
+ :return: (float) Angle in radian in [-pi, pi]
107
+ """
108
+ while angle > np .pi :
109
+ angle -= 2.0 * np .pi
73
110
74
- while ( angle < - math .pi ) :
75
- angle = angle + 2.0 * math .pi
111
+ while angle < - np .pi :
112
+ angle += 2.0 * np .pi
76
113
77
114
return angle
78
115
79
116
80
117
def calc_target_index (state , cx , cy ):
81
-
82
- # calc frant axle position
83
- fx = state .x + L * math .cos (state .yaw )
84
- fy = state .y + L * math .sin (state .yaw )
85
-
86
- # search nearest point index
118
+ """
119
+ Compute index in the trajectory list of the target.
120
+
121
+ :param state: (State object)
122
+ :param cx: [float]
123
+ :param cy: [float]
124
+ :return: (int, float)
125
+ """
126
+ # Calc front axle position
127
+ fx = state .x + L * np .cos (state .yaw )
128
+ fy = state .y + L * np .sin (state .yaw )
129
+
130
+ # Search nearest point index
87
131
dx = [fx - icx for icx in cx ]
88
132
dy = [fy - icy for icy in cy ]
89
- d = [math .sqrt (idx ** 2 + idy ** 2 ) for (idx , idy ) in zip (dx , dy )]
90
- mind = min (d )
91
- ind = d .index (mind )
133
+ d = [np .sqrt (idx ** 2 + idy ** 2 ) for (idx , idy ) in zip (dx , dy )]
134
+ error_front_axle = min (d )
135
+ target_idx = d .index (error_front_axle )
92
136
93
- tyaw = pi_2_pi ( math . atan2 (fy - cy [ind ], fx - cx [ind ]) - state .yaw )
94
- if tyaw > 0.0 :
95
- mind = - mind
137
+ target_yaw = normalize_angle ( np . arctan2 (fy - cy [target_idx ], fx - cx [target_idx ]) - state .yaw )
138
+ if target_yaw > 0.0 :
139
+ error_front_axle = - error_front_axle
96
140
97
- return ind , mind
141
+ return target_idx , error_front_axle
98
142
99
143
100
144
def main ():
145
+ """Plot an example of Stanley steering control on a cubic spline."""
101
146
# target course
102
147
ax = [0.0 , 100.0 , 100.0 , 50.0 , 60.0 ]
103
148
ay = [0.0 , 0.0 , - 30.0 , - 20.0 , 0.0 ]
@@ -107,26 +152,26 @@ def main():
107
152
108
153
target_speed = 30.0 / 3.6 # [m/s]
109
154
110
- T = 100.0 # max simulation time
155
+ max_simulation_time = 100.0
111
156
112
- # initial state
113
- state = State (x = - 0.0 , y = 5.0 , yaw = math .radians (20.0 ), v = 0.0 )
157
+ # Initial state
158
+ state = State (x = - 0.0 , y = 5.0 , yaw = np .radians (20.0 ), v = 0.0 )
114
159
115
- lastIndex = len (cx ) - 1
160
+ last_idx = len (cx ) - 1
116
161
time = 0.0
117
162
x = [state .x ]
118
163
y = [state .y ]
119
164
yaw = [state .yaw ]
120
165
v = [state .v ]
121
166
t = [0.0 ]
122
- target_ind , mind = calc_target_index (state , cx , cy )
167
+ target_idx , _ = calc_target_index (state , cx , cy )
123
168
124
- while T >= time and lastIndex > target_ind :
125
- ai = PIDControl (target_speed , state .v )
126
- di , target_ind = stanley_control (state , cx , cy , cyaw , target_ind )
127
- state = update (state , ai , di )
169
+ while max_simulation_time >= time and last_idx > target_idx :
170
+ ai = pid_control (target_speed , state .v )
171
+ di , target_idx = stanley_control (state , cx , cy , cyaw , target_idx )
172
+ state . update (ai , di )
128
173
129
- time = time + dt
174
+ time += dt
130
175
131
176
x .append (state .x )
132
177
y .append (state .y )
@@ -138,14 +183,14 @@ def main():
138
183
plt .cla ()
139
184
plt .plot (cx , cy , ".r" , label = "course" )
140
185
plt .plot (x , y , "-b" , label = "trajectory" )
141
- plt .plot (cx [target_ind ], cy [target_ind ], "xg" , label = "target" )
186
+ plt .plot (cx [target_idx ], cy [target_idx ], "xg" , label = "target" )
142
187
plt .axis ("equal" )
143
188
plt .grid (True )
144
189
plt .title ("Speed[km/h]:" + str (state .v * 3.6 )[:4 ])
145
190
plt .pause (0.001 )
146
191
147
192
# Test
148
- assert lastIndex >= target_ind , "Cannot goal"
193
+ assert last_idx >= target_idx , "Cannot reach goal"
149
194
150
195
if show_animation :
151
196
plt .plot (cx , cy , ".r" , label = "course" )
0 commit comments