refactoring, perfomance optimizations, bug fixing

This commit is contained in:
Vadzim Valchkovich 2023-06-07 12:43:21 +02:00
parent b26f7b244e
commit ac1659d484
20 changed files with 240 additions and 103 deletions

View File

@ -1,18 +1,22 @@
import os
import random
from pathlib import Path
import logging
import numpy as np
import tensorflow as tf
from tensorflow import keras
from termcolor import colored
class Predictor:
def __init__(self):
# Turn off interactive logging
tf.get_logger().setLevel(logging.ERROR)
# Load the trained model
self.model = keras.models.load_model('Network/trained_model.h5')
# Load the class names
self.class_names = ['table', 'done', 'order']
self.class_names = ['table', 'table', 'order']
# Path to the folder containing test images
self.test_images_folder = 'Network/Testing/'
@ -29,11 +33,11 @@ class Predictor:
test_image = np.reshape(test_image, (1, 100, 100, 3))
# Make predictions
predictions = self.model.predict(test_image)
predictions = self.model.predict(test_image, verbose=None)
predicted_class_index = np.argmax(predictions[0])
predicted_class = self.class_names[predicted_class_index]
print(predicted_class)
print(colored("Predicted class: ", "yellow")+f"{predicted_class}")
return predicted_class
def random_path_img(self) -> str:

View File

@ -1,20 +1,28 @@
import random
from termcolor import colored
from src.Engine import Engine
from src.obj.Waiter import Waiter
from src.obj.Block import Block
from src.obj.Kitchen import Kitchen
from src.obj.Table import Table
from src.UserController import UserController
from src.StateController import StateController
from src.controller.UserController import UserController
from src.controller.StateController import StateController
from src.controller.ImageController import ImageController
from Network.Predictor import Predictor
print(colored("Initialization...", "green"))
SCREEN_SIZE = [800, 800]
SQUARE_SIZE = 40
SQUARE_SIZE = 80
SLEEP_DURATION = 0.125
waiter = Waiter([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE)
kitchen = Kitchen([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE)
store = ImageController(SQUARE_SIZE)
waiter = Waiter([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE, store)
kitchen = Kitchen([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE, store)
objects = []
for i in range(150):
for i in range(25):
pos = [0, 0]
@ -23,15 +31,19 @@ for i in range(150):
random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE - 1)]
if (random.randint(0, 1)):
objects.append(Block(pos, 0, SQUARE_SIZE, SCREEN_SIZE))
objects.append(Block(pos, 0, SQUARE_SIZE, SCREEN_SIZE, store))
else:
objects.append(Table(pos, 0, SQUARE_SIZE, SCREEN_SIZE))
objects.append(Table(pos, 0, SQUARE_SIZE, SCREEN_SIZE, store))
user = UserController(waiter)
state = StateController(waiter)
engine = Engine(SCREEN_SIZE, SQUARE_SIZE, kitchen, user, state)
predictor = Predictor()
engine = Engine(SCREEN_SIZE, SQUARE_SIZE, kitchen,
user, state, predictor, SLEEP_DURATION)
for o in objects:
engine.subscribe(o)
print(colored("Starting model...", "green"))
engine.loop()

View File

@ -1,26 +1,31 @@
import time
import pygame
from .obj.Goal import Goal
from .obj.Object import Object
from .obj.Waiter import Waiter
from .obj.Kitchen import Kitchen
from .UserController import UserController
from .StateController import StateController
from .decisionTree.TreeConcept import TreeEngine
from src.obj.Goal import Goal
from src.obj.Object import Object
from src.obj.Waiter import Waiter
from src.obj.Kitchen import Kitchen
from src.controller.UserController import UserController
from src.controller.StateController import StateController
from src.decisionTree.TreeConcept import TreeEngine
from Network.Predictor import Predictor
from queue import PriorityQueue
from termcolor import colored
from src.obj.PriorityItem import PriorityItem
class Engine:
def __init__(self, screen_size, square_size, kitchen: Kitchen, user: UserController, state: StateController):
def __init__(self, screen_size, square_size, kitchen: Kitchen, user: UserController, state: StateController, predictor: Predictor, sleep_duration):
pygame.display.set_caption('Waiter Agent')
self.action_clock = 0
self.sleep_duration = sleep_duration
self.tree = TreeEngine()
self.kitchen: Kitchen = kitchen
self.user: Waiter = user
self.state: StateController = state
self.predictor: Predictor = predictor
self.screen_size: list[int] = screen_size
self.screen = pygame.display.set_mode(self.screen_size)
@ -63,7 +68,9 @@ class Engine:
def action(self):
if not self.state.path:
if self.goals:
self.state.graphsearch(self)
if not self.state.graphsearch(self) and self.goals:
self.objects.remove(self.goals.pop().parent)
self.user.handler(self)
self.predict()
else:
@ -72,25 +79,31 @@ class Engine:
state = self.user.obj.changeState(self.state.path.pop())
self.clock_increment(state.cost)
print("Action:\t{0}\tCost:\t{1}\tCost so far: {2}\tBattery: {3}".format(
state.agent_role,
state.cost,
state.cost_so_far,
self.user.obj.battery)
)
# '''
print(colored("Action:\t", "blue")+f"{state.agent_role}", end='\t')
print(colored("Cost:\t", "blue")+f"{state.cost}", end='\t')
print(colored("Cost so far: ", "blue") +
f"{state.cost_so_far}", end='\t')
print(colored("Battery: ", "blue") +
f"{self.user.obj.battery}", end='\t')
print(colored("Basket capacity: ", "blue") +
f"{self.user.obj.basket_capacity}", end='\t')
print(colored("Memory capacity: ", "blue") +
f"{self.user.obj.memory_capacity}")
# '''
# waiter interaction
for o in self.objects:
if self.user.obj.chechNeighbor(o):
o.updateState(self.action_clock)
o.updateState(self.action_clock, self.predictor)
if o.compare_pos(self.user.obj.position):
o.action(self.user.obj, self.action_clock)
if self.kitchen.compare_pos(self.user.obj.position):
self.kitchen.action(self.user.obj, self.action_clock)
time.sleep(0.5)
time.sleep(self.sleep_duration)
def clock_increment(self, action_time):
self.action_clock += action_time
@ -119,8 +132,8 @@ class Engine:
pygame.display.flip()
def appendGoalPosition(self, position):
self.goals.append(Goal(position, self.square_size, self.screen_size))
def appendGoal(self, parent):
self.goals.append(Goal(parent))
def predict(self):
@ -170,16 +183,19 @@ class Engine:
# empty | new order | waiting for dish | have a dish |
# is actual
1 if o.isActual() else 0,
1 if o.isActual(self.action_clock) else 0,
# yes | no |
]
p = self.tree.make_predict(dataset)
goal_queue.put((p, o.position))
goal_queue.put(PriorityItem(o, p[0]))
self.goals.clear()
if goal_queue.queue:
priority, goal = goal_queue.queue[0]
if priority == 2:
self.appendGoalPosition(self.kitchen.position)
item: PriorityItem = goal_queue.get()
if item.priority == 2:
self.appendGoal(self.kitchen)
if self.kitchen.compare_pos(self.user.obj.position):
self.clock_increment(100)
else:
self.appendGoalPosition(goal)
self.appendGoal(item.obj)

View File

@ -0,0 +1,20 @@
import os
import pygame
class ImageController():
def __init__(self, square_size):
self.images = {}
for role in os.listdir('src/img/'):
image = pygame.image.load('src/img/{0}'.format(role))
image = pygame.transform.scale(
image,
(square_size, square_size)
)
self.images[role.split('.')[0]] = image
def getRoleImage(self, role):
return self.images[role]

View File

@ -1,11 +1,12 @@
from .obj.TemporaryState import TemporaryState
from src.obj.TemporaryState import TemporaryState
from queue import PriorityQueue
from termcolor import colored
class StateController:
def __init__(self, istate):
self.path = []
self.explored = []
def __init__(self, istate: TemporaryState):
self.path: list[TemporaryState] = []
self.explored: list[TemporaryState] = []
self.fringe = PriorityQueue()
self.istate = istate
self.goal = istate.position
@ -23,14 +24,14 @@ class StateController:
self.path.append(self.path[-1].parent)
total_cost += self.path[-1].cost
print("Total path cost:\t{0}".format(total_cost))
print(colored(f"Total path cost: ", "green")+f"{total_cost}")
return self.path
def graphsearch(self, engine): # A*
print("Search path")
self.goal = list(engine.goals[-1].position)
print(colored(f"Search path to ", "yellow")+f"{self.goal}")
self.reset()
start = TemporaryState(self.istate, 0)
@ -49,16 +50,13 @@ class StateController:
engine.redraw()
self.reset()
for o in engine.objects:
o.compare_pos(engine.goals[-1].position)
o.agent_role = "block"
engine.goals.pop()
print("Not found")
print(colored("Not found", "red"))
return False
def succ(self, state, engine):
def succ(self, state: TemporaryState, engine):
if state.collide_test():
return
elif any(e.compare(state) for e in self.explored):
@ -74,9 +72,7 @@ class StateController:
state.cost_so_far = self.explored[-1].cost_so_far + state.cost
in_explored = any([state.compare(s) for s in self.explored]
)
in_explored = any([state.compare(s) for s in self.explored])
in_frige = any([state.compare(f) for f in self.fringe.queue])
if not in_explored and not in_frige:

View File

@ -9,7 +9,7 @@ def convertDataset():
4: ["no", "yes"],
5: ["no", "yes"],
6: ["no", "yes"],
7: ["empty", "new order", "waiting for dish", "have a dish"],
7: ["empty", "new order", "waiting for cooking", "waiting for the dish"],
8: ["no", "yes"],
9: ["high priority", "low priority", "return to kitchen"]
}

View File

@ -6,7 +6,8 @@ def generateRawDataset():
battery_values = ['high', 'low']
distance_values = ['far', 'close']
mood_values = ['undefined', 'good', 'bad']
status_values = ['empty', 'new order', 'waiting for dish', 'have a dish']
status_values = ["empty", "new order",
"waiting for cooking", "waiting for the dish"]
other = ['no', 'yes']
training_data = []
@ -97,9 +98,9 @@ def getPriority(dataset) -> str:
return PRIORITY['kitchen']
elif IS_ACTUAL == "no":
return PRIORITY['high']
elif STATUS_OF_TABLE == "empty" or STATUS_OF_TABLE == "have a dish":
elif STATUS_OF_TABLE == "empty" or STATUS_OF_TABLE == "waiting for cooking":
return PRIORITY['kitchen']
elif STATUS_OF_TABLE == "new order":
elif STATUS_OF_TABLE == "new order" or STATUS_OF_TABLE == "waiting for the dish":
return mood_and_distance()
elif BASKET_IS_EMPTY == "yes":
return PRIORITY['kitchen']

BIN
src/img/dish_cooking.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
src/img/dish_done.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
src/img/unknown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View File

@ -2,8 +2,8 @@ from src.obj.Object import Object
class Block(Object):
def __init__(self, position, orientation, square_size, screen_size):
super().__init__("block", position, orientation, square_size, screen_size)
def __init__(self, position, orientation, square_size, screen_size, store):
super().__init__("block", position, orientation, square_size, screen_size, store)
def collide_test(self, waiter: Object) -> bool:
return waiter.position == self.position

View File

@ -2,8 +2,15 @@ from src.obj.Object import Object
class Goal(Object):
def __init__(self, position, square_size, screen_size):
super().__init__("goal", position, 0, square_size, screen_size)
def __init__(self, parent: Object):
super().__init__("goal", parent.position, 0,
parent.square_size, parent.screen_size, parent.store)
self.parent = parent
if parent.agent_role != 'kitchen':
print("waiting_time:", parent.waiting_time, end='\t')
print("cooking_time:", parent.cooking_time, end='\t')
print("is_actual:", parent.is_actual)
def collide_test(self, waiter: Object) -> bool:
return waiter.position == self.position

View File

@ -2,9 +2,30 @@ from src.obj.Object import Object
class Kitchen(Object):
def __init__(self, position, orientation, square_size, screen_size):
super().__init__("kitchen", position, orientation, square_size, screen_size)
def __init__(self, position, orientation, square_size, screen_size, store):
super().__init__("kitchen", position, orientation, square_size, screen_size, store)
self.cooking = []
self.done = []
def action(self, waiter, current_time):
waiter.combine_orders(current_time)
self.cook_dishes(current_time)
waiter.combine_orders(current_time, self)
waiter.recharge()
def cook_dishes(self, current_time):
for table in self.cooking:
if table.dish_is_ready(current_time):
self.done.append(table)
table.setMark("dish_done")
for table in self.done:
if table in self.cooking:
self.cooking.remove(table)
def take_order(self, order):
order.start_cooking()
order.setMark("dish_cooking")
self.cooking.append(order)
def has_dishes(self) -> bool:
return len(self.done) > 0

13
src/obj/Mark.py Normal file
View File

@ -0,0 +1,13 @@
from src.obj.Object import Object
class Mark(Object):
def __init__(self, parent, mark_type):
super().__init__(
mark_type,
parent.position,
parent.orientation,
parent.square_size,
parent.screen_size,
parent.store
)

View File

@ -1,19 +1,17 @@
import pygame
from src.controller.ImageController import ImageController
class Object:
def __init__(self, agent_role, position, orientation, square_size, screen_size):
def __init__(self, agent_role, position, orientation, square_size, screen_size, store: ImageController):
self.agent_role = agent_role
self.position = position
self.orientation = orientation % 4
self.square_size = square_size
self.screen_size = screen_size
self.square_count = screen_size[0] // square_size
self.image = pygame.image.load(
'src/img/{0}.png'.format(self.agent_role))
self.image = pygame.transform.scale(
self.image, (self.square_size, self.square_size))
self.image = store.getRoleImage(agent_role)
self.store = store
self.rect = pygame.Rect(position[0] * square_size,
position[1] * square_size,
@ -21,9 +19,7 @@ class Object:
def change_role(self, new_role):
self.agent_role = new_role
self.image = pygame.image.load('src/img/{0}.png'.format(new_role))
self.image = pygame.transform.scale(
self.image, (self.square_size, self.square_size))
self.image = self.store.getRoleImage(new_role)
def get_angle(self):
'''
@ -58,5 +54,5 @@ class Object:
def action(self, obj):
pass
def updateState(self, current_time):
def updateState(self, current_time, predictor):
pass

19
src/obj/PriorityItem.py Normal file
View File

@ -0,0 +1,19 @@
class PriorityItem():
def __init__(self, obj, priority):
self.obj = obj
self.priority = priority
def __eq__(self, __value) -> bool:
return self.priority == __value.priority
def __lt__(self, __value) -> bool:
return self.priority < __value.priority
def __le__(self, __value) -> bool:
return self.priority <= __value.priority
def __gt__(self, __value) -> bool:
return self.priority > __value.priority
def __ge__(self, __value) -> bool:
return self.priority >= __value.priority

View File

@ -1,31 +1,45 @@
import random
from src.obj.Object import Object
from Network.Predictor import Predictor
from src.obj.Mark import Mark
class Table(Object):
def __init__(self, position, orientation, square_size, screen_size):
super().__init__("table", position, orientation, square_size, screen_size)
def __init__(self, position, orientation, square_size, screen_size, store):
super().__init__("table", position, orientation, square_size, screen_size, store)
self.waiting_time = 0
self.cooking_time = 0
self.is_actual = False
self.p = Predictor()
self.mark = Mark(self, "unknown")
def isActual(self, current_time):
if self.is_actual and self.agent_role == "table":
if current_time - self.waiting_time > 1000:
self.reset(current_time)
def isActual(self):
return self.is_actual
def updateState(self, current_time):
def setMark(self, mark_type):
self.mark = Mark(self, mark_type)
def unsetMark(self):
self.mark = None
def blit(self, screen):
super().blit(screen)
if self.mark:
self.mark.blit(screen)
def updateState(self, current_time, predictor):
if self.is_actual:
return
self.unsetMark()
self.is_actual = True
# here must be neural network choise
new_role = self.p.predict(self.p.random_path_img())
new_role = predictor.predict(predictor.random_path_img())
self.change_role(new_role, current_time)
if self.agent_role == "table":
return
elif self.agent_role == "wait":
self.cooking_time = random.randint(0, 300)
if self.agent_role == "wait":
self.cooking_time = random.randint(0, 1000)
def dish_is_ready(self, current_time):
return current_time - self.waiting_time > self.cooking_time
@ -46,6 +60,7 @@ class Table(Object):
def reset(self, current_time):
self.is_actual = False
self.mark = Mark(self, "unknown")
self.change_role("table", current_time)
def set_order(self, current_time):
@ -56,6 +71,10 @@ class Table(Object):
if self.agent_role == "order":
self.change_role("wait", current_time)
def start_cooking(self):
if self.agent_role == "wait":
self.cooking_time = random.randint(0, 1000)
def set_done(self, current_time):
if self.agent_role == "wait":
self.change_role("done", current_time)
@ -72,12 +91,12 @@ class Table(Object):
def get_customers_count(self) -> int:
return self.customers
def get_mood(self, current_time) -> int: # перапісаць
def get_mood(self, current_time) -> int:
if self.agent_role == "table":
return 2 # undefined
diff = current_time - self.waiting_time
return 0 if diff >= 300 else 1 # 0 - bad; 1 - good
return 0 if diff >= 1000 else 1 # 0 - bad; 1 - good
def action(self, waiter, current_time):
if self.is_order():

View File

@ -8,7 +8,11 @@ class TemporaryState(Waiter):
copy.copy(parent.orientation),
copy.copy(parent.square_size),
copy.copy(parent.screen_size),
copy.copy(parent.basket))
parent.store,
copy.copy(parent.basket),
copy.copy(parent.memory),
copy.copy(parent.battery)
)
self.agent_role = action
self.parent = parent
self.cost = cost

View File

@ -3,11 +3,11 @@ from src.obj.Object import Object
class Waiter(Object):
def __init__(self, position, orientation, square_size, screen_size, basket=[], memory=[], battery=300):
super().__init__("waiter", position, orientation, square_size, screen_size)
def __init__(self, position, orientation, square_size, screen_size, store, basket=[], memory=[], battery=300):
super().__init__("waiter", position, orientation, square_size, screen_size, store)
self.battery = battery
self.basket_size = 4
self.memory_size = 4
self.basket_capacity = 4
self.memory_capacity = 4
self.basket = basket
self.memory = memory
self.prev_position = copy.deepcopy(self.position)
@ -17,6 +17,7 @@ class Waiter(Object):
self.position = copy.deepcopy(state.position)
self.orientation = copy.copy(state.orientation)
self.basket = copy.copy(state.basket)
self.memory = copy.copy(state.memory)
self.battery -= state.cost
return state
@ -24,24 +25,32 @@ class Waiter(Object):
return table in self.basket
def basket_is_full(self) -> bool:
return self.basket_size == 0
return self.basket_capacity == 0
def basket_is_empty(self) -> bool:
return self.basket_size == 4
return self.basket_capacity == 4
def combine_orders(self, current_time):
while not self.basket_is_full() and not self.memory_is_empty():
dish = self.memory.pop()
def combine_orders(self, current_time, kitchen):
# take orders
for table in self.memory:
kitchen.take_order(table)
self.memory = []
self.memory_capacity = 4
# take dishes
while not self.basket_is_full() and kitchen.has_dishes():
dish = kitchen.done.pop()
dish.set_done(current_time)
self.basket.append(dish)
self.basket_size -= 1
self.memory_size += 1
self.basket_capacity -= 1
def deliver_dish(self, table, current_time):
if table in self.basket:
table.reset(current_time)
self.basket.remove(table)
self.basket_size += 1
self.basket_capacity += 1
def order_in_memory(self, table) -> bool:
return table in self.memory
@ -50,7 +59,7 @@ class Waiter(Object):
return not self.memory
def memory_is_full(self) -> bool:
return self.memory_size == 0
return self.memory_capacity == 0
def collect_order(self, table, current_time):
if self.memory_is_full():
@ -58,7 +67,7 @@ class Waiter(Object):
if table.agent_role == "order":
table.set_wait(current_time)
self.memory.append(table)
self.memory_size -= 1
self.memory_capacity -= 1
def battery_status(self) -> int:
return 1 if self.battery >= 100 else 0