예제 #1
0
def plot_path(start_angles, end_angles, iterations, obstacles):
    """Plots a path between two given angle configurations."""
    start_node = Node(start_angles)
    end_node = Node(end_angles)

    if (not valid_path_configuration(start_node, obstacles)) or (
            not valid_path_configuration(end_node, obstacles)):
        raise Exception("Invalid configuration")

    pos = end_node.end_effector_pos
    path, _ = linear_rrt_to_point(start_node.angles, pos[0], pos[1], pos[2],
                                  obstacles, iterations)

    g = Graph(start_node.angles, end_node.angles)

    if path is not None:
        g.add_vex(path[0], g.start_node)
        for i in range(len(path)):
            try:
                g.add_vex(path[i], path[i - 1])
            except IndexError:
                pass
        arm_plot.plot_3d(g, path, obstacles)
    else:
        print("No Path Found :(")
예제 #2
0
def linear_rrt_to_point(start_angles,
                        end_x,
                        end_y,
                        end_z,
                        obstacles,
                        num_iter=15):
    """Generates a linear path to a desired end effector position, and maneuvers around obstacles with RRT if necessary.

    Args:
        start_angles: The initial angles of the arm.
        end_x, end_y, end_z: The desired end effector position of the arm.
        num_iter: The number of angle configurations to be generated on the path in between the start and end positions.
        obstacles: An array of float arrays representing cube obstacles.
        degrees: Whether to convert to and from degrees for motor output.

    Returns:
        An array of Node instances or float arrays representing a valid path between the start and end configurations
    """
    end_angles = Node.from_point((end_x, end_y, end_z)).angles
    g = generate_linear_path(start_angles, end_angles, num_iter)

    linear_path = g[0].nodes
    if path_is_colliding(linear_path, obstacles):
        print("Finding OPC path")
        g = opc.find_path((end_x, end_y, end_z), start_angles, obstacles)
        if g.success:
            linear_path = rrt.dijkstra(g)
        else:
            linear_path = None

    if linear_path is None:
        return linear_path, False

    return linear_path, True
예제 #3
0
def compute_step_sizes(start_angles, end_angles, num_iter):
    """Computes each arm angle's step size based on how long it needs to travel to go from the start to end pose.

    Depending on whether the anticipated path goes through the no-go zone, switches that path to go the other way
    around.

    Args:
        start_angles: The initial angles of the arm.
        end_angles: The desired angles of the arm.
        num_iter: The number of angle configurations to be generated on the path in between the start and end positions.
        arm: An instance of precision_arm.PrecisionArm, containing the bounds of each arm angle.

    Returns:
        A array containing the step sizes of each angle of the arm.
    """

    # Strategy for bounding angles: split into two quadrants: (1) less than the first bound,
    #                                                         (2) greater than the second bound.
    # If both are in the same quadrant, use the closest angle distance as usual.
    # If start_angles[i] in (1) and end_angles[i] in (2) and angle distance > 0, we must go the other way.
    # If start_angles[i] in (2) and end_angles[i] in (1) and angle distance < 0, we must go the other way.
    step_sizes = []
    true_angle_distances = true_angle_distances_arm(start_angles, end_angles)

    bounds = Node(None).bounds
    for i in range(len(start_angles)):
        if i >= len(bounds) or bounds[i] is None:
            angle_distance = end_angles[i] - start_angles[i]
            step_size = angle_distance / num_iter
            if angle_distance > math.pi:
                new_angle_dist = (2 * math.pi - end_angles[i] +
                                  start_angles[i])
                step_size = -new_angle_dist / num_iter
            elif angle_distance < -math.pi:
                new_angle_dist = (2 * math.pi - start_angles[i] +
                                  end_angles[i])
                step_size = new_angle_dist / num_iter
        else:
            bound_1 = bounds[i][0]
            bound_2 = bounds[i][1]
            th_1 = start_angles[i]
            th_2 = end_angles[i]
            angle_dist = true_angle_distances[i]

            if th_1 < bound_1 and th_2 > bound_2:
                if true_angle_distances[i] > 0:
                    angle_dist = -(2 * math.pi - true_angle_distances[i])

            if th_2 < bound_1 and th_1 > bound_2:
                if true_angle_distances[i] < 0:
                    angle_dist = 2 * math.pi + true_angle_distances[i]

            step_size = angle_dist / num_iter

        step_sizes.append(step_size)
    return step_sizes
예제 #4
0
    def __init__(self, start_angles, end_angles, target_end_pos=None):
        self.start_node = Node(start_angles)
        if end_angles is not None:
            self.end_node = Node(end_angles)

        if target_end_pos is not None:
            self.target_end_pos = target_end_pos
        else:
            self.target_end_pos = self.end_node.end_effector_pos

        self.edges = []
        self.success = False

        self.node_to_index = {self.start_node: 0}
        self.neighbors = {0: []}
        self.distances = {0: 0.}
        self.ranking = []
        self.nodes = [self.start_node]
        self.end_effectors = np.array([self.start_node.end_effector_pos])
예제 #5
0
    def add_vex(self, node, parent):
        try:
            idx = self.node_to_index[node]
        except KeyError:
            parent_idx = self.node_to_index[parent]
            idx = len(self.nodes)

            dist = parent.optimistic_cost + Node.distance(parent, node)
            node.optimistic_cost = dist

            self.neighbors[idx] = []

            self.add_edge(idx, parent_idx, Node.distance(parent, node))

            self.nodes.append(node)
            self.node_to_index[node] = idx
            self.ranking.append(node)
            self.ranking.sort(key=lambda n: dist + line.distance(
                n.end_effector_pos, self.target_end_pos))
            self.end_effectors = np.vstack(
                [self.end_effectors, node.end_effector_pos])
        return idx
예제 #6
0
def linear_rrt_test(num_trials, obstacles, iter_per_path=10):
    """Runs num_trials of a linear rrt pathing approach.

    Success is defined as converging to the desired end position, and any intermediate RRT converging to the desired end
    position.
    """

    start_time = time.time()
    s_count = 0
    for i in range(num_trials):
        print("Trial", i)
        start_node = Node(configuration=None)
        end_node = Node(configuration=None)

        while not valid_path_configuration(start_node, obstacles):
            start_node = Node(None)

        while not valid_path_configuration(end_node, obstacles):
            end_node = Node(None)

        if cd.arm_is_colliding_prisms(end_node, obstacles):
            raise Exception("Approved a colliding node")

        _, success = linear_rrt_to_point(start_node.angles,
                                         end_node.end_effector_pos, obstacles,
                                         iter_per_path)

        if success:
            s_count = s_count + 1
        else:
            print("start_pos =", format(start_node.angles))
            print("end_pos =", format(end_node.angles))

    print("success rate in {t} trials: {r}".format(t=num_trials,
                                                   r=(s_count / num_trials) *
                                                   100))
    print("average time per trial: {}".format(
        (time.time() - start_time) / num_trials))
예제 #7
0
def find_path(target_end_point,
              start_angles,
              obs,
              n_iter=150,
              radius=.01,
              step_size=.1):
    """Executes the Optimistic Predictive Cost algorithm to find a collision-free path between two configurations.
    Arguments:
        end_node: A Node representing the target arm configuration.
        start_angles: A float array representing the initial angles of the arm.
        obs: An array of float arrays representing obstacles.
        n_iter: The maximum amount of iterations to find a path.
        radius: The maximum distance from the target end effector position needed to converge.
        step_size: The distance, in radians, between each edge in the graph.
    Returns:
        A graph g containing a collision-free path if success = True, or no path if success = False.
      """
    g = Graph(start_angles, None, target_end_point)
    g.ranking.append(g.start_node)
    g.end_node = Node.from_point(target_end_point)

    close_to_end = False

    for i in range(n_iter):
        try:
            best_node = g.ranking.pop(0)
        except IndexError:
            global stack_empty_error
            stack_empty_error += 1
            return g

        if arm_is_colliding_prisms(best_node, obs):
            continue

        dist_to_goal = line.distance(best_node.end_effector_pos,
                                     target_end_point)
        if dist_to_goal < radius:
            g.end_node = best_node
            g.success = True
            return g

        # if dist_to_goal < 10 * radius and not close_to_end:
        #     step_size /= 10
        #     close_to_end = True

        expand(best_node, 4, step_size, g)

    return g
예제 #8
0
def random_start_environment(num_obstacles,
                             bounds,
                             obstacle_bounds,
                             obstacle_size=.2):
    """Generates a start environment for a run of RRT.

     Returns:
         A Node representing a valid start configuration.
         A Node representing a valid end configuration.
         A set of [num_obstacles] obstacles that do not collide with the start or end configurations.
    """

    random_start_node = Node(configuration=None)
    target_end_pos = [
        random.uniform(bounds[0][0], bounds[0][1]),
        random.uniform(bounds[1][0], bounds[1][1]),
        random.uniform(bounds[2][0], bounds[2][1])
    ]

    random_end_node = Node.from_point(target_end_pos, random_start_node.angles)

    while (not random_end_node.valid_configuration()):
        target_end_pos = [
            random.uniform(bounds[0][0], bounds[0][1]),
            random.uniform(bounds[1][0], bounds[1][1]),
            random.uniform(bounds[2][0], bounds[2][1])
        ]
        random_end_node = Node.from_point(target_end_pos, random_start_node.angles)\

    current_obstacles = generate_random_obstacles(
        num_obstacles, obstacle_bounds, max_side_length=obstacle_size)
    while arm_is_colliding_prisms(random_end_node, current_obstacles):
        current_obstacles = generate_random_obstacles(
            num_obstacles, obstacle_bounds, max_side_length=obstacle_size)

    while arm_is_colliding_prisms(
            random_start_node,
            current_obstacles) or not random_start_node.valid_configuration():
        random_start_node = Node(None)

    print("start angles:", random_start_node.angles)
    print("end angles:", random_end_node.angles)
    print("obstacles:", current_obstacles)

    return random_start_node, random_end_node, current_obstacles, target_end_pos
예제 #9
0
def random_start_environment(num_obstacles, bounds, obstacle_size=.2):
    """Generates a start environment for a run of RRT.

     Returns:
         An Node representing a valid start configuration.
         An Node representing a valid end configuration.
         A set of [num_obstacles] obstacles that do not collide with the start or end configurations.
    """

    random_start_node = Node(configuration=None)
    random_end_node = Node.from_point([
        random.uniform(bounds[0][0], bounds[0][1]),
        random.uniform(bounds[1][0], bounds[1][1]),
        random.uniform(bounds[2][0], bounds[2][1])
    ], random_start_node.angles)

    max_tries = 10
    tries = 1
    while not random_end_node.valid_configuration():
        random_end_node = Node.from_point([
            random.uniform(bounds[0][0], bounds[0][1]),
            random.uniform(bounds[1][0], bounds[1][1]),
            random.uniform(bounds[2][0], bounds[2][1])
        ], random_start_node.angles)
        tries += 1
        if tries > max_tries:
            return None, None, None

    current_obstacles = obstacle_generation.generate_random_obstacles(
        num_obstacles, bounds, max_side_length=obstacle_size)
    while arm_is_colliding_prisms(random_end_node, current_obstacles):
        current_obstacles = obstacle_generation.generate_random_obstacles(
            num_obstacles, bounds, max_side_length=obstacle_size)

    while arm_is_colliding_prisms(
            random_start_node,
            current_obstacles) or not random_start_node.valid_configuration():
        random_start_node = Node(None)

    #print("start angles:", random_start_node.angles)
    #print("end angles:", random_end_node.angles)
    #print("obstacles:", current_obstacles)

    return random_start_node, random_end_node, current_obstacles
예제 #10
0
def steer(rand_angles, near_angles, step_size):
    """Generates a new node a certain distance along the path between the nearest node to the random node.

    Args:
        rand_angles: The angles of the randomly generated node.
        near_angles: The angles of the node closest to the randomly generated node.
        step_size: The distance from the nearest node for the new node to be generated.

    Returns:
        An instance of Node representing the new node of the tree.
    """

    dirn = true_angle_distances_arm(np.array(near_angles),
                                    np.array(rand_angles))
    length = np.linalg.norm(dirn)
    dirn = (dirn / length) * min(step_size, length)

    new_angles = (near_angles[0] + dirn[0], near_angles[1] + dirn[1],
                  near_angles[2] + dirn[2], near_angles[3] + dirn[3],
                  near_angles[4] + dirn[4])
    return Node(new_angles)
예제 #11
0
def generate_linear_path(start_angles, end_angles, num_iter):
    """Generates a linear path of arm configurations that moves an arm configuration from start_angles to end_angles.

    Args:
        start_angles: The initial angles of the arm.
        end_angles: The desired angles of the arm.
        num_iter: The number of angle configurations to be generated on the path in between the start and end positions.
    Returns:
        a path of nodes from start_pos to end_pos, with [num_iter] iterations of equal length.
    """

    step_sizes = compute_step_sizes(start_angles, end_angles, num_iter)

    g = Graph(start_angles, end_angles)

    current_angles = start_angles
    current_idx = 0
    current_node = g.nodes[0]
    for i in range(num_iter):
        new_angles = np.add(current_angles, step_sizes)
        new_angles = np.mod(new_angles, math.pi * 2)

        new_node = Node(new_angles)

        new_idx = g.add_vex(new_node, current_node)

        dist = line.distance(new_node.end_effector_pos,
                             current_node.end_effector_pos)

        current_angles = new_angles
        current_idx = new_idx
        current_node = new_node

        if i == num_iter - 1:
            rounded_current = np.around(current_angles, decimals=4)
            rounded_end = np.around(end_angles, decimals=4)

            if not np.array_equal(rounded_current, rounded_end):
                return g, False
    return g, True
예제 #12
0
def expand(node, directions, step_size, g):
    """ Expands from a node in a certain amount of directions for length [step_size]. """
    for i in range(directions):
        new_angles_pos = list.copy(node.angles)
        new_angles_neg = list.copy(node.angles)

        new_angles_pos[i] += step_size
        new_angles_neg[i] -= step_size

        node_pos = Node(new_angles_pos)
        node_neg = Node(new_angles_neg)

        if not visited(node_pos, g) and node_pos.valid_configuration():
            g.add_vex(node_pos, node)

        if not visited(node_neg, g) and node_neg.valid_configuration():
            g.add_vex(node_neg, node)
예제 #13
0
def inverse_test(threshold, num_trials, bounds):
    error_count = 0
    for i in range(num_trials):
        print("Trial:", i)
        point = [
            random.uniform(bounds[0][0], bounds[0][1]),
            random.uniform(bounds[1][0], bounds[1][1]),
            random.uniform(bounds[2][0], bounds[2][1])
        ]

        node = Node.from_point(point)

        print("Target point:", point)
        print("Obtained point:", node.end_effector_pos)
        diff = np.linalg.norm(node.end_effector_pos, point)
        print("Difference:", diff)

        if diff > threshold:
            print("BAD!!!")
            error_count += 1
        else:
            print("GOOD!!!!")

    print("Success rate", error_count / num_trials)
예제 #14
0
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import art3d
from arm_node import Node
import kinpy as kp

chain = kp.build_chain_from_urdf(
    open("models/SimpleArmModelforURDF.URDF").read())
chain = kp.build_serial_chain_from_urdf(
    open("models/SimpleArmModelforURDF.URDF").read(), "hand_1", "base_link")

angle_config = kp.ik.inverse_kinematics(chain,
                                        kp.Transform(pos=[0.2, 0.2, 0.2]))
ret = chain.forward_kinematics(angle_config, end_only=False)
print(ret)

node = Node.from_point([.2, .2, .2])
print(node.joint_positions)

# # Generate locations of each joint. Input angles here.
# th = {'Rev2': 0, 'Rev3': 0, 'Rev4': 0, 'Rev5': 0, 'Rev6': math.pi/2}
# ret = chain.forward_kinematics(th)
#
# print(chain.get_joint_parameter_names())
# print(chain)
# print(ret)
# print(ret['base_link'])
#
#
ax = plt.axes(projection='3d')

# Draw lines in between each joint
예제 #15
0
        if graph_list[i].success:
            plt.scatter(i, times[i], c='green')
        else:
            plt.scatter(i, times[i], c='red')
    plt.yticks(np.arange(0, 11, 1))
    plt.ylim(0, 10)
    plt.xlabel('Trial')
    plt.ylabel('Time (s)')
    plt.show()


def plot_ik_trial(target_point, obtained_config):
    """ Visualizes the distance between a target point and a point obtained through inverse kinematics. """
    ax = plt.axes(projection='3d')
    plot_nodes(ax, [target_point])
    plot_arm_configs(ax, [obtained_config], None)


if __name__ == "__main__":
    configs = []
    for i in range(1):
        node = Node(None)
        configs.append(node)

    ax = plt.axes(projection='3d')
    configure_graph(ax)

    plot_arm_configs(ax, configs, [])

    plt.show()
def main():
    """
    Places a prism randomly in 3D space, and draws a larger prism around it that represents it's
    bounding box. Then checks if a randomly generated arm collides the outer prism at any point.

    """
    fig = plt.figure()
    ax = plt.axes(projection="3d")
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    # Set the limits for the range of plotted values
    lim = .6
    plt.xlim(-lim, lim)
    plt.ylim(-lim, lim)
    ax.set_zlim(-lim, lim)
    # create the prism to be avoided (hard coded)
    lim = .3
    location = random.uniform(-lim, lim)
    prism = ([0, 0, 0, .2, .3, .4])
    exoPrism = ([
        prism[0] - .05, prism[1] - .05, prism[2] - .05, prism[3] + .1,
        prism[4] + .1, prism[5] + .1
    ])
    plot_linear_prism(ax, prism, 'blue')
    plot_linear_prism(ax, exoPrism, 'red')
    lim = .5
    """
    This code was used to generate a single line, and has since been replaced 
    by code that generates a random N link arm. 
    
        randomX1 = random.uniform(-lim, lim)
        randomY1 = random.uniform(-lim, lim)
        randomZ1 = random.uniform(-lim, lim)
    
        randomX2 = random.uniform(-lim, lim)
        randomY2 = random.uniform(-lim, lim)
        randomZ2 = random.uniform(-lim, lim)
        line_seg = ([randomX1, randomY1, randomZ1,
                     randomX2, randomY2, randomZ2])  # ([-.1, .1, .1, .3, .1, .1])
        # line_seg = ([-.3,.15,-.2,.6,.15,.2]) # intersects x faces
        # line_seg = ([.16, -.3, .2, .16, .3, .2]) # intersects y faces
        # line_seg = ([.2, -.2, -.3, .2, .15, .5]) # intersects z faces
        xs = ([line_seg[0], line_seg[3]])
        ys = ([line_seg[1], line_seg[4]])
        zs = ([line_seg[2], line_seg[5]])
        plot_lines(ax, xs, ys, zs, None)
    """
    # Create a random arm
    arm = Node(None)
    plot_arm(ax, arm, None)

    class Index:
        def draw(self, event):
            plt.close()
            main()

    axnext = plt.axes([0.81, 0.05, 0.1, 0.075])
    run = Button(axnext, 'ReRun')
    callback = Index()
    run.on_clicked(callback.draw)
    if checkCollision(exoPrism, arm):
        plt.title("Collision Detected!")
    else:
        plt.title("Wow no Collision")
    plt.show()
예제 #17
0
def rrt(start_angles,
        end_angles,
        obstacles,
        n_iter=300,
        radius=0.02,
        angle_threshold=1,
        stepSize=.05,
        heuristic_threshold=10):
    """Uses the RRT algorithm to determine a collision-free path from start_angles to end_angles.

    Args:
        start_angles: An array of length 5 representing the initial angle configuration of the arm.
        end_angles: An array of length 5 representing the desired angle configuration.
        obstacles: An array of float arrays representing cube obstacles.
        n_iter: Maximum number of iterations to find a path.
        radius: Maximum distance between the final end effector position and the second-to-last position in a path.
        angle_threshold: Maximum distance between the final angles and the second-to-last angles in a path.
        stepSize: Distance between nodes in the graph.
        heuristic_threshold: Maximum number of times a new node can fail to expand from any given node in the graph.

    Returns:
        An instance of rrtgraph.Graph containing a list of instances of Node, and a path of Node instances between
        the start and end nodes, if successful.
        A boolean indicator representing whether a path was found.
    """
    G = Graph(start_angles, end_angles)

    for i in range(n_iter):
        rand_node = Node(None)
        if arm_is_colliding_prisms(rand_node, obstacles):
            continue

        if i % 2 == 0 or not G.ranking:
            nearest_node, nearest_node_index = nearest(
                G, rand_node.end_effector_pos)
            if nearest_node is None:
                continue

            new_node = steer(rand_node.angles, nearest_node.angles, stepSize)

            if arm_is_colliding_prisms(
                    new_node, obstacles) or not new_node.valid_configuration():
                continue

            nearest_to_new, _ = nearest(G, new_node.end_effector_pos)

            G.add_vex(new_node, nearest_to_new)
            # dist = line.distance(new_node.end_effector_pos, nearest_to_new.end_effector_pos)
            # G.add_edge(newidx, nearest_to_new_idx, dist)

        else:
            new_node, newidx = extend_heuristic(G, rand_node, stepSize,
                                                heuristic_threshold, obstacles)
            if arm_is_colliding_prisms(new_node, obstacles):
                continue

        end_eff_dist_to_goal = line.distance(new_node.end_effector_pos,
                                             G.end_node.end_effector_pos)
        angle_dist_to_goal = np.linalg.norm(
            true_angle_distances_arm(new_node.angles, G.end_node.angles))

        if end_eff_dist_to_goal < radius and not G.success:
            if arm_is_colliding_prisms(G.end_node, obstacles):
                raise Exception("Adding a colliding node")
            desired_position = G.end_node.end_effector_pos
            if angle_dist_to_goal > angle_threshold:
                # tries to get a closer arm configuration to the second-to-last arm configration with inverse kinematics
                G.end_node = Node.from_point(desired_position,
                                             start_config=new_node.angles)

            endidx = G.add_vex(G.end_node, new_node)
            # G.add_edge(newidx, endidx, end_eff_dist_to_goal)
            G.success = True
            print("Iterations:", i)
            break
    return G