Skip to content

Commit 69d7712

Browse files
committed
feat: Add support for reverse mode and related function adjustments
1 parent 57d0ae1 commit 69d7712

File tree

1 file changed

+89
-51
lines changed

1 file changed

+89
-51
lines changed

PathTracking/pure_pursuit/pure_pursuit.py

Lines changed: 89 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,48 @@
1010
import math
1111
import matplotlib.pyplot as plt
1212

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+
1318
# Parameters
1419
k = 0.1 # look forward gain
1520
Lfc = 2.0 # [m] look-ahead distance
1621
Kp = 1.0 # speed proportional gain
1722
dt = 0.1 # [s] time tick
1823
WB = 2.9 # [m] wheel base of vehicle
1924

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+
2033
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
2236

2337
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):
2639
self.x = x
2740
self.y = y
2841
self.yaw = yaw
2942
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))
3246

3347
def update(self, a, delta):
3448
self.x += self.v * math.cos(self.yaw) * dt
3549
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)
3752
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))
4055

4156
def calc_distance(self, point_x, point_y):
4257
dx = self.rear_x - point_x
@@ -51,13 +66,15 @@ def __init__(self):
5166
self.y = []
5267
self.yaw = []
5368
self.v = []
69+
self.direction = []
5470
self.t = []
5571

5672
def append(self, t, state):
5773
self.x.append(state.x)
5874
self.y.append(state.y)
5975
self.yaw.append(state.yaw)
6076
self.v.append(state.v)
77+
self.direction.append(state.direction)
6178
self.t.append(t)
6279

6380

@@ -117,14 +134,22 @@ def pure_pursuit_steer_control(state, trajectory, pind):
117134
if ind < len(trajectory.cx):
118135
tx = trajectory.cx[ind]
119136
ty = trajectory.cy[ind]
120-
else: # toward goal
137+
else:
121138
tx = trajectory.cx[-1]
122139
ty = trajectory.cy[-1]
123140
ind = len(trajectory.cx) - 1
124141

125142
alpha = math.atan2(ty - state.rear_y, tx - state.rear_x) - state.yaw
126143

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
128153

129154
return delta, ind
130155

@@ -141,23 +166,23 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
141166
plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw),
142167
fc=fc, ec=ec, head_width=width, head_length=width)
143168
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):
145170
"""
146-
绘制带四个车轮的车辆模型
171+
Plot vehicle model with four wheels
147172
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
152178
"""
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"""
161186
wheel = np.array([
162187
[-WHEEL_LEN/2, WHEEL_WIDTH/2],
163188
[WHEEL_LEN/2, WHEEL_WIDTH/2],
@@ -166,24 +191,25 @@ def plot_wheel(x, y, yaw, steer=0.0):
166191
[-WHEEL_LEN/2, WHEEL_WIDTH/2]
167192
])
168193

169-
# 如果有转向角,先旋转车轮
194+
# Rotate wheel if steering
170195
if steer != 0:
171196
c, s = np.cos(steer), np.sin(steer)
172197
rot_steer = np.array([[c, -s], [s, c]])
173198
wheel = wheel @ rot_steer.T
174199

175-
# 考虑车辆朝向
200+
# Apply vehicle heading rotation
176201
c, s = np.cos(yaw), np.sin(yaw)
177202
rot_yaw = np.array([[c, -s], [s, c]])
178203
wheel = wheel @ rot_yaw.T
179204

180-
# 平移到指定位置
205+
# Translate to position
181206
wheel[:, 0] += x
182207
wheel[:, 1] += y
183208

209+
# Plot wheel with color
184210
plt.plot(wheel[:, 0], wheel[:, 1], color=color)
185211

186-
# 计算车身四个角点
212+
# Calculate vehicle body corners
187213
corners = np.array([
188214
[-LENGTH/2, WIDTH/2],
189215
[LENGTH/2, WIDTH/2],
@@ -192,55 +218,67 @@ def plot_wheel(x, y, yaw, steer=0.0):
192218
[-LENGTH/2, WIDTH/2]
193219
])
194220

195-
# 旋转矩阵
221+
# Rotation matrix
196222
c, s = np.cos(yaw), np.sin(yaw)
197223
Rot = np.array([[c, -s], [s, c]])
198224

199-
# 旋转并平移车身
225+
# Rotate and translate vehicle body
200226
rotated = corners @ Rot.T
201227
rotated[:, 0] += x
202228
rotated[:, 1] += y
203229

204-
# 绘制车身
230+
# Plot vehicle body
205231
plt.plot(rotated[:, 0], rotated[:, 1], color=color)
206232

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
209239
plot_wheel(x + LENGTH/4 * c - WIDTH/2 * s,
210240
y + LENGTH/4 * s + WIDTH/2 * c,
211-
yaw, steer)
212-
# 前轮(右)
241+
yaw, steer, front_color)
242+
# Front right
213243
plot_wheel(x + LENGTH/4 * c + WIDTH/2 * s,
214244
y + LENGTH/4 * s - WIDTH/2 * c,
215-
yaw, steer)
216-
# 后轮(左)
245+
yaw, steer, front_color)
246+
# Rear left
217247
plot_wheel(x - LENGTH/4 * c - WIDTH/2 * s,
218248
y - LENGTH/4 * s + WIDTH/2 * c,
219-
yaw)
220-
# 后轮(右)
249+
yaw, color=rear_color)
250+
# Rear right
221251
plot_wheel(x - LENGTH/4 * c + WIDTH/2 * s,
222252
y - LENGTH/4 * s - WIDTH/2 * c,
223-
yaw)
253+
yaw, color=rear_color)
224254

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
226264
def on_key(event):
227265
global pause_simulation
228-
if event.key == ' ': # 空格键
266+
if event.key == ' ': # Space key
229267
pause_simulation = not pause_simulation
230268
elif event.key == 'escape':
231269
exit(0)
232270

233271
def main():
234272
# 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)
236274
cy = [math.sin(ix / 5.0) * ix / 2.0 for ix in cx]
237275

238276
target_speed = 10.0 / 3.6 # [m/s]
239277

240278
T = 100.0 # max simulation time
241279

242280
# 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)
244282

245283
lastIndex = len(cx) - 1
246284
time = 0.0
@@ -260,31 +298,31 @@ def main():
260298

261299
time += dt
262300
states.append(time, state)
263-
264301
if show_animation: # pragma: no cover
265302
plt.cla()
266303
# for stopping simulation with the esc key.
267304
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)
269307
plt.plot(cx, cy, "-r", label="course")
270308
plt.plot(states.x, states.y, "-b", label="trajectory")
271309
plt.plot(cx[target_ind], cy[target_ind], "xg", label="target")
272310
plt.axis("equal")
273311
plt.grid(True)
274312
plt.title("Speed[km/h]:" + str(state.v * 3.6)[:4])
275-
plt.legend() # 添加这一行显示图例
313+
plt.legend() # Add legend display
276314

277-
# 添加暂停状态显示
315+
# Add pause state display
278316
if pause_simulation:
279317
plt.text(0.02, 0.95, 'PAUSED', transform=plt.gca().transAxes,
280318
bbox=dict(facecolor='red', alpha=0.5))
281319

282320
plt.pause(0.001)
283321

284-
# 暂停处理
322+
# Handle pause state
285323
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
288326
exit(0)
289327

290328
# Test

0 commit comments

Comments
 (0)