def suspension_vel_der_diff_l_r_f_r_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) susp_fl = session_data[networking.Fields.susp_fl.value] susp_fr = session_data[networking.Fields.susp_fr.value] susp_rl = session_data[networking.Fields.susp_rl.value] susp_rr = session_data[networking.Fields.susp_rr.value] susp_data = [susp_fl, susp_fr, susp_rl, susp_rr] susp_data = [ data_processing.derive_no_nan(susp, race_time) for susp in susp_data ] susp_vel_fl = session_data[networking.Fields.susp_vel_fl.value] susp_vel_fr = session_data[networking.Fields.susp_vel_fr.value] susp_vel_rl = session_data[networking.Fields.susp_vel_rl.value] susp_vel_rr = session_data[networking.Fields.susp_vel_rr.value] susp_vel = [susp_vel_fl, susp_vel_fr, susp_vel_rl, susp_vel_rr] susp_data = np.array(susp_data) - np.array(susp_vel) labels = ['susp_fl', 'susp_fr', 'susp_rl', 'susp_rr'] x_points = np.array([race_time] * len(susp_data)) y_points = np.array(susp_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Suspension velocity derived over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Suspension velocity derived - given (mm/s)', flip_y=True, min_max_annotations=True)
def suspension_bars(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) time_differences = data_processing.differences(race_time) # prevent negative times due to next lap time_differences[time_differences < 0.0] = np.finfo( time_differences.dtype).eps susp_fl = session_data[networking.Fields.susp_fl.value] susp_fr = session_data[networking.Fields.susp_fr.value] susp_rl = session_data[networking.Fields.susp_rl.value] susp_rr = session_data[networking.Fields.susp_rr.value] susp_data = np.array([susp_fl, susp_fr, susp_rl, susp_rr]) time_data = np.repeat(np.expand_dims(time_differences, axis=0), 4, axis=0) susp_min = susp_data.min() susp_max = susp_data.max() susp_min_ids = (susp_min == susp_data) susp_max_ids = (susp_max == susp_data) series_labels = ['front left', 'front right', 'rear left', 'rear right'] series_labels = [ l + ', bump min: {:.1f} s, bump max: {:.1f} s'.format( time_differences[susp_min_ids[li]].sum(), time_differences[susp_max_ids[li]].sum()) for li, l in enumerate(series_labels) ] bar_plot( ax, data=susp_data, weights=time_data, num_bins=20, title='Suspension dislocation, min: {:.1f} mm, max: {:.1f} mm'.format( susp_min, susp_max), x_label='Suspension dislocation (mm)', y_label='Accumulated Time (s)', series_labels=series_labels)
def power_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) energy, kinetic_energy, potential_energy = data_processing.get_energy( session_data=session_data) power = data_processing.derive_no_nan(energy, race_time) full_acceleration = data_processing.get_full_acceleration_mask( session_data=session_data) not_full_acceleration = np.logical_not(full_acceleration) power_full_acceleration = power.copy() power_full_acceleration[not_full_acceleration] = 0.0 power_not_full_acceleration = power.copy() power_not_full_acceleration[full_acceleration] = 0.0 power_data = np.array( [power_full_acceleration, power_not_full_acceleration]) labels = ['Power at full throttle (kW)', 'Power otherwise (kW)'] y_points = power_data x_points = np.array([race_time] * y_points.shape[0]) line_plot(ax, x_points=x_points, y_points=y_points, title='Power over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Power (kW)', min_max_annotations=False)
def suspension_lr_fr_angles_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) susp_fl = session_data[networking.Fields.susp_fl.value] susp_fr = session_data[networking.Fields.susp_fr.value] susp_rl = session_data[networking.Fields.susp_rl.value] susp_rr = session_data[networking.Fields.susp_rr.value] susp_left_right = (susp_fl + susp_rl) * 0.5 - (susp_fr + susp_rr) * 0.5 susp_front_rear = (susp_fl + susp_fr) * 0.5 - (susp_rl + susp_rr) * 0.5 def height_diff_to_angle(displacement, dist): angle = np.arcsin(displacement / dist) return np.rad2deg(angle) # approximately wheelbase and track for Audi Quattro S1 -> should be accurate enough for other cars angle_left_right = height_diff_to_angle(susp_left_right / 1000.0, 1.800) angle_front_rear = height_diff_to_angle(susp_front_rear / 1000.0, 2.200) angle_data = np.array([-angle_left_right, -angle_front_rear]) labels = ['left-right angle', 'front-rear angle'] x_points = np.array([race_time] * len(angle_data)) y_points = np.array(angle_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Suspension dislocation angle over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Suspension dislocation angle (deg)', min_max_annotations=True)
def gear_rpm_bars(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) time_differences = data_processing.differences(race_time) # prevent negative times due to next lap time_differences[time_differences < 0.0] = np.finfo( time_differences.dtype).eps rpm = session_data[networking.Fields.rpm.value] data_gear = session_data[networking.Fields.gear.value] range_gears = list(set(data_gear)) range_gears.sort() total_time = time_differences.sum() gear_ids = [data_gear == gear for gear in range_gears] gear_times = [time_differences[g] for g in gear_ids] gear_rpms = [rpm[g] for g in gear_ids] gear_time_sums = [gt.sum() for gt in gear_times] if total_time == 0.0: gear_ratio = [0.0] * len(gear_time_sums) else: gear_ratio = [gts / total_time for gts in gear_time_sums] series_labels = [ 'Gear {0}: {1:.1f}%'.format(int(g), gear_ratio[gi] * 100.0) for gi, g in enumerate(range_gears) ] bar_plot(ax, data=gear_rpms, weights=gear_times, num_bins=20, title='Gear RPM', x_label='RPM', y_label='Accumulated Time (s)', series_labels=series_labels)
def drift_angle_change_bars(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) time_differences = data_processing.differences(race_time) # prevent negative times due to next lap time_differences[time_differences < 0.0] = np.finfo( time_differences.dtype).eps speed_ms = session_data[networking.Fields.speed_ms.value] drift_angle_deg = data_processing.get_drift_angle(session_data) drift_angle_deg_der = data_processing.derive_no_nan(drift_angle_deg, time_steps=race_time) # filter very slow parts fast_enough = speed_ms > 1.0 # m/s drift_angle_deg_der = drift_angle_deg_der[fast_enough] drift_angle_deg_der = np.abs(drift_angle_deg_der) # filter out rare extreme values outlier_threshold = np.nanpercentile(drift_angle_deg_der, 99) usual_values = drift_angle_deg_der < outlier_threshold drift_angle_deg_der = drift_angle_deg_der[usual_values] time_differences = time_differences[fast_enough] time_differences = time_differences[usual_values] series_labels = [''] bar_plot(ax, data=[drift_angle_deg_der], weights=[time_differences], num_bins=10, title='Drift Angle Change Histogram', x_label='Drift Angle Change (deg/s)', y_label='Accumulated Time (s)', series_labels=series_labels)
def rotation_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) forward_local_xyz = data_processing.get_forward_dir_3d(session_data) sideward_local_xyz = data_processing.get_sideward_dir_3d(session_data) def get_vertical_angle_dislocation(dirs): global_dirs = data_processing.normalize_3d_vectors( dirs[0], dirs[1], np.zeros_like(dirs[2])) dirs_dislocation = (dirs * global_dirs).sum(axis=0) dirs_angle = np.arccos(dirs_dislocation) dirs_angle[dirs[2] < 0.0] = -dirs_angle[dirs[2] < 0.0] dirs_angle_deg = np.rad2deg(dirs_angle) return dirs_angle_deg sideward_angle_deg = get_vertical_angle_dislocation(sideward_local_xyz) forward_angle_deg = get_vertical_angle_dislocation(forward_local_xyz) labels = ['Sideward Rotation Angle', 'Forward Rotation Angle'] x_points = np.array([race_time] * len(labels)) y_points = np.array([sideward_angle_deg, forward_angle_deg]) line_plot(ax, x_points=x_points, y_points=y_points, title='Rotation Angles over Time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Angle (deg)', min_max_annotations=True)
def drift_over_speed(ax, session_data): steering = np.abs(session_data[networking.Fields.steering.value]) speed_ms = session_data[networking.Fields.speed_ms.value] drift_angle_deg = data_processing.get_drift_angle(session_data) race_time = data_processing.get_run_time_cleaned(session_data=session_data) drift_angle_deg_der = data_processing.derive_no_nan(drift_angle_deg, time_steps=race_time) # filter very slow parts fast_enough = speed_ms > 1.0 # m/s steering = steering[fast_enough] speed_ms = speed_ms[fast_enough] # drift_angle_deg = drift_angle_deg[fast_enough] drift_angle_deg_der = drift_angle_deg_der[fast_enough] colors = [steering] scales = [25] alphas = [0.5] labels = ['drift over steer'] # scatter_plot(ax, x_points=[speed_ms], y_points=[drift_angle_deg], # title='Drift over speed (steering as color)', # labels=labels, colors=colors, scales=scales, alphas=alphas, # x_label='Speed (m/s)', y_label='Drift angle (deg)') scatter_plot(ax, x_points=[speed_ms], y_points=[drift_angle_deg_der], title='Drift change over speed (steering as color)', labels=labels, colors=colors, scales=scales, alphas=alphas, x_label='Speed (m/s)', y_label='Drift angle (deg/s)')
def suspension_l_r_f_r_bars(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) time_differences = data_processing.differences(race_time) # prevent negative times due to next lap time_differences[time_differences < 0.0] = np.finfo( time_differences.dtype).eps susp_fl = session_data[networking.Fields.susp_fl.value] susp_fr = session_data[networking.Fields.susp_fr.value] susp_rl = session_data[networking.Fields.susp_rl.value] susp_rr = session_data[networking.Fields.susp_rr.value] susp_left = (susp_fl + susp_rl) * 0.5 susp_right = (susp_fr + susp_rr) * 0.5 susp_front = (susp_fl + susp_fr) * 0.5 susp_rear = (susp_rl + susp_rr) * 0.5 susp_data = np.array([susp_left, susp_right, susp_front, susp_rear]) time_data = np.repeat(np.expand_dims(time_differences, axis=0), 4, axis=0) susp_min = susp_data.min() susp_max = susp_data.max() series_labels = ['left', 'right', 'front', 'rear'] bar_plot( ax, data=susp_data, weights=time_data, num_bins=20, title='Average Suspension dislocation, min: {:.1f} mm, max: {:.1f} mm'. format(susp_min, susp_max), x_label='Suspension dislocation (mm)', y_label='Accumulated Time (s)', series_labels=series_labels)
def slip_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) speed_ms = session_data[networking.Fields.speed_ms.value] wsp_fl = session_data[networking.Fields.wsp_fl.value] wsp_fr = session_data[networking.Fields.wsp_fr.value] wsp_rl = session_data[networking.Fields.wsp_rl.value] wsp_rr = session_data[networking.Fields.wsp_rr.value] wsp = [wsp_fl, wsp_fr, wsp_rl, wsp_rr] slip = [w - speed_ms for w in wsp] wsp_data = np.array(slip) labels = ['front left', 'front right', 'rear left', 'rear right'] x_points = np.array([race_time] * len(wsp_data)) y_points = np.array(wsp_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Wheel slip over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Wheel slip (m/s)', min_max_annotations=True)
def suspension_l_r_f_r_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) susp_fl = session_data[networking.Fields.susp_fl.value] susp_fr = session_data[networking.Fields.susp_fr.value] susp_rl = session_data[networking.Fields.susp_rl.value] susp_rr = session_data[networking.Fields.susp_rr.value] susp_left = (susp_fl + susp_rl) * 0.5 susp_right = (susp_fr + susp_rr) * 0.5 susp_front = (susp_fl + susp_fr) * 0.5 susp_rear = (susp_rl + susp_rr) * 0.5 susp_data = np.array([susp_left, susp_right, susp_front, susp_rear]) labels = ['left', 'right', 'front', 'rear'] x_points = np.array([race_time] * len(susp_data)) y_points = np.array(susp_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Average suspension dislocation over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Suspension dislocation (mm)', flip_y=True, min_max_annotations=True)
def energy_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) energy, kinetic_energy, potential_energy = data_processing.get_energy( session_data=session_data) energy_data = np.array([energy, kinetic_energy, potential_energy]) labels = ['Energy (kJ)', 'Kinetic Energy (kJ)', 'Potential Energy (kJ)'] y_points = energy_data x_points = np.array([race_time] * y_points.shape[0]) line_plot(ax, x_points=x_points, y_points=y_points, title='Energy over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Energy (kJ)', min_max_annotations=False)
def plot_p_over_vel(ax, session_data): data_gear = session_data[networking.Fields.gear.value] range_gears = list(set(data_gear)) range_gears.sort() labels = ['Gear {}'.format(str(g)) for g in range_gears] scale = 50.0 alphas = [0.5] * len(labels) colors = [static_colors[i] for i, g in enumerate(range_gears)] full_acceleration_mask = data_processing.get_full_acceleration_mask( session_data=session_data) energy, kinetic_energy, potential_energy = data_processing.get_energy( session_data=session_data) times_steps = data_processing.get_run_time_cleaned( session_data=session_data) power = data_processing.derive_no_nan(x=energy, time_steps=times_steps) / 1000.0 x_points = [] y_points = [] scales = [] for gear in range_gears: current_gear = session_data[networking.Fields.gear.value] == gear interesting = np.logical_and(current_gear, full_acceleration_mask) speed_ms = session_data[networking.Fields.speed_ms.value] x_points += [speed_ms[interesting]] y_points += [power[interesting]] scales += [np.ones_like(speed_ms[interesting]) * scale] scatter_plot(ax, x_points=x_points, y_points=y_points, title='Power over velocity (full throttle)', labels=labels, colors=colors, scales=scales, alphas=alphas, x_label='Velocity (m/s)', y_label='Power (kW)')
def inputs_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) throttle = session_data[networking.Fields.throttle.value] brakes = session_data[networking.Fields.brakes.value] steering = session_data[networking.Fields.steering.value] input_data = np.array([throttle, brakes, steering]) labels = ['throttle', 'brakes', 'steering'] y_points = input_data x_points = np.array([race_time] * y_points.shape[0]) line_plot(ax, x_points=x_points, y_points=y_points, title='Inputs over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Inputs', min_max_annotations=False)
def suspension_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) susp_fl = session_data[networking.Fields.susp_fl.value] susp_fr = session_data[networking.Fields.susp_fr.value] susp_rl = session_data[networking.Fields.susp_rl.value] susp_rr = session_data[networking.Fields.susp_rr.value] susp_data = np.array([susp_fl, susp_fr, susp_rl, susp_rr]) labels = ['front left', 'front right', 'rear left', 'rear right'] y_points = susp_data x_points = np.array([race_time] * y_points.shape[0]) line_plot(ax, x_points=x_points, y_points=y_points, title='Suspension dislocation over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Suspension dislocation (mm)', flip_y=True, min_max_annotations=True)
def suspension_vel_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) susp_vel_fl = session_data[networking.Fields.susp_vel_fl.value] susp_vel_fr = session_data[networking.Fields.susp_vel_fr.value] susp_vel_rl = session_data[networking.Fields.susp_vel_rl.value] susp_vel_rr = session_data[networking.Fields.susp_vel_rr.value] susp_data = np.array([susp_vel_fl, susp_vel_fr, susp_vel_rl, susp_vel_rr]) labels = ['susp_vel_fl', 'susp_vel_fr', 'susp_vel_rl', 'susp_vel_rr'] x_points = np.array([race_time] * len(susp_data)) y_points = np.array(susp_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Suspension velocity over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Suspension velocity (mm/s)', min_max_annotations=True)
def wheel_speed_lr_fr_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) wsp_fl = session_data[networking.Fields.wsp_fl.value] wsp_fr = session_data[networking.Fields.wsp_fr.value] wsp_rl = session_data[networking.Fields.wsp_rl.value] wsp_rr = session_data[networking.Fields.wsp_rr.value] wsp_left_right = (wsp_fl + wsp_rl) * 0.5 - (wsp_fr + wsp_rr) * 0.5 wsp_front_rear = (wsp_fl + wsp_fr) * 0.5 - (wsp_rl + wsp_rr) * 0.5 wsp_data = np.array([wsp_left_right, wsp_front_rear]) labels = ['left-right', 'front-rear'] x_points = np.array([race_time] * len(wsp_data)) y_points = np.array(wsp_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Wheel speed difference over time', labels=labels, alpha=0.5, x_label='Time (s)', y_label='Wheel speed difference (m/s)', min_max_annotations=True)
def ground_contact_over_time(ax, session_data): race_time = data_processing.get_run_time_cleaned(session_data=session_data) def get_ground_contact(susp_vel: np.ndarray, susp_vel_lim=100.0, variance_max=100.0, filter_length=6) -> list: # filter_length = 6 -> 100 ms (0.1 s) at 60 FPS def get_variance_convolved(data: np.ndarray, filter_length: int): box_filter = np.array( [1.0] * filter_length) # filter_length = 6 -> 100 ms at 60 FPS sum_conv = np.convolve(data, box_filter, mode='same') mean_conv = sum_conv / float(filter_length) var_sqr_conv = data - mean_conv var_conv = var_sqr_conv * var_sqr_conv return var_conv # extending by max x mm/s susp_vel_lim = np.logical_and(susp_vel < 0.0, susp_vel > -susp_vel_lim) susp_var = get_variance_convolved(susp_vel, filter_length) < variance_max ground_contact_mask = np.logical_and(susp_var, susp_vel_lim) # extending for more than 0.1s box_filter = np.array([1.0] * filter_length) ground_contact_mask = np.convolve( ground_contact_mask, box_filter, mode='same') >= float( filter_length * 0.5) return ground_contact_mask susp_vel_fl = session_data[networking.Fields.susp_vel_fl.value] susp_vel_fr = session_data[networking.Fields.susp_vel_fr.value] susp_vel_rl = session_data[networking.Fields.susp_vel_rl.value] susp_vel_rr = session_data[networking.Fields.susp_vel_rr.value] susp_vel = [susp_vel_fl, susp_vel_fr, susp_vel_rl, susp_vel_rr] ground_contact_masks = [ get_ground_contact(susp, susp_vel_lim=100.0, variance_max=100.0, filter_length=6) for susp in susp_vel ] # boolean mask to 0.0 or 1.0, also take sum ground_contact = [gc.astype(np.float) for gc in ground_contact_masks] ground_contact_sum = [np.sum(np.array(ground_contact), axis=0)] ground_contact = ground_contact_sum + ground_contact ground_contact = [ gc + gci * 0.05 for gci, gc in enumerate(ground_contact) ] # small offset to avoid overlaps ground_contact_data = np.array(ground_contact) labels = [ 'sum contact', 'fl contact', 'fr contact', 'rl contact', 'rr contact' ] x_points = np.array([race_time] * len(ground_contact_data)) y_points = np.array(ground_contact_data) line_plot(ax, x_points=x_points, y_points=y_points, title='Ground contact over lap time', labels=labels, alpha=0.5, x_label='Lap time (s)', y_label='Ground contact', min_max_annotations=True)