add auto mode moving - integrate astar, neural network and decision tree

This commit is contained in:
Dominik Cupał 2021-06-23 14:26:59 +02:00
parent f07eabf522
commit 8851493d9d
9 changed files with 128 additions and 138 deletions

View File

@ -10,6 +10,12 @@ virtualenv venv
source activate source activate
pip3 install -r requirements.txt pip3 install -r requirements.txt
``` ```
#### 2.1 Graphviz
For ubuntu, we probably need to download graphviz library by apt-get
```bash
sudo apt-get install graphviz
```
### 3. Run application ### 3. Run application
```bash ```bash
python3 main.py python3 main.py
@ -37,13 +43,20 @@ HORIZONTAL_NUM_OF_FIELDS = 3
#### 4.1 Save generated map: #### 4.1 Save generated map:
```bash ```bash
python main.py --save-map python3 main.py --save-map
``` ```
Map will be saved in maps directory. Map will be saved in maps directory.
Generated filename: map-uuid Generated filename: map-uuid
#### 4.2 Load map #### 4.2 Load map
```bash ```bash
python main.py --load-map=name_of_map python3 main.py --load-map=name_of_map
``` ```
Map must be in the maps directory with json extension. Map must be in the maps directory with json extension.
#### 4.3 Auto mode
Tractor will make own decisions such as harvesting, hydrating and so on using a decision tree.
It also will be moving by a star algorithm and it will be checking fields using a neural network during harvesting crops.
```bash
python3 main.py --auto-mode
```

View File

@ -24,6 +24,13 @@ class App:
self.__tractor = Tractor(self.__board) self.__tractor = Tractor(self.__board)
self.__bot_is_running = Event() self.__bot_is_running = Event()
self.__moves = None self.__moves = None
self.__auto_mode = self.get_auto_mode_arg(args)
def get_auto_mode_arg(self, args: list[Arg]):
for arg in args:
if arg.get_arg_name() == AUTO_MODE:
return True
return False
def initialize(self): def initialize(self):
pygame.init() pygame.init()
@ -79,7 +86,7 @@ class App:
self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running) self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running)
if keys[pygame.K_c]: if keys[pygame.K_c]:
self.get_moves_by_a_star() self.get_moves_by_a_star_with_random_coords()
if not self.__moves: if not self.__moves:
print(f"A Star is failed") print(f"A Star is failed")
else: else:
@ -88,7 +95,7 @@ class App:
self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running) self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running)
if keys[pygame.K_a]: if keys[pygame.K_a]:
self.auto_moving() self.auto_moving_mode()
def update_screen(self) -> None: def update_screen(self) -> None:
pygame.display.flip() pygame.display.flip()
@ -96,19 +103,46 @@ class App:
def quit(self) -> None: def quit(self) -> None:
pygame.quit() pygame.quit()
def auto_moving(self) -> None: def auto_moving_mode(self):
self.__tractor.choose_action() coords, action = self.get_coords_and_action()
def get_moves_by_a_star(self) -> None:
x, y = self.__tractor.get_position() x, y = self.__tractor.get_position()
node = Node(None, x, y, self.__tractor.get_direction(), 0, "movement", "initial state") if coords is not None:
board = copy.deepcopy(self.__board) x1, y1 = coords
else:
x1, y1 = None, None
print(action, coords)
if action != A_DO_NOTHING and x1 == x and y == y1:
self.__tractor.do_action_on_fields(action)
elif action == A_DO_NOTHING:
print(action)
elif not self.__moves:
print(f"A Star is failed")
else:
print(f"A Star is succeed")
self.__bot_is_running.set()
self.__tractor.run_auto_bot_handler(action, self.__moves, self.__bot_is_running)
def get_coords_and_action(self) -> tuple[tuple[int, int], str]:
coords, action = self.__tractor.choose_action()
if coords is not None:
self.get_moves_by_a_star(coords)
else:
self.__moves = None
return coords, action[0]
def get_moves_by_a_star_with_random_coords(self) -> None:
x1 = random.randint(0, HORIZONTAL_NUM_OF_FIELDS) x1 = random.randint(0, HORIZONTAL_NUM_OF_FIELDS)
y1 = random.randint(0, VERTICAL_NUM_OF_FIELDS) y1 = random.randint(0, VERTICAL_NUM_OF_FIELDS)
dest = (x1, y1) dest = (x1, y1)
self.__moves = AStar.search_solution(PriorityQueue(), Queue(), dest, node, self.get_moves_by_a_star(dest)
def get_moves_by_a_star(self, coords: tuple[int, int]) -> None:
x, y = self.__tractor.get_position()
node = Node(None, x, y, self.__tractor.get_direction(), 0, "movement", "initial state")
board = copy.deepcopy(self.__board)
self.__moves = AStar.search_solution(PriorityQueue(), Queue(), coords, node,
lambda n=node, b=board: AStar.succ(n, b), lambda n=node, b=board: AStar.succ(n, b),
lambda d=dest, n=node: AStar.goaltest_by_coords(d, n), board) lambda d=coords, n=node: AStar.goaltest_by_coords(d, n), board)
def get_moves_by_bfs(self) -> None: def get_moves_by_bfs(self) -> None:
x, y = self.__tractor.get_position() x, y = self.__tractor.get_position()
@ -123,11 +157,14 @@ class App:
while self.__running: while self.__running:
self.__clock.tick(FPS) self.__clock.tick(FPS)
for event in pygame.event.get(): for event in pygame.event.get():
self.event_handler(event) self.event_handler(event)
if not self.__bot_is_running.is_set(): if not self.__bot_is_running.is_set() and not self.__auto_mode:
self.keys_pressed_handler() self.keys_pressed_handler()
if not self.__bot_is_running.is_set() and self.__auto_mode:
self.auto_moving_mode()
self.loop_handler() self.loop_handler()
self.update_screen() self.update_screen()

View File

@ -89,7 +89,7 @@ class AStar(Graphsearch):
@staticmethod @staticmethod
def search_solution(fringe: PriorityQueue, explored: Queue, destination: tuple[int, int], istate: Node, def search_solution(fringe: PriorityQueue, explored: Queue, destination: tuple[int, int], istate: Node,
succ: Callable[[Node, Board], list], succ: Callable[[Node, Board], list],
goaltest: Callable[[tuple[int,int], Node], bool], board: Board) -> Union[bool, list]: goaltest: Callable[[tuple[int, int], Node], bool], board: Board) -> Union[bool, list]:
print(f"Start A*") print(f"Start A*")
fringe.put(PriorityItem(istate, 0)) fringe.put(PriorityItem(istate, 0))

View File

@ -26,7 +26,9 @@ class CommandLineParser:
if arg == SAVE_MAP: if arg == SAVE_MAP:
print("Saving map") print("Saving map")
results.append(Arg(SAVE_MAP)) results.append(Arg(SAVE_MAP))
if arg == AUTO_MODE:
print("Auto mode")
results.append(Arg(AUTO_MODE))
elif arg.find(LOAD_MAP, 0, len(LOAD_MAP)-1): elif arg.find(LOAD_MAP, 0, len(LOAD_MAP)-1):
cmd = arg.split("=") cmd = arg.split("=")
if len(cmd) == 2: if len(cmd) == 2:

View File

@ -1,68 +0,0 @@
from tensorflow.keras.models import Sequential, save_model
from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras.losses import sparse_categorical_crossentropy
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from config import RESOURCE_DIR
import os
# labels
labels = ["cabbage", "carrot", "corn", "lettuce", "paprika", "potato", "tomato"]
# Data configuration
training_set_folder = os.path.join(RESOURCE_DIR, 'smaller_train')
test_set_folder = os.path.join(RESOURCE_DIR, 'smaller_test')
# Model config
batch_size = 25
img_width, img_height, img_num_channels = 25, 25, 3
loss_function = sparse_categorical_crossentropy
no_classes = 7
no_epochs = 40
optimizer = Adam()
verbosity = 1
# Determine shape of the data
input_shape = (img_width, img_height, img_num_channels)
# Create a generator
train_datagen = ImageDataGenerator(
rescale=1./255
)
train_datagen = train_datagen.flow_from_directory(
training_set_folder,
save_to_dir=os.path.join(RESOURCE_DIR, "adapted-images"),
save_format='jpeg',
batch_size=batch_size,
target_size=(25, 25),
class_mode='sparse')
# Create the model
model = Sequential()
model.add(Conv2D(16, kernel_size=(5, 5), activation='relu', input_shape=input_shape))
model.add(Conv2D(32, kernel_size=(5, 5), activation='relu'))
model.add(Conv2D(64, kernel_size=(5, 5), activation='relu'))
model.add(Conv2D(128, kernel_size=(5, 5), activation='relu'))
model.add(Flatten())
model.add(Dense(16, activation='relu'))
model.add(Dense(no_classes, activation='softmax'))
# Display a model summary
model.summary()
# Compile the model
model.compile(loss=loss_function,
optimizer=optimizer,
metrics=['accuracy'])
# Start training
model.fit(
train_datagen,
epochs=no_epochs,
shuffle=False)
# Saving model
filepath = os.path.join(RESOURCE_DIR, 'saved_model')
save_model(model, filepath)

View File

@ -59,10 +59,10 @@ class Sand(Soil):
self._value = VALUE_OF_SAND self._value = VALUE_OF_SAND
def transform(self) -> list: def transform(self) -> list:
if not self.is_hydrated : if not self.is_sowed :
return [0, 1, 0, 0]
else:
return [0, 0, 1, 0] return [0, 0, 1, 0]
else:
return [0, 1, 0, 0]
class Grass(Plant): class Grass(Plant):

View File

@ -1,30 +0,0 @@
from tensorflow.keras.models import load_model
from tensorflow import keras as k
import numpy as np
from config import RESOURCE_DIR
import os
# Load the model
model = load_model(
os.path.join(RESOURCE_DIR, "saved_model"),
custom_objects=None,
compile=True
)
# Data fror predictions
img_width, img_height, img_num_channels = 25, 25, 3
labels = ["cabbage", "carrot", "corn", "lettuce", "paprika", "potato", "tomato"]
# Predictions
filename = 'Image_1.jpg'
loaded_image = k.preprocessing.image.load_img(path=RESOURCE_DIR + '/smaller_test/potato/' + filename,
target_size=(img_width, img_height, img_num_channels))
# convert to array and resample dividing by 255
img_array = k.preprocessing.image.img_to_array(loaded_image) / 255.
# add sample dimension. the predictor is expecting (1, CHANNELS, IMG_WIDTH, IMG_HEIGHT)
img_np_array = np.expand_dims(img_array, axis=0)
predictions = model.predict(img_np_array)
prediction = np.argmax(predictions[0])
print(f'Ground truth: {filename} - Prediction: {labels[prediction]}')

View File

@ -31,7 +31,7 @@ class Tractor(BaseField):
self.__board = board self.__board = board
self.__harvested_corps = [] self.__harvested_corps = []
self.__fuel = 10 self.__fuel = 10
self.__neural_network = None self.__neural_network = NeuralNetwork()
self.__tree = DecisionTree() self.__tree = DecisionTree()
self.__weather = Weather() self.__weather = Weather()
@ -105,7 +105,10 @@ class Tractor(BaseField):
field.is_sowed = True field.is_sowed = True
def harvest(self) -> None: def harvest(self) -> None:
if self.check_field(Crops): field = self.get_field_from_board()
prediction = self.__neural_network.check(field)
if prediction.capitalize() in CROPS:
print('Harvest') print('Harvest')
field = self.get_field_from_board() field = self.get_field_from_board()
self.harvest_crops(field) self.harvest_crops(field)
@ -194,26 +197,47 @@ class Tractor(BaseField):
print(f"Length of Moves {len(moves)}") # - {3 ** len(moves)}") print(f"Length of Moves {len(moves)}") # - {3 ** len(moves)}")
while len(moves) > 0: while len(moves) > 0:
movement, action = moves.pop(0) movement, action = moves.pop(0)
# do action # do action
time.sleep(0.5) time.sleep(0.5)
print(f"Action {action}") self.do_action_on_fields(action)
if action == A_FERTILIZE:
self.fertilize()
elif action == A_SOW:
self.sow()
self.hydrate()
elif action == A_HYDRATE:
self.hydrate()
elif action == A_HARVEST:
self.harvest()
time.sleep(1)
# move # move
time.sleep(1)
self.move_or_rotate(movement) self.move_or_rotate(movement)
time.sleep(TIME_OF_MOVING) time.sleep(TIME_OF_MOVING)
is_running.clear() is_running.clear()
def do_action_on_fields(self, action: str) -> None:
print(f"Action {action}")
if action == A_FERTILIZE:
self.fertilize()
elif action == A_SOW:
self.sow()
# self.hydrate()
elif action == A_HYDRATE:
self.hydrate()
elif action == A_HARVEST:
self.harvest()
def run_auto_bot_handler(self, action: str, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
thread = threading.Thread(target=self.run_auto_bot, args=(action, moves, is_running), daemon=True)
thread.start()
def run_auto_bot(self, action_type: str, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
print(moves)
# print('Auto mode')
while len(moves) > 0:
movement, action = moves.pop(0)
# move
self.move_or_rotate(movement)
time.sleep(0.7)
self.do_action_on_fields(action_type)
time.sleep(1)
is_running.clear()
def move_or_rotate(self, movement: str) -> None: def move_or_rotate(self, movement: str) -> None:
print(f"Move {movement}") print(f"Move {movement}")
if movement == M_GO_FORWARD: if movement == M_GO_FORWARD:
@ -309,16 +333,27 @@ class Tractor(BaseField):
is_running.clear() is_running.clear()
def choose_action(self) -> tuple[tuple[int,int],str]: def choose_action(self) -> tuple[tuple[int, int], str]:
vectors = self.__board.convert_fields_to_vectors() vectors = self.__board.convert_fields_to_vectors()
print(vectors) print(vectors)
coords = None coords = None
action = None action = None
for i in range(HORIZONTAL_NUM_OF_FIELDS):
for j in range(VERTICAL_NUM_OF_FIELDS): i_max = HORIZONTAL_NUM_OF_FIELDS
j_max = VERTICAL_NUM_OF_FIELDS
i_min = random.randint(0, HORIZONTAL_NUM_OF_FIELDS - 1)
j_min = random.randint(0, VERTICAL_NUM_OF_FIELDS - 1)
i = i_min
flag = True
while i < i_max and flag:
for j in range(j_min, j_max):
action = self.__tree.make_decision(self.__weather, vectors[i][j]) action = self.__tree.make_decision(self.__weather, vectors[i][j])
if action != A_DO_NOTHING: if action != A_DO_NOTHING:
coords = (i, j) coords = (i, j)
flag = False
break break
i += 1
print(coords, action) print(coords, action)
return coords, action return coords, action

View File

@ -14,7 +14,7 @@ __all__ = (
'A_SOW', 'A_HARVEST', 'A_HYDRATE', 'A_FERTILIZE', 'A_DO_NOTHING', 'A_SOW', 'A_HARVEST', 'A_HYDRATE', 'A_FERTILIZE', 'A_DO_NOTHING',
'TYPES_OF_ACTION', 'D_NORTH', 'D_EAST', 'D_SOUTH', 'D_WEST', 'TYPES_OF_ACTION', 'D_NORTH', 'D_EAST', 'D_SOUTH', 'D_WEST',
'VALUE_OF_CROPS', 'VALUE_OF_PLANT', 'VALUE_OF_SAND', 'VALUE_OF_CLAY', 'VALUE_OF_CROPS', 'VALUE_OF_PLANT', 'VALUE_OF_SAND', 'VALUE_OF_CLAY',
'MAP_FILE_NAME', 'JSON', 'SAVE_MAP', 'LOAD_MAP', 'MAP_FILE_NAME', 'JSON', 'SAVE_MAP', 'LOAD_MAP', 'AUTO_MODE',
'TRAINING_SET_DIR', 'TEST_SET_DIR', 'ADAPTED_IMG_DIR', 'MODEL_DIR', 'TRAINING_SET_DIR', 'TEST_SET_DIR', 'ADAPTED_IMG_DIR', 'MODEL_DIR',
'DATA_DIR','IMG_DECISION_TREE','MODEL_TREE_FILENAME','DATA_TRAINING_FOR_DECISION_TREE' 'DATA_DIR','IMG_DECISION_TREE','MODEL_TREE_FILENAME','DATA_TRAINING_FOR_DECISION_TREE'
) )
@ -86,10 +86,10 @@ A_DO_NOTHING = "do nothing"
TYPES_OF_ACTION = [A_SOW, A_HARVEST, A_HYDRATE, A_FERTILIZE, A_DO_NOTHING] TYPES_OF_ACTION = [A_SOW, A_HARVEST, A_HYDRATE, A_FERTILIZE, A_DO_NOTHING]
# Costs fields: # Costs fields:
VALUE_OF_CROPS = 0.25 VALUE_OF_CROPS = 0.2
VALUE_OF_PLANT = 0.5 VALUE_OF_PLANT = 0.3
VALUE_OF_SAND = 0.75 VALUE_OF_SAND = 0.4
VALUE_OF_CLAY = 1 VALUE_OF_CLAY = 0.5
# Weather # Weather
W_SUNNY = 'Sunny' W_SUNNY = 'Sunny'
@ -113,3 +113,4 @@ TIME_OF_MOVING = 2
# Args # Args
SAVE_MAP = '--save-map' SAVE_MAP = '--save-map'
LOAD_MAP = '--load-map' LOAD_MAP = '--load-map'
AUTO_MODE = '--auto-mode'