Skip to content

Commit 1c6263f

Browse files
committed
updated metric script to show acceleration and remove dot plots
1 parent 2caf27c commit 1c6263f

1 file changed

Lines changed: 120 additions & 48 deletions

File tree

testing/test_comfort_metrics.py

Lines changed: 120 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import json
77
import matplotlib.pyplot as plt
88
import numpy as np
9+
from matplotlib.collections import LineCollection
10+
from matplotlib.colors import Normalize
911

1012
CMAP = "RdYlGn"
1113

@@ -21,7 +23,7 @@ def compute_safety_factor(value, safe_thresh, unsafe_thresh, flip=False):
2123
factor = 0.0
2224
else:
2325
factor = 1.0 - (abs_val - safe_thresh) / (unsafe_thresh - safe_thresh)
24-
26+
2527
if flip:
2628
return 1.0 - factor
2729
return factor
@@ -40,7 +42,7 @@ def parse_behavior_log(filename):
4042
heading_rates = []
4143
pedestrian_times = []
4244
pedestrian_distances = []
43-
45+
4446
with open(filename, 'r') as f:
4547
for line in f:
4648
try:
@@ -73,8 +75,8 @@ def parse_behavior_log(filename):
7375
pedestrian_times.append(t)
7476
dist = np.sqrt(x_agent**2 + y_agent**2)
7577
pedestrian_distances.append(dist)
76-
77-
return (np.array(times), np.array(accelerations), np.array(heading_rates),
78+
79+
return (np.array(times), np.array(accelerations), np.array(heading_rates),
7880
np.array(pedestrian_times), np.array(pedestrian_distances))
7981

8082
def parse_tracker_csv(filename):
@@ -111,69 +113,137 @@ def add_safety_colorbar(figure):
111113
cbar = figure.colorbar(sm, cax=cbar_ax)
112114
cbar.set_label("Comfort/Safety Level")
113115

114-
def plot_metrics(time_jerk, jerk, time_heading_acc, heading_acc, vehicle_time, cte,
116+
def plot_metrics(time_jerk, jerk, time_heading_acc, heading_acc, time_accel, accel, vehicle_time, cte,
115117
x_actual, y_actual, x_desired, y_desired, pedestrian_times, pedestrian_distances):
116118
"""Plots all metrics in 2x3 grid"""
117119
fig, axs = plt.subplots(2, 3, figsize=(12, 8))
118120
fig.subplots_adjust(hspace=0.375, wspace=0.35)
119-
axs[1,2].axis('off')
121+
# axs[1,2].axis('off')
120122

121123
plot_jerk(axs[0,0], time_jerk, jerk)
122124
plot_heading_acceleration(axs[0,1], time_heading_acc, heading_acc)
125+
plot_acceleration(axs[0,2], time_accel, accel)
123126
plot_crosstrack_error(axs[1,0], vehicle_time, cte)
124127
plot_position(axs[1,1], x_actual, y_actual, x_desired, y_desired)
125-
plot_pedestrian_dist(axs[0,2], pedestrian_times, pedestrian_distances)
126-
128+
plot_pedestrian_dist(axs[1,2], pedestrian_times, pedestrian_distances)
129+
127130
# Colorbar on the right side
128131
add_safety_colorbar(fig)
129-
130132
plt.show()
131133

132134
def plot_jerk(axis, time, jerk, safe_thresh=1.0, unsafe_thresh=2.5):
133-
"""Plots vehicle jerk (rate of acceleration) vs. time"""
134-
safety_scores = np.vectorize(compute_safety_factor)(jerk, safe_thresh, unsafe_thresh)
135-
136-
axis.plot(time, jerk, color="black", linewidth=0.8, alpha=0.5)
137-
axis.scatter(time, jerk, c=safety_scores, cmap=CMAP, vmin=0, vmax=1, edgecolors="black")
135+
"""
136+
Plots vehicle jerk (rate of acceleration) vs. time as a colored line.
137+
"""
138+
# compute safety scores
139+
safety = np.vectorize(compute_safety_factor)(jerk, safe_thresh, unsafe_thresh)
140+
141+
# build line segments
142+
points = np.vstack([time, jerk]).T.reshape(-1,1,2)
143+
segments = np.concatenate([points[:-1], points[1:]], axis=1)
144+
145+
# create colored LineCollection
146+
norm = Normalize(vmin=0, vmax=1)
147+
lc = LineCollection(segments, cmap=CMAP, norm=norm, linewidth=1.5)
148+
lc.set_array(safety[:-1])
149+
axis.add_collection(lc)
138150

151+
# set limits & labels
152+
axis.set_xlim(time.min(), time.max())
153+
axis.set_ylim(jerk.min(), jerk.max())
139154
axis.set_xlabel("Time (s)")
140155
axis.set_ylabel("Jerk (m/s³)")
141156
axis.set_title("Vehicle Jerk Over Time")
142157
axis.grid(True)
143158

159+
def plot_acceleration(axis, time, acceleration, safe_thresh=0.5, unsafe_thresh=1.5):
160+
"""
161+
Plots vehicle acceleration vs. time as a colored line.
162+
"""
163+
safety = np.vectorize(compute_safety_factor)(acceleration, safe_thresh, unsafe_thresh)
164+
165+
points = np.vstack([time, acceleration]).T.reshape(-1,1,2)
166+
segments = np.concatenate([points[:-1], points[1:]], axis=1)
167+
168+
norm = Normalize(vmin=0, vmax=1)
169+
lc = LineCollection(segments, cmap=CMAP, norm=norm, linewidth=1.5)
170+
lc.set_array(safety[:-1])
171+
axis.add_collection(lc)
172+
173+
axis.set_xlim(time.min(), time.max())
174+
axis.set_ylim(acceleration.min(), acceleration.max())
175+
axis.set_xlabel("Time (s)")
176+
axis.set_ylabel("Acceleration (m/s²)")
177+
axis.set_title("Vehicle Acceleration Over Time")
178+
axis.grid(True)
179+
144180
def plot_heading_acceleration(axis, time, heading_acc, safe_thresh=0.0075, unsafe_thresh=0.25):
145-
"""Plots vehicle heading acceleration vs. time"""
146-
safety_scores = np.vectorize(compute_safety_factor)(heading_acc, safe_thresh, unsafe_thresh)
147-
148-
axis.plot(time, heading_acc, color="black", linewidth=0.8, alpha=0.5)
149-
axis.scatter(time, heading_acc, c=safety_scores, cmap=CMAP, vmin=0, vmax=1, edgecolors="black")
150-
181+
"""
182+
Plots vehicle heading acceleration vs. time as a colored line.
183+
"""
184+
safety = np.vectorize(compute_safety_factor)(heading_acc, safe_thresh, unsafe_thresh)
185+
186+
points = np.vstack([time, heading_acc]).T.reshape(-1,1,2)
187+
segments = np.concatenate([points[:-1], points[1:]], axis=1)
188+
189+
norm = Normalize(vmin=0, vmax=1)
190+
lc = LineCollection(segments, cmap=CMAP, norm=norm, linewidth=1.5)
191+
lc.set_array(safety[:-1])
192+
axis.add_collection(lc)
193+
194+
axis.set_xlim(time.min(), time.max())
195+
axis.set_ylim(heading_acc.min(), heading_acc.max())
151196
axis.set_xlabel("Time (s)")
152197
axis.set_ylabel("Heading Acceleration (rad/s²)")
153198
axis.set_title("Vehicle Heading Acceleration Over Time")
154199
axis.grid(True)
155200

156201
def plot_crosstrack_error(axis, time, cte, safe_thresh=0.1, unsafe_thresh=0.4):
157-
"""Plots vehicle cross track error vs. time"""
158-
safety_scores = np.vectorize(compute_safety_factor)(cte, safe_thresh, unsafe_thresh)
159-
160-
axis.plot(time, cte, color="black", linewidth=0.8, alpha=0.5)
161-
axis.scatter(time, cte, c=safety_scores, cmap=CMAP, vmin=0, vmax=1, edgecolors="black")
162-
202+
"""
203+
Plots vehicle cross track error vs. time as a colored line.
204+
"""
205+
# compute safety scores for each point
206+
safety = np.vectorize(compute_safety_factor)(cte, safe_thresh, unsafe_thresh)
207+
208+
points = np.vstack([time, cte]).T.reshape(-1,1,2)
209+
segments = np.concatenate([points[:-1], points[1:]], axis=1)
210+
211+
lc = LineCollection(segments, cmap=CMAP, norm=Normalize(0,1))
212+
lc.set_array(safety[:-1])
213+
lc.set_linewidth(2.0)
214+
axis.add_collection(lc)
215+
216+
# set axis limits
217+
axis.set_xlim(time.min(), time.max())
218+
axis.set_ylim(cte.min(), cte.max())
219+
163220
axis.set_xlabel("Time (s)")
164221
axis.set_ylabel("Cross Track Error")
165222
axis.set_title("Vehicle Cross Track Error Over Time")
166223
axis.grid(True)
167224

168-
def plot_position(axis, x_actual, y_actual, x_desired, y_desired, safe_thresh=1, unsafe_thresh=2.5):
169-
"""Plots vehicle actual and desired positions vs. time"""
170-
position_error = np.sqrt((x_desired - x_actual) ** 2 + (y_desired - y_actual) ** 2)
171-
safety_scores = np.vectorize(compute_safety_factor)(position_error, safe_thresh, unsafe_thresh)
172-
173-
axis.plot(y_desired, x_desired, marker='.', linestyle=':', color='blue', label='Desired')
174-
axis.plot(y_actual, x_actual, color="black", linewidth=0.8, alpha=0.5)
175-
axis.scatter(y_actual, x_actual, c=safety_scores, cmap=CMAP, vmin=0, vmax=1, edgecolors="black")
176-
225+
def plot_position(axis, x_actual, y_actual, x_desired, y_desired,
226+
safe_thresh=1.0, unsafe_thresh=2.5):
227+
"""
228+
Plots vehicle actual and desired positions
229+
"""
230+
# compute positional error and safety at each point
231+
pos_error = np.sqrt((x_desired - x_actual)**2 + (y_desired - y_actual)**2)
232+
safety = np.vectorize(compute_safety_factor)(pos_error, safe_thresh, unsafe_thresh)
233+
234+
# actual path segments
235+
actual_pts = np.vstack([y_actual, x_actual]).T.reshape(-1,1,2)
236+
actual_segs = np.concatenate([actual_pts[:-1], actual_pts[1:]], axis=1)
237+
norm = Normalize(vmin=0, vmax=1)
238+
lc_actual = LineCollection(actual_segs, cmap=CMAP, norm=norm)
239+
lc_actual.set_array(safety[:-1])
240+
lc_actual.set_linewidth(2.0)
241+
axis.add_collection(lc_actual)
242+
243+
# desired path as dashed gray line
244+
axis.plot(y_desired, x_desired,
245+
linestyle='--', linewidth=1.5, color='gray', label='Desired')
246+
177247
axis.set_xlabel("Y Position (m)")
178248
axis.set_ylabel("X Position (m)")
179249
axis.set_title("Vehicle Position vs. Desired Position")
@@ -186,7 +256,7 @@ def plot_pedestrian_dist(axis, pedestrian_times, pedestrian_distances, safe_thre
186256
safety_scores = np.vectorize(compute_safety_factor)(pedestrian_distances, safe_thresh, unsafe_thresh, flip=True)
187257
axis.plot(pedestrian_times, pedestrian_distances, color="black", linewidth=0.8, alpha=0.5)
188258
axis.scatter(pedestrian_times, pedestrian_distances, c=safety_scores, cmap=CMAP, vmin=0, vmax=1, edgecolors="black")
189-
259+
190260
axis.set_xlabel("Time (s)")
191261
axis.set_ylabel("Pedestrian Distance (m)")
192262
axis.set_title("Pedestrian Distance Over Time")
@@ -196,41 +266,43 @@ def plot_pedestrian_dist(axis, pedestrian_times, pedestrian_distances, safe_thre
196266
if len(sys.argv) != 2:
197267
print("Usage: python test_comfort_metrics.py <log_directory>")
198268
sys.exit(1)
199-
269+
200270
log_dir = sys.argv[1]
201271
behavior_file = os.path.join(log_dir, "behavior.json")
202272
tracker_file = os.path.join(log_dir, "PurePursuitTrajectoryTracker_debug.csv")
203-
273+
204274
# if behavior.json doesn't exist, print error and exit
205275
if not os.path.exists(behavior_file):
206276
print("Error: behavior.json file is missing in log folder.")
207277
sys.exit(1)
208-
278+
209279
# Parse behavior log file and compute metrics
210280
times, accelerations, heading_rates, ped_times, ped_distances = parse_behavior_log(behavior_file)
211281
time_jerk, jerk = compute_derivative(times, accelerations)
212282
time_heading_acc, heading_acc = compute_derivative(times, heading_rates)
213-
283+
214284
# Pure pursuit tracker file exists: parse and plot all metrics
215285
if os.path.exists(tracker_file):
216286
vehicle_time, cte, x_actual, y_actual, x_desired, y_desired = parse_tracker_csv(tracker_file)
217-
plot_metrics(time_jerk, jerk, time_heading_acc, heading_acc, vehicle_time, cte,
287+
plot_metrics(time_jerk, jerk, time_heading_acc, heading_acc, times, accelerations, vehicle_time, cte,
218288
x_actual, y_actual, x_desired, y_desired, ped_times, ped_distances)
219-
289+
220290
print("RMS Cross Track Error:", np.sqrt(np.mean(cte**2)), "m")
221291
print("RMS Position Error:", np.sqrt(np.mean((x_actual - x_desired)**2 + (y_actual - y_desired)**2)), 'm')
222292
# Pure pursuit tracker file is missing: plot only behavior.json metrics
223293
else:
224294
print("Tracker file is missing. Skipping cross track error and vehicle position plots.")
225295
# Plot only jerk, heading acceleration, and pedestrian distance
226-
fig, axs = plt.subplots(1, 3, figsize=(12, 4))
227-
plot_jerk(axs[0], time_jerk, jerk)
228-
plot_heading_acceleration(axs[1], time_heading_acc, heading_acc)
229-
plot_pedestrian_dist(axs[2], ped_times, ped_distances)
296+
fig, axs = plt.subplots(2, 2, figsize=(12, 4))
297+
plot_jerk(axs[0, 0], time_jerk, jerk)
298+
plot_heading_acceleration(axs[0, 1], time_heading_acc, heading_acc)
299+
plot_acceleration(axs[1, 0], times, accelerations)
300+
plot_pedestrian_dist(axs[1, 1], ped_times, ped_distances)
301+
230302
add_safety_colorbar(fig)
231303
plt.show()
232-
304+
233305
print("RMS Jerk:", np.sqrt(np.mean(jerk**2)), "m/s³")
234306
print("RMS Heading Acceleration:", np.sqrt(np.mean(heading_acc**2)), "rad/s²")
235307
if len(ped_distances) > 0:
236-
print("Minimum Pedestrian Distance:", np.min(ped_distances), "m")
308+
print("Minimum Pedestrian Distance:", np.min(ped_distances), "m")

0 commit comments

Comments
 (0)