A*路径规划的可视化非常缓慢 / Python

3
我尝试使用Python和Pygame实现最短路径查找算法。看起来算法和可视化效果都可以,但是当我将起点和终点节点设置得很远时,找到路径所需的时间成倍增加。查找5或6个节点长度的路径需要几个小时。我认为问题出在astar函数中的一个循环中,但我不知道具体是哪个循环以及如何解决它。我在互联网上搜索了该问题,但未找到任何解决方案。以下是代码:
import pygame


class cube():
    rows = 30
    w = 840
    def __init__(self, pos=None, color=None):
        self.pos = pos
        self.color = color
        self.neighbours = []


    def draw(self, surface):
        dis = self.w // self.rows
        i = self.pos[0]
        j = self.pos[1]


        pygame.draw.rect(surface, self.color, (i * dis + 1, j * dis + 1, dis - 2, dis - 2))
class node(cube):

    def __init__(self, pos=None, color=None, parent=None):
        super().__init__(pos, color)
        self.parent = parent

        self.g = 0
        self.h = 0
        self.f = self.g + self.h

    def __eq__(self, other):
        return self.pos == other.pos

//here is the astar function

def astar(start, end):
    global rows, path, obs_list, start_node, end_node, window, closed_list, blue, children, red, green, grey

    counter = 0
    green = (0,255,0)
    red = (255,0,0)
    blue = (0,0,255)
    grey = (170,170,170)
    start_node = node(start, red)
    end_node = node(end, red)
    start_node.g = start_node.h = start_node.f = 0
    end_node.g = end_node.h = end_node.f = 0


    open_list = []
    closed_list = []
    open_list.append(start_node)

    while len(open_list) > 0:

        # Get the current node
        current_node = open_list[0]

        current_index = 0
        for index, item in enumerate(open_list):
            if item.f < current_node.f:
                current_node = item

                current_index = index

        # Pop current off open list, add to closed list
        open_list.pop(current_index)
        closed_list.append(current_node)
        visualize(window, open_list)


        if current_node.pos == end_node.pos:

            path = []
            current = current_node
            while current is not None:
                path.append(current.pos)
                current = current.parent
            return path[::-1]  # Return reversed path

        children = []
        for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]:
            node_position = (current_node.pos[0] + new_position[0], current_node.pos[1] + new_position[1])



            #check if neighbour isn't a obstacle
            if node_position in obs_list:
              #  print("not possible")
                continue

            #check if neighbour isn't in closed list

            #create a new node
            new_node = node(node_position, green, current_node)



            # new node is the child of the previous node. Add child node to the list
            children.append(new_node)
        #     print(children)
            #loop through children
            for child in children:


                for closed_child in closed_list:
                    if child == closed_child:
                        continue

                child.g = current_node.g + 10
                child.h = ((child.pos[0] - end_node.pos[0]) **2) + ((child.pos[1] - end_node.pos[1]) **2)
                child.f = child.g + child.h

                for open_node in open_list:
                    if child == open_node and child.g > open_node.g:
                        continue



          #       visualize(window, children)
                    # Add the child to the open list
                open_list.append(child)


def draw_grid(w, rows, window):
    size_between = w // rows
    x = 0
    y = 0
    for l in range(rows):
        x = x + size_between
        y = y + size_between

        pygame.draw.line(window, (255,255,255), (x, 0), (x, w))
        pygame.draw.line(window, (255,255,255), (0,y), (w,y))


def visualize(surface, list):
    global red, blue, green, grey, counter
    for i,c in enumerate(list):
        c.draw(surface)
        pygame.display.update()




def show_path(surface):
    global path
    for i in path:
        node_path = cube(i, color=(0,0,255))
        node_path.draw(surface)
        pygame.display.update()

    for c in (path):
        node_p = cube(c, color=(35, 180, 89))
        node_p.draw(surface)

def draw_inital(surface):
    surface.fill((0, 0, 0))
    draw_grid(840, 30, surface)
    start_node.draw(surface)
    end_node.draw(surface)
    pygame.display.update()

def mouse_press(x):
    global rows, window, obs, start_node, end_node, obs_list
    t = x[0]
    w = x[1]

    g1 = t // (840 // rows)
    g2 = w // (840 // rows)
    obs = cube((g1,g2), (125, 125, 125))


    if obs.pos == start_node.pos:
        obs.color = (255,0,0)
        obs.draw(window)
        pygame.display.update()

    elif obs.pos == end_node.pos:

        obs.color = (255, 0, 0)
        obs.draw(window)
        pygame.display.update()

    else:
        if obs.pos not in obs_list:

            obs_list.append(obs.pos)
            obs.draw(window)
            pygame.display.update()




def main():
    global start_node, end_node, rows, window, counter, obs_list, start, end
    width = 840
    rows = 30
    window = pygame.display.set_mode((width,width))

    start = (12, 24)
    end = (12, 26)
    obs_list = []

    start_node = node(start, color=(255,0,0))
    end_node = node(end, color=(255,0,0))

    start_node.draw(window)
    end_node.draw(window)
    draw_inital(window)

    loop = True
    while loop:

        ev = pygame.event.get()
        for event in ev:
            if event.type == pygame.QUIT:
                pygame.quit()
            if pygame.mouse.get_pressed()[0]:
                try:
                    pos = pygame.mouse.get_pos()
                    mouse_press(pos)

                except AttributeError:
                    pass
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    loop = False
                    break

    path = astar(start, end)
    print(path)
    show_path(window)




    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


main()

在此提问前,问题应该是具体且明确的,并且提供最短的可重现特定问题的代码。如果你还没有缩小问题范围,那么这通常不适用于 Stack Overflow。可以考虑 [codereview.se] —— 在那里,可运行 的代码是被允许和必须的(尽管他们有其他规则——例如,您需要接受所有可能的增强建议,而不仅仅是与性能相关的建议)。请参阅《面向 Stack Overflow 用户的代码审查指南》(https://codereview.meta.stackexchange.com/questions/5777/a-guide-to-code-review-for-stack-overflow-users)。 - Charles Duffy
2
投票保持此问题开放。 - Unapiedra
1个回答

3

你的代码很难读懂。以下是一些观察结果。

小bug

child.g = current_node.g + 10

需要转化为

child.g = current_node.g + 1

这样解决了问题,但只是侥幸。它实际上并不是解决方案!

真正的 Bug

你正在向 open_list 添加太多元素。

        for new_position in ...:
            # create a new node
            new_node = node(node_position, green, current_node)
            ...
            children.append(new_node)
            #     print(children)
            # loop through children
            for child in children:
                ...
                open_list.append(child)

注意对于每个邻居(for new_position in ...)(共 8 次),您都会将一个新子节点添加到 children 列表中,然后遍历到目前为止所有的子节点。这样会添加 1/2 * 8 * 8 = 32 个子节点。
一个简单的解决方法是每个邻居/子节点只需要一个循环而不是现在的两个。
优先队列
您没有使用“优先队列”来作为开放列表。这意味着对于从开放列表中弹出的每个元素,您都要扫描整个列表。
请使用这个: https://docs.python.org/3/library/heapq.html 集合成员资格
for closed_child in closed_list:
    if child == closed_child:
        continue

应该可以实现,但需要将“node”类型变为可哈希的:

closed_list = set()
...
if child in closed_list:
    continue

启发式函数

错误:在计算欧几里得距离时,你忘记了开根号。


非常感谢您的澄清。对于那段代码,我感到很抱歉。在我的未来问题中,我会使它更易读。 - sorccery

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接