A*算法原理與實現(python)

A*算法概述

A*算法是路徑規劃表現較好的算法之一。所謂路規劃是,從A點到B點找到一條合適的路徑,使得智能體完成從A到B的運動。所謂合適,是指智能體定義的參數指標,如步數最少,代價最低等等。不同的應用場景具有不同的參數指標。路徑規劃廣泛應用於智能體尋路、八數碼難題遊戲等場景。

A*算法原理

在路徑規劃中,由於可能有障礙存在,使得路徑上的一些點不可用。在路徑規劃中,需要避開這些點。在尋找合理路徑中,如何評價路徑呢。在A*算法中,通常採用
F=G+H F=G+H

來評估該路徑。其中G是從起始點A到指定點的移動代價。H是指定點到終點B的代價。其中G是可以通過計算獲得的,而H不可以準確獲得,其原因在於在指定點到終點B的路徑上我們並不知道障礙物是怎麼樣的,因此H是一種估計,通常被稱之爲試探法。這裏我們使用 Manhattan 方法,計算從當前方格橫向或縱向移動到達目標所經過的方格數,忽略對角移動,然後把總數乘以 10 。之所以叫做 Manhattan 方法,是因爲這很像統計從一個地點到另一個地點所穿過的街區數,而你不能斜向穿過街區。重要的是,計算 H 是,要忽略路徑中的障礙物。這是對剩餘距離的估算值,而不是實際值,因此才稱爲試探法。當H爲0時,A*算法退化成迪傑斯特拉算法。

移動代價可以通過距離公式來計算,距離的計算有多種維度,包括曼哈頓距離、歐拉距離等等,應根據具體的場景選取合適的計算方式。

在使用A算法進行路徑規劃時,有兩個重要的列表將被使用,那就是openlist和closelist。openlist存放是路徑可能存放的點,即openlist是個待檢查的列表。而closelist是不在需要關注的點,即某點被openlist移除,就會被放到closelist。
A
的算法步驟如下:
1、把起點加入到openlist
2、然後重複下面過程
*.遍歷openlist,找到路徑評價F值最小的節點,把它當做當前要處理的節點。
*.把該節點從openlist移到closelist
*.對該節點的所有鄰居節點做如下處理
**.如果不在openlist裏,則加入openlist,並把當前節點設置爲父節點,記錄該鄰居節的F,G,H值
**.如果在openlist,如果當前節點到該鄰居節點的路徑代價小於當前節點的路徑代價,則用更小的值來代替該 鄰居節點的G,同時把當前節點設置爲鄰居節點的父節點。
3、當把終點加到openlist時候,證明路徑找到,並沿着父節點可以找到路徑,否則查找終點失敗,並且openlist是空的,此時沒有路徑

A*算法代碼實現示例

一個代碼示例如下:

# -*- coding: utf-8 -*-
# @Time    : 2019/12/2 22:37
# @Author  : HelloWorld!
# @FileName: A_Star.py
# @Software: PyCharm
# @Operating System: Windows 10
# @Python.version: 3.5


class Array2D:
    """

        說明:

            1.構造方法需要兩個參數,即二維數組的寬和高

            2.成員變量w和h是二維數組的寬和高

            3.使用:‘對象[x][y]’可以直接取到相應的值

            4.數組的默認值都是0

    """

    def __init__(self, w, h):

        self.w = w

        self.h = h

        self.data = []

        self.data = [[0 for y in range(h)] for x in range(w)]

    def showArray2D(self):

        for y in range(self.h):

            for x in range(self.w):
                print(self.data[x][y], end=' ')

            print("")

    def __getitem__(self, item):

        return self.data[item]


class Point:
    """

    表示一個點

    """

    def __init__(self, x, y):
        self.x = x;
        self.y = y

    def __eq__(self, other):
        if self.x == other.x and self.y == other.y:
            return True

        return False

    def __str__(self):
        return "x:" + str(self.x) + ",y:" + str(self.y)


class AStar:
    """

    AStar算法的Python3.x實現

    """

    class Node:  # 描述AStar算法中的節點數據

        def __init__(self, point, endPoint, g=0):
            self.point = point  # 自己的座標

            self.father = None  # 父節點

            self.g = g  # g值,g值在用到的時候會重新算

            self.h = (abs(endPoint.x - point.x) + abs(endPoint.y - point.y)) * 10  # 計算h值

    def __init__(self, map2d, startPoint, endPoint, passTag=0):

        """

        構造AStar算法的啓動條件

        :param map2d: Array2D類型的尋路數組

        :param startPoint: Point或二元組類型的尋路起點

        :param endPoint: Point或二元組類型的尋路終點

        :param passTag: int類型的可行走標記(若地圖數據!=passTag即爲障礙)

        """

        # 開啓表

        self.openList = []

        # 關閉表

        self.closeList = []

        # 尋路地圖

        self.map2d = map2d

        # 起點終點

        if isinstance(startPoint, Point) and isinstance(endPoint, Point):

            self.startPoint = startPoint

            self.endPoint = endPoint

        else:

            self.startPoint = Point(*startPoint)

            self.endPoint = Point(*endPoint)

        # 可行走標記

        self.passTag = passTag

    def getMinNode(self):

        """

        獲得openlist中F值最小的節點

        :return: Node

        """

        currentNode = self.openList[0]

        for node in self.openList:

            if node.g + node.h < currentNode.g + currentNode.h:
                currentNode = node

        return currentNode

    def pointInCloseList(self, point):

        for node in self.closeList:

            if node.point == point:
                return True

        return False

    def pointInOpenList(self, point):

        for node in self.openList:

            if node.point == point:
                return node

        return None

    def endPointInCloseList(self):

        for node in self.openList:

            if node.point == self.endPoint:
                return node

        return None

    def searchNear(self, minF, offsetX, offsetY):

        """

        搜索節點周圍的點

        :param minF:F值最小的節點

        :param offsetX:座標偏移量

        :param offsetY:

        :return:

        """

        # 越界檢測

        if minF.point.x + offsetX < 0 or minF.point.x + offsetX > self.map2d.w - 1 or minF.point.y + offsetY < 0 or minF.point.y + offsetY > self.map2d.h - 1:
            return

        # 如果是障礙,就忽略

        if self.map2d[minF.point.x + offsetX][minF.point.y + offsetY] != self.passTag:
            return

        # 如果在關閉表中,就忽略

        currentPoint = Point(minF.point.x + offsetX, minF.point.y + offsetY)

        if self.pointInCloseList(currentPoint):
            return

        # 設置單位花費

        if offsetX == 0 or offsetY == 0:

            step = 10

        else:

            step = 14

        # 如果不再openList中,就把它加入openlist

        currentNode = self.pointInOpenList(currentPoint)

        if not currentNode:
            currentNode = AStar.Node(currentPoint, self.endPoint, g=minF.g + step)

            currentNode.father = minF

            self.openList.append(currentNode)

            return

        # 如果在openList中,判斷minF到當前點的G是否更小

        if minF.g + step < currentNode.g:  # 如果更小,就重新計算g值,並且改變father

            currentNode.g = minF.g + step

            currentNode.father = minF

    def start(self):

        """

        開始尋路

        :return: None或Point列表(路徑)

        """

        # 判斷尋路終點是否是障礙

        if self.map2d[self.endPoint.x][self.endPoint.y] != self.passTag:
            return None

        # 1.將起點放入開啓列表

        startNode = AStar.Node(self.startPoint, self.endPoint)

        self.openList.append(startNode)

        # 2.主循環邏輯

        while True:

            # 找到F值最小的點

            minF = self.getMinNode()

            # 把這個點加入closeList中,並且在openList中刪除它

            self.closeList.append(minF)

            self.openList.remove(minF)

            # 判斷這個節點的上下左右節點

            self.searchNear(minF, 0, -1)

            self.searchNear(minF, 0, 1)

            self.searchNear(minF, -1, 0)

            self.searchNear(minF, 1, 0)

            # 判斷是否終止

            point = self.endPointInCloseList()

            if point:  # 如果終點在關閉表中,就返回結果

                # print("關閉表中")

                cPoint = point

                pathList = []

                while True:

                    if cPoint.father:

                        pathList.append(cPoint.point)

                        cPoint = cPoint.father

                    else:

                        # print(pathList)

                        # print(list(reversed(pathList)))

                        # print(pathList.reverse())

                        return list(reversed(pathList))

            if len(self.openList) == 0:
                return None

if __name__ == '__main__':

    #創建一個10*10的地圖

    map2d=Array2D(10,10)

    #設置障礙

    map2d[4][0]= 1

    map2d[4][1] = 1

    map2d[4][2] = 0

    map2d[4][3] = 1

    map2d[4][4] = 1

    map2d[4][5] = 1

    map2d[4][6] = 1

    #顯示地圖當前樣子

    map2d.showArray2D()

    #創建AStar對象,並設置起點爲0,0終點爲9,0

    aStar=AStar(map2d,Point(0,0),Point(9,0))

    #開始尋路

    pathList=aStar.start()

    #遍歷路徑點,在map2d上以'8'顯示

    for point in pathList:

        map2d[point.x][point.y]=8

        # print(point)

    print("----------------------")

    #再次顯示地圖

    map2d.showArray2D()

結果如下:

0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
----------------------
0 8 8 8 1 8 8 8 8 8 
0 0 0 8 1 8 0 0 0 0 
0 0 0 8 8 8 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 

參考文獻
https://blog.csdn.net/qq_39687901/article/details/88554716
https://blog.csdn.net/ctwy291314/article/details/95055008
https://blog.csdn.net/qq_39687901/article/details/80753433

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章