diff --git a/workflows/ci.yml b/.github/workflows/ci.yml similarity index 100% rename from workflows/ci.yml rename to .github/workflows/ci.yml diff --git a/workflows/codebase-diagram.yml b/.github/workflows/codebase-diagram.yml similarity index 100% rename from workflows/codebase-diagram.yml rename to .github/workflows/codebase-diagram.yml diff --git a/workflows/docs.yml b/.github/workflows/docs.yml similarity index 100% rename from workflows/docs.yml rename to .github/workflows/docs.yml diff --git a/workflows/test.yml b/.github/workflows/test.yml similarity index 100% rename from workflows/test.yml rename to .github/workflows/test.yml diff --git a/README.md b/README.md index a71cdd0..6083e4d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -# Path Planner - -![Visualization of the codebase](./codebase-diagram.svg) +# Speed Profiler ## Features -plans the path :) \ No newline at end of file diff --git a/scripts/conegenerator.py b/scripts/conegenerator.py deleted file mode 100644 index 89253c1..0000000 --- a/scripts/conegenerator.py +++ /dev/null @@ -1,66 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -# The arc is a semi-circle, from pi (180 degrees) to 0 -theta = np.linspace(np.pi, 0, int(2 * np.pi * 3)) # Assuming there's a point every 1.5m in the outer arc with radius 6m -# Define the radius of outer and inner semi-circles - -curve_radius = 6 -track_width = 5 - -r_outer = curve_radius + track_width/2 # radius of outer semi-circle -r_inner = curve_radius - track_width/2 # radius of inner semi-circle -# Compute x, y coordinates for points along the outer and inner semi-circles - - -y_outer_arc = r_outer * np.cos(theta) + r_outer - (r_outer-r_inner)/2 -x_outer_arc = r_outer * np.sin(theta) + 20 -y_inner_arc = r_inner * np.cos(theta) + r_outer - (r_outer-r_inner)/2 # Offset by r_outer to align with outer arc -x_inner_arc = r_inner * np.sin(theta) + 20 - -# Compute the straight line sections (assuming they are 10m long) -x_straight = np.linspace(2, 19, int(15/1)) # Points every 2m for the straight line -x_straight_exit = np.linspace(2, 19, int(15/1)) # Exit straight line -y_outer_straight = np.full_like(x_straight, - (r_outer-r_inner)/2) # Outer straight line is at x = 0 -y_inner_straight = np.full_like(x_straight, + (r_outer-r_inner)/2) # Inner straight line is at x = 3 -y_outer_straight_exit = np.full_like(x_straight_exit, 2*r_outer - (r_outer-r_inner)/2) # Outer exit straight line is at x = 2*r_outer -y_inner_straight_exit = np.full_like(x_straight_exit, 2*r_outer - (r_outer-r_inner)/2 - (r_outer-r_inner)) # Inner exit straight line is at x = 2*r_outer - 3 - -x_orange = [1,1,1,1] -y_orange = [track_width/2, - track_width/2, 2*curve_radius + track_width/2, 2*curve_radius - track_width/2] - -# Concatenate straight line, arc sections and exit straight line -x_outer = np.concatenate((x_straight, x_outer_arc, x_straight_exit)) -y_outer = np.concatenate((y_outer_straight, y_outer_arc, y_outer_straight_exit)) -x_inner = np.concatenate((x_straight, x_inner_arc, x_straight_exit)) -y_inner = np.concatenate((y_inner_straight, y_inner_arc, y_inner_straight_exit)) - -# Create the plot -plt.figure(figsize=(10, 10)) -plt.plot(x_outer, y_outer, 'o', label='Outer Path') -plt.plot(x_inner, y_inner, 'o', label='Inner Path') -plt.plot(x_orange, y_orange, 'o', label='Orange Cone') - -s = "" -for i in range(len(x_inner)): - s += "yellow,%f,%f,0,0,0,0\n" % (x_inner[i],y_inner[i]) - -for i in range(len(x_outer)): - s += "blue,%f,%f,0,0,0,0\n" % (x_outer[i],y_outer[i]) - -for i in range(len(x_orange)): - s += "big_orange,%f,%f,0,0,0,0\n" % (x_orange[i],y_orange[i]) - - -f = open("map.csv", 'w') -f.write(s) -f.close() - -print(s) - -plt.legend() -plt.title('U-turn Track with Entry and Exit Straight Sections') -plt.xlabel('x (m)') -plt.ylabel('y (m)') -plt.grid(True) -plt.axis('equal') # Ensure the x and y axis scales are equal -plt.show() \ No newline at end of file diff --git a/scripts/map.csv b/scripts/map.csv deleted file mode 100644 index 7182099..0000000 --- a/scripts/map.csv +++ /dev/null @@ -1,100 +0,0 @@ -yellow,2.000000,2.500000,0,0,0,0 -yellow,3.214286,2.500000,0,0,0,0 -yellow,4.428571,2.500000,0,0,0,0 -yellow,5.642857,2.500000,0,0,0,0 -yellow,6.857143,2.500000,0,0,0,0 -yellow,8.071429,2.500000,0,0,0,0 -yellow,9.285714,2.500000,0,0,0,0 -yellow,10.500000,2.500000,0,0,0,0 -yellow,11.714286,2.500000,0,0,0,0 -yellow,12.928571,2.500000,0,0,0,0 -yellow,14.142857,2.500000,0,0,0,0 -yellow,15.357143,2.500000,0,0,0,0 -yellow,16.571429,2.500000,0,0,0,0 -yellow,17.785714,2.500000,0,0,0,0 -yellow,19.000000,2.500000,0,0,0,0 -yellow,20.000000,2.500000,0,0,0,0 -yellow,20.643123,2.559594,0,0,0,0 -yellow,21.264346,2.736347,0,0,0,0 -yellow,21.842513,3.024240,0,0,0,0 -yellow,22.357935,3.413469,0,0,0,0 -yellow,22.793060,3.890779,0,0,0,0 -yellow,23.133072,4.439916,0,0,0,0 -yellow,23.366390,5.042180,0,0,0,0 -yellow,23.485070,5.677061,0,0,0,0 -yellow,23.485070,6.322939,0,0,0,0 -yellow,23.366390,6.957820,0,0,0,0 -yellow,23.133072,7.560084,0,0,0,0 -yellow,22.793060,8.109221,0,0,0,0 -yellow,22.357935,8.586531,0,0,0,0 -yellow,21.842513,8.975760,0,0,0,0 -yellow,21.264346,9.263653,0,0,0,0 -yellow,20.643123,9.440406,0,0,0,0 -yellow,20.000000,9.500000,0,0,0,0 -yellow,2.000000,9.500000,0,0,0,0 -yellow,3.214286,9.500000,0,0,0,0 -yellow,4.428571,9.500000,0,0,0,0 -yellow,5.642857,9.500000,0,0,0,0 -yellow,6.857143,9.500000,0,0,0,0 -yellow,8.071429,9.500000,0,0,0,0 -yellow,9.285714,9.500000,0,0,0,0 -yellow,10.500000,9.500000,0,0,0,0 -yellow,11.714286,9.500000,0,0,0,0 -yellow,12.928571,9.500000,0,0,0,0 -yellow,14.142857,9.500000,0,0,0,0 -yellow,15.357143,9.500000,0,0,0,0 -yellow,16.571429,9.500000,0,0,0,0 -yellow,17.785714,9.500000,0,0,0,0 -yellow,19.000000,9.500000,0,0,0,0 -blue,2.000000,-2.500000,0,0,0,0 -blue,3.214286,-2.500000,0,0,0,0 -blue,4.428571,-2.500000,0,0,0,0 -blue,5.642857,-2.500000,0,0,0,0 -blue,6.857143,-2.500000,0,0,0,0 -blue,8.071429,-2.500000,0,0,0,0 -blue,9.285714,-2.500000,0,0,0,0 -blue,10.500000,-2.500000,0,0,0,0 -blue,11.714286,-2.500000,0,0,0,0 -blue,12.928571,-2.500000,0,0,0,0 -blue,14.142857,-2.500000,0,0,0,0 -blue,15.357143,-2.500000,0,0,0,0 -blue,16.571429,-2.500000,0,0,0,0 -blue,17.785714,-2.500000,0,0,0,0 -blue,19.000000,-2.500000,0,0,0,0 -blue,20.000000,-2.500000,0,0,0,0 -blue,21.561871,-2.355271,0,0,0,0 -blue,23.070554,-1.926014,0,0,0,0 -blue,24.474673,-1.226846,0,0,0,0 -blue,25.726413,-0.281576,0,0,0,0 -blue,26.783146,0.877606,0,0,0,0 -blue,27.608888,2.211224,0,0,0,0 -blue,28.175518,3.673865,0,0,0,0 -blue,28.463740,5.215719,0,0,0,0 -blue,28.463740,6.784281,0,0,0,0 -blue,28.175518,8.326135,0,0,0,0 -blue,27.608888,9.788776,0,0,0,0 -blue,26.783146,11.122394,0,0,0,0 -blue,25.726413,12.281576,0,0,0,0 -blue,24.474673,13.226846,0,0,0,0 -blue,23.070554,13.926014,0,0,0,0 -blue,21.561871,14.355271,0,0,0,0 -blue,20.000000,14.500000,0,0,0,0 -blue,2.000000,14.500000,0,0,0,0 -blue,3.214286,14.500000,0,0,0,0 -blue,4.428571,14.500000,0,0,0,0 -blue,5.642857,14.500000,0,0,0,0 -blue,6.857143,14.500000,0,0,0,0 -blue,8.071429,14.500000,0,0,0,0 -blue,9.285714,14.500000,0,0,0,0 -blue,10.500000,14.500000,0,0,0,0 -blue,11.714286,14.500000,0,0,0,0 -blue,12.928571,14.500000,0,0,0,0 -blue,14.142857,14.500000,0,0,0,0 -blue,15.357143,14.500000,0,0,0,0 -blue,16.571429,14.500000,0,0,0,0 -blue,17.785714,14.500000,0,0,0,0 -blue,19.000000,14.500000,0,0,0,0 -big_orange,1.000000,2.500000,0,0,0,0 -big_orange,1.000000,-2.500000,0,0,0,0 -big_orange,1.000000,14.500000,0,0,0,0 -big_orange,1.000000,9.500000,0,0,0,0 diff --git a/tests/test_interpolate.py b/tests/test_interpolate.py deleted file mode 100644 index 9af81c4..0000000 --- a/tests/test_interpolate.py +++ /dev/null @@ -1,52 +0,0 @@ -import numpy as np - -from speed_profiler.interpolate import interpolate_with_least_squares - - -def test_interpolate_with_least_squares(planner_parameters): - points_per_waypoint = planner_parameters['points_per_waypoint'] - path_gen_margin = planner_parameters['path_gen_margin'] - total_waypoints = 10 - - total_points_in_path = total_waypoints * points_per_waypoint - expected_positive = np.linspace( - 1 - path_gen_margin, - (total_waypoints - 1) + path_gen_margin, - total_points_in_path - ) - expected_negative = np.negative(expected_positive) - expected_zero = [0] * total_points_in_path - - test_cases = [ - { - 'initial_vehicle_position': np.array([0, 0]), - 'waypoints': np.array([[x, 0] for x in range(total_waypoints)]), - 'expected_path': np.column_stack((expected_positive, expected_zero)) - }, - { - 'initial_vehicle_position': np.array([0, 0]), - 'waypoints': np.array([[x, 0] for x in range(0, -total_waypoints, -1)]), - 'expected_path': np.column_stack((expected_negative, expected_zero)) - }, - { - 'initial_vehicle_position': np.array([0, 0]), - 'waypoints': np.array([[0, y] for y in range(total_waypoints)]), - 'expected_path': np.column_stack((expected_zero, expected_positive)) - }, - { - 'initial_vehicle_position': np.array([0, 0]), - 'waypoints': np.array([[0, y] for y in range(0, -total_waypoints, -1)]), - 'expected_path': np.column_stack((expected_zero, expected_negative)) - }, - ] - - for test_case in test_cases: - initial_vehicle_position = test_case['initial_vehicle_position'] - waypoints = test_case['waypoints'] - expected_path = test_case['expected_path'] - path = interpolate_with_least_squares( - waypoints, initial_vehicle_position, points_per_waypoint, path_gen_margin - ) - - assert len(path) == len(expected_path) - assert np.linalg.norm(path - expected_path) < 0.1 diff --git a/tests/test_planner.py b/tests/test_planner.py deleted file mode 100644 index 5474295..0000000 --- a/tests/test_planner.py +++ /dev/null @@ -1,170 +0,0 @@ -import sys - -import numpy as np -from numpy import random -import pytest - -from speed_profiler.planner import Planner -from speed_profiler.utils import calculate_min_distance_to_point - - -@pytest.fixture -def planner(planner_parameters): - return Planner(planner_parameters) - - -@pytest.fixture -def linear_track(planner_parameters): - initial_vehicle_pose = np.array([0, 0, 0]) - - distance_between_waypoints = 3 - total_waypoints = 10 - total_points_in_path = (total_waypoints + 1) * planner_parameters['points_per_waypoint'] - - x_coordinates = range( - distance_between_waypoints, - total_waypoints * distance_between_waypoints, - distance_between_waypoints - ) - - orange_cones = [np.array([0, 1]), np.array([0, -1])] - blue_cones = [np.array([x, -1]) for x in x_coordinates] - yellow_cones = [np.array([x, 1]) for x in x_coordinates] - - waypoints = compute_waypoints_as_cone_averages(orange_cones, blue_cones, yellow_cones) - - path_x = np.linspace( - initial_vehicle_pose[0] - planner_parameters['path_gen_margin'], - waypoints[-1][0] + planner_parameters['path_gen_margin'], - total_points_in_path - ) - path_y = [0] * total_points_in_path - - path = np.column_stack((path_x, path_y)) - - return { - 'orange_cones': orange_cones, - 'blue_cones': blue_cones, - 'yellow_cones': yellow_cones, - 'waypoints': waypoints, - 'path': path, - 'initial_vehicle_pose': initial_vehicle_pose, - 'track_name': 'acceleration' - } - - -@pytest.fixture -def circular_track_open(planner_parameters): - return create_circular_track(planner_parameters, open_track=True, max_angle_degrees=300) - - -@pytest.fixture -def circular_track_closed(planner_parameters): - return create_circular_track(planner_parameters) - - -@pytest.mark.parametrize('track', ['linear_track', 'circular_track_open', 'circular_track_closed']) -def test_generate_waypoints(planner, track, request): - track = request.getfixturevalue(track) - expected_waypoints = track['waypoints'] - - waypoints = planner._generate_waypoints( - track['orange_cones'], track['blue_cones'], track['yellow_cones'] - ) - - assert len(waypoints) == 2 * len(expected_waypoints) - - for waypoint in waypoints: - assert calculate_min_distance_to_point(waypoint, expected_waypoints) < sys.float_info.epsilon - - -@pytest.mark.parametrize('track', ['linear_track', 'circular_track_open', 'circular_track_closed']) -def test_filter_waypoints(planner, track, request): - track = request.getfixturevalue(track) - expected_waypoints = track['waypoints'] - - # Random deviation [..., [xk, yk], ...] , where xk and yk are between -0.5 and 0.5 - random_deviation = random.random_sample((len(expected_waypoints), 2)) - 0.5 - - unfiltered_waypoints = np.vstack((expected_waypoints, expected_waypoints + random_deviation)) - filtered_waypoints = planner._filter_waypoints(unfiltered_waypoints) - - assert len(filtered_waypoints) == len(expected_waypoints) - assert np.linalg.norm(np.array(filtered_waypoints) - np.array(expected_waypoints)) < sys.float_info.epsilon - - -@pytest.mark.parametrize('track', ['linear_track', 'circular_track_open', 'circular_track_closed']) -def test_construct_path(planner, track, request): - track = request.getfixturevalue(track) - expected_path = track['path'] - - planner.initial_vehicle_pose = track['initial_vehicle_pose'] - planner.track_name = track['track_name'] - path, _ = planner.construct_path( - track['orange_cones'], track['blue_cones'], track['yellow_cones'] - ) - - path_xy = path[:, 0:2] - assert len(path_xy) == len(expected_path) - assert np.linalg.norm(path_xy - expected_path) < 0.1 - - -def create_circular_track(planner_parameters, open_track=False, max_angle_degrees=None): - inner_radius = 14.0 - outer_radius = 18.0 - center_radius = (inner_radius + outer_radius) / 2 - - step = 10 - if open_track: - max_interpolated_angle_degrees = max_angle_degrees - step - else: - max_angle_degrees = 360 - max_interpolated_angle_degrees = 360 - - angles = [np.deg2rad(theta) for theta in range(10, max_angle_degrees, step)] - - orange_cones = [np.array([inner_radius, 0]), np.array([outer_radius, 0])] - blue_cones = [ - np.array([outer_radius * np.cos(theta), outer_radius * np.sin(theta)]) - for theta in angles - ] - yellow_cones = [ - np.array([inner_radius * np.cos(theta), inner_radius * np.sin(theta)]) - for theta in angles - ] - - waypoints = compute_waypoints_as_cone_averages(orange_cones, blue_cones, yellow_cones) - - total_interpolated_angles = ( - len(waypoints) * planner_parameters['points_per_waypoint'] - if open_track - else (len(waypoints) + 1) * planner_parameters['points_per_waypoint'] - ) - - interpolated_angles = np.deg2rad( - np.linspace(0, max_interpolated_angle_degrees, total_interpolated_angles) - ) - path = np.array([ - [center_radius * np.cos(theta), center_radius * np.sin(theta)] - for theta in interpolated_angles - ]) - - return { - 'orange_cones': orange_cones, - 'blue_cones': blue_cones, - 'yellow_cones': yellow_cones, - 'waypoints': waypoints, - 'path': path, - 'initial_vehicle_pose': np.array([center_radius, 0, np.pi/2]), - 'track_name': 'trackdrive' - } - - -def compute_waypoints_as_cone_averages(orange_cones, blue_cones, yellow_cones): - yellow_blue_waypoints = [ - (yellow_cone + blue_cone) / 2 - for yellow_cone, blue_cone in zip(yellow_cones, blue_cones) - ] - orange_waypoint = (orange_cones[0] + orange_cones[1]) / 2 - - return [orange_waypoint] + yellow_blue_waypoints