예제 #1
0
def test_current_heading_is_within_threshold_returns_true():

    robot_status = RobotStatus((20, 20), 90)
    robot_status.target_heading = 90
    robot_status.heading = 89.95

    assert robot_status.heading_has_reached_target_heading_threshold() is True
예제 #2
0
    def set_game_map(self, game_map_data):

        robot_information = game_map_data.get("robot")
        if robot_information:
            self.logger.log(
                "Pathfinder - Assigning start position = {0} and orientation = {1}"
                .format(robot_information[0], robot_information[1]))
            self.robot_status = RobotStatus(robot_information[0],
                                            robot_information[1])
        else:
            self.robot_status = RobotStatus((20, 20), 90)

        table_corners_positions = game_map_data.get("table_corners")
        if table_corners_positions:
            self.logger.log(
                "Pathfinding - Assigning table corner positions: {0}".format(
                    table_corners_positions))
            self.figures.compute_positions(table_corners_positions[0],
                                           table_corners_positions[1],
                                           table_corners_positions[2],
                                           table_corners_positions[3])
        else:
            self.figures.compute_positions((0, 0), (0, 231), (112, 231),
                                           (112, 0))

        obstacles = game_map_data.get("obstacles")
        if obstacles:

            self.logger.log(
                "Pathfinder - Assigning obstacles: {0}".format(obstacles))

            self.graph = Graph()
            southeastern_x = int(table_corners_positions[0][0])
            southeastern_y = int(table_corners_positions[0][1])
            northwestern_x = int(table_corners_positions[2][0])
            northwestern_y = int(table_corners_positions[2][1])

            int_obstacles = []
            for obstacle in obstacles:
                x, y = obstacle[0]
                int_obstacles.append([(int(x), int(y)), obstacle[1]])
            self.graph.initialize_graph_matrix(
                (southeastern_x, southeastern_y),
                (northwestern_x, northwestern_y), int_obstacles)
        else:
            self.graph.initialize_graph_matrix((0, 0), (112, 231), [])

        drawing_zone_corners = game_map_data.get("drawing_zone")
        if drawing_zone_corners:
            self.logger.log(
                "Pathfinding - Assigning drawing zone corners: {0}".format(
                    drawing_zone_corners))
            self.game_map.set_drawing_zone_borders(
                game_map_data.get("drawing_zone"))
            self.game_map.set_antenna_search_points(table_corners_positions[3])
        else:
            self.game_map.set_drawing_zone_borders(
                ((26, 27), (26, 87), (86, 87), (86, 27)))
            self.game_map.set_antenna_search_points((112, 0))
예제 #3
0
def test_given_finishing_rotation_movement_then_rotation_status_is_reinitialized_and_new_robot_position_is_assigned(
):
    position = (48, 37)
    robot_status = RobotStatus((50, 50), 90)
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())

    servo_wheels_manager.finish_rotation_and_set_new_robot_position(
        position, robot_status)

    assert servo_wheels_manager.rotation_status == RotationStatus.ROTATING
    assert robot_status.get_position() == position
예제 #4
0
def test_given_start_heading_correction_for_rotation_then_rotation_status_is_updated_and_command_is_sent_to_wheels(
):
    heading = 75
    robot_status = RobotStatus((50, 50), 90)
    robot_status.target_heading = 79
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())
    wheels_controller = SimulatedWheelsController()

    servo_wheels_manager.rotating_start_heading_correction(
        heading, robot_status, wheels_controller)

    assert servo_wheels_manager.rotation_status == RotationStatus.CORRECTING_HEADING
    assert int(wheels_controller.last_degrees_of_rotation_given) == 4
예제 #5
0
def test_given_at_checkpoint_when_finishing_translation_then_translation_status_is_updated_and_no_new_vector_is_generated(
):
    position = (50.1, 54.9)
    robot_status = RobotStatus((45, 50), 90)
    robot_status.target_position = (50, 55)
    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = robot_status
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())

    path_status, new_vector = servo_wheels_manager.finish_translation_and_get_new_path_status_and_vector(
        position, pathfinder)

    assert servo_wheels_manager.translation_status == TranslationStatus.MOVING
    assert new_vector is None
    assert path_status == PathStatus.CHECKPOINT_REACHED
예제 #6
0
def test_given_start_of_position_correction_for_translation_change_translation_status_and_send_translation_vector_towards_current_target_node(
):
    position = (40, 50)
    robot_status = RobotStatus((30, 30), 90)
    robot_status.target_position = (60, 65)

    wheels_controller = SimulatedWheelsController()
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())

    servo_wheels_manager.translating_start_position_correction(
        position, robot_status, wheels_controller)

    vector_x, vector_y = robot_status.get_translation_vector()

    assert int(vector_x) == 20
    assert int(vector_y) == 15
    assert servo_wheels_manager.translation_status == TranslationStatus.CORRECTING_POSITION

    position = (50.1, 54.9)
    robot_status = RobotStatus((45, 50), 90)
    robot_status.target_position = (50, 55)
    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = robot_status
    pathfinder.nodes_queue_to_checkpoint.append((100, 150))
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())
예제 #7
0
def test_given_at_intermediary_node_when_finishing_translation_then_translation_status_is_updated_and_new_vector_towards_next_node_is_generated(
):
    position = (50.1, 54.9)
    robot_status = RobotStatus((45, 50), 90)
    robot_status.target_position = (50, 55)
    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = robot_status
    pathfinder.nodes_queue_to_checkpoint.append((100, 150))
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())

    path_status, new_vector = servo_wheels_manager.finish_translation_and_get_new_path_status_and_vector(
        position, pathfinder)
    new_vector_x, new_vector_y = new_vector

    assert servo_wheels_manager.translation_status == TranslationStatus.MOVING
    assert round(new_vector_x, 1) == 49.9
    assert round(new_vector_y, 1) == 95.1
    assert path_status == PathStatus.INTERMEDIATE_NODE_REACHED
예제 #8
0
def test_generate_new_vector_returns_correct_vector():

    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = RobotStatus((20, 20), 90)
    pathfinder.robot_status.target_position = (20, 20)
    real_robot_position = (30, 30)

    assert (pathfinder.robot_status.
            generate_new_translation_vector_towards_current_target(
                real_robot_position) == (-10, -10))
예제 #9
0
def test_given_start_of_heading_correction_for_translation_change_translation_status_and_send_rotation_vector_towards_standard_heading(
):
    heading = 80
    robot_status = RobotStatus((20, 20), 80)
    wheels_controller = SimulatedWheelsController()
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())

    servo_wheels_manager.translating_start_heading_correction(
        heading, robot_status, wheels_controller)

    assert int(wheels_controller.last_degrees_of_rotation_given) == 10
    assert servo_wheels_manager.translation_status == TranslationStatus.CORRECTING_HEADING
예제 #10
0
def test_if_not_close_enough_to_next_node_carry_on_to_next_node_without_changing_anything(
):

    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = RobotStatus((20, 20), 90)
    pathfinder.robot_status.target_position = (30, 30)
    pathfinder.nodes_queue_to_checkpoint.append((20, 40))

    path_status, new_vector = pathfinder.get_vector_to_next_node((25, 25))

    assert path_status == PathStatus.MOVING_TOWARDS_TARGET
    assert new_vector is None
    assert pathfinder.robot_status.target_position == (30, 30)
예제 #11
0
def test_if_close_enough_to_next_node_and_it_is_checkpoint_return_checkpoint_reached_status(
):

    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = RobotStatus((20, 20), 90)
    pathfinder.robot_status.target_position = (30, 30)
    pathfinder.nodes_queue_to_checkpoint.clear()

    path_status, new_vector = pathfinder.get_vector_to_next_node(
        (29.91, 29.91))

    assert path_status == PathStatus.CHECKPOINT_REACHED
    assert new_vector is None
    assert pathfinder.robot_status.target_position == (29.91, 29.91)
예제 #12
0
def test_if_close_enough_to_next_node_switch_to_new_vector_and_return_moving_to_checkpoint_status(
):

    pathfinder = Pathfinder(ExecutionLogger())
    pathfinder.robot_status = RobotStatus((20, 20), 90)
    pathfinder.robot_status.target_position = (30, 30)
    pathfinder.nodes_queue_to_checkpoint.append((20, 40))

    path_status, new_vector = pathfinder.get_vector_to_next_node(
        (29.91, 29.91))

    assert path_status == PathStatus.INTERMEDIATE_NODE_REACHED
    assert new_vector == (-9.91, 10.09)
    assert pathfinder.robot_status.target_position == (20, 40)
예제 #13
0
class Pathfinder():
    def __init__(self, logger):

        self.logger = logger

        self.game_map = GameMap()
        self.figures = FiguresInformation()
        self.robot_status = None

        self.graph = None

        self.nodes_queue_to_checkpoint = deque()  # in cm
        self.filtered_nodes_queue_to_checkpoint = deque()  # TODO: remove this

    def reinitialize(self):

        self.robot_status = None
        self.graph = None
        self.nodes_queue_to_checkpoint = deque()
        self.figures.compute_positions((0, 0), (0, 231), (112, 231), (112, 0))
        self.game_map = GameMap()

    def set_game_map(self, game_map_data):

        robot_information = game_map_data.get("robot")
        if robot_information:
            self.logger.log(
                "Pathfinder - Assigning start position = {0} and orientation = {1}"
                .format(robot_information[0], robot_information[1]))
            self.robot_status = RobotStatus(robot_information[0],
                                            robot_information[1])
        else:
            self.robot_status = RobotStatus((20, 20), 90)

        table_corners_positions = game_map_data.get("table_corners")
        if table_corners_positions:
            self.logger.log(
                "Pathfinding - Assigning table corner positions: {0}".format(
                    table_corners_positions))
            self.figures.compute_positions(table_corners_positions[0],
                                           table_corners_positions[1],
                                           table_corners_positions[2],
                                           table_corners_positions[3])
        else:
            self.figures.compute_positions((0, 0), (0, 231), (112, 231),
                                           (112, 0))

        obstacles = game_map_data.get("obstacles")
        if obstacles:

            self.logger.log(
                "Pathfinder - Assigning obstacles: {0}".format(obstacles))

            self.graph = Graph()
            southeastern_x = int(table_corners_positions[0][0])
            southeastern_y = int(table_corners_positions[0][1])
            northwestern_x = int(table_corners_positions[2][0])
            northwestern_y = int(table_corners_positions[2][1])

            int_obstacles = []
            for obstacle in obstacles:
                x, y = obstacle[0]
                int_obstacles.append([(int(x), int(y)), obstacle[1]])
            self.graph.initialize_graph_matrix(
                (southeastern_x, southeastern_y),
                (northwestern_x, northwestern_y), int_obstacles)
        else:
            self.graph.initialize_graph_matrix((0, 0), (112, 231), [])

        drawing_zone_corners = game_map_data.get("drawing_zone")
        if drawing_zone_corners:
            self.logger.log(
                "Pathfinding - Assigning drawing zone corners: {0}".format(
                    drawing_zone_corners))
            self.game_map.set_drawing_zone_borders(
                game_map_data.get("drawing_zone"))
            self.game_map.set_antenna_search_points(table_corners_positions[3])
        else:
            self.game_map.set_drawing_zone_borders(
                ((26, 27), (26, 87), (86, 87), (86, 27)))
            self.game_map.set_antenna_search_points((112, 0))

    def get_vector_to_next_node(self, current_robot_position=None):

        if current_robot_position:
            self.robot_status.set_position(current_robot_position)

        if self.robot_status.position_has_reached_target_position_within_threshold(
        ):
            if self.nodes_queue_to_checkpoint:
                new_vector = self.robot_status.generate_new_translation_vector_towards_new_target(
                    self.nodes_queue_to_checkpoint.popleft())
                return PathStatus.INTERMEDIATE_NODE_REACHED, new_vector
            else:
                self.robot_status.generate_new_translation_vector_towards_new_target(
                    self.robot_status.get_position())
                return PathStatus.CHECKPOINT_REACHED, None
        else:
            return PathStatus.MOVING_TOWARDS_TARGET, None

    def get_point_of_interest(self, point_of_interest):
        return self.game_map.get_point_of_interest(point_of_interest)

    def get_current_path(self):
        path = deque()
        for node in self.nodes_queue_to_checkpoint:
            path.append(node)

        path.appendleft(self.robot_status.target_position)
        path.appendleft(self.robot_status.get_position())

        return path

    def generate_path_to_checkpoint_a_to_b(self, checkpoint_position):

        self.nodes_queue_to_checkpoint.clear()
        self.nodes_queue_to_checkpoint.append(checkpoint_position)

        self.robot_status.generate_new_translation_vector_towards_new_target(
            self.nodes_queue_to_checkpoint.popleft())

    def generate_path_to_checkpoint(self, checkpoint_position):

        self.nodes_queue_to_checkpoint.clear()

        checkpoint_i, checkpoint_j = self.graph.get_grid_element_index_from_position(
            checkpoint_position)
        if self.graph.matrix[checkpoint_i][checkpoint_j] == math.inf:
            raise CheckpointNotAccessibleError(
                "This checkpoint is not accessible.")

        source_vertex = self.graph.get_grid_element_index_from_position(
            self.robot_status.get_position())
        current_vertex = source_vertex
        destination_vertex = self.graph.get_grid_element_index_from_position(
            checkpoint_position)

        visited_vertices = []
        unsolved_vertices = dict(
            ((i, j), math.inf) for j in range(self.graph.matrix_height)
            for i in range(self.graph.matrix_width)
            if self.graph.get_weight_of_element((i, j)) != math.inf)
        parent_of_vertices = defaultdict()

        # Initialize weight of source vertex to 0
        unsolved_vertices[current_vertex] = 0
        vertices_weights = dict(unsolved_vertices)

        # Generate vertices' parents dictionary
        while current_vertex != destination_vertex:
            current_vertex = min(unsolved_vertices, key=unsolved_vertices.get)
            unsolved_vertices.pop(current_vertex)
            visited_vertices.append(current_vertex)
            for neighbour in self.graph.get_four_neighbours_indexes_from_element_index(
                    current_vertex):
                if neighbour in unsolved_vertices:
                    new_weight = vertices_weights[
                        current_vertex] + self.graph.get_edge_distance(
                            current_vertex, neighbour)
                    if new_weight < vertices_weights[neighbour]:
                        vertices_weights[neighbour] = new_weight
                        unsolved_vertices[neighbour] = new_weight
                        parent_of_vertices[neighbour] = current_vertex

        if vertices_weights[destination_vertex] == math.inf:
            raise CheckpointNotAccessibleError(
                "This checkpoint is not accessible.")

        # Rebuild path
        while current_vertex != source_vertex:
            self.nodes_queue_to_checkpoint.appendleft(
                self.graph.get_position_from_grid_element_index(
                    *current_vertex))
            current_vertex = parent_of_vertices[current_vertex]

        # Remove superfluous nodes
        self.nodes_queue_to_checkpoint = self.filter_path(
            self.nodes_queue_to_checkpoint, PATH_FILTER_WIDTH)
        self.nodes_queue_to_checkpoint = self.filter_path(
            self.nodes_queue_to_checkpoint, 2)

        self.robot_status.generate_new_translation_vector_towards_new_target(
            self.nodes_queue_to_checkpoint.popleft())

    def is_checkpoint_accessible(self, checkpoint_position):
        checkpoint_i, checkpoint_j = self.graph.get_grid_element_index_from_position(
            checkpoint_position)
        return not self.graph.matrix[checkpoint_i][checkpoint_j] == math.inf

    def filter_path(self, nodes_queue, filter_width):
        points_of_discontinuity = deque([nodes_queue[0]])
        current_point_index = 0
        slope = self.compute_slope(nodes_queue, current_point_index,
                                   filter_width)

        while current_point_index < (len(nodes_queue) - filter_width):
            current_point_index += 1
            new_slope = self.compute_slope(nodes_queue, current_point_index,
                                           filter_width)

            if math.fabs(new_slope - slope) >= PATH_SLOPE_THRESHOLD:
                points_of_discontinuity.append(
                    nodes_queue[current_point_index + filter_width - 2])
                current_point_index = current_point_index + filter_width - 2
                slope = new_slope

        points_of_discontinuity.append(nodes_queue[-1])
        return points_of_discontinuity

    def compute_slope(self, nodes_queue, start_index, length_to_consider):
        try:
            dx = nodes_queue[start_index + length_to_consider -
                             1][0] - nodes_queue[start_index][0]
            dy = nodes_queue[start_index + length_to_consider -
                             1][1] - nodes_queue[start_index][1]
            slope = dy / dx
        except ZeroDivisionError:
            slope = math.inf
        return slope
예제 #14
0
""" Unit tests for servo wheel manager """
from design.interfacing.simulated_controllers import SimulatedWheelsController
from design.pathfinding.constants import TranslationStatus, RotationStatus
from design.pathfinding.pathfinder import Pathfinder, PathStatus
from design.pathfinding.robot_status import RobotStatus
from design.pathfinding.servo_wheels_manager import ServoWheelsManager
from design.utils.execution_logger import ExecutionLogger

heading = 80
robot_status = RobotStatus((20, 20), 80)
wheels_controller = SimulatedWheelsController()
servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())


def test_given_start_of_heading_correction_for_translation_change_translation_status_and_send_rotation_vector_towards_standard_heading(
):
    heading = 80
    robot_status = RobotStatus((20, 20), 80)
    wheels_controller = SimulatedWheelsController()
    servo_wheels_manager = ServoWheelsManager(None, None, ExecutionLogger())

    servo_wheels_manager.translating_start_heading_correction(
        heading, robot_status, wheels_controller)

    assert int(wheels_controller.last_degrees_of_rotation_given) == 10
    assert servo_wheels_manager.translation_status == TranslationStatus.CORRECTING_HEADING


def test_given_start_of_position_correction_for_translation_change_translation_status_and_send_translation_vector_towards_current_target_node(
):
    position = (40, 50)