From bbc334244144fb8fec6f3d893fee4a40ccea02a3 Mon Sep 17 00:00:00 2001 From: KelvinYang0320 Date: Sun, 1 Jan 2023 18:18:48 +0800 Subject: [PATCH] Refactored Deepbots: DeepbotsEnv & Robot Class 1. Inherited from Webots Robot class 2. Refactored SupervisorEnv name to DeepbotsEnv 3. Shortened imports 4. Removed setup folder 5. Renamed lots of class name --- deepbots/robots/__init__.py | 2 + ...t_emitter_receiver_csv.py => csv_robot.py} | 10 +-- ..._receiver.py => emitter_receiver_robot.py} | 16 ++--- deepbots/setup/__init__.py | 0 deepbots/setup/deepworlds_setup.py | 0 deepbots/supervisor/__init__.py | 4 ++ .../controllers/csv_supervisor_env.py | 61 +++++++++++++++++ ...isor_env.py => deepbots_supervisor_env.py} | 4 +- ....py => emitter_receiver_supervisor_env.py} | 65 +------------------ ..._supervisor.py => robot_supervisor_env.py} | 12 ++-- deepbots/supervisor/wrappers/__init__.py | 2 + .../supervisor/wrappers/keyboard_printer.py | 14 ++-- .../wrappers/tensorboard_wrapper.py | 14 ++-- 13 files changed, 107 insertions(+), 97 deletions(-) rename deepbots/robots/controllers/{robot_emitter_receiver_csv.py => csv_robot.py} (93%) rename deepbots/robots/controllers/{robot_emitter_receiver.py => emitter_receiver_robot.py} (90%) delete mode 100644 deepbots/setup/__init__.py delete mode 100644 deepbots/setup/deepworlds_setup.py create mode 100644 deepbots/supervisor/controllers/csv_supervisor_env.py rename deepbots/supervisor/controllers/{supervisor_env.py => deepbots_supervisor_env.py} (97%) rename deepbots/supervisor/controllers/{supervisor_emitter_receiver.py => emitter_receiver_supervisor_env.py} (62%) rename deepbots/supervisor/controllers/{robot_supervisor.py => robot_supervisor_env.py} (89%) diff --git a/deepbots/robots/__init__.py b/deepbots/robots/__init__.py index e69de29..e8cafa6 100644 --- a/deepbots/robots/__init__.py +++ b/deepbots/robots/__init__.py @@ -0,0 +1,2 @@ +from deepbots.robots.controllers.csv_robot import CSVRobot +from deepbots.robots.controllers.emitter_receiver_robot import EmitterReceiverRobot diff --git a/deepbots/robots/controllers/robot_emitter_receiver_csv.py b/deepbots/robots/controllers/csv_robot.py similarity index 93% rename from deepbots/robots/controllers/robot_emitter_receiver_csv.py rename to deepbots/robots/controllers/csv_robot.py index 516b08b..b3344d3 100644 --- a/deepbots/robots/controllers/robot_emitter_receiver_csv.py +++ b/deepbots/robots/controllers/csv_robot.py @@ -1,10 +1,10 @@ from collections.abc import Iterable -from deepbots.robots.controllers.robot_emitter_receiver import \ - RobotEmitterReceiver +from deepbots.robots.controllers.emitter_receiver_robot import \ + EmitterReceiverRobot -class RobotEmitterReceiverCSV(RobotEmitterReceiver): +class CSVRobot(EmitterReceiverRobot): """ Basic implementation of a robot that can emit and receive messages to/from the supervisor in string utf-8 form that are Comma Separated Values, @@ -39,8 +39,8 @@ def initialize_comms(self, emitter_name, receiver_name): supervisor node :return: The initialized emitter and receiver references """ - emitter = self.robot.getDevice(emitter_name) - receiver = self.robot.getDevice(receiver_name) + emitter = self.getDevice(emitter_name) + receiver = self.getDevice(receiver_name) receiver.enable(self.timestep) return emitter, receiver diff --git a/deepbots/robots/controllers/robot_emitter_receiver.py b/deepbots/robots/controllers/emitter_receiver_robot.py similarity index 90% rename from deepbots/robots/controllers/robot_emitter_receiver.py rename to deepbots/robots/controllers/emitter_receiver_robot.py index ebb029a..40003ae 100644 --- a/deepbots/robots/controllers/robot_emitter_receiver.py +++ b/deepbots/robots/controllers/emitter_receiver_robot.py @@ -2,9 +2,9 @@ from controller import Robot -class RobotEmitterReceiver: +class EmitterReceiverRobot(Robot): """ - This RobotEmitterReceiver implements only the most basic run method, that + This EmitterReceiverRobot implements only the most basic run method, that steps the robot and calls the handle_emitter, handle_receiver methods that are needed for communication with the supervisor. @@ -12,7 +12,7 @@ class RobotEmitterReceiver: and the handle_emitter, handle_receiver, initialize_comms methods are all abstract and need to be implemented according to their docstrings. For a simpler RobotController that implements the methods in a basic form - inherit the RobotEmitterReceiverCSV subclass or other emitter-receiver + inherit the CSVRobot subclass or other emitter-receiver subclasses. """ def __init__(self, @@ -35,10 +35,10 @@ def __init__(self, :param timestep: int, positive or None """ - self.robot = Robot() + super().__init__() if timestep is None: - self.timestep = int(self.robot.getBasicTimeStep()) + self.timestep = int(self.getBasicTimeStep()) else: self.timestep = timestep @@ -78,8 +78,8 @@ def initialize_comms(self, emitter_name, receiver_name): A basic example implementation can be: - emitter = self.robot.getDevice("emitter") - receiver = self.robot.getDevice("receiver") + emitter = self.getDevice("emitter") + receiver = self.getDevice("receiver") receiver.enable(self.timestep) return emitter, receiver @@ -113,6 +113,6 @@ def run(self): This method should be called by a robot manager to run the robot. """ - while self.robot.step(self.timestep) != -1: + while self.step(self.timestep) != -1: self.handle_receiver() self.handle_emitter() diff --git a/deepbots/setup/__init__.py b/deepbots/setup/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/deepbots/setup/deepworlds_setup.py b/deepbots/setup/deepworlds_setup.py deleted file mode 100644 index e69de29..0000000 diff --git a/deepbots/supervisor/__init__.py b/deepbots/supervisor/__init__.py index e69de29..aa8fafd 100644 --- a/deepbots/supervisor/__init__.py +++ b/deepbots/supervisor/__init__.py @@ -0,0 +1,4 @@ +from deepbots.supervisor.controllers.robot_supervisor_env import RobotSupervisorEnv +from deepbots.supervisor.controllers.csv_supervisor_env import CSVSupervisorEnv +from deepbots.supervisor.controllers.deepbots_supervisor_env import DeepbotsSupervisorEnv +from deepbots.supervisor.controllers.emitter_receiver_supervisor_env import EmitterReceiverSupervisorEnv diff --git a/deepbots/supervisor/controllers/csv_supervisor_env.py b/deepbots/supervisor/controllers/csv_supervisor_env.py new file mode 100644 index 0000000..692bb7f --- /dev/null +++ b/deepbots/supervisor/controllers/csv_supervisor_env.py @@ -0,0 +1,61 @@ +from collections.abc import Iterable + +from deepbots.supervisor.controllers.emitter_receiver_supervisor_env import EmitterReceiverSupervisorEnv + + +class CSVSupervisorEnv(EmitterReceiverSupervisorEnv): + """ + This class implements the emitter-receiver scheme using Comma Separated + Values. + """ + def __init__(self, + emitter_name="emitter", + receiver_name="receiver", + timestep=None): + """ + The constructor just passes the arguments provided to the parent + class contructor. + + :param emitter_name: The name of the emitter device on the + supervisor node + :param receiver_name: The name of the receiver device on the + supervisor node + :param timestep: The supervisor controller timestep + """ + super(CSVSupervisorEnv, self).__init__(emitter_name, receiver_name, + timestep) + + def handle_emitter(self, action): + """ + Implementation of the handle_emitter method expecting an iterable + with Comma Separated Values (CSV). + + :param action: Whatever the use-case uses as an action, e.g. + an integer representing discrete actions + :type action: Iterable, for multiple values the CSV format is + required, e.g. [0, 1] for two actions + """ + assert isinstance(action, Iterable), \ + "The action object should be Iterable" + + message = (",".join(map(str, action))).encode("utf-8") + self.emitter.send(message) + + def handle_receiver(self): + """ + Implementation of the handle_receiver method expecting an iterable + with Comma Separated Values (CSV). + + :return: Returns the message received from the robot, returns None + if no message is received + :rtype: List of string values + """ + if self.receiver.getQueueLength() > 0: + try: + string_message = self.receiver.getString() + except AttributeError: + string_message = self.receiver.getData().decode("utf-8") + self.receiver.nextPacket() + return string_message.split(",") + else: + return None diff --git a/deepbots/supervisor/controllers/supervisor_env.py b/deepbots/supervisor/controllers/deepbots_supervisor_env.py similarity index 97% rename from deepbots/supervisor/controllers/supervisor_env.py rename to deepbots/supervisor/controllers/deepbots_supervisor_env.py index 78a58f0..d2da665 100644 --- a/deepbots/supervisor/controllers/supervisor_env.py +++ b/deepbots/supervisor/controllers/deepbots_supervisor_env.py @@ -2,7 +2,7 @@ from controller import Supervisor -class SupervisorEnv(Supervisor, gym.Env): +class DeepbotsSupervisorEnv(Supervisor, gym.Env): """ This class is the highest class in deepbots class hierarchy, inheriting both the Webots Supervisor controller and the basic gym.Env. @@ -18,7 +18,7 @@ class SupervisorEnv(Supervisor, gym.Env): compatible with reinforcement learning agents that work with the gym interface. Moreover, a problem-agnostic reset method is provided. Please use any of the children supervisor classes to be - inherited by your own class, such as the RobotSupervisor class. + inherited by your own class, such as the RobotSupervisorEnv class. Nevertheless, advanced users can inherit this class to create their own supervisor classes if they wish. """ diff --git a/deepbots/supervisor/controllers/supervisor_emitter_receiver.py b/deepbots/supervisor/controllers/emitter_receiver_supervisor_env.py similarity index 62% rename from deepbots/supervisor/controllers/supervisor_emitter_receiver.py rename to deepbots/supervisor/controllers/emitter_receiver_supervisor_env.py index f0c5d6b..c5e8a50 100644 --- a/deepbots/supervisor/controllers/supervisor_emitter_receiver.py +++ b/deepbots/supervisor/controllers/emitter_receiver_supervisor_env.py @@ -1,11 +1,10 @@ -from collections.abc import Iterable from warnings import warn, simplefilter -from deepbots.supervisor.controllers.supervisor_env import SupervisorEnv +from deepbots.supervisor.controllers.deepbots_supervisor_env import DeepbotsSupervisorEnv from controller import Supervisor -class SupervisorEmitterReceiver(SupervisorEnv): +class EmitterReceiverSupervisorEnv(DeepbotsSupervisorEnv): """ This is the base class for the emitter - receiver scheme. @@ -26,7 +25,7 @@ def __init__(self, supervisor node :param timestep: The supervisor controller timestep """ - super(SupervisorEmitterReceiver, self).__init__() + super(EmitterReceiverSupervisorEnv, self).__init__() if timestep is None: self.timestep = int(self.getBasicTimeStep()) @@ -116,61 +115,3 @@ def timestep(self, value): :param value: The new controller timestep in milliseconds """ self._timestep = int(value) - - -class SupervisorCSV(SupervisorEmitterReceiver): - """ - This class implements the emitter-receiver scheme using Comma Separated - Values. - """ - def __init__(self, - emitter_name="emitter", - receiver_name="receiver", - timestep=None): - """ - The constructor just passes the arguments provided to the parent - class contructor. - - :param emitter_name: The name of the emitter device on the - supervisor node - :param receiver_name: The name of the receiver device on the - supervisor node - :param timestep: The supervisor controller timestep - """ - super(SupervisorCSV, self).__init__(emitter_name, receiver_name, - timestep) - - def handle_emitter(self, action): - """ - Implementation of the handle_emitter method expecting an iterable - with Comma Separated Values (CSV). - - :param action: Whatever the use-case uses as an action, e.g. - an integer representing discrete actions - :type action: Iterable, for multiple values the CSV format is - required, e.g. [0, 1] for two actions - """ - assert isinstance(action, Iterable), \ - "The action object should be Iterable" - - message = (",".join(map(str, action))).encode("utf-8") - self.emitter.send(message) - - def handle_receiver(self): - """ - Implementation of the handle_receiver method expecting an iterable - with Comma Separated Values (CSV). - - :return: Returns the message received from the robot, returns None - if no message is received - :rtype: List of string values - """ - if self.receiver.getQueueLength() > 0: - try: - string_message = self.receiver.getString() - except AttributeError: - string_message = self.receiver.getData().decode("utf-8") - self.receiver.nextPacket() - return string_message.split(",") - else: - return None diff --git a/deepbots/supervisor/controllers/robot_supervisor.py b/deepbots/supervisor/controllers/robot_supervisor_env.py similarity index 89% rename from deepbots/supervisor/controllers/robot_supervisor.py rename to deepbots/supervisor/controllers/robot_supervisor_env.py index 9d0e902..6aa9007 100644 --- a/deepbots/supervisor/controllers/robot_supervisor.py +++ b/deepbots/supervisor/controllers/robot_supervisor_env.py @@ -1,11 +1,11 @@ from warnings import warn, simplefilter -from deepbots.supervisor.controllers.supervisor_env import SupervisorEnv +from deepbots.supervisor.controllers.deepbots_supervisor_env import DeepbotsSupervisorEnv from controller import Supervisor -class RobotSupervisor(SupervisorEnv): +class RobotSupervisorEnv(DeepbotsSupervisorEnv): """ - The RobotSupervisor class implements both a robot controller and a + The RobotSupervisorEnv class implements both a robot controller and a supervisor RL environment, referred to as Robot-Supervisor scheme. This class can be used when there is no need to separate the Robot @@ -18,17 +18,17 @@ class RobotSupervisor(SupervisorEnv): The user needs to implement the regular methods for the environment, reward(), get_observations(), get_default_observation, etc., from - SupervisorEnv according to their use-case in addition to the method + DeepbotsSupervisorEnv according to their use-case in addition to the method apply_action() introduced here. apply_action(): - (similar to use_message_data() of RobotEmitterReceiverCSV) + (similar to use_message_data() of CSVRobot) This method takes an action argument and translates it to a robot action, e.g. motor speeds. Note that apply_action() is called during step(). """ def __init__(self, timestep=None): - super(RobotSupervisor, self).__init__() + super(RobotSupervisorEnv, self).__init__() if timestep is None: self.timestep = int(self.getBasicTimeStep()) diff --git a/deepbots/supervisor/wrappers/__init__.py b/deepbots/supervisor/wrappers/__init__.py index e69de29..ddc2aa9 100644 --- a/deepbots/supervisor/wrappers/__init__.py +++ b/deepbots/supervisor/wrappers/__init__.py @@ -0,0 +1,2 @@ +from deepbots.supervisor.wrappers.keyboard_printer import KeyboardPrinter +from deepbots.supervisor.wrappers.tensorboard_wrapper import TensorboardLogger diff --git a/deepbots/supervisor/wrappers/keyboard_printer.py b/deepbots/supervisor/wrappers/keyboard_printer.py index 3e43ac2..96e232d 100644 --- a/deepbots/supervisor/wrappers/keyboard_printer.py +++ b/deepbots/supervisor/wrappers/keyboard_printer.py @@ -1,16 +1,16 @@ from controller import Keyboard -from deepbots.supervisor.controllers.supervisor_env import SupervisorEnv +from deepbots.supervisor.controllers.deepbots_supervisor_env import DeepbotsSupervisorEnv -class KeyboardPrinter(SupervisorEnv): +class KeyboardPrinter(DeepbotsSupervisorEnv): def __init__(self, controller): self.controller = controller self.keyboard = Keyboard() self.keyboard.enable(self.controller.timestep) def step(self, action): - observation, reward, isDone, info = self.controller.step(action) + observation, reward, is_done, info = self.controller.step(action) key = self.keyboard.getKey() # DEBUG CONTROLS if key == Keyboard.CONTROL + ord("A"): @@ -23,13 +23,13 @@ def step(self, action): print() print("Observations: ", self.controller.observation) - return observation, reward, isDone, info + return observation, reward, is_done, info def is_done(self): - isDone = self.controller.is_done() - if isDone: + is_done = self.controller.is_done() + if is_done: print("Done") - return isDone + return is_done def get_observations(self): return self.controller.get_observations() diff --git a/deepbots/supervisor/wrappers/tensorboard_wrapper.py b/deepbots/supervisor/wrappers/tensorboard_wrapper.py index 8508914..e1348d1 100644 --- a/deepbots/supervisor/wrappers/tensorboard_wrapper.py +++ b/deepbots/supervisor/wrappers/tensorboard_wrapper.py @@ -1,10 +1,10 @@ import numpy as np from tensorboardX import SummaryWriter -from deepbots.supervisor.controllers.supervisor_env import SupervisorEnv +from deepbots.supervisor.controllers.deepbots_supervisor_env import DeepbotsSupervisorEnv -class TensorboardLogger(SupervisorEnv): +class TensorboardLogger(DeepbotsSupervisorEnv): def __init__(self, controller, log_dir="logs/results", @@ -29,7 +29,7 @@ def __init__(self, self.file_writer = SummaryWriter(log_dir, flush_secs=30) def step(self, action): - observation, reward, isDone, info = self.controller.step(action) + observation, reward, is_done, info = self.controller.step(action) if (self.v_action > 1): self.file_writer.add_histogram( @@ -47,7 +47,7 @@ def step(self, action): self.file_writer.add_scalar("Rewards/Per Global Step", reward, self.step_global) - if (isDone): + if (is_done): self.file_writer.add_scalar( "Is Done/Per Reset step", self.step_cntr, @@ -60,13 +60,13 @@ def step(self, action): self.step_cntr += 1 self.step_global += 1 - return observation, reward, isDone, info + return observation, reward, is_done, info def is_done(self): - isDone = self.controller.is_done() + is_done = self.controller.is_done() self.file_writer.flush() - return isDone + return is_done def get_observations(self): obs = self.controller.get_observations()