10
10
import math
11
11
import matplotlib .pyplot as plt
12
12
13
+ import sys
14
+ import pathlib
15
+ sys .path .append (str (pathlib .Path (__file__ ).parent .parent .parent ))
16
+ from utils .angle import angle_mod
17
+
13
18
# Parameters
14
19
k = 0.1 # look forward gain
15
20
Lfc = 2.0 # [m] look-ahead distance
16
21
Kp = 1.0 # speed proportional gain
17
22
dt = 0.1 # [s] time tick
18
23
WB = 2.9 # [m] wheel base of vehicle
19
24
25
+
26
+ # Vehicle parameters
27
+ LENGTH = WB + 1.0 # Vehicle length
28
+ WIDTH = 2.0 # Vehicle width
29
+ WHEEL_LEN = 0.6 # Wheel length
30
+ WHEEL_WIDTH = 0.2 # Wheel width
31
+
32
+
20
33
show_animation = True
21
- pause_simulation = False # 新增暂停标志
34
+ pause_simulation = False # Flag for pause simulation
35
+ is_reverse_mode = False # Flag for reverse driving mode
22
36
23
37
class State :
24
-
25
- def __init__ (self , x = 0.0 , y = 0.0 , yaw = 0.0 , v = 0.0 ):
38
+ def __init__ (self , x = 0.0 , y = 0.0 , yaw = 0.0 , v = 0.0 , is_reverse = False ):
26
39
self .x = x
27
40
self .y = y
28
41
self .yaw = yaw
29
42
self .v = v
30
- self .rear_x = self .x - ((WB / 2 ) * math .cos (self .yaw ))
31
- self .rear_y = self .y - ((WB / 2 ) * math .sin (self .yaw ))
43
+ self .direction = - 1 if is_reverse else 1 # Direction based on reverse flag
44
+ self .rear_x = self .x - self .direction * ((WB / 2 ) * math .cos (self .yaw ))
45
+ self .rear_y = self .y - self .direction * ((WB / 2 ) * math .sin (self .yaw ))
32
46
33
47
def update (self , a , delta ):
34
48
self .x += self .v * math .cos (self .yaw ) * dt
35
49
self .y += self .v * math .sin (self .yaw ) * dt
36
- self .yaw += self .v / WB * math .tan (delta ) * dt
50
+ self .yaw += self .direction * self .v / WB * math .tan (delta ) * dt
51
+ self .yaw = angle_mod (self .yaw )
37
52
self .v += a * dt
38
- self .rear_x = self .x - ((WB / 2 ) * math .cos (self .yaw ))
39
- self .rear_y = self .y - ((WB / 2 ) * math .sin (self .yaw ))
53
+ self .rear_x = self .x - self . direction * ((WB / 2 ) * math .cos (self .yaw ))
54
+ self .rear_y = self .y - self . direction * ((WB / 2 ) * math .sin (self .yaw ))
40
55
41
56
def calc_distance (self , point_x , point_y ):
42
57
dx = self .rear_x - point_x
@@ -51,13 +66,15 @@ def __init__(self):
51
66
self .y = []
52
67
self .yaw = []
53
68
self .v = []
69
+ self .direction = []
54
70
self .t = []
55
71
56
72
def append (self , t , state ):
57
73
self .x .append (state .x )
58
74
self .y .append (state .y )
59
75
self .yaw .append (state .yaw )
60
76
self .v .append (state .v )
77
+ self .direction .append (state .direction )
61
78
self .t .append (t )
62
79
63
80
@@ -117,14 +134,22 @@ def pure_pursuit_steer_control(state, trajectory, pind):
117
134
if ind < len (trajectory .cx ):
118
135
tx = trajectory .cx [ind ]
119
136
ty = trajectory .cy [ind ]
120
- else : # toward goal
137
+ else :
121
138
tx = trajectory .cx [- 1 ]
122
139
ty = trajectory .cy [- 1 ]
123
140
ind = len (trajectory .cx ) - 1
124
141
125
142
alpha = math .atan2 (ty - state .rear_y , tx - state .rear_x ) - state .yaw
126
143
127
- delta = math .atan2 (2.0 * WB * math .sin (alpha ) / Lf , 1.0 )
144
+ # Reverse steering angle when reversing
145
+ delta = state .direction * math .atan2 (2.0 * WB * math .sin (alpha ) / Lf , 1.0 )
146
+
147
+ if delta > math .pi / 4 :
148
+ delta = math .pi / 4
149
+ elif delta < - math .pi / 4 :
150
+ delta = - 1 * math .pi / 4
151
+ else :
152
+ delta = delta
128
153
129
154
return delta , ind
130
155
@@ -141,23 +166,23 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
141
166
plt .arrow (x , y , length * math .cos (yaw ), length * math .sin (yaw ),
142
167
fc = fc , ec = ec , head_width = width , head_length = width )
143
168
plt .plot (x , y )
144
- def plot_vehicle (x , y , yaw , steer = 0.0 , color = 'blue' ):
169
+ def plot_vehicle (x , y , yaw , steer = 0.0 , color = 'blue' , is_reverse = False ):
145
170
"""
146
- 绘制带四个车轮的车辆模型
171
+ Plot vehicle model with four wheels
147
172
Args:
148
- x, y: 车辆中心位置
149
- yaw: 车辆航向角
150
- steer: 转向角
151
- color: 车辆颜色
173
+ x, y: Vehicle center position
174
+ yaw: Vehicle heading angle
175
+ steer: Steering angle
176
+ color: Vehicle color
177
+ is_reverse: Flag for reverse mode
152
178
"""
153
- # 车辆参数
154
- LENGTH = WB + 1.0 # 车长
155
- WIDTH = 2.0 # 车宽
156
- WHEEL_LEN = 0.6 # 车轮长度
157
- WHEEL_WIDTH = 0.2 # 车轮宽度
158
-
159
- def plot_wheel (x , y , yaw , steer = 0.0 ):
160
- """绘制单个车轮"""
179
+ # Adjust heading angle in reverse mode
180
+ if is_reverse :
181
+ yaw = angle_mod (yaw + math .pi ) # Rotate heading by 180 degrees
182
+ steer = - steer # Reverse steering direction
183
+
184
+ def plot_wheel (x , y , yaw , steer = 0.0 , color = color ):
185
+ """Plot single wheel"""
161
186
wheel = np .array ([
162
187
[- WHEEL_LEN / 2 , WHEEL_WIDTH / 2 ],
163
188
[WHEEL_LEN / 2 , WHEEL_WIDTH / 2 ],
@@ -166,24 +191,25 @@ def plot_wheel(x, y, yaw, steer=0.0):
166
191
[- WHEEL_LEN / 2 , WHEEL_WIDTH / 2 ]
167
192
])
168
193
169
- # 如果有转向角,先旋转车轮
194
+ # Rotate wheel if steering
170
195
if steer != 0 :
171
196
c , s = np .cos (steer ), np .sin (steer )
172
197
rot_steer = np .array ([[c , - s ], [s , c ]])
173
198
wheel = wheel @ rot_steer .T
174
199
175
- # 考虑车辆朝向
200
+ # Apply vehicle heading rotation
176
201
c , s = np .cos (yaw ), np .sin (yaw )
177
202
rot_yaw = np .array ([[c , - s ], [s , c ]])
178
203
wheel = wheel @ rot_yaw .T
179
204
180
- # 平移到指定位置
205
+ # Translate to position
181
206
wheel [:, 0 ] += x
182
207
wheel [:, 1 ] += y
183
208
209
+ # Plot wheel with color
184
210
plt .plot (wheel [:, 0 ], wheel [:, 1 ], color = color )
185
211
186
- # 计算车身四个角点
212
+ # Calculate vehicle body corners
187
213
corners = np .array ([
188
214
[- LENGTH / 2 , WIDTH / 2 ],
189
215
[LENGTH / 2 , WIDTH / 2 ],
@@ -192,55 +218,67 @@ def plot_wheel(x, y, yaw, steer=0.0):
192
218
[- LENGTH / 2 , WIDTH / 2 ]
193
219
])
194
220
195
- # 旋转矩阵
221
+ # Rotation matrix
196
222
c , s = np .cos (yaw ), np .sin (yaw )
197
223
Rot = np .array ([[c , - s ], [s , c ]])
198
224
199
- # 旋转并平移车身
225
+ # Rotate and translate vehicle body
200
226
rotated = corners @ Rot .T
201
227
rotated [:, 0 ] += x
202
228
rotated [:, 1 ] += y
203
229
204
- # 绘制车身
230
+ # Plot vehicle body
205
231
plt .plot (rotated [:, 0 ], rotated [:, 1 ], color = color )
206
232
207
- # 绘制四个车轮
208
- # 前轮(左)
233
+ # Plot wheels (darker color for front wheels)
234
+ front_color = 'darkblue'
235
+ rear_color = color
236
+
237
+ # Plot four wheels
238
+ # Front left
209
239
plot_wheel (x + LENGTH / 4 * c - WIDTH / 2 * s ,
210
240
y + LENGTH / 4 * s + WIDTH / 2 * c ,
211
- yaw , steer )
212
- # 前轮(右)
241
+ yaw , steer , front_color )
242
+ # Front right
213
243
plot_wheel (x + LENGTH / 4 * c + WIDTH / 2 * s ,
214
244
y + LENGTH / 4 * s - WIDTH / 2 * c ,
215
- yaw , steer )
216
- # 后轮(左)
245
+ yaw , steer , front_color )
246
+ # Rear left
217
247
plot_wheel (x - LENGTH / 4 * c - WIDTH / 2 * s ,
218
248
y - LENGTH / 4 * s + WIDTH / 2 * c ,
219
- yaw )
220
- # 后轮(右)
249
+ yaw , color = rear_color )
250
+ # Rear right
221
251
plot_wheel (x - LENGTH / 4 * c + WIDTH / 2 * s ,
222
252
y - LENGTH / 4 * s - WIDTH / 2 * c ,
223
- yaw )
253
+ yaw , color = rear_color )
224
254
225
- # 添加键盘事件处理函数
255
+ # Add direction arrow
256
+ arrow_length = LENGTH / 3
257
+ plt .arrow (x , y ,
258
+ - arrow_length * math .cos (yaw ) if is_reverse else arrow_length * math .cos (yaw ),
259
+ - arrow_length * math .sin (yaw ) if is_reverse else arrow_length * math .sin (yaw ),
260
+ head_width = WIDTH / 4 , head_length = WIDTH / 4 ,
261
+ fc = 'r' , ec = 'r' , alpha = 0.5 )
262
+
263
+ # Keyboard event handler
226
264
def on_key (event ):
227
265
global pause_simulation
228
- if event .key == ' ' : # 空格键
266
+ if event .key == ' ' : # Space key
229
267
pause_simulation = not pause_simulation
230
268
elif event .key == 'escape' :
231
269
exit (0 )
232
270
233
271
def main ():
234
272
# target course
235
- cx = np .arange (0 , 50 , 0.5 )
273
+ cx = - 1 * np . arange ( 0 , 50 , 0.5 ) if is_reverse_mode else np .arange (0 , 50 , 0.5 )
236
274
cy = [math .sin (ix / 5.0 ) * ix / 2.0 for ix in cx ]
237
275
238
276
target_speed = 10.0 / 3.6 # [m/s]
239
277
240
278
T = 100.0 # max simulation time
241
279
242
280
# initial state
243
- state = State (x = - 0.0 , y = - 3.0 , yaw = 0.0 , v = 0.0 )
281
+ state = State (x = - 0.0 , y = - 3.0 , yaw = math . pi if is_reverse_mode else 0.0 , v = 0.0 , is_reverse = is_reverse_mode )
244
282
245
283
lastIndex = len (cx ) - 1
246
284
time = 0.0
@@ -260,31 +298,31 @@ def main():
260
298
261
299
time += dt
262
300
states .append (time , state )
263
-
264
301
if show_animation : # pragma: no cover
265
302
plt .cla ()
266
303
# for stopping simulation with the esc key.
267
304
plt .gcf ().canvas .mpl_connect ('key_release_event' , on_key )
268
- plot_vehicle (state .x , state .y , state .yaw , di ) # di 是转向角
305
+ # Pass is_reverse parameter
306
+ plot_vehicle (state .x , state .y , state .yaw , di , is_reverse = is_reverse_mode )
269
307
plt .plot (cx , cy , "-r" , label = "course" )
270
308
plt .plot (states .x , states .y , "-b" , label = "trajectory" )
271
309
plt .plot (cx [target_ind ], cy [target_ind ], "xg" , label = "target" )
272
310
plt .axis ("equal" )
273
311
plt .grid (True )
274
312
plt .title ("Speed[km/h]:" + str (state .v * 3.6 )[:4 ])
275
- plt .legend () # 添加这一行显示图例
313
+ plt .legend () # Add legend display
276
314
277
- # 添加暂停状态显示
315
+ # Add pause state display
278
316
if pause_simulation :
279
317
plt .text (0.02 , 0.95 , 'PAUSED' , transform = plt .gca ().transAxes ,
280
318
bbox = dict (facecolor = 'red' , alpha = 0.5 ))
281
319
282
320
plt .pause (0.001 )
283
321
284
- # 暂停处理
322
+ # Handle pause state
285
323
while pause_simulation :
286
- plt .pause (0.1 ) # 降低 CPU 占用
287
- if not plt .get_fignums (): # 检查窗口是否被关闭
324
+ plt .pause (0.1 ) # Reduce CPU usage
325
+ if not plt .get_fignums (): # Check if window is closed
288
326
exit (0 )
289
327
290
328
# Test
0 commit comments