Skip to content

Commit eeefce4

Browse files
gg diagram for dynamic limits
1 parent 924f690 commit eeefce4

1 file changed

Lines changed: 152 additions & 0 deletions

File tree

testing/test_gg_diagram.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
import sys
5+
import os
6+
sys.path.append(os.getcwd())
7+
8+
import json
9+
import matplotlib.pyplot as plt
10+
import numpy as np
11+
12+
def parse_behavior_log(filename):
13+
"""
14+
Parses the behavior log file and extracts the following data:
15+
- vehicle time
16+
- vehicle acceleration
17+
- vehicle heading rate
18+
- speed
19+
"""
20+
times = []
21+
accelerations = []
22+
heading_rates = []
23+
speeds = []
24+
25+
with open(filename, 'r') as f:
26+
for line in f:
27+
try:
28+
entry = json.loads(line)
29+
except json.JSONDecodeError:
30+
print(f"Skipping invalid JSON line: {line.strip()}")
31+
continue
32+
# Process vehicle state data
33+
if "vehicle" in entry:
34+
t = entry.get("time")
35+
vehicle_data = entry["vehicle"].get("data", {})
36+
acceleration = vehicle_data.get("acceleration")
37+
heading_rate = vehicle_data.get("heading_rate")
38+
speed = vehicle_data.get("speed")
39+
# Only add if all fields are available
40+
if None not in (t, acceleration, heading_rate, speed):
41+
times.append(t)
42+
accelerations.append(acceleration)
43+
heading_rates.append(heading_rate)
44+
speeds.append(speed)
45+
return (np.array(times), np.array(accelerations), np.array(heading_rates),
46+
np.array(speeds))
47+
48+
def parse_tracker_csv(filename):
49+
"""
50+
Parses the pure pursuit tracker log file and extracts the following data:
51+
- vehicle time (from column index 18)
52+
- Crosstrack error (from column index 20)
53+
- X position actual (from column index 2)
54+
- Y position actual (from column index 5)
55+
- X position desired (from column index 11)
56+
- Y position desired (from column index 14)
57+
"""
58+
59+
data = np.genfromtxt(filename, delimiter=',', skip_header=1)
60+
vehicle_time = data[:, 18]
61+
cte = data[:, 20]
62+
x_actual, y_actual = data[:, 2], data[:, 5]
63+
x_desired, y_desired = data[:, 11], data[:, 14]
64+
return vehicle_time, cte, x_actual, y_actual, x_desired, y_desired
65+
66+
def compute_derivative(times, values):
67+
"""
68+
Computes the derivative of array with respect to time.
69+
Returns the time array and derivative values.
70+
"""
71+
dt = np.diff(times)
72+
dv = np.diff(values)
73+
derivative = dv / dt
74+
return times[1:], derivative
75+
76+
def compute_gs(times, xs, ys):
77+
print(times)
78+
print(xs)
79+
xtimes, vxs = compute_derivative(times, xs)
80+
ytimes, vys = compute_derivative(times, ys)
81+
axtimes, axs = compute_derivative(times[1:], vxs)
82+
aytimes, ays = compute_derivative(times[1:], vys)
83+
g = 9.81
84+
longitudinal_gs = axs / g
85+
lateral_gs = ays / g
86+
return longitudinal_gs, lateral_gs
87+
88+
def plot_position(axis, x_actual, y_actual, x_desired, y_desired, safe_thresh=1, unsafe_thresh=2.5):
89+
"""Plots vehicle actual and desired positions vs. time"""
90+
position_error = np.sqrt((x_desired - x_actual) ** 2 + (y_desired - y_actual) ** 2)
91+
# safety_scores = np.vectorize(compute_safety_factor)(position_error, safe_thresh, unsafe_thresh)
92+
93+
axis.plot(y_desired, x_desired, linestyle='--', color='blue', label='Desired')
94+
axis.plot(y_actual, x_actual, color="black", linewidth=0.8, alpha=0.5, label='Actual')
95+
# axis.scatter(y_actual, x_actual, c=safety_scores, cmap=CMAP, vmin=0, vmax=1, edgecolors="black")
96+
97+
axis.set_xlabel("Y Position (m)")
98+
axis.set_ylabel("X Position (m)")
99+
axis.set_title("Vehicle Position vs. Desired Position")
100+
axis.legend()
101+
axis.grid(True)
102+
103+
def plot_gg_diagram(axis, longitudinal_gs, lateral_gs):
104+
"""Plots gg diagram"""
105+
# Plot G-G diagram
106+
axis.scatter(longitudinal_gs, lateral_gs, alpha=0.5, label="Data Points")
107+
108+
# Draw friction ellipse (assuming µ = 1.0)
109+
mu = 1.0
110+
theta = np.linspace(0, 2 * np.pi, 100)
111+
axis.plot(mu * np.cos(theta), mu * np.sin(theta), 'r', label="Theoretical Limit")
112+
113+
axis.axhline(0, color='black', linewidth=0.8)
114+
axis.axvline(0, color='black', linewidth=0.8)
115+
axis.set_xlabel("Lateral Acceleration (g)")
116+
axis.set_ylabel("Longitudinal Acceleration (g)")
117+
axis.set_title("G-G Diagram from Acceleration & Yaw Rate")
118+
axis.legend()
119+
axis.grid()
120+
121+
if __name__=='__main__':
122+
if len(sys.argv) != 2:
123+
print("Usage: python test_comfort_metrics.py <log_directory>")
124+
sys.exit(1)
125+
126+
log_dir = sys.argv[1]
127+
behavior_file = os.path.join(log_dir, "behavior.json")
128+
tracker_file = os.path.join(log_dir, "PurePursuitTrajectoryTracker_debug.csv")
129+
130+
# if behavior.json doesn't exist, print error and exit
131+
if not os.path.exists(behavior_file):
132+
print("Error: behavior.json file is missing in log folder.")
133+
sys.exit(1)
134+
135+
# Parse behavior log file and compute metrics
136+
times, accelerations, heading_rates, speeds = parse_behavior_log(behavior_file)
137+
time_jerk, jerk = compute_derivative(times, accelerations)
138+
time_heading_acc, heading_acc = compute_derivative(times, heading_rates)
139+
140+
# Pure pursuit tracker file exists: parse and plot all metrics
141+
if os.path.exists(tracker_file):
142+
vehicle_time, cte, x_actual, y_actual, x_desired, y_desired = parse_tracker_csv(tracker_file)
143+
144+
longitudinal_gs, lateral_gs = compute_gs(vehicle_time, x_actual, y_actual)
145+
# print(longitudinal_gs)
146+
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
147+
plot_gg_diagram(axs[0], longitudinal_gs, lateral_gs)
148+
plot_position(axs[1], x_actual, y_actual, x_desired, y_desired)
149+
plt.show()
150+
# Pure pursuit tracker file is missing: plot only behavior.json metrics
151+
else:
152+
print("Tracker file is missing. Skipping cross track error and vehicle position plots.")

0 commit comments

Comments
 (0)