def debug_rotation(ast_trial): """ Plot what the values look like when rotated, debugging tool """ try: if ast_trial.is_ast_trial: rotated_points = AsteriskCalculations.rotate_points( ast_trial.poses, AsteriskCalculations.rotations[ ast_trial.trial_translation]) else: rotated_points = ast_trial except: rotated_points = ast_trial # plot the points x = pd.Series.to_list(rotated_points["x"].dropna()) y = pd.Series.to_list(rotated_points["y"].dropna()) plt.plot(x, y, color="xkcd:dark orange") # plot the target line tar_x, tar_y = AsteriskPlotting.get_c(100) plt.plot(tar_x, tar_y, color="xkcd:dark blue") # show plot plt.show()
def generate_target_line(self, n_samples=100): """ Using object trajectory (self.poses), build a line to compare to for frechet distance. Updates this attribute on object. :param n_samples: number of samples for target line. Defaults to 100 """ x_vals, y_vals = aplt.get_direction(self.trial_translation, n_samples) target_line = np.column_stack((x_vals, y_vals)) # get last object pose and use it for determining how far target line should go last_obj_pose = self.poses.tail(1).to_numpy()[0] target_line_length = acalc.narrow_target(last_obj_pose, target_line) if target_line_length: distance_travelled = acalc.t_distance( [0, 0], target_line[target_line_length + 1]) final_target_ln = target_line[:target_line_length] else: distance_travelled = acalc.t_distance([0, 0], target_line[0]) final_target_ln = target_line[:1] # TODO: distance travelled has error because it is built of target line... maybe use last_obj_pose instead? return final_target_ln, distance_travelled
def calc_max_area_region(ast_trial, percent_window_size=0.2): """ Calculates the area of max error by sliding a window of 20% normalized length along the target line Seems that 10% is too small in regions of fast movement """ # TODO: cheating again by rotating points... what about negative values? points = AsteriskCalculations.rotate_points(ast_trial.poses, AsteriskCalculations.rotations[ast_trial.trial_translation]) t_x, t_y = AsteriskPlotting.get_c(100) # TODO: maybe make target_line a pandas dataframe targets = np.column_stack((t_x, t_y)) #AsteriskMetrics.debug_rotation(points) # prepare bound size bound_size = 0.5 * (percent_window_size * ast_trial.total_distance) x_center = bound_size + 0 # just being explicit here -> x_min is 0 x_max = 2 * bound_size max_area_calculated = 0 x_center_at_max = x_center while x_max <= ast_trial.total_distance: # print(f"Now at {x_center} || {x_max}/{ast_trial.total_distance}") bounded_points = AsteriskCalculations.get_points_df(points, x_center, bound_size) b_x = pd.Series.to_list(bounded_points["x"].dropna()) b_y = pd.Series.to_list(bounded_points["y"].dropna()) bounded_points_not_df = np.column_stack((b_x, b_y)) target_points = AsteriskCalculations.get_points_list(targets, x_center, bound_size) try: area_calculated = sm.area_between_two_curves(bounded_points_not_df, target_points) except ValueError or IndexError: # usually this triggers if there aren't enough points (more than one) in the window # if there aren't enough points, make enough points! try: area_calculated = AsteriskCalculations.interpolate_points(points, x_center, bounded_points, bound_size, target_points) print("Successful interpolation!") except Exception as e: print("Interpolation Failed.") print(e) # if not points were found at all in this region, depending on bound size area_calculated = 0 x_center = x_center + 0.1 * bound_size # want to step in 1% increments x_max = x_center + bound_size if np.abs(area_calculated) > max_area_calculated: max_area_calculated = np.abs(area_calculated) x_center_at_max = x_center # percentage along the target_line line that the center of max error was located x_center_perc = x_center_at_max / 0.5 # gives us percentage along the full target line, for easy comparing # x_center_at_max_r = AsteriskMetrics.rotate_point([x_center_at_max, 0], # -1 * AsteriskMetrics.rotations[ast_trial.trial_translation]) # print(f"results: {max_area_calculated}, {x_center_perc}") return max_area_calculated, x_center_perc
def plot_direction(self, translation, rotation="n", show_plot=True, save_plot=False): """ Plot the average path for each hand data object contained in this object for a specific translation rotation pair. """ colors = [ "tab:blue", "tab:purple", "tab:red", "tab:olive", "tab:cyan", "tab:green", "tab:pink", "tab:orange" ] # grab the average values from all hands avg_label = f"{translation}_{rotation}" averages_to_plot = [] hand_order = [] for h in self.hands: hand_order.append(h.hand.get_name()) for a in h.averages: a_label = f"{a.trial_translation}_{a.trial_rotation}" if a_label == avg_label: averages_to_plot.append(a) for i, a in enumerate(averages_to_plot): a_x, a_y, _ = a.get_poses() plt.plot(a_x, a_y, color=colors[i], label=hand_order[i]) # plot the straight line t_x, t_y = AsteriskPlotting.get_direction(translation) plt.plot(t_x, t_y, color="r", linestyle="dashed") if save_plot: # added the zero to guarantee that it comes first plt.savefig(f"pics/0all_{rotation}.jpg", format='jpg') # name -> tuple: subj, hand names print("Figure saved.") print(" ") if show_plot: plt.legend() plt.show() return plt
def plot_all_target_lines(self, order_of_colors): """ Plot all target lines on a plot for easy reference :param order_of_colors: """ x_a, y_a = aplt.get_a() x_b, y_b = aplt.get_b() x_c, y_c = aplt.get_c() x_d, y_d = aplt.get_d() x_e, y_e = aplt.get_e() x_f, y_f = aplt.get_f() x_g, y_g = aplt.get_g() x_h, y_h = aplt.get_h() ideal_xs = [x_a, x_b, x_c, x_d, x_e, x_f, x_g, x_h] ideal_ys = [y_a, y_b, y_c, y_d, y_e, y_f, y_g, y_h] for i in range(8): plt.plot(ideal_xs[i], ideal_ys[i], color=order_of_colors[i], label='ideal', linestyle='--')
def plot_orientation_errors(self, translation, subject=None, rotation="n", show_plot=True, save_plot=False): """ line plot of orientation error throughout a trial for a specific direction :param translation: the type of translation :param subject: list of subjects. If none is provided, uses all of them :param rotation: type of rotation. Defaults to "n" :param show_plot: flag to show plot. Default is true :param save_plot: flat to save plot as a file. Default is False """ if subject: trials = self._get_ast_dir(translation, subject, rotation) else: trials = self._get_ast_dir(translation, self.subjects_containing, rotation) # if self.averages and incl_avg: # TODO: have an option to include the average? # for a in self.averages: # if a.trial_translation==direction_label and a.trial_rotation==rotation_label: # trials.append(a) for t in trials: rot_err = t.calc_rot_err() # currently using the get_c function to generate a normalized set of x values to use as x values x, _ = aplt.get_c(len(rot_err)) # will need to multiply by 2 to get it to go to 1.0 instead of 0.5 plt.plot(2*x, rot_err, label=f"Orientation Err {t.subject}, trial {t.trial_num}") if save_plot: if subject: plt.savefig(f"pics/angerror_{self.hand.get_name()}_{subject}_{translation}_{rotation}.jpg", format='jpg') else: plt.savefig(f"pics/angerror_{self.hand.get_name()}_all_{translation}_{rotation}.jpg", format='jpg') # name -> tuple: subj, hand names print("Figure saved.") print(" ") if show_plot: plt.legend() plt.show()
def plot_trial(self, use_filtered=True, show_plot=True, save_plot=False): """ Plot the poses in the trial, using marker size to denote the error in twist from the desired twist :param use_filtered: Gives option to return filtered or unfiltered data :param show_plot: flag to show plot. Default is true :param save_plot: flat to save plot as a file. Default is False """ data_x, data_y, theta = self.get_poses(use_filtered) # experimenting... # junk = 70 # data_x = data_x[0:(len(data_x)-junk)] # data_y = data_y[0:(len(data_y)-junk)] plt.plot(data_x, data_y, color="xkcd:dark blue", label='trajectory') # plot data points separately to show angle error with marker size for n in range(len(data_x)): # TODO: rn having difficulty doing marker size in a batch, so plotting each point separately plt.plot(data_x[n], data_y[n], 'r.', alpha=0.5, markersize=5 * theta[n]) target_x, target_y = [], [] for t in self.target_line: target_x.append(t[0]) target_y.append(t[1]) #target_x, target_y = aplt.get_direction(self.trial_translation) plt.plot(target_x, target_y, color="xkcd:pastel blue", label="target_line", linestyle="-") max_x = max(data_x) max_y = max(data_y) min_x = min(data_x) min_y = min(data_y) plt.xlabel('X') plt.ylabel('Y') plt.title('Path of Object') # gives a realistic view of what the path looks like plt.xticks(np.linspace(aplt.round_half_down(min_x, decimals=2), aplt.round_half_up(max_x, decimals=2), 10), rotation=30) if self.trial_translation in ["a", "b", "c", "g", "h"]: plt.yticks( np.linspace(0, aplt.round_half_up(max_y, decimals=2), 10)) else: plt.yticks( np.linspace(aplt.round_half_down(min_y, decimals=2), 0, 10)) # experimenting... # plt.xticks(np.linspace(-10, # 80, 10), rotation=30) # plt.yticks(np.linspace(-10, 80, 10)) # plt.gca().set_aspect('equal', adjustable='box') plt.title(f"Plot: {self.generate_name()}") if save_plot: plt.savefig(f"pics/plot_{self.generate_name()}.jpg", format='jpg') # name -> tuple: subj, hand names print("Figure saved.") print(" ") if show_plot: plt.legend() plt.show()
def check_no_backtracking(data, translation_label, threshold=5, debug_rotation=False): """ True if no (or little) backtracking, False is there is parameters ---------- - data - dataframe, not normalized yet - threshold - how much backtracking to allow, default is 10 mm returns ------- - decision - (True/False that there's no significant backtracking) - (the most backtracking, threshold) - provided for debugging, when True provides the highest cumulated backtrack TODO: test this one more thoroughly! """ # convert to mm data = data * [1., 1., 1., 1000., 1000., 1000., 1., 1000.] data = data.round(4) # rotate everything to c direction rotated_df = AsteriskCalculations.rotate_points( data, AsteriskCalculations.rotations[translation_label]) if debug_rotation: # plot the points x = pd.Series.to_list(rotated_df["x"].dropna()) y = pd.Series.to_list(rotated_df["y"].dropna()) plt.plot(x, y, color="xkcd:dark orange") # plot the target line tar_x, tar_y = AsteriskPlotting.get_c(100) plt.plot(tar_x * 100, tar_y * 100, color="xkcd:dark blue") # show plot plt.show() # calculate the delta x between each point dxs = [] # hold the delta x vals prev_x = 0 for p in rotated_df.iterrows(): x = p[1]['x'] dx = x - prev_x dxs.append(dx) prev_x = x rotated_df["dx"] = dxs # for those in the wrong direction, see how much accumulates and how far they get c_val = 0 # variable which stores cumulative values max_c_val = 0 for p in rotated_df.iterrows(): if p[1]["dx"] < 0: c_val = c_val + p[1]["dx"] if c_val > threshold: return False, (c_val, threshold) if abs(c_val) > abs(max_c_val): max_c_val = c_val else: c_val = 0 print(f"c:{c_val} for {p[1]['x']}, {p[1]['dx']}") # if we went through the entire path and found no significant backtracking, then True! return True, (c_val, threshold)