JH721 SW자율차 [Path Planning] //14주차-2

JH·2021년 7월 13일
0

자율 자동차 SW 개발

목록 보기
37/37

A* 알고리즘 코드

Astar Algorithm.py
Astar Algorithm_2(8Puzzle).py

from queue import PriorityQueue
from math import sqrt

class State(object):
  def __init__(self, value, parent, start = "801234567", goal="012345678") :
    self.children= []
    self.parent = parent
    self.value = value #"801234567" position을 의미
    self.dist = 0 #cost의미
    
    if parent: #parent가 존재한다면
      self.path = parent.path[:] #parent의 path를 물려받아 새롭게 추가한다
      self.path.append(value)
      self.start = parent.start # parent의 start를 따라간다. (start)
      self.goal = parent.goal #parent의 goal을 따라간다. (goal)

    else:
      self.path = [value]
      self.start = start
      self.goal = goal

    def GetDist(self): #뒤에서 정의
      pass
    def CreateChildren(self): #뒤에서 정의
      pass

#cost를 계산, 경로를 찾는 코드
class State_Puzzle(State):
  def __init__(self, value, parent, start=0, goal=0):
    #상속받은 State객체를 불러옴(State객체의 __init__함수를 실행)
    super(State_Puzzle, self).__init__(value, parent, start, goal)
    self.dist = self.GetDist() #State_Puzzle을 실행함과 동시에 GetDist()를 실행하겠다.

  def GetDist(self):
    #goal에 도달 한 경우
    if self.value == self.goal:
      return 0
    dist = 0 #시작 값

    # 9줄짜리 goal
    # -> 3*3을 의미함
    # 한줄에 3개가 있따.
    # 3개 -> size
    # self.goal = '012345678'

    # self.goal = '876543210'
    # len(self.goal) = 9

    # 801
    # 345
    # 672

    # 012
    # 345
    # 678

    size = sqrt(len(self.goal)).real #한 줄에 몇개 있는가, 실수 값으로 표현

    # 휴리스틱 계산 (Manhattan distance로 계산)
    for n in range(len(self.goal)):
      piece = self.goal[n]
      # 목표 상태(goal state)를 2D로 표현하면 그 위치는 어떻게 되는가
      # goal state = "012345678"
      goal_x = int(n / size) # 1 (4를 기준으로)
      goal_y = n - goal_x * size # 1 (나머지와 같은 의미)
      # 줄글로 표현되어있는 퍼즐을 시제로 2D space로 표현했을 때 x, y 좌표가 무엇인가

      # 현재 상태(goal state)를 2D로 표현하면 그 위치는 어떻게 되는가
      # self.value "801234567"
      value_x = int(self.value.index(piece) / size)
      value_y = self.value.index(piece) - value_x * size

      dist += abs(goal_x - value_x) + abs(goal_y - value_y) #heuristic, 앞으로 goal까지 몇칸을 움직여야 하는지 알 수 없...
      #현재 6이라는 piece가 (2,1 )위치에 있고,
      # 사실 목표로하는 state에서, 6은 (2,0)위치에 있어야 함
      # -> "6"이라는 piece에 대한 cost를, |2-2| + |1-0|이라고 heuristic하게 정의한다. (맨하탄 디스턴스)
      
    #dist = 1 + 1 + 2 + 0 + 0 + 0 + 0 + 0 + 4 -> 라고 heuristic을 정의할 것
    # dist = h
    # len(self.path) = g # 현재 상태로 오기 위해서 실제로 몇번 움직였나
    # f = g + h로 놓고, f를 최소화하는 방향으로 A*를 돌릴 것이다.
    return dist + len(self.path)


  def CreateChildren(self):
    
    size = int(sqrt(len(self.goal)).real)
    if not self.children:
      # self.value = "823407561"
      i = self.value.index("0")
      # 현재 상태에서, 0의 위치를 확인
      # 0의 상하좌우를 봐서, 움직일 수 있는 state가 있는지 확인한다.
      if not int(i % size) == size -1:
        val = self.value #"823407561"
        val = val[:i] + val[i] + val[i-1] + val[i+1:] # "823" + "0" + "4" + "7561"
        child = State_Puzzle(val, self)
        self.children.append(child)

      if not int(i % size) == 0 : #0의 위치가 맨 아랫줄에 있지 않다면(0이 위치하는 index가 6/7/8이 아니라면)
        val = self.value
        val = val[:i-1] + val[i] + val[i-1] + val[i+1:]
        child = State_Puzzle(val, self)
        self.children.append(child)

      if i>size - 1 : #0의 위치가 맨 윗줄에 있지 않다면(0이 위치하는 index가 0/1/2가 아니라면)
        val = self.value
        val = value[:i-size] + val[i] + val[i-size+1:i] + val[i-size] + val[i+1:] # 8 + 0 + 34+ 2 + 7561
        child = State_Puzzle(val, self)
        self.children.append(child)

    


# Astar 알고리즘 실행
class Astar_solver:
  def __init__(self, start, goal):
    self.path = []
    self.visitedQueue = [] #closed list
    self.priorityQueue = PriorityQueue() # 우선순위 queue(비용과 node를 함께 넣는 개념), open list에 해당
    self.start = start
    self.goal = goal

  def Solve(self):
    # parent는 해당 state를 방문하기 전에 어떤 static를 거쳐왔는지를 저장하는 list
    # start는 시작지점 state, goal은 목표지점 state
    #                         (현재상태), parent, start, goal)
    startState = State_Puzzle(self.start, [], self.start, self.goal)
    # count는 A*에 직접적으로 이용되지는 않을 것.
    # 코드를 돌리는 이용자 입장에서, A*알고리즘을 통해 총 몇번의 탐색 과정을 거쳤는지 확인하기 위함
    count = 0 #아무것도 세지 않은 상태.

    # priorityQueue는 fringe의 개념, 탐색 후보군을 모아놓는 list
    # (해당 후보군에 대한 cost, 해당 후보군을 몇번째로 탐색했는가, 탐색 후보군 state)
    # 여기서 해당 후보군에 대한 cost가 낮은 순서대로 priorityQuue에서 꺼내 탐색할 예정
    self.priorityQueue.put((0, count, startState)) #제일 먼저, start state를 집어넣는다. start state에 대한 cost = 0

    # 앞서 정의해둔 cost에 따라, 비용을 최소화하는 방향으로 탐색을 진행하겠다.
    # self.priorityQueue는 open list와 유사한 개념
    # self.visitedQueue는 closed list와 유사한 개념
    # while loop안에서는 priorityQueue(open list)에 있는 state를 한개 한개씩 꺼내서 goal state인지 확인하고, 아니라면 다음 state(children)을 확인하는 과정이 진행됩니다.
    # 1. priorityQueue에서 하나의 state를 뺀다
    # 2. 해당 state가 goal인지 확인하고, 아니라면 해당 state의 다음 state를 priorityQueue에 추가한다.
    # 3. 이 과정을 self.path가 없거나, priorityQueue가 배어있을 때 까지 반복합니다
    # priorityQueue가 비어있을 때 까지 반복함이라 함은, startstate에서부터 탐색을 시작했을 때, 도달할 수 있는 모든 경로를 고려한다는 의미로 
    # self.path가 비어있을 때까지 반복이라 함은 goal을 찾을 때 까지 반복한다는 의미
    while(not self.path and self.priorityQueue.qsize()):
      closestChild = self.priorityQueue.get()[2] # cost가 가장 낮은 state를 뽑습니다.
      closestChild.CreateChildren() #뽑은 대상에 대해서, 다음 탐색 후보군을 탐색합니다.
      self.visitedQueue.append(closestChild.value) # 현재 탐색하고 있는 state가, 이전에 탐색해본 노드가 아닐 경우에만 돌아간다.
      for child in closestChild.children: #뽑은 대상에 대한 다음 탐색 후보군들, 이들에 대한 cost 산출
        if child.value not in self.visitedQueue: #child에는 child로 오기까지 어떤 state를 거쳐 왔는지에 대한 정보가 path에 저장되어있음
          count += 1 #몇번째로 해당 state를 탐색하는지.
          if not child.dist: #child.dist=0인 경우. 이는 곧 goal을 찾은 경우를 의미함
            self.path = child.path # child에넌 child로 오기까지 어떤 state를 거쳐 왔는지에 대한 정보가 path에 저장되어있음
            break
          self.priorityQueue.put((child.dist, count, child)) # goal을 찾지 못한 경우에는 cost, count, child를  openlist에 추가하여 다음 탐색 단계에서 
    if not self.path:
      #위 while 루프가, goal을 찾음으로써 끝난다면 self.path = []이므로 여기에 걸리지않음
      #모든 state를 탐새갷ㅆ는데 최종 goal을 찾지 못한 경우에만 이 if에 걸려서 path를 찾을 수 없다는 문장이 출력됨
      print("Goal of " + self.goal + "is not possible")

    return self.path


start = ("801526347")
goal = ("012345678")
print('starting...')

a = Astar_solver(start, goal)
a.Solve()

for i in range(len(a.path)):
  print(i)
  print(a.path[i][:3])
  print(a.path[i][3:6])
  print(a.path[i][6:9])
  print('\n')

profile
JH.velog

0개의 댓글