def calc_hybrid_astar_path(sx, sy, syaw, gx, gy, gyaw, ox, oy, xyreso,
                           yawreso):
    syaw0 = rs_path.pi_2_pi(syaw)
    gyaw0 = rs_path.pi_2_pi(gyaw)
    global tox, toy
    ox, oy = ox[:], oy[:]
    tox, toy = ox[:], oy[:]
    kdtree = KDTree(np.vstack((tox, toy)).T)
    c = calc_config(ox, oy, xyreso, yawreso)
    nstart = Node(round(sx / xyreso), round(sy / xyreso),
                  round(syaw0 / yawreso), True, [sx], [sy], [syaw0], [True],
                  0.0, 0.0, -1)
    ngoal = Node(round(gx / xyreso), round(gy / xyreso),
                 round(gyaw0 / yawreso), True, [gx], [gy], [gyaw0], [True],
                 0.0, 0.0, -1)
    h_dp = grid_a_star.calc_dist_policy(ngoal.x[-1], ngoal.y[-1], ox, oy,
                                        xyreso, 1.0)
    openset, closedset = {}, {}
    fnode = None
    openset[calc_index(nstart, c)] = nstart
    pq = queue.PriorityQueue()
    pq.put(calc_index(nstart, c), calc_cost(nstart, h_dp, ngoal, c))
    u, d = calc_motion_inputs()
    nmotion = len(u)
    times = 0
    while 1:
        if len(openset) == 0:
            print("Error: Cannot find path, No open set")
            return []
        c_id = min(
            openset,
            key=lambda o: openset[o].cost + calc_cost(nstart, h_dp, ngoal, c))
        current = openset[c_id]
        # move current node from open to closed
        del openset[c_id]
        closedset[c_id] = current
        isupdated, fpath = update_node_with_analystic_expantion(
            current, ngoal, c, ox, oy, kdtree)
        if isupdated:  # found
            fnode = fpath
            break
        for i in range(nmotion):
            node = calc_next_node(current, c_id, u[i], d[i], c)
            if verify_index(node, c, ox, oy, kdtree) == False:
                continue
            node_ind = calc_index(node, c)
            # If it is already in the closed set, skip it
            if node_ind in closedset:
                continue
            if not node_ind in openset:
                openset[node_ind] = node
                pq.put(node_ind, calc_cost(nstart, h_dp, ngoal, c))
            else:
                if openset[node_ind].cost > node.cost:
                    # If so, update the node to have a new parent
                    openset[node_ind] = node
            times = times + 1
    print("final expand node:", len(openset) + len(closedset))
    path = get_final_path(closedset, fnode, nstart, c)
    return path
def calc_next_node(current, c_id, u, d, c):

    arc_l = XY_GRID_RESOLUTION * 1.5

    nlist = math.ceil(arc_l / MOTION_RESOLUTION) + 1

    xlist, ylist, yawlist = [], [], []

    xlist_0 = current.x[-1] + d * MOTION_RESOLUTION * math.cos(current.yaw[-1])
    ylist_0 = current.y[-1] + d * MOTION_RESOLUTION * math.sin(current.yaw[-1])
    yawlist_0 = rs_path.pi_2_pi(current.yaw[-1] +
                                d * MOTION_RESOLUTION / WB * math.tan(u))
    xlist.append(xlist_0)
    ylist.append(ylist_0)
    yawlist.append(yawlist_0)

    for i in range(1, int(nlist)):
        xlist_i = xlist[i -
                        1] + d * MOTION_RESOLUTION * math.cos(yawlist[i - 1])
        ylist_i = ylist[i -
                        1] + d * MOTION_RESOLUTION * math.sin(yawlist[i - 1])
        yawlist_i = rs_path.pi_2_pi(yawlist[i - 1] +
                                    d * MOTION_RESOLUTION / WB * math.tan(u))
        xlist.append(xlist_i)
        ylist.append(ylist_i)
        yawlist.append(yawlist_i)

    xind = round(xlist[-1] / c.xyreso)
    yind = round(ylist[-1] / c.xyreso)
    yawind = round(yawlist[-1] / c.yawreso)

    addedcost = 0.0
    if d > 0:
        direction = True
        addedcost += abs(arc_l)
    else:
        direction = False
        addedcost += abs(arc_l) * BACK_COST

    # swich back penalty
    if direction != current.direction:  # switch back penalty
        addedcost += SB_COST

    # steer penalyty
    addedcost += STEER_COST * abs(u)

    # steer change penalty
    addedcost += STEER_CHANGE_COST * abs(current.steer - u)

    cost = current.cost + addedcost

    directions = [direction for i in range(len(xlist))]
    node = Node(xind, yind, yawind, direction, xlist, ylist, yawlist,
                directions, u, cost, c_id)

    return node
Example #3
0
def calc_next_node(current, c_id, u, d, c):

    arc_l = XY_GRID_RESOLUTION*1.5

    nlist = int(math.floor(arc_l/MOTION_RESOLUTION)) + 1
    xlist = np.zeros(nlist)
    ylist = np.zeros(nlist)
    yawlist = np.zeros(nlist)
    yaw1list = np.zeros(nlist)

    xlist[0] = current.x[-1] + d * MOTION_RESOLUTION*math.cos(current.yaw[-1])
    ylist[0] = current.y[-1] + d * MOTION_RESOLUTION*math.sin(current.yaw[-1])
    yawlist[0] = rs_path.pi_2_pi(current.yaw[-1] + d*MOTION_RESOLUTION/WB * math.tan(u))
    yaw1list[0] = rs_path.pi_2_pi(current.yaw1[-1] + d*MOTION_RESOLUTION/LT*math.sin(current.yaw[-1]-current.yaw1[-1]))
 
    for i in range(nlist-1):
        xlist[i+1] = xlist[i] + d * MOTION_RESOLUTION*math.cos(yawlist[i])
        ylist[i+1] = ylist[i] + d * MOTION_RESOLUTION*math.sin(yawlist[i])
        yawlist[i+1] = rs_path.pi_2_pi(yawlist[i] + d*MOTION_RESOLUTION/WB * math.tan(u))
        yaw1list[i+1] = rs_path.pi_2_pi(yaw1list[i] + d*MOTION_RESOLUTION/LT*math.sin(yawlist[i]-yaw1list[i]))
 
    xind = int(round(xlist[-1]/c.xyreso))
    yind = int(round(ylist[-1]/c.xyreso))
    yawind = int(round(yawlist[-1]/c.yawreso))


    addedcost = 0.0
    if d > 0:
        direction = 1
        addedcost += abs(arc_l)
    else:
        direction = 0
        addedcost += abs(arc_l) * BACK_COST

    # switch back penalty
    if direction != current.direction: # switch back penalty
        addedcost += SB_COST

    # steer penalty
    addedcost += STEER_COST*abs(u)

    # steer change penalty
    addedcost += STEER_CHANGE_COST*abs(current.steer - u)

    # jacknif cost
    addedcost += JACKKNIF_COST * sum([abs(rs_path.pi_2_pi(yawlist[i] - yaw1list[i])) for i in range(len(yawlist))])

    cost = current.cost + addedcost 

    directions = [direction for i in range(len(xlist))]
    node = Node(xind, yind, yawind, direction, xlist, ylist, yawlist, yaw1list, directions, u, cost, c_id)

    return node
Example #4
0
def calc_rs_path_cost(rspath, yaw1):
    # calculate the path cost
    cost = 0.0
    for l in rspath.lengths:
        if l >= 0: # forward
            cost += l
        else: # back
            cost += abs(l) * BACK_COST
    
    # swich back penalty
    for i in range(len(rspath.lengths) - 1):
        if rspath.lengths[i] * rspath.lengths[i+1] < 0.0: # switch back
            cost += SB_COST

    # steer penalyty
    for ctype in rspath.ctypes:
        if ctype != "S": # curve
            cost += STEER_COST*abs(MAX_STEER)

    # ==steer change penalty
    # calc steer profile
    nctypes = len(rspath.ctypes)
    ulist = np.zeros(nctypes)
    for i in range(nctypes):
        if rspath.ctypes[i] == "R":
            ulist[i] = - MAX_STEER
        elif rspath.ctypes[i] == "L":
            ulist[i] = MAX_STEER
 
    for i in range(len(rspath.ctypes) - 1):
        cost += STEER_CHANGE_COST*abs(ulist[i+1] - ulist[i])
    # print ("cost: ", cost)
    cost += JACKKNIF_COST * sum([abs(rs_path.pi_2_pi(rspath.yaw[i] - yaw1[i])) for i in range(len(yaw1))])
    return cost
Example #5
0
def update_node_with_analystic_expantion(current, ngoal, c, ox, oy, kdtree, gyaw1):
    # use Reed-Shepp model to find a path between current node and goal node without obstacles, which will not run every time for computational reasons.
    apath = analystic_expantion(current, ngoal, c, ox, oy, kdtree)
    if apath != None:
        fx = apath.x[1:]
        fy = apath.y[1:]
        fyaw =  apath.yaw[1:]
        steps = MOTION_RESOLUTION*apath.directions
        yaw1 = tralierlib.calc_trailer_yaw_from_xyyaw(apath.x, apath.y, apath.yaw, current.yaw1[-1], steps)
        if abs(rs_path.pi_2_pi(yaw1[-1] - gyaw1)) >= GOAL_TYAW_TH:
            return 0, None #no update

        fcost = current.cost + calc_rs_path_cost(apath, yaw1)
        fyaw1 = yaw1[1:]
        fpind = calc_index(current, c)

        fd = []
        for d in apath.directions[1:]:
            if d >= 0:
                fd.append(1)
            else:
                fd.append(0)

        fsteer = 0.0

        fpath = Node(current.xind, current.yind, current.yawind, current.direction, fx, fy, fyaw, fyaw1, fd, fsteer, fcost, fpind)

        return 1, fpath

    return 0, None #no update
Example #6
0
def update_node_with_analystic_expantion(current, ngoal, c, ox, oy, kdtree,
                                         gyaw1):
    """
    This function updates the current node with "analystic" data
    :param current: Node, current node
    :param ngoal: Node, goal node
    :param c: Config, configuration space
    :param ox: meters, list of x positions of each obstacle
    :param oy: meters, list of y positions of each obstacle
    :param kdtree: kdtree, KD tree made from ox and oy
    :param gyaw1: radians, goal position trailer yaw
    :return:
    """

    apath = analystic_expantion(current, ngoal, c, ox, oy, kdtree)

    # Formulate data for the final node
    if apath:  # if apath = [], skip the if statement since there is no path (ignore the unresolved attribute reference
        # errors as they won't happen since apath is only a list if it is empty and then the if statement is false and
        # the block doesn't execute)
        fx = apath.x[1:-1]
        fy = apath.y[1:-1]
        fyaw = apath.yaw[1:-1]
        steps = [MOTION_RESOLUTION * direct for direct in apath.directions]
        yaw1 = trailerlib.calc_trailer_yaw_from_xyyaw(apath.x, apath.y,
                                                      apath.yaw,
                                                      current.yaw1[-1], steps)
        # check if the trailer yaw is outside the trailer yaw threshold
        if abs(rs_path.pi_2_pi(yaw1[-1] - gyaw1)) >= GOAL_TYAW_TH:
            return False, [
            ]  # current node is not the goal node based on trailer
        if abs(fx[-1] - ngoal.x[-1]) >= XY_GRID_RESOLUTION or abs(
                fy[-1] - ngoal.y[-1]) >= XY_GRID_RESOLUTION:
            return False, [
            ]  # current node is not the goal node based on xy location
        fcost = current.cost + calc_rs_path_cost(apath,
                                                 yaw1)  # cost to next node
        fyaw1 = yaw1[1:-1]  # array of trailer yaws
        fpind = calc_index(current, c)  # index of parent node
        fd = []  # array of directions
        for d in apath.directions[1:-1]:
            if d >= 0:
                fd.append(True)
            else:
                fd.append(False)

        fsteer = 0

        fpath = Node(current.xind, current.yind, current.yawind,
                     current.direction, fx, fy, fyaw, fyaw1, fd, fsteer, fcost,
                     fpind)

        return True, fpath

    return False, []
Example #7
0
def calc_rs_path_cost(rspath, yaw1):
    """
    This function calculates the total cost for a given RS path and yaw1
    :param rspath: rs_path.Path, RS path
    :param yaw1: radians, trailer yaw
    :return: total cost
    """

    # Initialize cost
    cost = 0

    # Update cost for length and direction of path
    for l in rspath.lengths:
        if l >= 0:
            cost += l
        else:
            cost += abs(l) * BACK_COST

    # Update cost if direction changes in the path (detected by change in sign of adjacent lengths)
    for i in (range(len(rspath.lengths) - 1)):
        if rspath.lengths[i] * rspath.lengths[i + 1] < 0:
            cost += SB_COST

    # Update cost for curved path
    for ctype in rspath.ctypes:
        if ctype != "S":
            cost += STEER_COST * abs(MAX_STEER)

    # Form list of steering inputs
    nctypes = len(rspath.ctypes)  # number of ctypes in the given path
    ulist = [
    ]  # list used to convert the string ctypes to numbers for cost calculation
    for i in range(nctypes):
        if rspath.ctypes[i] == "R":
            ulist.append(-MAX_STEER)
        elif rspath.ctypes[i] == "L":
            ulist.append(MAX_STEER)
        else:
            ulist.append(0)

    # Update cost for changing direction of turn
    for i in range(nctypes - 2):
        cost += STEER_CHANGE_COST * abs(ulist[i + 1] - ulist[i])

    # Update cost to prevent jackknifes (the most the trailer folds toward the truck, the higher the cost)
    cost += JACKKNIF_COST * sum(
        np.absolute(rs_path.pi_2_pi(np.subtract(rspath.yaw, yaw1))))

    return cost
def calc_hybrid_astar_path(sx, sy, syaw, gx, gy, gyaw, ox, oy, xyreso,
                           yawreso):

    # sx: start x position[m]
    # sy: start y position[m]
    # gx: goal x position[m]
    # gx: goal x position[m]
    # ox: x position list of Obstacles[m]
    # oy: y position list of Obstacles[m]
    # xyreso: grid resolution[m]
    # yawreso: yaw angle resolution[rad]

    syaw0 = rs_path.pi_2_pi(syaw)
    gyaw0 = rs_path.pi_2_pi(gyaw)
    global tox, toy
    ox, oy = ox[:], oy[:]
    tox, toy = ox[:], oy[:]
    kdtree = KDTree(np.vstack((tox, toy)).T)

    # a = np.array((1,2,3))
    # b = np.array((2,3,4))
    # c = np.hstack((a,b))
    # tree = spatial.KDTree((a,b),10)
    # print tree.date

    c = calc_config(ox, oy, xyreso, yawreso)
    #c.prn_obj()
    nstart = Node(round(sx / xyreso), round(sy / xyreso),
                  round(syaw0 / yawreso), True, [sx], [sy], [syaw0], [True],
                  0.0, 0.0, -1)
    ngoal = Node(round(gx / xyreso), round(gy / xyreso),
                 round(gyaw0 / yawreso), True, [gx], [gy], [gyaw0], [True],
                 0.0, 0.0, -1)
    h_dp = calc_holonomic_with_obstacle_heuristic(ngoal, ox, oy, xyreso)
    openset, closedset = {}, {}
    fnode = None
    openset[calc_index(nstart, c)] = nstart
    pq = Q.PriorityQueue()
    pq.put(calc_index(nstart, c), calc_cost(nstart, h_dp, ngoal, c))
    u, d = calc_motion_inputs()
    nmotion = len(u)

    if vehicle_lib.check_collision(ox, oy, [sx], [sy], [syaw0],
                                   kdtree) == False:
        print('1111111')
        return []
    if vehicle_lib.check_collision(ox, oy, [gx], [gy], [gyaw0],
                                   kdtree) == False:
        print('2222222')
        return []

    times = 0
    while 1:
        # if times >100:
        #     return []
        if len(openset) == 0:
            print("Error: Cannot find path, No open set")
            return []

        c_id = min(
            openset,
            key=lambda o: openset[o].cost + calc_cost(nstart, h_dp, ngoal, c))

        #c_id = pq.get()
        current = openset[c_id]

        # move current node from open to closed
        del openset[c_id]
        closedset[c_id] = current

        # visualize_x = []
        # visualize_y = []
        # for v in closedset.values():
        #     visualize_x.append(v.x[-1])
        #     visualize_y.append(v.y[-1])
        # print visualize_x,visualize_y
        # plt.plot(tox, toy, ".k")
        # plt.plot(visualize_x,visualize_y,'.k')
        # plt.pause(0.1)

        isupdated, fpath = update_node_with_analystic_expantion(
            current, ngoal, c, ox, oy, kdtree, True)
        if isupdated:  # found
            fnode = fpath
            break

        for i in range(nmotion):
            node = calc_next_node(current, c_id, u[i], d[i], c)

            if verify_index(node, c, ox, oy, kdtree) == False:
                continue

            node_ind = calc_index(node, c)
            #print 'expend node id:', node_ind
            # If it is already in the closed set, skip it
            if node_ind in closedset:
                continue

            if node_ind not in openset:
                openset[node_ind] = node
                pq.put(node_ind, calc_cost(nstart, h_dp, ngoal, c))
            else:
                if openset[node_ind].cost > node.cost:
                    # If so, update the node to have a new parent
                    openset[node_ind] = node
            times = times + 1
    print("final expand node:", len(openset) + len(closedset))
    path = get_final_path(closedset, fnode, nstart, c)
    return path
Example #9
0
def calc_hybrid_astar_path(sx, sy, syaw, syaw1, gx, gy, gyaw, gyaw1, ox, oy,
                           xyreso, yawreso):
    """

    :param sx:      meters, start x position
    :param sy:      meters, start y position
    :param syaw:    radians, start tractor angle
    :param syaw1:   radians, start trailer angle
    :param gx:      meters, goal x position
    :param gy:      meters, goal y position
    :param gyaw:    radians, goal tractor angle
    :param gyaw1:   radians, goal trailer angle
    :param ox:      meters, list of x coordinates of each obstacle
    :param oy:      meters, list of y coordinates of each obstacle
    :param xyreso:  meters, xy grid resolution
    :param yawreso: radians, yaw resolution
    :return:
    """

    syaw, gyaw = rs_path.pi_2_pi(syaw), rs_path.pi_2_pi(
        gyaw)  # function used to ensure yaw is between -pi and pi
    oxy = [(ox[i], oy[i]) for i in range(0, len(ox))
           ]  # forms a list of tuples from the two lists ox and oy for
    # input to kd.create

    # forms a kd tree from the xy points of the obstacles
    print('Forming Obstacle KD Tree...')
    kdtree = spatial.KDTree(oxy)

    # Form the configuration space
    c = calc_config(ox, oy, xyreso, yawreso)

    # Define the starting node
    nstart = Node(round(sx / xyreso), round(sy / xyreso),
                  round(syaw / yawreso), True, [sx], [sy], [syaw], [syaw1],
                  [True], 0, 0, -1)

    # Define the goal node
    ngoal = Node(round(gx / xyreso), round(gy / xyreso), round(gyaw / yawreso),
                 True, [gx], [gy], [gyaw], [gyaw1], [True], 0, 0, -1)

    # Determine holonomic costs of each xy coordinate on the grid (numpy dependent)
    print('Calculating Holonomic Costs...')
    h_dp = calc_holonomic_with_obstacle_heuristics(ngoal, ox, oy, xyreso)

    opened = {}  # Dictionary to hold open nodes
    closed = {}  # Dictionary to hold closed nodes
    pq = {
    }  # Dictionary to hold queue of indexes and costs, used to determine which node in open to expand next
    fnode = []

    # Add the start node to the dictionary of open nodes
    opened[calc_index(nstart, c)] = nstart

    # Add the start node to the dictionary of indexes and costs
    pq[calc_index(nstart, c)] = calc_cost(nstart, h_dp, c)

    u, d = calc_motion_inputs()
    nmotion = len(u)
    iteration = 1

    print('Commencing Hybrid A* Search...')
    while True:
        if iteration % 10 == 0:
            print(
                f'{iteration} loops complete. {len(opened)} nodes opened.  {len(closed)} nodes closed.'
            )
        iteration += 1

        # Exit while loop and return nothing if open is expended and a solution is not found
        if not opened:
            print("Error: Cannot find path, No open nodes remaining.")
            return []

        # Obtain the index of the next node
        c_id = min(pq.keys())

        # Removed the obtained index from the queue
        pq.pop(c_id)

        # Get the node with the obtained index and remove it from open
        current = opened.get(c_id)
        opened.pop(c_id)

        # Add the current node to the closed list
        closed.update({c_id: current})

        # get full data of current node and isupdated flag
        isupdated, fpath = update_node_with_analystic_expantion(
            current, ngoal, c, ox, oy, kdtree, gyaw1)

        if isupdated:  # goal has been found
            fnode = fpath
            break  # exit the while loop

        inityaw1 = current.yaw1[0]

        # cycle through all inputs and check if each next node is in closed, else add it to open if not already there
        for i in range(nmotion):
            node = calc_next_node(current, c_id, u[i], d[i], c)
            if not verify_index(node, c, ox, oy, inityaw1, kdtree):
                continue  # continue with next node since this node's configuration is invalid

            node_ind = calc_index(node, c)

            # Check if node is already in the closed dictionary
            if node_ind in closed:
                continue  # continue with next node since this one is already closed

            # Check if node is already in the open dictionary
            if node_ind not in opened:
                opened.update({node_ind: node})  # add the node to open
                pq.update({node_ind: calc_cost(node, h_dp, c)
                           })  # add the node cost information to pq
            else:
                if opened[
                        node_ind].cost > node.cost:  # if node is in the open dictionary, but the new node cost is
                    # lower, update the node in open
                    opened[node_ind] = node

    print('Forming Final Path...')

    path = get_final_path(closed, fnode, nstart, c)

    return path
Example #10
0
def calc_next_node(current, c_id, u, d, c):
    """
    This function calculates the next node based on the current node and the given inputs.
    :param current: Node, current node
    :param c_id: index, index of current node
    :param u: input, steering input
    :param d: input, driving input
    :param c: Config, Cspace
    :return:
    """

    arc_l = XY_GRID_RESOLUTION * 1.5

    nlist = math.floor(arc_l / MOTION_RESOLUTION) + 1

    # Initial motion from the current node
    xlist = [current.x[-1] + d * MOTION_RESOLUTION * math.cos(current.yaw[-1])]
    ylist = [current.y[-1] + d * MOTION_RESOLUTION * math.sin(current.yaw[-1])]
    yawlist = [
        rs_path.pi_2_pi(current.yaw[-1] +
                        d * MOTION_RESOLUTION * math.tan(u) / WB)
    ]
    yaw1list = [
        rs_path.pi_2_pi(current.yaw1[-1] + d * MOTION_RESOLUTION *
                        math.sin(current.yaw[-1] - current.yaw1[-1]) / LT)
    ]

    # Discrete path from current node to the next node for the given inputs
    for i in range(nlist - 1):
        xlist.append(xlist[-1] + d * MOTION_RESOLUTION * math.cos(yawlist[-1]))
        ylist.append(ylist[-1] + d * MOTION_RESOLUTION * math.sin(yawlist[-1]))
        yawlist.append(
            rs_path.pi_2_pi(yawlist[-1] +
                            d * MOTION_RESOLUTION * math.tan(u) / WB))
        yaw1list.append(
            rs_path.pi_2_pi(yaw1list[-1] + d * MOTION_RESOLUTION *
                            math.sin(yawlist[-1] - yaw1list[-1]) / LT))

    # Determine the index in each dimension of the next node
    xind = round(xlist[-1] / c.xyreso)
    yind = round(ylist[-1] / c.xyreso)
    yawind = round(yawlist[-1] / c.yawreso)

    # Calculate cost to the next node
    addedcost = 0  # Initialize cost of moving from current node to the next node

    # Cost of traveling length in given direction
    if d > 0:
        direction = True
        addedcost += abs(arc_l)
    else:
        direction = False
        addedcost += abs(arc_l) * BACK_COST

    # Cost of switching direction
    if direction != current.direction:  # A direction change occured
        addedcost += SB_COST

    # Cost of steering
    addedcost += STEER_COST * abs(u)

    # Cost of changing direction
    addedcost += STEER_CHANGE_COST * abs(current.steer - u)

    # Cost of acute angle between trailer and tractor
    addedcost += JACKKNIF_COST * sum(
        np.absolute(rs_path.pi_2_pi(np.subtract(yawlist, yaw1list))))

    cost = current.cost + addedcost

    # Form list of directions for the node class
    directions = [direction for i in range(len(xlist))]

    node = Node(xind, yind, yawind, direction, xlist, ylist, yawlist, yaw1list,
                directions, u, cost, c_id)

    return node
Example #11
0
def calc_hybrid_astar_path(sx, sy, syaw, syaw1, gx, gy, gyaw, gyaw1, ox, oy, xyreso, yawreso):
    """
    sx: start x position [m]
    sy: start y position [m]
    gx: goal x position [m]
    gx: goal x position [m]
    ox: x position list of Obstacles [m]
    oy: y position list of Obstacles [m]
    xyreso: grid resolution [m]
    yawreso: yaw angle resolution [rad]
    """
    ox = np.asarray(ox)
    oy = np.asarray(oy)
    
    syaw = rs_path.pi_2_pi(syaw)
    gyaw = rs_path.pi_2_pi(gyaw)

    kdtree = KDTree(np.asarray([ox, oy]).T)

    # get the configuration of whole map (length, width. obstacles)
    c = Config()
    ox, oy = c.calc_config(ox, oy, xyreso, yawreso)
    nstart = Node(int(round(sx/xyreso)), int(round(sy/xyreso)), int(round(syaw/yawreso)), 1, np.asarray([sx]), np.asarray([sy]), np.asarray([syaw]), np.asarray([syaw1]), np.asarray([1]), 0.0, 0.0, -1)
    ngoal = Node(int(round(gx/xyreso)), int(round(gy/xyreso)), int(round(gyaw/yawreso)), 1, np.asarray([gx]), np.asarray([gy]), np.asarray([gyaw]), np.asarray([gyaw1]), np.asarray([1]), 0.0, 0.0, -1)

    # To get a map with the h cost (distance to goal node) of each grid (without obstacle)
    h_dp = calc_holonomic_with_obstacle_heuristic(ngoal, ox, oy, xyreso)

    # print (h_dp)

    open_set = {}
    closed_set = {}
    fnode = None

    open_set[calc_index(nstart, c)] = nstart
    pq =[]
    heappush(pq, (calc_cost(nstart, h_dp, c), calc_index(nstart, c)))

    u, d = calc_motion_inputs()
    nmotion = len(u)

    while (1):
        starttime = time.time()
        # print ("pq: ",pq)
        print ("pq: ",len(pq))
        # print ("open_set: ",open_set)
        if len(open_set) == 0:
            print("Error: Cannot find path, No open set")
            return []

        c_id = heappop(pq)[1]
        current = open_set[c_id]
        #move current node from open to closed
        del open_set[c_id]
        closed_set[c_id] = current

        # use Reed-Shepp model to find a path between current node and goal node without obstacles, which will not run every time for computational reasons.
        isupdated, fpath = update_node_with_analystic_expantion(current, ngoal, c, ox, oy, kdtree, gyaw1)
        print ("isupdated: ", isupdated)
        if isupdated: # found
            fnode = fpath
            break

        inityaw1 = current.yaw1[0]

        for i in range(nmotion):
            # print ("current.xind: ", current.xind, "  current.yind: ", current.yind, "  current.cost: ", current.cost, "  c_id: ", c_id, "  u[i]: ", u[i], "  d[i]: ", d[i])
            # For each node, it will have multiple possible points but with one x,y index
            node = calc_next_node(current, c_id, u[i], d[i], c)
            # print ("node.xind: ", node.xind, "  node.yind: ", node.yind, "  node.cost: ", node.cost)

            if not verify_index(node, c, ox, oy, inityaw1, kdtree): 
                continue

            node_ind = calc_index(node, c)

            # If it is already in the closed set, skip it
            if node_ind in closed_set:  
                continue

            if node_ind not in open_set:
                open_set[node_ind] = node
                heappush(pq, (calc_cost(node, h_dp, c), node_ind))
                
            else:
                if open_set[node_ind].cost > node.cost:
                    # If so, update the node to have a new parent
                    open_set[node_ind] = node
        endtime = time.time()
        print ("onetime: ", endtime-starttime)
    print("final expand node:", len(open_set) + len(closed_set))
    ##################################
    path = get_final_path(closed_set, fnode, nstart, c)

    # ====Animation=====
    animation.show_animation(path, ox, oy, sx, sy, syaw, syaw1, gx, gy, gyaw, gyaw1)

    return path