88import numpy as np
99from matplotlib .collections import LineCollection
1010from matplotlib .colors import Normalize
11+ from matplotlib import cm
1112import os
1213
1314CMAP = "RdYlGn"
@@ -104,13 +105,18 @@ def compute_derivative(times, values):
104105 """
105106 dt = np .diff (times )
106107 dv = np .diff (values )
107- derivative = dv / dt
108+
109+ # Avoid division by zero or very small values
110+ mask = np .abs (dt ) > 1e-10
111+ derivative = np .zeros_like (dt )
112+ derivative [mask ] = dv [mask ] / dt [mask ]
113+
108114 return times [1 :], derivative
109115
110116def add_safety_colorbar (figure ):
111117 """Adds a colorbar to the right of the figure"""
112118 cbar_ax = figure .add_axes ([0.92 , 0.2 , 0.02 , 0.6 ])
113- sm = plt . cm .ScalarMappable (cmap = CMAP )
119+ sm = cm .ScalarMappable (cmap = CMAP )
114120 cbar = figure .colorbar (sm , cax = cbar_ax )
115121 cbar .set_label ("Comfort/Safety Level" )
116122
@@ -149,9 +155,18 @@ def plot_jerk(axis, time, jerk, safe_thresh=1.0, unsafe_thresh=2.5):
149155 lc .set_array (safety [:- 1 ])
150156 axis .add_collection (lc )
151157
152- # set limits & labels
158+ # Set axis limits safely, avoiding NaN/Inf values
159+ if len (jerk ) > 0 :
160+ valid_jerk = jerk [~ np .isnan (jerk ) & ~ np .isinf (jerk )]
161+ if len (valid_jerk ) > 0 :
162+ ymin , ymax = valid_jerk .min (), valid_jerk .max ()
163+ # Add small margin if min equals max
164+ if ymin == ymax :
165+ ymin -= 0.1
166+ ymax += 0.1
167+ axis .set_ylim (ymin , ymax )
168+
153169 axis .set_xlim (time .min (), time .max ())
154- axis .set_ylim (jerk .min (), jerk .max ())
155170 axis .set_xlabel ("Time (s)" )
156171 axis .set_ylabel ("Jerk (m/s³)" )
157172 axis .set_title ("Vehicle Jerk Over Time" )
@@ -171,8 +186,17 @@ def plot_acceleration(axis, time, acceleration, safe_thresh=0.5, unsafe_thresh=1
171186 lc .set_array (safety [:- 1 ])
172187 axis .add_collection (lc )
173188
189+ # Set axis limits safely
190+ if len (acceleration ) > 0 :
191+ valid_accel = acceleration [~ np .isnan (acceleration ) & ~ np .isinf (acceleration )]
192+ if len (valid_accel ) > 0 :
193+ ymin , ymax = valid_accel .min (), valid_accel .max ()
194+ if ymin == ymax :
195+ ymin -= 0.1
196+ ymax += 0.1
197+ axis .set_ylim (ymin , ymax )
198+
174199 axis .set_xlim (time .min (), time .max ())
175- axis .set_ylim (acceleration .min (), acceleration .max ())
176200 axis .set_xlabel ("Time (s)" )
177201 axis .set_ylabel ("Acceleration (m/s²)" )
178202 axis .set_title ("Vehicle Acceleration Over Time" )
@@ -192,8 +216,17 @@ def plot_heading_acceleration(axis, time, heading_acc, safe_thresh=0.0075, unsaf
192216 lc .set_array (safety [:- 1 ])
193217 axis .add_collection (lc )
194218
219+ # Set axis limits safely
220+ if len (heading_acc ) > 0 :
221+ valid_heading = heading_acc [~ np .isnan (heading_acc ) & ~ np .isinf (heading_acc )]
222+ if len (valid_heading ) > 0 :
223+ ymin , ymax = valid_heading .min (), valid_heading .max ()
224+ if ymin == ymax :
225+ ymin -= 0.1
226+ ymax += 0.1
227+ axis .set_ylim (ymin , ymax )
228+
195229 axis .set_xlim (time .min (), time .max ())
196- axis .set_ylim (heading_acc .min (), heading_acc .max ())
197230 axis .set_xlabel ("Time (s)" )
198231 axis .set_ylabel ("Heading Acceleration (rad/s²)" )
199232 axis .set_title ("Vehicle Heading Acceleration Over Time" )
@@ -214,10 +247,17 @@ def plot_crosstrack_error(axis, time, cte, safe_thresh=0.1, unsafe_thresh=0.4):
214247 lc .set_linewidth (2.0 )
215248 axis .add_collection (lc )
216249
217- # set axis limits
250+ # Set axis limits safely
251+ if len (cte ) > 0 :
252+ valid_cte = cte [~ np .isnan (cte ) & ~ np .isinf (cte )]
253+ if len (valid_cte ) > 0 :
254+ ymin , ymax = valid_cte .min (), valid_cte .max ()
255+ if ymin == ymax :
256+ ymin -= 0.1
257+ ymax += 0.1
258+ axis .set_ylim (ymin , ymax )
259+
218260 axis .set_xlim (time .min (), time .max ())
219- axis .set_ylim (cte .min (), cte .max ())
220-
221261 axis .set_xlabel ("Time (s)" )
222262 axis .set_ylabel ("Cross Track Error" )
223263 axis .set_title ("Vehicle Cross Track Error Over Time" )
@@ -286,17 +326,31 @@ def plot_pedestrian_dist(axis, pedestrian_times, pedestrian_distances, safe_thre
286326
287327 # Parse behavior log file and compute metrics
288328 times , accelerations , heading_rates , ped_times , ped_distances = parse_behavior_log (behavior_file )
289- time_jerk , jerk = compute_derivative (times , accelerations )
290- time_heading_acc , heading_acc = compute_derivative (times , heading_rates )
329+
330+ # Ensure we have valid data before computing derivatives
331+ if len (times ) > 1 and len (accelerations ) > 1 and len (heading_rates ) > 1 :
332+ time_jerk , jerk = compute_derivative (times , accelerations )
333+ time_heading_acc , heading_acc = compute_derivative (times , heading_rates )
334+ else :
335+ print ("Warning: Not enough data points to compute derivatives." )
336+ time_jerk , jerk = np .array ([]), np .array ([])
337+ time_heading_acc , heading_acc = np .array ([]), np .array ([])
291338
292339 # Pure pursuit tracker file exists: parse and plot all metrics
293340 if os .path .exists (tracker_file ):
294341 vehicle_time , cte , x_actual , y_actual , x_desired , y_desired = parse_tracker_csv (tracker_file )
295342 plot_metrics (time_jerk , jerk , time_heading_acc , heading_acc , times , accelerations , vehicle_time , cte ,
296343 x_actual , y_actual , x_desired , y_desired , ped_times , ped_distances )
297344
298- print ("RMS Cross Track Error:" , np .sqrt (np .mean (cte ** 2 )), "m" )
299- print ("RMS Position Error:" , np .sqrt (np .mean ((x_actual - x_desired )** 2 + (y_actual - y_desired )** 2 )), 'm' )
345+ # Calculate RMS values only for valid data points
346+ valid_cte = cte [~ np .isnan (cte ) & ~ np .isinf (cte )]
347+ if len (valid_cte ) > 0 :
348+ print ("RMS Cross Track Error:" , np .sqrt (np .mean (valid_cte ** 2 )), "m" )
349+
350+ valid_pos_error = np .sqrt ((x_actual - x_desired )** 2 + (y_actual - y_desired )** 2 )
351+ valid_pos_error = valid_pos_error [~ np .isnan (valid_pos_error ) & ~ np .isinf (valid_pos_error )]
352+ if len (valid_pos_error ) > 0 :
353+ print ("RMS Position Error:" , np .sqrt (np .mean (valid_pos_error ** 2 )), 'm' )
300354 # Pure pursuit tracker file is missing: plot only behavior.json metrics
301355 else :
302356 print ("Tracker file is missing. Skipping cross track error and vehicle position plots." )
@@ -310,7 +364,14 @@ def plot_pedestrian_dist(axis, pedestrian_times, pedestrian_distances, safe_thre
310364 add_safety_colorbar (fig )
311365 plt .show ()
312366
313- print ("RMS Jerk:" , np .sqrt (np .mean (jerk ** 2 )), "m/s³" )
314- print ("RMS Heading Acceleration:" , np .sqrt (np .mean (heading_acc ** 2 )), "rad/s²" )
367+ # Calculate RMS values only for valid data points
368+ valid_jerk = jerk [~ np .isnan (jerk ) & ~ np .isinf (jerk )]
369+ if len (valid_jerk ) > 0 :
370+ print ("RMS Jerk:" , np .sqrt (np .mean (valid_jerk ** 2 )), "m/s³" )
371+
372+ valid_heading_acc = heading_acc [~ np .isnan (heading_acc ) & ~ np .isinf (heading_acc )]
373+ if len (valid_heading_acc ) > 0 :
374+ print ("RMS Heading Acceleration:" , np .sqrt (np .mean (valid_heading_acc ** 2 )), "rad/s²" )
375+
315376 if len (ped_distances ) > 0 :
316377 print ("Minimum Pedestrian Distance:" , np .min (ped_distances ), "m" )
0 commit comments