Skip to content

Commit d896949

Browse files
committed
initial trajectory generation and feasibility check functions
1 parent a408a71 commit d896949

1 file changed

Lines changed: 192 additions & 0 deletions

File tree

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from scipy.optimize import minimize
4+
5+
def trajectory_generation(init_state, final_state, w_c=1.0, w_eps=1.0):
6+
N = 50
7+
T = 0.1
8+
v = 10.0
9+
Lr = 1.5
10+
small_constant = 1e-3
11+
12+
def optimize_with_weights(dynamic_weights_c=None, dynamic_weights_eps=None):
13+
y = np.linspace(init_state['y'], final_state['y'], N)
14+
psi = np.linspace(init_state['psi'], final_state['psi'], N)
15+
c = np.linspace(init_state['c'], final_state['c'], N)
16+
eps = np.zeros(N - 1)
17+
18+
def cost(x):
19+
y_, psi_, c_, eps_ = np.split(x, [N-1, 2*(N-1), 3*(N-1)])
20+
c_seq = np.concatenate(([init_state['c']], c_))
21+
cost_c = np.sum((dynamic_weights_c if dynamic_weights_c is not None else w_c) * c_seq**2)
22+
cost_eps = np.sum((dynamic_weights_eps if dynamic_weights_eps is not None else w_eps) * eps_**2)
23+
return cost_c + cost_eps
24+
25+
def dynamics_constraints(x):
26+
y_, psi_, c_, eps_ = np.split(x, [N-1, 2*(N-1), 3*(N-1)])
27+
constraints = []
28+
y_prev, psi_prev, c_prev = init_state['y'], init_state['psi'], init_state['c']
29+
for k in range(N-1):
30+
dy = v * np.sin(psi_prev + c_prev * Lr) * T
31+
dpsi = v * c_prev * T
32+
dc = eps_[k] * T
33+
constraints.append(y_[k] - (y_prev + dy))
34+
constraints.append(psi_[k] - (psi_prev + dpsi))
35+
constraints.append(c_[k] - (c_prev + dc))
36+
y_prev, psi_prev, c_prev = y_[k], psi_[k], c_[k]
37+
constraints.append(y_[-1] - final_state['y'])
38+
constraints.append(psi_[-1] - final_state['psi'])
39+
constraints.append(c_[-1] - final_state['c'])
40+
return constraints
41+
42+
x0 = np.concatenate([y[1:], psi[1:], c[1:], eps])
43+
result = minimize(cost, x0, constraints={'type': 'eq', 'fun': dynamics_constraints}, options={'maxiter': 1000})
44+
45+
if result.success:
46+
y_, psi_, c_, eps_ = np.split(result.x, [N-1, 2*(N-1), 3*(N-1)])
47+
y_full = np.concatenate(([init_state['y']], y_))
48+
psi_full = np.concatenate(([init_state['psi']], psi_))
49+
c_full = np.concatenate(([init_state['c']], c_))
50+
return y_full, psi_full, c_full, eps_
51+
else:
52+
raise RuntimeError("Optimization failed")
53+
54+
# First pass
55+
y1, psi1, c1, eps1 = optimize_with_weights()
56+
57+
# Shape weights based on result
58+
dynamic_weights_c = np.abs(c1) + small_constant
59+
dynamic_weights_eps = np.abs(eps1) + small_constant
60+
61+
# Second pass with weighted cost
62+
y2, psi2, c2, eps2 = optimize_with_weights(dynamic_weights_c, dynamic_weights_eps)
63+
return y2, psi2, c2, eps2
64+
65+
def plot_trajectory(y_traj, psi_traj, c_traj, label):
66+
N = len(y_traj)
67+
T = 0.1
68+
v = 5.0
69+
x_traj = np.zeros(N)
70+
for i in range(1, N):
71+
dx = v * np.cos(psi_traj[i-1] + c_traj[i-1] * 1.5) * T
72+
x_traj[i] = x_traj[i-1] + dx
73+
74+
plt.figure(figsize=(8, 5))
75+
plt.plot(x_traj, y_traj, label=label)
76+
plt.scatter([x_traj[0], x_traj[-1]], [y_traj[0], y_traj[-1]], color='red', zorder=5)
77+
plt.annotate("init", (x_traj[0], y_traj[0]), textcoords="offset points", xytext=(-10,10), ha='center')
78+
plt.annotate("final", (x_traj[-1], y_traj[-1]), textcoords="offset points", xytext=(10,10), ha='center')
79+
plt.xlabel('x (m)')
80+
plt.ylabel('y (m)')
81+
plt.title('Trajectory from Initial to Final State')
82+
plt.legend()
83+
plt.axis('equal')
84+
plt.grid(True)
85+
plt.show()
86+
87+
def plot_dynamics(psi_traj, c_traj, eps_traj):
88+
time_steps = np.arange(len(psi_traj))
89+
eps_time = np.arange(len(eps_traj))
90+
91+
plt.figure(figsize=(12, 8))
92+
93+
plt.subplot(3, 1, 1)
94+
plt.plot(time_steps, psi_traj, label="Heading (ψ)")
95+
plt.ylabel("ψ (rad)")
96+
plt.grid(True)
97+
plt.legend()
98+
99+
plt.subplot(3, 1, 2)
100+
plt.plot(time_steps, c_traj, label="Curvature (c)", color='orange')
101+
plt.ylabel("Curvature (1/m)")
102+
plt.grid(True)
103+
plt.legend()
104+
105+
plt.subplot(3, 1, 3)
106+
plt.plot(eps_time, eps_traj, label="Curvature Rate (ε)", color='green')
107+
plt.xlabel("Time Step")
108+
plt.ylabel("ε (1/m²)")
109+
plt.grid(True)
110+
plt.legend()
111+
112+
plt.tight_layout()
113+
plt.show()
114+
115+
# --- Test Case ---
116+
def test_trajectory_generation():
117+
init_state = {'y': 0.0, 'psi': 0.0, 'c': 0.0}
118+
final_state = {'y': 10.0, 'psi': 0.0, 'c': 0.0}
119+
120+
y_traj, psi_traj, c_traj, eps_traj = trajectory_generation(init_state, final_state)
121+
plot_trajectory(y_traj, psi_traj, c_traj, label="Generated trajectory")
122+
plot_dynamics(psi_traj, c_traj, eps_traj)
123+
124+
125+
126+
127+
def feasibility_check(trajectory, cone_map, car_width=2.0, safety_margin=0.3, v=10.0, Lr=1.5, T=0.1):
128+
"""
129+
Check if the car trajectory collides with any cones.
130+
131+
Parameters:
132+
- trajectory: list of (y, psi, c) states
133+
- cone_map: list of (x, y) cone positions
134+
- car_width: width of the vehicle in meters
135+
- safety_margin: buffer around the vehicle
136+
- v: vehicle constant speed (used for x position estimation)
137+
- Lr: distance to rear axle
138+
- T: time step
139+
140+
Returns:
141+
- feasible: True if no collisions
142+
- collisions: list of indices of cones that were collided with (for plotting purpose)
143+
- x_vals, y_vals: trajectory positions for plotting (for plotting purpose)
144+
"""
145+
y_vals, psi_vals, c_vals = zip(*trajectory)
146+
x_vals = [0.0]
147+
for i in range(1, len(trajectory)):
148+
dx = v * np.cos(psi_vals[i-1] + c_vals[i-1] * Lr) * T
149+
x_vals.append(x_vals[-1] + dx)
150+
151+
collision_radius = (car_width / 2.0) + safety_margin
152+
collisions = []
153+
154+
for j, (cone_x, cone_y) in enumerate(cone_map):
155+
for x, y in zip(x_vals, y_vals):
156+
if np.hypot(x - cone_x, y - cone_y) < collision_radius:
157+
collisions.append(j)
158+
break
159+
160+
feasible = len(collisions) == 0
161+
return feasible, collisions, x_vals, y_vals
162+
163+
# --- Test Case ---
164+
def test_feasibility_check():
165+
N = 50
166+
y_traj = np.linspace(0, 10, N)
167+
psi_traj = np.linspace(0, 0.1, N)
168+
c_traj = np.linspace(0, 0.2, N)
169+
trajectory = list(zip(y_traj, psi_traj, c_traj))
170+
171+
# Cone map near the path
172+
cone_map = [(5.0, 1.0), (10.0, 1.5), (15.0, 2.0), (25.0, 4.0), (25.0, 10.0), (16.0, 9.0), (40.0, 5.0)]
173+
174+
# Run check
175+
feasible, collisions, x_vals, y_vals = feasibility_check(trajectory, cone_map)
176+
177+
# Plot
178+
plt.figure(figsize=(10, 6))
179+
plt.plot(x_vals, y_vals, label="Trajectory", linewidth=2)
180+
for i, (cx, cy) in enumerate(cone_map):
181+
color = 'red' if i in collisions else 'green'
182+
plt.scatter(cx, cy, color=color, s=100, label=f'Cone {i}' if i == 0 else "")
183+
plt.xlabel("x (m)")
184+
plt.ylabel("y (m)")
185+
plt.title("Trajectory and Cone Map")
186+
plt.legend()
187+
plt.axis("equal")
188+
plt.grid(True)
189+
plt.show()
190+
191+
print("Feasible:", feasible)
192+
print("Collisions with cones:", collisions)

0 commit comments

Comments
 (0)