From 749b98e800b239852778c67d1dfeaf87f69a17c3 Mon Sep 17 00:00:00 2001 From: Brian Delhaisse Date: Wed, 8 May 2019 07:43:45 +0200 Subject: [PATCH] update several classes: add copy/deepcopy methods (ongoing) --- pyrobolearn/actions/action.py | 106 +++++++++----- pyrobolearn/actions/basic_actions.py | 32 ++++- pyrobolearn/actions/gym_actions.py | 16 +++ .../actions/robot_actions/joint_actions.py | 91 ++++++++++++ .../actions/robot_actions/link_actions.py | 20 ++- .../actions/robot_actions/robot_actions.py | 16 +++ pyrobolearn/actorcritics/actorcritic.py | 30 ++++ pyrobolearn/approximators/approximator.py | 55 ++++++- pyrobolearn/dynamics/basic_dynamic.py | 26 ++-- pyrobolearn/dynamics/dynamic.py | 135 +++++++++++++----- pyrobolearn/dynamics/nn_dynamic.py | 32 ++--- pyrobolearn/dynamics/robot_dynamic.py | 8 +- pyrobolearn/envs/env.py | 71 +++++++-- pyrobolearn/policies/basic_policy.py | 44 +++--- pyrobolearn/policies/cpg_policy.py | 22 +-- pyrobolearn/policies/dmp_policy.py | 118 ++++++++++++--- pyrobolearn/policies/neat_policy.py | 20 +-- pyrobolearn/policies/nn_policy.py | 32 ++--- pyrobolearn/policies/policy.py | 71 ++++++--- pyrobolearn/rewards/basic_rewards.py | 3 +- pyrobolearn/rewards/reward.py | 32 ++++- pyrobolearn/robots/actuators/actuator.py | 42 ++++++ pyrobolearn/robots/actuators/joints.py | 61 ++++++++ pyrobolearn/robots/aibo.py | 4 +- pyrobolearn/robots/allegrohand.py | 4 +- pyrobolearn/robots/ant.py | 4 +- pyrobolearn/robots/anymal.py | 4 +- pyrobolearn/robots/atlas.py | 4 +- pyrobolearn/robots/ballbot.py | 4 +- pyrobolearn/robots/base.py | 41 +++++- pyrobolearn/robots/baxter.py | 4 +- pyrobolearn/robots/bb8.py | 4 +- pyrobolearn/robots/blackbird.py | 4 +- pyrobolearn/robots/cartpole.py | 4 +- pyrobolearn/robots/cassie.py | 4 +- pyrobolearn/robots/centauro.py | 4 +- pyrobolearn/robots/cogimon.py | 4 +- pyrobolearn/robots/coman.py | 4 +- pyrobolearn/robots/crab.py | 4 +- pyrobolearn/robots/cubli.py | 6 +- pyrobolearn/robots/darwin.py | 4 +- pyrobolearn/robots/edo.py | 4 +- pyrobolearn/robots/epuck.py | 4 +- pyrobolearn/robots/f10_racecar.py | 4 +- pyrobolearn/robots/fetch.py | 4 +- pyrobolearn/robots/franka.py | 4 +- pyrobolearn/robots/half_cheetah.py | 4 +- pyrobolearn/robots/hand.py | 9 +- pyrobolearn/robots/hopper.py | 4 +- pyrobolearn/robots/hubo.py | 4 +- pyrobolearn/robots/humanoid.py | 4 +- pyrobolearn/robots/husky.py | 4 +- pyrobolearn/robots/hyq.py | 4 +- pyrobolearn/robots/hyq2max.py | 4 +- pyrobolearn/robots/icub.py | 4 +- pyrobolearn/robots/jaco.py | 4 +- pyrobolearn/robots/kr5.py | 4 +- pyrobolearn/robots/kuka_iiwa.py | 4 +- pyrobolearn/robots/kuka_lwr.py | 4 +- pyrobolearn/robots/laikago.py | 4 +- pyrobolearn/robots/legged_robot.py | 16 +-- pyrobolearn/robots/littledog.py | 4 +- pyrobolearn/robots/manipulator.py | 8 +- pyrobolearn/robots/manipulator2d.py | 4 +- pyrobolearn/robots/minitaur.py | 4 +- pyrobolearn/robots/mkz.py | 4 +- pyrobolearn/robots/morphex.py | 4 +- pyrobolearn/robots/nao.py | 4 +- pyrobolearn/robots/opendog.py | 4 +- pyrobolearn/robots/pepper.py | 10 +- pyrobolearn/robots/phantomx.py | 4 +- pyrobolearn/robots/pleurobot.py | 4 +- pyrobolearn/robots/pr2.py | 4 +- pyrobolearn/robots/quadcopter.py | 4 +- pyrobolearn/robots/rhex.py | 4 +- pyrobolearn/robots/robot.py | 60 +++++--- pyrobolearn/robots/rrbot.py | 4 +- pyrobolearn/robots/sawyer.py | 4 +- pyrobolearn/robots/sea_hexapod.py | 4 +- pyrobolearn/robots/sea_snake.py | 4 +- pyrobolearn/robots/sensors/camera.py | 4 +- pyrobolearn/robots/sensors/contact.py | 4 +- pyrobolearn/robots/sensors/force_torque.py | 4 +- pyrobolearn/robots/sensors/joints.py | 29 +++- pyrobolearn/robots/sensors/links.py | 28 +++- pyrobolearn/robots/sensors/sensor.py | 51 ++++++- pyrobolearn/robots/shadowhand.py | 4 +- pyrobolearn/robots/softhand.py | 4 +- pyrobolearn/robots/swimmer.py | 4 +- pyrobolearn/robots/uav.py | 12 +- pyrobolearn/robots/usv.py | 4 +- pyrobolearn/robots/uuv.py | 4 +- pyrobolearn/robots/walker2d.py | 4 +- pyrobolearn/robots/walkman.py | 10 +- pyrobolearn/robots/wam.py | 4 +- pyrobolearn/robots/wheeled_robot.py | 12 +- pyrobolearn/robots/youbot.py | 16 +-- pyrobolearn/states/state.py | 40 +++++- pyrobolearn/tasks/reinforcement.py | 3 +- pyrobolearn/tasks/task.py | 44 ++++++ pyrobolearn/values/value.py | 126 ++++++++++++++++ pyrobolearn/worlds/world.py | 32 ++++- 102 files changed, 1431 insertions(+), 445 deletions(-) diff --git a/pyrobolearn/actions/action.py b/pyrobolearn/actions/action.py index 60a9f6d..a082ab9 100644 --- a/pyrobolearn/actions/action.py +++ b/pyrobolearn/actions/action.py @@ -4,10 +4,11 @@ This file defines the `Action` class, which is returned by the policy and given to the environment. """ +import copy +import collections +# from abc import ABCMeta, abstractmethod import numpy as np import torch -import collections -from abc import ABCMeta, abstractmethod import gym from pyrobolearn.utils.data_structures.orderedset import OrderedSet @@ -46,7 +47,7 @@ class Action(object): [2] "OpenAI gym": https://gym.openai.com/ and https://github.com/openai/gym """ - def __init__(self, actions=(), data=None, space=None, name=None): + def __init__(self, actions=(), data=None, space=None, name=None, ticks=1): """ Initialize the action. The action contains some kind of data, or is a combination of other actions. @@ -55,6 +56,7 @@ class Action(object): data) data (np.ndarray): data associated to this action space (gym.space): space associated with the given data + ticks (int): number of ticks to sleep before setting the next action data. Warning: Both arguments can not be provided to the action. @@ -85,19 +87,24 @@ class Action(object): self._distribution = None # for sampling self._normalizer = None self._noiser = None # for noise - self._name = name + self.name = name # create ordered set which is useful if this action is a combination of multiple actions self._actions = OrderedSet() if self._data is None: self.add(actions) + # set ticks and counter + self.cnt = 0 + self.ticks = int(ticks) + # reset action # self.reset() ############################## # Properties (Getter/Setter) # ############################## + @property def actions(self): """ @@ -313,6 +320,8 @@ class Action(object): """ Set the name of the action. """ + if name is None: + name = self.__class__.__name__ if not isinstance(name, str): raise TypeError("Expecting the name to be a string.") self._name = name @@ -370,20 +379,20 @@ class Action(object): """ return len(np.unique(self.dimension)) - @property - def distribution(self): - """ - Get the current distribution used when sampling the action - """ - pass - - @distribution.setter - def distribution(self, distribution): - """ - Set the distribution to the action. - """ - # check if distribution is discrete/continuous - pass + # @property + # def distribution(self): + # """ + # Get the current distribution used when sampling the action + # """ + # return None + # + # @distribution.setter + # def distribution(self, distribution): + # """ + # Set the distribution to the action. + # """ + # # check if distribution is discrete/continuous + # pass ########### # Methods # @@ -440,12 +449,17 @@ class Action(object): Write the action values to the simulator for each action. This has to be overwritten by the child class. """ - if self.has_data(): # write the current action - self._write(data) - else: # read each action - if self.actions: - for action, d in zip(self.actions, data): - action._write(d) + # if time to write + if self.cnt % self.ticks == 0: + + if self.has_data(): # write the current action + self._write(data) + else: # read each action + if self.actions: + for action, d in zip(self.actions, data): + action._write(d) + + self.cnt += 1 # return the data # return self.data @@ -687,24 +701,17 @@ class Action(object): # Operator Overloading # ######################## - def __repr__(self): + def __str__(self): + """Return a string describing the action.""" if self._data is None: lst = [self.__class__.__name__ + '('] for action in self.actions: - lst.append('\t' + action.__repr__() + ',') + lst.append('\t' + action.__str__() + ',') lst.append(')') return '\n'.join(lst) else: return '%s(%s)' % (self.name, self._data) - # def __str__(self): - # """ - # String to represent the action. Need to be provided by each child class. - # """ - # if self._data is None: - # return [str(action) for action in self._actions] - # return str(self) - def __call__(self, data=None): """ Compute/read the action and return it. It is an alias to the `self.write()` method. @@ -871,15 +878,16 @@ class Action(object): def __sub__(self, other): """ Remove the other action(s) from the current action. - :param other: - :return: + + Args: + other (Action): action to be removed. """ if not isinstance(other, Action): raise TypeError("Expecting another action, instead got {}".format(type(other))) s1 = self._actions if self._data is None else OrderedSet([self]) s2 = other._actions if other._data is None else OrderedSet([other]) s = s1 - s2 - if len(s) == 1: # just one element + if len(s) == 1: # just one element return s[0] return Action(s) @@ -888,7 +896,7 @@ class Action(object): Remove one or several actions from the combined action. Args: - other: + other (Action): action to be removed. """ if not isinstance(other, Action): raise TypeError("Expecting another action, instead got {}".format(type(other))) @@ -896,3 +904,25 @@ class Action(object): raise RuntimeError("This operation is only available for a combined action") s = other._actions if other._data is None else OrderedSet([other]) self._actions -= s + + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(actions=self.actions, data=self._data, space=self._space, name=self.name, + ticks=self.ticks) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + actions = [copy.deepcopy(action, memo) for action in self.actions] + data = copy.deepcopy(self._data) + space = copy.deepcopy(self._space) + action = self.__class__(actions=actions, data=data, space=space, name=self.name, ticks=self.ticks) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = action + + return action diff --git a/pyrobolearn/actions/basic_actions.py b/pyrobolearn/actions/basic_actions.py index 4c68644..6db82cf 100644 --- a/pyrobolearn/actions/basic_actions.py +++ b/pyrobolearn/actions/basic_actions.py @@ -4,7 +4,7 @@ This includes notably the fixed and functional actions. """ -import numpy as np +import copy from pyrobolearn.actions import Action @@ -31,6 +31,21 @@ class FixedAction(Action): def _write(self, data=None): pass + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(value=self._data) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + data = copy.deepcopy(self._data) + action = self.__class__(value=data) + memo[self] = action + return action + class FunctionalAction(Action): r"""Functional Action. @@ -45,3 +60,18 @@ class FunctionalAction(Action): def _write(self, data=None): self.data = self.function(data) + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(function=self.function, data=self._data) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + function = copy.deepcopy(self.function) + data = copy.deepcopy(self._data) + action = self.__class__(function=function, initial_data=data) + memo[self] = action + return action diff --git a/pyrobolearn/actions/gym_actions.py b/pyrobolearn/actions/gym_actions.py index db7b9ff..d65a643 100644 --- a/pyrobolearn/actions/gym_actions.py +++ b/pyrobolearn/actions/gym_actions.py @@ -7,6 +7,7 @@ from the gym environment, and keep it as an attribute of the class. This can the the various policies defined in the pyrobolearn framework. """ +import copy import gym from pyrobolearn.actions.action import Action @@ -48,6 +49,21 @@ class GymAction(Action): def _write(self, data=None): pass + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(gym_env=self.env) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + env = copy.deepcopy(self.env) + action = self.__class__(gym_env=env) + memo[self] = action + return action + # Tests if __name__ == '__main__': diff --git a/pyrobolearn/actions/robot_actions/joint_actions.py b/pyrobolearn/actions/robot_actions/joint_actions.py index 4d6e581..552a425 100644 --- a/pyrobolearn/actions/robot_actions/joint_actions.py +++ b/pyrobolearn/actions/robot_actions/joint_actions.py @@ -4,6 +4,7 @@ This includes notably the joint positions, velocities, and force/torque actions. """ +import copy import numpy as np from abc import ABCMeta @@ -49,6 +50,22 @@ class JointAction(RobotAction): def bounds(self): return self.robot.get_joint_limits(self.joints) + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(robot=self.robot, joint_ids=self.joints) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + joints = copy.deepcopy(self.joints) + action = self.__class__(robot=robot, joint_ids=joints) + memo[self] = action + return action + class JointPositionAction(JointAction): r"""Joint Position Action @@ -67,6 +84,25 @@ class JointPositionAction(JointAction): else: self.robot.set_joint_positions(data, self.joints, kp=self.kp, kd=self.kd, forces=self.max_force) + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(robot=self.robot, joint_ids=self.joints, kp=self.kp, kd=self.kd, max_force=self.max_force) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + joints = copy.deepcopy(self.joints) + kp = copy.deepcopy(self.kp) + kd = copy.deepcopy(self.kd) + max_force = copy.deepcopy(self.max_force) + action = self.__class__(robot=robot, joint_ids=joints, kp=kp, kd=kd, max_force=max_force) + memo[self] = action + return action + class JointVelocityAction(JointAction): r"""Joint Velocity Action @@ -107,6 +143,25 @@ class JointPositionAndVelocityAction(JointAction): self.robot.set_joint_positions(data[:self.idx], self.joints, kp=self.kp, kd=self.kd, velocities=data[self.idx:], forces=self.max_force) + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(robot=self.robot, joint_ids=self.joints, kp=self.kp, kd=self.kd, max_force=self.max_force) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + joints = copy.deepcopy(self.joints) + kp = copy.deepcopy(self.kp) + kd = copy.deepcopy(self.kd) + max_force = copy.deepcopy(self.max_force) + action = self.__class__(robot=robot, joint_ids=joints, kp=kp, kd=kd, max_force=max_force) + memo[self] = action + return action + # class JointPositionVelocityAccelerationAction(JointAction): # r"""Set the joint positions, velocities, and accelerations. @@ -136,6 +191,24 @@ class JointForceAction(JointAction): data = np.clip(data, self.f_min, self.f_max) self.robot.set_joint_torques(data, self.joints) + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(robot=self.robot, joint_ids=self.joints, f_min=self.f_min, f_max=self.f_max) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + joints = copy.deepcopy(self.joints) + f_min = copy.deepcopy(self.f_min) + f_max = copy.deepcopy(self.f_max) + action = self.__class__(robot=robot, joint_ids=joints, f_min=f_min, f_max=f_max) + memo[self] = action + return action + class JointAccelerationAction(JointAction): r"""Joint Acceleration Action @@ -157,3 +230,21 @@ class JointAccelerationAction(JointAction): else: data = np.clip(data, self.a_min, self.a_max) self.robot.set_joint_accelerations(data, self.joints) + + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(robot=self.robot, joint_ids=self.joints, a_min=self.a_min, a_max=self.a_max) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + joints = copy.deepcopy(self.joints) + a_min = copy.deepcopy(self.a_min) + a_max = copy.deepcopy(self.a_max) + action = self.__class__(robot=robot, joint_ids=joints, f_min=a_min, f_max=a_max) + memo[self] = action + return action diff --git a/pyrobolearn/actions/robot_actions/link_actions.py b/pyrobolearn/actions/robot_actions/link_actions.py index e786b4f..c64e5eb 100644 --- a/pyrobolearn/actions/robot_actions/link_actions.py +++ b/pyrobolearn/actions/robot_actions/link_actions.py @@ -4,6 +4,7 @@ This includes notably the link positions, velocities, and force/torque actions. """ +import copy from abc import ABCMeta from pyrobolearn.actions.robot_actions.robot_actions import RobotAction @@ -30,7 +31,7 @@ class LinkAction(RobotAction): Args: robot (Robot): robot instance - jointIds (int, int[N]): joint id or list of joint ids + link_ids (int, int[N]): link id or list of link ids """ super(LinkAction, self).__init__(robot) @@ -39,12 +40,29 @@ class LinkAction(RobotAction): link_ids = robot.get_link_ids() self.links = link_ids + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(self.robot, self.links) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + links = copy.deepcopy(self.links) + action = self.__class__(robot, links) + memo[self] = action + return action + class LinkPositionAction(LinkAction): r"""Link position action Set the link position(s) using IK. """ + def __init__(self, robot, link_ids=None): super(LinkPositionAction, self).__init__(robot, link_ids) diff --git a/pyrobolearn/actions/robot_actions/robot_actions.py b/pyrobolearn/actions/robot_actions/robot_actions.py index 2ff6962..7200367 100644 --- a/pyrobolearn/actions/robot_actions/robot_actions.py +++ b/pyrobolearn/actions/robot_actions/robot_actions.py @@ -8,6 +8,7 @@ Dependencies: - `pyrobolearn.robots` """ +import copy from abc import ABCMeta from pyrobolearn.actions import Action @@ -47,3 +48,18 @@ class RobotAction(Action): def is_continuous(self): return True + + def __copy__(self): + """Return a shallow copy of the action. This can be overridden in the child class.""" + return self.__class__(robot=self.robot) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the action. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + robot = copy.deepcopy(self.robot, memo) + action = self.__class__(robot=robot) + memo[self] = action + return action diff --git a/pyrobolearn/actorcritics/actorcritic.py b/pyrobolearn/actorcritics/actorcritic.py index 3b0602c..243f219 100644 --- a/pyrobolearn/actorcritics/actorcritic.py +++ b/pyrobolearn/actorcritics/actorcritic.py @@ -8,6 +8,7 @@ Dependencies: - `pyrobolearn.values` """ +import copy import itertools import torch @@ -143,6 +144,35 @@ class ActorCritic(object): value = self.evaluate(state, to_numpy=to_numpy) return action, value + ############# + # Operators # + ############# + + def __str__(self): + """Return a string describing the object.""" + return self.__class__.__name__ + + def __copy__(self): + """Return a shallow copy of the approximator. This can be overridden in the child class.""" + return self.__class__(policy=self.actor, value=self.critic) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + policy = copy.deepcopy(self.actor, memo) if isinstance(self.actor, Policy) else copy.deepcopy(self.actor) + value = copy.deepcopy(self.critic, memo) if isinstance(self.critic, ValueApproximator) \ + else copy.deepcopy(self.critic) + actor_critic = self.__class__(policy=policy, value=value) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = actor_critic + + return actor_critic + class SharedActorCritic(object): r"""Shared Actor Critic diff --git a/pyrobolearn/approximators/approximator.py b/pyrobolearn/approximators/approximator.py index 4f66f66..87221d3 100644 --- a/pyrobolearn/approximators/approximator.py +++ b/pyrobolearn/approximators/approximator.py @@ -10,6 +10,7 @@ Dependencies: - `pyrobolearn.actions` """ +import copy import collections import numpy as np import torch @@ -313,7 +314,8 @@ class Approximator(object): """ return self.model.is_generative() - def _size(self, x): + @staticmethod + def _size(x): """Return the total size of a `State`, `Action`, numpy.array, or torch.Tensor.""" size = 0 if isinstance(x, (State, Action)): @@ -381,7 +383,8 @@ class Approximator(object): processor.reset() self.model.reset() - def __convert_to_numpy(self, x, to_numpy=True): + @staticmethod + def __convert_to_numpy(x, to_numpy=True): """Convert the given argument to a numpy array if specified.""" if to_numpy and isinstance(x, torch.Tensor): if x.requires_grad: @@ -389,7 +392,8 @@ class Approximator(object): return x.numpy() return x - def merge_inputs(self, x=None, to_numpy=True): + @staticmethod + def merge_inputs(x=None, to_numpy=True): """ Merge the inputs of the approximator. @@ -503,14 +507,53 @@ class Approximator(object): """Predict the output using the inner learning model given the input.""" return self.predict(x) - def __repr__(self): - """Return a representation of the model.""" - return self.model.__str__() + # def __repr__(self): + # """Return a representation of the model.""" + # return self.model.__str__() def __str__(self): """Return a string describing the model.""" return self.model.__str__() + def __copy__(self): + """Return a shallow copy of the approximator. This can be overridden in the child class.""" + return self.__class__(inputs=self.inputs, outputs=self.outputs, model=self.model, + preprocessors=self.preprocessors, postprocessors=self.postprocessors) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + def get_inputs_outputs(items): + if isinstance(items, list): + elements = [] + for item in items: + if isinstance(item, (Action, State)): + elements.append(copy.deepcopy(item, memo)) + else: + elements.append(copy.deepcopy(item)) + elif isinstance(items, (Action, State)): + elements = copy.deepcopy(items, memo) + else: + elements = copy.deepcopy(items) + return elements + + inputs = get_inputs_outputs(self.inputs) + outputs = get_inputs_outputs(self.outputs) + model = copy.deepcopy(self.model, memo) if self.model is not None else None + preprocessors = [copy.deepcopy(preprocessor, memo) for preprocessor in self.preprocessors] + postprocessors = [copy.deepcopy(postprocessor, memo) for postprocessor in self.postprocessors] + approximator = self.__class__(inputs=inputs, outputs=outputs, model=model, + preprocessors=preprocessors, postprocessors=postprocessors) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = approximator + + return approximator + # Tests if __name__ == '__main__': diff --git a/pyrobolearn/dynamics/basic_dynamic.py b/pyrobolearn/dynamics/basic_dynamic.py index d73e130..e65ae02 100644 --- a/pyrobolearn/dynamics/basic_dynamic.py +++ b/pyrobolearn/dynamics/basic_dynamic.py @@ -27,24 +27,24 @@ class LinearDynamicModel(ParametrizedDynamicModel): Cons: very limited """ - def __init__(self, states, actions, next_states=None, distributions=None, preprocessors=None, postprocessors=None): + def __init__(self, state, action, next_state=None, distributions=None, preprocessors=None, postprocessors=None): """ Initialize the linear dynamic transition function / probability :math:`p(s_{t+1} | s_t, a_t)`. Args: - states (State): state inputs. - actions (Action): action inputs. - next_states (State, None): state outputs. If None, it will take the state inputs as the outputs. + state (State): state inputs. + action (Action): action inputs. + next_state (State, None): state outputs. If None, it will take the state inputs as the outputs. distributions (torch.distributions.Distribution): distribution to use to sample the next state. If None, it will be deterministic. preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output """ - if next_states is None: - next_states = states - model = LinearApproximator(inputs=[states, actions], outputs=next_states, preprocessors=preprocessors, + if next_state is None: + next_state = state + model = LinearApproximator(inputs=[state, action], outputs=next_state, preprocessors=preprocessors, postprocessors=postprocessors) - super(LinearDynamicModel, self).__init__(states, actions, model=model, next_states=next_states, + super(LinearDynamicModel, self).__init__(state, action, model=model, next_state=next_state, distributions=distributions) @@ -55,14 +55,14 @@ class PieceWiseLinearDynamicModel(ParametrizedDynamicModel): Cons: poor scalability """ - def __init__(self, states, actions, next_states=None, distributions=None, preprocessors=None, postprocessors=None): + def __init__(self, state, action, next_state=None, distributions=None, preprocessors=None, postprocessors=None): """ Initialize the piece wise linear dynamic transition function / probability :math:`p(s_{t+1} | s_t, a_t)`. Args: - states (State): state inputs. - actions (Action): action inputs. - next_states (State, None): state outputs. If None, it will take the state inputs as the outputs. + state (State): state inputs. + action (Action): action inputs. + next_state (State, None): state outputs. If None, it will take the state inputs as the outputs. distributions (torch.distributions.Distribution): distribution to use to sample the next state. If None, it will be deterministic. preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input @@ -70,5 +70,5 @@ class PieceWiseLinearDynamicModel(ParametrizedDynamicModel): """ # TODO model = None - super(PieceWiseLinearDynamicModel, self).__init__(states, actions, model=model, next_states=next_states, + super(PieceWiseLinearDynamicModel, self).__init__(state, action, model=model, next_state=next_state, distributions=distributions) diff --git a/pyrobolearn/dynamics/dynamic.py b/pyrobolearn/dynamics/dynamic.py index 35d4015..d98995c 100644 --- a/pyrobolearn/dynamics/dynamic.py +++ b/pyrobolearn/dynamics/dynamic.py @@ -10,8 +10,9 @@ Dependencies: - `pyrobolearn.approximators` (and thus `pyrobolearn.models`) """ -from abc import ABCMeta, abstractmethod +import copy import collections +from abc import ABCMeta, abstractmethod import numpy as np import torch @@ -74,24 +75,24 @@ class DynamicModel(object): """ __metaclass__ = ABCMeta - def __init__(self, states, actions, next_states=None, preprocessors=None, postprocessors=None): + def __init__(self, state, action, next_state=None, preprocessors=None, postprocessors=None): """ Initialize the dynamic transition probability :math:`p(s_{t+1} | s_t, a_t)`, or dynamic transition function :math:`s_{t+1} = f(s_t, a_t)`. Args: - states (State): state inputs. - actions (Action): action inputs. - next_states (State, None): state outputs. If None, it will take the state inputs as the outputs. + state (State): state inputs. + action (Action): action inputs. + next_state (State, None): state outputs. If None, it will take the state inputs as the outputs. preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output """ # set inputs - self.states = states - self.actions = actions + self.state = state + self.action = action # set outputs - self.next_states = next_states + self.next_state = next_state self.state_data = None # preprocessors and postprocessors @@ -112,43 +113,43 @@ class DynamicModel(object): ############## @property - def states(self): + def state(self): """Return the state instance.""" - return self._states + return self._state - @states.setter - def states(self, states): + @state.setter + def state(self, state): """Set the states.""" - if not isinstance(states, State): - raise TypeError("Expecting the given states to be an instance of `State`, instead got: " - "{}".format(type(states))) - self._states = states + if not isinstance(state, State): + raise TypeError("Expecting the given state to be an instance of `State`, instead got: " + "{}".format(type(state))) + self._state = state @property - def actions(self): + def action(self): """Return thge action instance.""" - return self._actions + return self._action - @actions.setter - def actions(self, actions): + @action.setter + def action(self, action): """Set the actions.""" - if not isinstance(actions, Action): - raise TypeError("Expecting the given actions to be an instance of `Action`, instead got: " - "{}".format(type(actions))) - self._actions = actions + if not isinstance(action, Action): + raise TypeError("Expecting the given action to be an instance of `Action`, instead got: " + "{}".format(type(action))) + self._action = action @property - def next_states(self): + def next_state(self): """Return the next state instance.""" return self._next_states - @next_states.setter - def next_states(self, states): + @next_state.setter + def next_state(self, states): """Set the next states.""" if states is None: - states = self.states + states = self.state elif not isinstance(states, State): - raise TypeError("Expecting the given next_states to be an instance of `State`, instead got: " + raise TypeError("Expecting the given next_state to be an instance of `State`, instead got: " "{}".format(type(states))) self._next_states = states @@ -214,7 +215,7 @@ class DynamicModel(object): state_data = [state_data] # go through each state and data - for idx, (state, data) in enumerate(zip(self.states, state_data)): + for idx, (state, data) in enumerate(zip(self.state, state_data)): if state.is_discrete(): # discrete state if isinstance(data, np.ndarray): # data state is a numpy array # check if given logits or not @@ -289,6 +290,14 @@ class DynamicModel(object): """ pass + ############# + # Operators # + ############# + + def __str__(self): + """Return a representation string about the object.""" + return self.__class__.__name__ + def __call__(self, states=None, actions=None, deterministic=False, to_numpy=True, set_state_data=True): """ Return predicted next state given the current state and action. @@ -307,6 +316,27 @@ class DynamicModel(object): return self.predict(states=states, actions=actions, deterministic=deterministic, to_numpy=to_numpy, set_state_data=set_state_data) + def __copy__(self): + """Return a shallow copy of the dynamic model. This can be overridden in the child class.""" + return self.__class__(state=self.state, action=self.action, next_state=self.next_state, + preprocessors=self.preprocessors, postprocessors=self.postprocessors) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the dynamic model. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) + action = copy.deepcopy(self.action, memo) + next_state = copy.deepcopy(self.next_state, memo) + preprocessors = [copy.deepcopy(preprocessor, memo) for preprocessor in self.preprocessors] + postprocessors = [copy.deepcopy(postprocessor, memo) for postprocessor in self.postprocessors] + dynamic = self.__class__(state=state, action=action, next_state=next_state, preprocessors=preprocessors, + postprocessors=postprocessors) + memo[self] = dynamic + return dynamic + class ParametrizedDynamicModel(DynamicModel): r"""Learnable Parametrized Dynamic Model @@ -314,23 +344,23 @@ class ParametrizedDynamicModel(DynamicModel): Dynamic model that can be trained. """ - def __init__(self, states, actions, model, next_states=None, distributions=None, preprocessors=None, + def __init__(self, state, action, model, next_state=None, distributions=None, preprocessors=None, postprocessors=None): """ Initialize the dynamic transition probability :math:`p(s_{t+1} | s_t, a_t)`. Args: - states (State): state inputs. - actions (Action): action inputs. + state (State): state inputs. + action (Action): action inputs. model (Approximator): approximator (inner learning model). - next_states (State, None): state outputs. If None, it will take the state inputs as the outputs. + next_state (State, None): state outputs. If None, it will take the state inputs as the outputs. distributions ((list of) torch.distributions.Distribution, None): distribution to use to sample the next state. If None, it will be deterministic if the model is deterministic. preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output """ # set inner model - super(ParametrizedDynamicModel, self).__init__(states, actions, next_states, preprocessors=preprocessors, + super(ParametrizedDynamicModel, self).__init__(state, action, next_state, preprocessors=preprocessors, postprocessors=postprocessors) self.model = model self.distributions = distributions @@ -364,9 +394,9 @@ class ParametrizedDynamicModel(DynamicModel): elif isinstance(distributions, torch.distributions.Distribution): distributions = [distributions] - if len(distributions) != 0 and len(distributions) != len(self.next_states): + if len(distributions) != 0 and len(distributions) != len(self.next_state): raise ValueError("Expecting the number of distributions (={}) to match the number of states (={})" - ".".format(len(distributions), len(self.next_states))) + ".".format(len(distributions), len(self.next_state))) self._distributions = distributions @property @@ -489,11 +519,40 @@ class ParametrizedDynamicModel(DynamicModel): # set the state data if specified if set_state_data: - self.next_states.data = data + self.next_state.data = data # return next state data return data + ############# + # Operators # + ############# + + def __copy__(self): + """Return a shallow copy of the dynamic model. This can be overridden in the child class.""" + return self.__class__(state=self.state, action=self.action, model=self.model, next_state=self.next_state, + distributions=self.distributions, preprocessors=self.preprocessors, + postprocessors=self.postprocessors) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the dynamic model. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) + action = copy.deepcopy(self.action, memo) + model = copy.deepcopy(self.model, memo) + distributions = [copy.deepcopy(distribution) for distribution in self.distributions] + next_state = copy.deepcopy(self.next_state, memo) + preprocessors = [copy.deepcopy(preprocessor, memo) for preprocessor in self.preprocessors] + postprocessors = [copy.deepcopy(postprocessor, memo) for postprocessor in self.postprocessors] + dynamic = self.__class__(state=state, action=action, model=model, next_state=next_state, + distributions=distributions, preprocessors=preprocessors, + postprocessors=postprocessors) + memo[self] = dynamic + return dynamic + # class GPDynamicModel(ParametrizedDynamicModel): # r"""Gaussian Process Dynamic Model diff --git a/pyrobolearn/dynamics/nn_dynamic.py b/pyrobolearn/dynamics/nn_dynamic.py index ccd7b41..9ea30e3 100644 --- a/pyrobolearn/dynamics/nn_dynamic.py +++ b/pyrobolearn/dynamics/nn_dynamic.py @@ -28,15 +28,15 @@ class NNDynamicModel(ParametrizedDynamicModel): Cons: requires lot of samples, overfitting,... """ - def __init__(self, states, actions, model, next_states=None, distributions=None, preprocessors=None, + def __init__(self, state, action, model, next_state=None, distributions=None, preprocessors=None, postprocessors=None): """ Initialize the NN dynamic model. Args: - states (State): state inputs. - actions (Action): action inputs. - next_states (State, None): state outputs. If None, it will take the state inputs as the outputs. + state (State): state inputs. + action (Action): action inputs. + next_state (State, None): state outputs. If None, it will take the state inputs as the outputs. model (NNApproximator, NN): neural network model. distributions (torch.distributions.Distribution): distribution to use to sample the next state. If None, it will be deterministic. @@ -46,11 +46,11 @@ class NNDynamicModel(ParametrizedDynamicModel): if model is None: raise TypeError("Expecting the model to be a neural network and not None.") elif not isinstance(model, NNApproximator): - if next_states is None: - next_states = states - model = NNApproximator(inputs=[states, actions], outputs=next_states, model=model, + if next_state is None: + next_state = state + model = NNApproximator(inputs=[state, action], outputs=next_state, model=model, preprocessors=preprocessors, postprocessors=postprocessors) - super(NNDynamicModel, self).__init__(states, actions, model=model, next_states=next_states, + super(NNDynamicModel, self).__init__(state, action, model=model, next_state=next_state, distributions=distributions) @@ -59,16 +59,16 @@ class MLPDynamicModel(NNDynamicModel): """ - def __init__(self, states, actions, next_states=None, hidden_units=(), activation_fct='Linear', + def __init__(self, state, action, next_state=None, hidden_units=(), activation_fct='Linear', last_activation_fct=None, dropout_prob=None, distributions=None, preprocessors=None, postprocessors=None): """ Initialize the multi-layer perceptron model. Args: - states (State): state inputs. - actions (Action): action inputs. - next_states (State, None): state outputs. If None, it will take the state inputs as the outputs. + state (State): state inputs. + action (Action): action inputs. + next_state (State, None): state outputs. If None, it will take the state inputs as the outputs. hidden_units (tuple, list of int): number of hidden units in each layer activation_fct (str): activation function to apply on each layer last_activation_fct (str, None): activation function to apply on the last layer @@ -78,11 +78,11 @@ class MLPDynamicModel(NNDynamicModel): preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output """ - if next_states is None: - next_states = states - model = MLPApproximator(inputs=[states, actions], outputs=next_states, hidden_units=hidden_units, + if next_state is None: + next_state = state + model = MLPApproximator(inputs=[state, action], outputs=next_state, hidden_units=hidden_units, activation=activation_fct, last_activation=last_activation_fct, dropout=dropout_prob) - super(MLPDynamicModel, self).__init__(states, actions, model=model, next_states=next_states, + super(MLPDynamicModel, self).__init__(state, action, model=model, next_state=next_state, distributions=distributions, preprocessors=preprocessors, postprocessors=postprocessors) diff --git a/pyrobolearn/dynamics/robot_dynamic.py b/pyrobolearn/dynamics/robot_dynamic.py index bddc955..fd19a29 100644 --- a/pyrobolearn/dynamics/robot_dynamic.py +++ b/pyrobolearn/dynamics/robot_dynamic.py @@ -22,8 +22,8 @@ class PhysicalDynamicModel(DynamicModel): Dynamic model described by mathematical/physical equations. """ - def __init__(self, states, actions): - super(PhysicalDynamicModel, self).__init__(states, actions) + def __init__(self, state, action): + super(PhysicalDynamicModel, self).__init__(state, action) class RobotDynamicModel(PhysicalDynamicModel): @@ -37,8 +37,8 @@ class RobotDynamicModel(PhysicalDynamicModel): * the states/actions have to be robot states/actions """ - def __init__(self, states, actions, robot): - super(RobotDynamicModel, self).__init__(states, actions) + def __init__(self, state, action, robot): + super(RobotDynamicModel, self).__init__(state, action) self.robot = robot ############## diff --git a/pyrobolearn/envs/env.py b/pyrobolearn/envs/env.py index 2ece6ef..bca2739 100644 --- a/pyrobolearn/envs/env.py +++ b/pyrobolearn/envs/env.py @@ -10,6 +10,8 @@ Dependencies: - (`pyrobolearn.envs.terminal_condition`) """ +import copy +import pickle # import gym from pyrobolearn.worlds import World, BasicWorld @@ -61,7 +63,7 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env Args: world (World): world of the environment. The world contains all the objects (including robots), and has access to the simulator. - states (State): states that are returned by the environment at each time step. + states ((list of) State): states that are returned by the environment at each time step. rewards (None, Reward): The rewards can be None when for instance we are in an imitation learning setting, instead of a reinforcement learning one. If None, only the state is returned by the environment. terminal_conditions (None, callable, TerminalCondition, list of TerminalCondition): A callable function or @@ -71,9 +73,10 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env physics_randomizers (None, PhysicsRandomizer, list of PhysicsRandomizer): physics randomizers. This will be called each time you reset the environment. extra_info (None, callable): Extra info returned by the environment at each time step. - actions (Action): actions that are given to the environment. Note that this is not used here in the current - environment as it should be the policy that performs the action. This is useful when creating policies - after the environment (that is, the policy can uses the environment's states and actions). + actions ((list of) Action): actions that are given to the environment. Note that this is not used here in + the current environment as it should be the policy that performs the action. This is useful when + creating policies after the environment (that is, the policy can uses the environment's states and + actions). """ # Check and set parameters (see corresponding properties) self.world = world @@ -103,7 +106,8 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env def world(self, world): """Set the world.""" if not isinstance(world, World): - raise TypeError("Expecting the 'world' argument to be an instance of World.") + raise TypeError("Expecting the given 'world' argument to be an instance of `World`, instead got: " + "{}".format(type(world))) self._world = world self.sim = self._world.simulator @@ -176,9 +180,8 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env """Set the rewards.""" if rewards is not None: if not isinstance(rewards, Reward): - raise TypeError("Expecting the 'rewards' argument to be an instance of Reward.") - else: - rewards = lambda: None + raise TypeError("Expecting the given 'rewards' argument to be an instance of `Reward` or None, " + "instead got: {}".format(type(rewards))) self._rewards = rewards @property @@ -301,7 +304,12 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env reward, done, info). Args: - action (Action, None): an action provided by the policy(ies) to the environment + actions (None, (list of) Action, (list of) np.array): an action provided by the policy(ies) to the + environment. Note that this is not used in this method; calling the actions should be done inside the + policy(ies), and not in the environment. The policy decides when to execute an action. Several problems + can appear by providing the actions in the environment instead of letting the policy executes them. + For instance, think about when there are multiple policies, when using multiprocessing, or when the + environment runs in real-time. Returns: observation (object): agent's observation of the current environment @@ -319,7 +327,7 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env # for action in actions: # action() # TODO: calling the actions should be done inside the policy(ies), and not in the environments. The policy - # decided when to execute an action. Think about when there are multiple policies, when using multiprocessing, + # decides when to execute an action. Think about when there are multiple policies, when using multiprocessing, # or when the environment runs in real-time. # if actions is not None and isinstance(actions, Action): # actions() @@ -329,7 +337,8 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env # compute reward # rewards = [reward.compute() for reward in self.rewards] - rewards = self.rewards() + if self.rewards is not None: + rewards = self.rewards() # compute terminating condition done = any([condition() for condition in self.terminal_conditions]) @@ -370,6 +379,46 @@ class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env for randomizer in self.physics_randomizers: randomizer.seed(seed) + ############# + # Operators # + ############# + + def __call__(self, actions=None): + """Alias to `step` method.""" + return self.step(actions) + + def __str__(self): + """Return a string describing the environment.""" + string = self.__class__.__name__ + '(\n\tworld=' + str(self.world) + ',\n\tstates=' + str(self.states) \ + + ',\n\trewards=' + str(self.rewards) + '\n)' + return string + + def __copy__(self): + """Return a shallow copy of the approximator. This can be overridden in the child class.""" + return self.__class__(world=self.world, states=self.states, rewards=self.rewards, + terminal_conditions=self.terminal_conditions, + initial_state_generators=self.state_generators, + physics_randomizers=self.physics_randomizers, + extra_info=self.extra_info, actions=self.actions) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the environment. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + world = copy.deepcopy(self.world, memo) + states = [copy.deepcopy(state, memo) for state in self.states] + rewards = None if self.rewards is None else copy.deepcopy(self.rewards, memo) + terminal_conditions = [copy.deepcopy(condition) for condition in self.terminal_conditions] + state_generators = [copy.deepcopy(generator, memo) for generator in self.state_generators] + physics_randomizers = [copy.deepcopy(randomizer, memo) for randomizer in self.physics_randomizers] + extra_info = copy.deepcopy(self.extra_info) + actions = None if self.actions is None else [copy.deepcopy(action, memo) for action in self.actions] + return self.__class__(world=world, states=states, rewards=rewards, terminal_conditions=terminal_conditions, + initial_state_generators=state_generators, physics_randomizers=physics_randomizers, + extra_info=extra_info, actions=actions) + class BasicEnv(Env): """Basic Environment class. diff --git a/pyrobolearn/policies/basic_policy.py b/pyrobolearn/policies/basic_policy.py index 1917d24..ba35ab3 100644 --- a/pyrobolearn/policies/basic_policy.py +++ b/pyrobolearn/policies/basic_policy.py @@ -49,18 +49,18 @@ class RandomPolicy(Policy): spaces = self.actions.space return [space.sample() for space in spaces] - def __init__(self, states, actions, rate=1, seed=None, preprocessors=None, postprocessors=None, *args, **kwargs): + def __init__(self, state, action, rate=1, seed=None, preprocessors=None, postprocessors=None, *args, **kwargs): """ Initialize the Random policy. Args: - actions (Action): At each step, by calling `policy.act(state)`, the `actions` are computed by the policy, - and should be given to the environment. As with the `states`, the type and size/shape of each action - can be inferred and could be used to automatically build a policy. The `action` connects the policy - with a controllable object (such as a robot) in the environment. - states (State): By giving the `states` to the policy, it can automatically infer the type and size/shape - of each state, and thus can be used to automatically build a policy. At each step, the `states` - are filled by the environment, and read by the policy. The `state` connects the policy with one or + action (Action): At each step, by calling `policy.act(state)`, the `action` is computed by the policy, + and can be given to the environment. As with the `state`, the type and size/shape of each inner + action can be inferred and could be used to automatically build a policy. The `action` connects the + policy with a controllable object (such as a robot) in the environment. + state (State): By giving the `state` to the policy, it can automatically infer the type and size/shape + of each inner state, and thus can be used to automatically build a policy. At each step, the `state` + is filled by the environment, and read by the policy. The `state` connects the policy with one or several objects (including robots) in the environment. Note that some policies don't use any state information. model (Approximator, Model, None): inner model or approximator @@ -72,8 +72,8 @@ class RandomPolicy(Policy): *args (list): list of arguments **kwargs (dict): dictionary of arguments """ - model = self.RandomModel(actions, seed=seed) - super(RandomPolicy, self).__init__(states, actions, model=model, rate=rate, preprocessors=preprocessors, + model = self.RandomModel(action, seed=seed) + super(RandomPolicy, self).__init__(state, action, model=model, rate=rate, preprocessors=preprocessors, postprocessors=postprocessors, *args, **kwargs) def sample(self, state=None): @@ -84,18 +84,18 @@ class LinearPolicy(Policy): """Linear Policy """ - def __init__(self, states, actions, rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): + def __init__(self, state, action, rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): """ Initialize the Linear Policy. Args: - actions (Action): At each step, by calling `policy.act(state)`, the `actions` are computed by the policy, - and should be given to the environment. As with the `states`, the type and size/shape of each action - can be inferred and could be used to automatically build a policy. The `action` connects the policy - with a controllable object (such as a robot) in the environment. - states (State): By giving the `states` to the policy, it can automatically infer the type and size/shape - of each state, and thus can be used to automatically build a policy. At each step, the `states` - are filled by the environment, and read by the policy. The `state` connects the policy with one or + action (Action): At each step, by calling `policy.act(state)`, the `action` is computed by the policy, + and can be given to the environment. As with the `state`, the type and size/shape of each inner + action can be inferred and could be used to automatically build a policy. The `action` connects the + policy with a controllable object (such as a robot) in the environment. + state (State): By giving the `state` to the policy, it can automatically infer the type and size/shape + of each inner state, and thus can be used to automatically build a policy. At each step, the `state` + is filled by the environment, and read by the policy. The `state` connects the policy with one or several objects (including robots) in the environment. Note that some policies don't use any state information. rate (int, float): rate (float) at which the policy operates if we are operating in real-time. If we are @@ -106,8 +106,8 @@ class LinearPolicy(Policy): *args (list): list of arguments **kwargs (dict): dictionary of arguments """ - model = LinearApproximator(states, actions, preprocessors=preprocessors, postprocessors=postprocessors) - super(LinearPolicy, self).__init__(states, actions, model, rate=rate, *args, **kwargs) + model = LinearApproximator(state, action, preprocessors=preprocessors, postprocessors=postprocessors) + super(LinearPolicy, self).__init__(state, action, model, rate=rate, *args, **kwargs) class PolicyFromQValue(Policy): @@ -137,7 +137,7 @@ class PolicyFromQValue(Policy): **kwargs (dict): dictionary of arguments """ self.value = value - super(PolicyFromQValue, self).__init__(states=value.state, actions=value.action, model=value, rate=rate, + super(PolicyFromQValue, self).__init__(state=value.state, action=value.action, model=value, rate=rate, preprocessors=preprocessors, postprocessors=postprocessors, *args, **kwargs) @@ -214,7 +214,7 @@ if __name__ == '__main__': from pyrobolearn.actions import FixedAction # check linear policy - policy = LinearPolicy(states=FixedState(range(4)), actions=FixedAction(range(2))) + policy = LinearPolicy(state=FixedState(range(4)), action=FixedAction(range(2))) print(policy) target = copy.deepcopy(policy) diff --git a/pyrobolearn/policies/cpg_policy.py b/pyrobolearn/policies/cpg_policy.py index d79a026..3559c00 100644 --- a/pyrobolearn/policies/cpg_policy.py +++ b/pyrobolearn/policies/cpg_policy.py @@ -27,7 +27,7 @@ class CPGPolicy(Policy): r"""Central Pattern Generator (CPG) Network policy """ - def __init__(self, states, actions, rate=1, cpg_network=None, amplitude=np.pi/4, offset=0., init_phase=0., + def __init__(self, state, action, rate=1, cpg_network=None, amplitude=np.pi / 4, offset=0., init_phase=0., freq=1., couple_hips=False, parent_coupling=True, child_coupling=True, hip_coupling_weight=None, hip_coupling_bias=0., parent_coupling_weight=None, parent_coupling_bias=0., child_coupling_weight=None, child_coupling_bias=0., update_amplitudes=True, update_offsets=True, @@ -38,8 +38,8 @@ class CPGPolicy(Policy): Initialize the CPG Network Policy. Args: - states (PhaseState): phase state. - actions (JointAction): joint action. Normally, it will be JointPositionAction. + state (PhaseState): phase state. + action (JointAction): joint action. Normally, it will be JointPositionAction. rate (int, float): rate (float) at which the policy operates if we are operating in real-time. If we are stepping deterministically in the simulator, it represents the number of ticks (int) to sleep before executing the model. @@ -97,23 +97,23 @@ class CPGPolicy(Policy): *args (list): other arguments given to the CPG network learning model. **kwargs (dict): other key + value arguments given to the CPG network learning model. """ - super(CPGPolicy, self).__init__(states, actions, rate=rate, preprocessors=preprocessors, + super(CPGPolicy, self).__init__(state, action, rate=rate, preprocessors=preprocessors, postprocessors=postprocessors, *args, **kwargs) # check actions - if not isinstance(actions, JointAction): + if not isinstance(action, JointAction): raise TypeError("Expecting the actions to be an instance of JointAction, instead got: " - "{}".format(type(actions))) + "{}".format(type(action))) # check states - if not isinstance(states, PhaseState): + if not isinstance(state, PhaseState): raise TypeError("Expecting the states to be an instance of PhaseState, instead got: " - "{}".format(type(states))) + "{}".format(type(state))) # get useful information from the state/action - timesteps = states.num_steps - robot = actions.robot - joints = set(actions.joints) + timesteps = state.num_steps + robot = action.robot + joints = set(action.joints) # create CPG network based on the robot kinematic structures if not provided if cpg_network is None: diff --git a/pyrobolearn/policies/dmp_policy.py b/pyrobolearn/policies/dmp_policy.py index a7ac9e3..e608d91 100644 --- a/pyrobolearn/policies/dmp_policy.py +++ b/pyrobolearn/policies/dmp_policy.py @@ -27,19 +27,42 @@ class DMPPolicy(Policy): r"""Dynamic Movement Primitive (DMP) policy """ - def __init__(self, states, actions, model, rate=1, *args, **kwargs): + def __init__(self, state, action, model, rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): + """ + Initialize the DMP policy. + + Args: + action (Action): At each step, by calling `policy.act(state)`, the `action` is computed by the policy, + and can be given to the environment. As with the `state`, the type and size/shape of each inner + action can be inferred and could be used to automatically build a policy. The `action` connects the + policy with a controllable object (such as a robot) in the environment. + state (State): By giving the `state` to the policy, it can automatically infer the type and size/shape + of each inner state, and thus can be used to automatically build a policy. At each step, the `state` + is filled by the environment, and read by the policy. The `state` connects the policy with one or + several objects (including robots) in the environment. Note that some policies don't use any state + information. + model (DMP): DMP model + rate (int, float): rate (float) at which the policy operates if we are operating in real-time. If we are + stepping deterministically in the simulator, it represents the number of ticks (int) to sleep before + executing the model. + preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input + postprocessors (Processor, list of Processor, None): post-processors to be applied to the output + *args (list): list of arguments (this is not used in this class). + **kwargs (dict): dictionary of arguments (this is not used in this class). + """ if not isinstance(model, DMP): raise TypeError("Expecting model to be an instance of DMP") - super(DMPPolicy, self).__init__(states, actions, model, rate=rate, *args, **kwargs) + super(DMPPolicy, self).__init__(state=state, action=action, model=model, rate=rate, + preprocessors=preprocessors, postprocessors=postprocessors, *args, **kwargs) # check actions - self.is_joint_position_action = JointPositionAction in actions or JointPositionAndVelocityAction in actions - self.is_joint_velocity_action = JointVelocityAction in actions or JointPositionAndVelocityAction in actions - self.is_joint_acceleration_action = JointAccelerationAction in actions + self.is_joint_position_action = JointPositionAction in action or JointPositionAndVelocityAction in action + self.is_joint_velocity_action = JointVelocityAction in action or JointPositionAndVelocityAction in action + self.is_joint_acceleration_action = JointAccelerationAction in action if not (self.is_joint_position_action or self.is_joint_velocity_action or self.is_joint_acceleration_action): raise ValueError("The actions do not have a joint position, velocity, or acceleration action.") - def inner_predict(self, state, to_numpy=False, return_logits=True, set_output_data=False): + def inner_predict(self, state, deterministic=True, to_numpy=False, return_logits=True, set_output_data=False): """Inner prediction step. Args: @@ -106,34 +129,97 @@ class DMPPolicy(Policy): class DiscreteDMPPolicy(DMPPolicy): r"""Discrete DMP Policy + + See Also: see documentation in `pyrobolearn.models.dmp.discrete_dmp.py` + + References: + [1] "Dynamical movement primitives: Learning attractor models for motor behaviors", Ijspeert et al., 2013 """ - def __init__(self, actions, states=None, num_basis=20, dt=0.01, y0=0, goal=1, forcing_terms=None, + def __init__(self, action, state=None, num_basis=20, dt=0.01, y0=0, goal=1, forcing_terms=None, stiffness=None, damping=None, rate=1): - if not isinstance(actions, Action): + """ + Initialize the discrete DMP policy. + + Args: + action: + state: + num_basis (int): number of basis functions + dt (float): step integration for Euler's method + y0 (float, np.array): initial position(s) + goal (float, np.array): goal(s) + forcing_terms (list, ForcingTerm): the forcing terms (which can have different basis functions) + stiffness (float): stiffness coefficient + damping (float): damping coefficient + rate: + """ + if not isinstance(action, Action): raise TypeError("Expecting actions to be an instance of the 'Action' class.") - model = DiscreteDMP(num_dmps=self._size(actions), num_basis=num_basis, dt=dt, y0=y0, goal=goal, + model = DiscreteDMP(num_dmps=self._size(action), num_basis=num_basis, dt=dt, y0=y0, goal=goal, forcing_terms=forcing_terms, stiffness=stiffness, damping=damping) - super(DiscreteDMPPolicy, self).__init__(states, actions, model, rate=rate) + super(DiscreteDMPPolicy, self).__init__(state, action, model, rate=rate) class RhythmicDMPPolicy(DMPPolicy): r"""Rhythmic DMP Policy + + See Also: see documentation in `pyrobolearn.models.dmp.rhythmic_dmp.py` + + References: + [1] "Dynamical movement primitives: Learning attractor models for motor behaviors", Ijspeert et al., 2013 """ - def __init__(self, actions, states=None, num_basis=20, dt=0.01, y0=0, goal=1, forcing_terms=None, + def __init__(self, action, state=None, num_basis=20, dt=0.01, y0=0, goal=1, forcing_terms=None, stiffness=None, damping=None, rate=1): - model = RhythmicDMP(num_dmps=self._size(actions), num_basis=num_basis, dt=dt, y0=y0, goal=goal, + """ + Initialize the Rhythmic DMP policy. + + Args: + action: + state: + num_basis (int): number of basis functions + dt (float): step integration for Euler's method + y0 (float, np.array): initial position(s) + goal (float, np.array): goal(s) + forcing_terms (list, ForcingTerm): the forcing terms (which can have different basis functions) + stiffness (float): stiffness coefficient + damping (float): damping coefficient + rate: + """ + model = RhythmicDMP(num_dmps=self._size(action), num_basis=num_basis, dt=dt, y0=y0, goal=goal, forcing_terms=forcing_terms, stiffness=stiffness, damping=damping) - super(RhythmicDMPPolicy, self).__init__(states, actions, model, rate=rate) + super(RhythmicDMPPolicy, self).__init__(state, action, model, rate=rate) class BioDiscreteDMPPolicy(DMPPolicy): r"""Bio Discrete DMP Policy + + See Also: see documentation in `pyrobolearn.models.dmp.biodiscrete_dmp.py` + + References: + [1] "Dynamical movement primitives: Learning attractor models for motor behaviors", Ijspeert et al., 2013 + [2] "Biologically-inspired Dynamical Systems for Movement Generation: Automatic Real-time Goal Adaptation + and Obstacle Avoidance", Hoffmann et al., 2009 + [3] "Learning and Generalization of Motor Skills by Learning from Demonstration", Pastor et al., 2009 """ - def __init__(self, actions, states=None, num_basis=20, dt=0.01, y0=0, goal=1, forcing_terms=None, + def __init__(self, action, state=None, num_basis=20, dt=0.01, y0=0, goal=1, forcing_terms=None, stiffness=None, damping=None, rate=1): - model = BioDiscreteDMP(num_dmps=self._size(actions), num_basis=num_basis, dt=dt, y0=y0, goal=goal, + """ + Initialize the biologically-inspired DMP policy. + + Args: + action: + state: + num_basis (int): number of basis functions + dt (float): step integration for Euler's method + y0 (float, np.array): initial position(s) + goal (float, np.array): goal(s) + forcing_terms (list, ForcingTerm): the forcing terms (which can have different basis functions) + stiffness (float): stiffness coefficient + damping (float): damping coefficient + rate: + """ + model = BioDiscreteDMP(num_dmps=self._size(action), num_basis=num_basis, dt=dt, y0=y0, goal=goal, forcing_terms=forcing_terms, stiffness=stiffness, damping=damping) - super(BioDiscreteDMPPolicy, self).__init__(states, actions, model, rate=rate) + super(BioDiscreteDMPPolicy, self).__init__(state, action, model, rate=rate) diff --git a/pyrobolearn/policies/neat_policy.py b/pyrobolearn/policies/neat_policy.py index 3b827ba..1c181ad 100644 --- a/pyrobolearn/policies/neat_policy.py +++ b/pyrobolearn/policies/neat_policy.py @@ -54,19 +54,19 @@ class NEATPolicy(Policy): [3] PyTorch NEAT (built upon NEAT-Python): https://github.com/uber-research/PyTorch-NEAT """ - def __init__(self, states, actions, num_hidden=0, activation_fct='relu', network_type='feedforward', + def __init__(self, state, action, num_hidden=0, activation_fct='relu', network_type='feedforward', aggregation='sum', weights_limits=(-20, 20), bias_limits=(-20, 20), rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): r"""Initialize the neural network policy for the NEAT algorithm. Args: - actions (Action): At each step, by calling `policy.act(state)`, the `actions` are computed by the policy, - and should be given to the environment. As with the `states`, the type and size/shape of each action - can be inferred and could be used to automatically build a policy. The `action` connects the policy - with a controllable object (such as a robot) in the environment. - states (State): By giving the `states` to the policy, it can automatically infer the type and size/shape - of each state, and thus can be used to automatically build a policy. At each step, the `states` - are filled by the environment, and read by the policy. The `state` connects the policy with one or + action (Action): At each step, by calling `policy.act(state)`, the `action` is computed by the policy, + and can be given to the environment. As with the `state`, the type and size/shape of each inner + action can be inferred and could be used to automatically build a policy. The `action` connects the + policy with a controllable object (such as a robot) in the environment. + state (State): By giving the `state` to the policy, it can automatically infer the type and size/shape + of each inner state, and thus can be used to automatically build a policy. At each step, the `state` + is filled by the environment, and read by the policy. The `state` connects the policy with one or several objects (including robots) in the environment. Note that some policies don't use any state information. num_hidden (int): number of units in the hidden layer @@ -82,10 +82,10 @@ class NEATPolicy(Policy): preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output """ - model = NEATApproximator(states, actions, num_hidden=num_hidden, activation_fct=activation_fct, + model = NEATApproximator(state, action, num_hidden=num_hidden, activation_fct=activation_fct, network_type=network_type, aggregation=aggregation, weights_limits=weights_limits, bias_limits=bias_limits, preprocessors=preprocessors, postprocessors=postprocessors) - super(NEATPolicy, self).__init__(states, actions, model, rate=rate, *args, **kwargs) + super(NEATPolicy, self).__init__(state, action, model, rate=rate, *args, **kwargs) ############## # Properties # diff --git a/pyrobolearn/policies/nn_policy.py b/pyrobolearn/policies/nn_policy.py index c0ea81b..c8cf30a 100644 --- a/pyrobolearn/policies/nn_policy.py +++ b/pyrobolearn/policies/nn_policy.py @@ -24,18 +24,18 @@ class NNPolicy(Policy): Defines the neural network policy. """ - def __init__(self, states, actions, model=None, rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): + def __init__(self, state, action, model=None, rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): """ Initialize the Neural network policy. Args: - actions (Action): At each step, by calling `policy.act(state)`, the `actions` are computed by the policy, - and should be given to the environment. As with the `states`, the type and size/shape of each action - can be inferred and could be used to automatically build a policy. The `action` connects the policy - with a controllable object (such as a robot) in the environment. - states (State): By giving the `states` to the policy, it can automatically infer the type and size/shape - of each state, and thus can be used to automatically build a policy. At each step, the `states` - are filled by the environment, and read by the policy. The `state` connects the policy with one or + action (Action): At each step, by calling `policy.act(state)`, the `action` is computed by the policy, + and can be given to the environment. As with the `state`, the type and size/shape of each inner + action can be inferred and could be used to automatically build a policy. The `action` connects the + policy with a controllable object (such as a robot) in the environment. + state (State): By giving the `state` to the policy, it can automatically infer the type and size/shape + of each inner state, and thus can be used to automatically build a policy. At each step, the `state` + is filled by the environment, and read by the policy. The `state` connects the policy with one or several objects (including robots) in the environment. Note that some policies don't use any state information. model (NN, NNApproximator): NN model @@ -44,8 +44,8 @@ class NNPolicy(Policy): executing the model. preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output - *args (list): list of arguments - **kwargs (dict): dictionary of arguments + *args (list): list of arguments (this is not used in this class). + **kwargs (dict): dictionary of arguments (this is not used in this class). """ if model is None: raise ValueError("Expecting a NN model for the NN policy") @@ -54,7 +54,7 @@ class NNPolicy(Policy): # checking the output dimension of the model and the dimension of actions pass - super(NNPolicy, self).__init__(states, actions, model, rate=rate, preprocessors=preprocessors, + super(NNPolicy, self).__init__(state, action, model, rate=rate, preprocessors=preprocessors, postprocessors=postprocessors, *args, **kwargs) # def act(self, state, deterministic=True): @@ -71,14 +71,14 @@ class MLPPolicy(NNPolicy): activation functions. """ - def __init__(self, states, actions, hidden_units=(), activation='linear', last_activation=None, + def __init__(self, state, action, hidden_units=(), activation='linear', last_activation=None, dropout=None, rate=1, preprocessors=None, postprocessors=None): """Initialize MLP policy. Args: - states (State): 1D-states that is feed to the policy (the input dimensions will be inferred from the + state (State): 1D-states that is feed to the policy (the input dimensions will be inferred from the states) - actions (Action): 1D-actions outputted by the policy and will be applied in the simulator (the output + action (Action): 1D-actions outputted by the policy and will be applied in the simulator (the output dimensions will be inferred from the actions) hidden_units (list/tuple of int): number of hidden units in the corresponding layer activation (None, str, or list/tuple of str/None): activation function to be applied after each layer. @@ -93,10 +93,10 @@ class MLPPolicy(NNPolicy): preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output """ - model = MLPApproximator(states, actions, hidden_units=hidden_units, + model = MLPApproximator(state, action, hidden_units=hidden_units, activation=activation, last_activation=last_activation, dropout=dropout, preprocessors=preprocessors, postprocessors=postprocessors) - super(MLPPolicy, self).__init__(states, actions, model, rate=rate) + super(MLPPolicy, self).__init__(state, action, model, rate=rate) # def act(self, state, deterministic=True): # return self.model.predict(state) diff --git a/pyrobolearn/policies/policy.py b/pyrobolearn/policies/policy.py index c149075..e3a8112 100644 --- a/pyrobolearn/policies/policy.py +++ b/pyrobolearn/policies/policy.py @@ -12,8 +12,9 @@ Dependencies: - `pyrobolearn.exploration` """ -import collections +import copy import pickle +import collections import numpy as np import torch @@ -116,19 +117,19 @@ class Policy(object): * `exploration.py`: describes how to explore using the policy """ - def __init__(self, states, actions, model=None, rate=1, preprocessors=None, postprocessors=None, - distribution=None, *args, **kwargs): + def __init__(self, state, action, model=None, rate=1, preprocessors=None, postprocessors=None, *args, **kwargs): + # distribution=None): r""" Initialize a policy (and the inner approximator / learning model). Args: - actions (Action): At each step, by calling `policy.act(state)`, the `actions` are computed by the policy, - and should be given to the environment. As with the `states`, the type and size/shape of each action - can be inferred and could be used to automatically build a policy. The `action` connects the policy - with a controllable object (such as a robot) in the environment. - states (State): By giving the `states` to the policy, it can automatically infer the type and size/shape - of each state, and thus can be used to automatically build a policy. At each step, the `states` - are filled by the environment, and read by the policy. The `state` connects the policy with one or + action (Action): At each step, by calling `policy.act(state)`, the `action` is computed by the policy, + and can be given to the environment. As with the `state`, the type and size/shape of each inner + action can be inferred and could be used to automatically build a policy. The `action` connects the + policy with a controllable object (such as a robot) in the environment. + state (State): By giving the `state` to the policy, it can automatically infer the type and size/shape + of each inner state, and thus can be used to automatically build a policy. At each step, the `state` + is filled by the environment, and read by the policy. The `state` connects the policy with one or several objects (including robots) in the environment. Note that some policies don't use any state information. model (Approximator, Model, None): inner model or approximator @@ -137,12 +138,11 @@ class Policy(object): executing the model. preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input postprocessors (Processor, list of Processor, None): post-processors to be applied to the output - distribution: - *args (list): list of arguments - **kwargs (dict): dictionary of arguments + *args (list): list of arguments (this is not used in this class). + **kwargs (dict): dictionary of arguments (this is not used in this class). """ - self.states = states - self.actions = actions + self.states = state + self.actions = action self.model = model self.rate = rate self.cnt = 0 @@ -258,7 +258,8 @@ class Policy(object): # Methods # ########### - def _size(self, items): + @staticmethod + def _size(items): """Compute the size of the given argument :attr:`items`.""" size = 0 if not isinstance(items, (list, tuple)): @@ -692,12 +693,12 @@ class Policy(object): return self.act(state=state, deterministic=deterministic, to_numpy=to_numpy, return_logits=return_logits, apply_action=apply_action) - def __repr__(self): - """Return representation of python object.""" - if self.__class__.__name__ == 'Policy': - if self.model is not None: - return "{}({})".format(self.__class__.__name__, self.model.__str__()) - return self.__class__.__name__ + # def __repr__(self): + # """Return representation of python object.""" + # if self.__class__.__name__ == 'Policy': + # if self.model is not None: + # return "{}({})".format(self.__class__.__name__, self.model.__str__()) + # return self.__class__.__name__ def __str__(self): """Return string describing the policy.""" @@ -705,3 +706,29 @@ class Policy(object): if self.model is not None: return "{}({})".format(self.__class__.__name__, self.model.__str__()) return self.__class__.__name__ + + def __copy__(self): + """Return a shallow copy of the policy. This can be overridden in the child class.""" + return self.__class__(states=self.states, actions=self.actions, model=self.model, rate=self.rate, + preprocessors=self.preprocessors, postprocessors=self.postprocessors) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the policy. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + states = copy.deepcopy(self.states, memo) + actions = copy.deepcopy(self.actions, memo) + model = copy.deepcopy(self.model, memo) + rate = copy.deepcopy(self.rate, memo) + preprocessors = [copy.deepcopy(preprocessor, memo) for preprocessor in self.preprocessors] + postprocessors = [copy.deepcopy(postprocessor, memo) for postprocessor in self.postprocessors] + policy = self.__class__(states=states, actions=actions, model=model, rate=rate, + preprocessors=preprocessors, postprocessors=postprocessors) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = policy + + return policy diff --git a/pyrobolearn/rewards/basic_rewards.py b/pyrobolearn/rewards/basic_rewards.py index b319ca9..96141c8 100644 --- a/pyrobolearn/rewards/basic_rewards.py +++ b/pyrobolearn/rewards/basic_rewards.py @@ -202,8 +202,9 @@ class DirectiveReward(Reward): if __name__ == '__main__': from pyrobolearn.rewards import cos - reward = 2*FixedReward(1, range=(-1., 1.)) + FixedReward(3)**2 - 10 + reward = 2*FixedReward(1, range=(-1., 1.)) - FixedReward(3)**2 - 10 reward += FixedReward(2) + print(reward) print("\n2*FixedReward(1, range=(-1., 1.)) + FixedReward(3)**2 - 10 + FixedReward(2) = {}".format(reward())) print("Is an instance of Reward? {}".format(isinstance(reward, Reward))) print("Inner rewards: {}".format(reward.rewards)) diff --git a/pyrobolearn/rewards/reward.py b/pyrobolearn/rewards/reward.py index 76730df..d39f8ff 100644 --- a/pyrobolearn/rewards/reward.py +++ b/pyrobolearn/rewards/reward.py @@ -95,8 +95,9 @@ class Reward(object): state (None, State): state on which depends the reward function. action (None, Action): action on which depends the reward function. rewards (None, list of Reward): list of intern rewards. - range (tuple of float): A tuple corresponding to the min and max possible rewards. By default, it is - [-infinity, infinity]. The computed reward is automatically clipped if it goes outside the range. + range (tuple of float, np.array of 2 float): A tuple corresponding to the min and max possible rewards. + By default, it is [-infinity, infinity]. The computed reward is automatically clipped if it goes + outside the range. """ # super(Reward, self).__init__(maximize=True) @@ -278,18 +279,41 @@ class Reward(object): # Operators # ############# - def __repr__(self): + def __str__(self): """Return a representation string about the reward function.""" if not self.rewards or self.rewards is None: return self.__class__.__name__ else: - lst = [reward.__repr__() for reward in self.rewards] + lst = [reward.__str__() for reward in self.rewards] return ' + '.join(lst) def __call__(self): # **kwargs): """Compute the reward function.""" return self.compute() # **kwargs) + def __copy__(self): + """Return a shallow copy of the reward function. This can be overridden in the child class.""" + return self.__class__(state=self.state, action=self.action, rewards=self.rewards, range=self.range) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the reward function. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) + action = copy.deepcopy(self.action, memo) + rewards = [copy.deepcopy(reward, memo) for reward in self.rewards] + range = copy.deepcopy(self.range) + reward = self.__class__(state=state, action=action, rewards=rewards, range=range) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = reward + + # return the copy + return reward + # for unary and binary operators, see `__init__()` method. def __build_reward(self, other): diff --git a/pyrobolearn/robots/actuators/actuator.py b/pyrobolearn/robots/actuators/actuator.py index 3576d64..9943218 100644 --- a/pyrobolearn/robots/actuators/actuator.py +++ b/pyrobolearn/robots/actuators/actuator.py @@ -23,6 +23,7 @@ class Actuator(object): All actuator classes inherit from this class. Actuators such as motors are often attached to the robot joints. Other actuators such as speakers, leds, and others are attached to links. """ + def __init__(self): pass @@ -35,3 +36,44 @@ class Actuator(object): # @simulator.setter # def simulator(self, simulator): # self.sim = simulator + + ########### + # Methods # + ########### + + def compute(self, *args, **kwargs): # TODO: call it actuate? + pass + + ############# + # Operators # + ############# + + def __call__(self, *args, **kwargs): + return self.compute(*args, **kwargs) + + # def __repr__(self): + # """Return a representation string about the class for debugging and development.""" + # return self.__class__.__name__ + + def __str__(self): + """Return a readable string about the class.""" + return self.__class__.__name__ + + def __copy__(self): + """Return a shallow copy of the actuator. This can be overridden in the child class.""" + return self.__class__() + + def __deepcopy__(self, memo={}): + """Return a deep copy of the actuator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + actuator = self.__class__() + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = actuator + + # return the copy + return actuator diff --git a/pyrobolearn/robots/actuators/joints.py b/pyrobolearn/robots/actuators/joints.py index 68f0bbc..066dc2a 100644 --- a/pyrobolearn/robots/actuators/joints.py +++ b/pyrobolearn/robots/actuators/joints.py @@ -2,6 +2,7 @@ """Define the various joint actuators used in robotics. """ +import copy import numpy as np from pyrobolearn.robots.actuators.actuator import Actuator @@ -24,10 +25,26 @@ class JointActuator(Actuator): For instance, given a target joint position value, the actuator computes the necessary torque to be applied on the joint using a simple PD control (with certain gains). """ + def __init__(self, joint_id): super(JointActuator, self).__init__() self.joint_id = joint_id + def __copy__(self): + """Return a shallow copy of the actuator. This can be overridden in the child class.""" + return self.__class__(joint_id=self.joint_id) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the actuator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + joint_id = copy.deepcopy(self.joint_id) + actuator = self.__class__(joint_id=joint_id) + memo[self] = actuator + return actuator + class PDJointActuator(JointActuator): r"""PD Joint Actuator @@ -61,10 +78,33 @@ class PDJointActuator(JointActuator): torque = np.clip(torque, self.min_torque, self.max_torque) return torque + def __copy__(self): + """Return a shallow copy of the actuator. This can be overridden in the child class.""" + return self.__class__(joint_id=self.joint_id, kp=self.kp, kd=self.kd, min_torque=self.min_torque, + max_torque=self.max_torque, latency=self.latency) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the actuator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + joint_id = copy.deepcopy(self.joint_id) + kp = copy.deepcopy(self.kp) + kd = copy.deepcopy(self.kd) + min_torque = copy.deepcopy(self.min_torque) + max_torque = copy.deepcopy(self.max_torque) + latency = copy.deepcopy(self.latency) + actuator = self.__class__(joint_id=joint_id, kp=kp, kd=kd, min_torque=min_torque, max_torque=max_torque, + latency=latency) + memo[self] = actuator + return actuator + class GearedActuator(JointActuator): r"""Geared Actuator """ + def __init__(self, joint_id): super(GearedActuator, self).__init__(joint_id) @@ -72,6 +112,7 @@ class GearedActuator(JointActuator): class DirectDriveActuator(JointActuator): r"""Direct Drive Actuator """ + def __init__(self, joint_id): super(DirectDriveActuator, self).__init__(joint_id) @@ -87,6 +128,7 @@ class SEA(JointActuator): [1] "Series elastic actuators", Pratt et al., 1995 [2] "Learning agile and dynamic motor skills for legged robots", Hwangbo et al., 2019 """ + def __init__(self, joint_id): super(SEA, self).__init__(joint_id) @@ -94,6 +136,7 @@ class SEA(JointActuator): class HydraulicActuator(JointActuator): r"""Hydraulic Actuator """ + def __init__(self, joint_id): super(HydraulicActuator, self).__init__(joint_id) @@ -105,10 +148,27 @@ class JointActuatorApproximator(JointActuator): actuator given for instance the joint positions. This function approximator has been trained on real data obtained from the real actuator and can thus be a better approximation of the way the actual actuator works. """ + def __init__(self, joint_id, approximator=None): super(JointActuatorApproximator, self).__init__(joint_id) self.approximator = approximator + def __copy__(self): + """Return a shallow copy of the actuator. This can be overridden in the child class.""" + return self.__class__(joint_id=self.joint_id, approximator=self.approximator) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the actuator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + joint_id = copy.deepcopy(self.joint_id) + approximator = copy.deepcopy(self.approximator, memo) + actuator = self.__class__(joint_id=joint_id, approximator=approximator) + memo[self] = actuator + return actuator + class ActuatorNet(JointActuatorApproximator): r"""Actuator Neural Network @@ -116,6 +176,7 @@ class ActuatorNet(JointActuatorApproximator): References: [1] "Learning agile and dynamic motor skills for legged robots", Hwangbo et al., 2019 """ + def __init__(self, joint_id, nn_model=None): super(ActuatorNet, self).__init__(joint_id, approximator=nn_model) diff --git a/pyrobolearn/robots/aibo.py b/pyrobolearn/robots/aibo.py index 8f47751..27b56cd 100644 --- a/pyrobolearn/robots/aibo.py +++ b/pyrobolearn/robots/aibo.py @@ -31,7 +31,7 @@ class Aibo(QuadrupedRobot): position=(0, 0, 0.02), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/aibo/aibo.urdf'): # check parameters if position is None: @@ -43,7 +43,7 @@ class Aibo(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(Aibo, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Aibo, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'aibo' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/allegrohand.py b/pyrobolearn/robots/allegrohand.py index fcb47d9..07990a9 100644 --- a/pyrobolearn/robots/allegrohand.py +++ b/pyrobolearn/robots/allegrohand.py @@ -28,7 +28,7 @@ class AllegroHand(Hand): simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), - scaling=1., + scale=1., left=False, fixed_base=True): # check parameters @@ -46,7 +46,7 @@ class AllegroHand(Hand): # else: urdf_path = os.path.dirname(__file__) + '/urdfs/allegrohand/allegro_right_hand.urdf' - super(AllegroHand, self).__init__(simulator, urdf_path, position, orientation, fixed_base, scaling) + super(AllegroHand, self).__init__(simulator, urdf_path, position, orientation, fixed_base, scale) self.name = 'allegro_hand' diff --git a/pyrobolearn/robots/ant.py b/pyrobolearn/robots/ant.py index d0cac62..e85ca12 100644 --- a/pyrobolearn/robots/ant.py +++ b/pyrobolearn/robots/ant.py @@ -27,7 +27,7 @@ class Ant(QuadrupedRobot): position=(0, 0, 0.2), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/mjcfs/ant.xml'): # check parameters if position is None: @@ -39,7 +39,7 @@ class Ant(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(Ant, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Ant, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'ant' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/anymal.py b/pyrobolearn/robots/anymal.py index 6cfea52..2cddd0f 100644 --- a/pyrobolearn/robots/anymal.py +++ b/pyrobolearn/robots/anymal.py @@ -35,7 +35,7 @@ class ANYmal(QuadrupedRobot): position=(0, 0, .6), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/anymal/anymal.urdf'): # check parameters if position is None: @@ -47,7 +47,7 @@ class ANYmal(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(ANYmal, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(ANYmal, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'anymal' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/atlas.py b/pyrobolearn/robots/atlas.py index f96ce76..4533138 100644 --- a/pyrobolearn/robots/atlas.py +++ b/pyrobolearn/robots/atlas.py @@ -31,7 +31,7 @@ class Atlas(BipedRobot, BiManipulatorRobot): position=(0, 0, 1.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/atlas/atlas_v4_with_multisense.urdf'): # check parameters if position is None: @@ -43,7 +43,7 @@ class Atlas(BipedRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Atlas, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Atlas, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'atlas' self.head = self.get_link_ids('head') if 'head' in self.link_names else None diff --git a/pyrobolearn/robots/ballbot.py b/pyrobolearn/robots/ballbot.py index 4f98f22..9e4877d 100644 --- a/pyrobolearn/robots/ballbot.py +++ b/pyrobolearn/robots/ballbot.py @@ -25,7 +25,7 @@ class Ballbot(Robot): """ def __init__(self, simulator, position=(0, 0, 0.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., urdf=os.path.dirname(__file__) + '/urdfs/ballbot/ballbot.urdf'): + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/ballbot/ballbot.urdf'): # check parameters if position is None: position = (0., 0., 0.) @@ -36,7 +36,7 @@ class Ballbot(Robot): if fixed_base is None: fixed_base = False - super(Ballbot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Ballbot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.ball = self.sim.load_urdf(os.path.dirname(__file__) + '/urdfs/ballbot/ball.urdf', position, orientation) self.name = 'ballbot' diff --git a/pyrobolearn/robots/base.py b/pyrobolearn/robots/base.py index b510a15..f34008b 100644 --- a/pyrobolearn/robots/base.py +++ b/pyrobolearn/robots/base.py @@ -6,6 +6,7 @@ Dependencies: - `pyrobolearn.utils` """ +import copy import numpy as np # import quaternion @@ -67,11 +68,11 @@ class Body(object): return self._id @id.setter - def id(self, id_): + def id(self, body_id): """Set the unique body id.""" - # if not isinstance(id_, int): - # raise TypeError("Expecting the given simulator to be an integer, instead got: {}".format(type(id_))) - self._id = id_ + # if not isinstance(body_id, int): + # raise TypeError("Expecting the given simulator to be an integer, instead got: {}".format(type(body_id))) + self._id = body_id @property def name(self): @@ -198,6 +199,38 @@ class Body(object): """Return the center of mass.""" return self.sim.get_center_of_mass_position(self.id) + ############# + # Operators # + ############# + + # def __repr__(self): + # """Return a representation string about the class for debugging and development.""" + # return self.__class__.__name__ + + def __str__(self): + """Return a readable string about the class.""" + return self.__class__.__name__ + + def __copy__(self): + """Return a shallow copy of the body. This can be overridden in the child class.""" + return self.__class__(simulator=self.simulator, body_id=self.id, name=self.name) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the body. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + simulator = copy.deepcopy(self.simulator, memo) + body = self.__class__(simulator=simulator, body_id=self.id, name=self.name) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = body + + # return the copy + return body + class MovableBody(Body): r"""Movable Body diff --git a/pyrobolearn/robots/baxter.py b/pyrobolearn/robots/baxter.py index 61e45af..ad08587 100644 --- a/pyrobolearn/robots/baxter.py +++ b/pyrobolearn/robots/baxter.py @@ -30,7 +30,7 @@ class Baxter(BiManipulatorRobot): position=(0, 0, 0.95), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/baxter/baxter.urdf'): # check parameters if position is None: @@ -42,7 +42,7 @@ class Baxter(BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Baxter, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Baxter, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'baxter' self.head = self.get_link_ids('head') if 'head' in self.link_names else None diff --git a/pyrobolearn/robots/bb8.py b/pyrobolearn/robots/bb8.py index 3e1d260..f2b3731 100644 --- a/pyrobolearn/robots/bb8.py +++ b/pyrobolearn/robots/bb8.py @@ -24,7 +24,7 @@ class BB8(Robot): """ def __init__(self, simulator, position=(0, 0, 0.4), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., urdf=os.path.dirname(__file__) + '/urdfs/bb8/bb8.urdf'): + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/bb8/bb8.urdf'): # check parameters if position is None: position = (0., 0., 0.4) @@ -35,7 +35,7 @@ class BB8(Robot): if fixed_base is None: fixed_base = False - super(BB8, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(BB8, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'bb8' diff --git a/pyrobolearn/robots/blackbird.py b/pyrobolearn/robots/blackbird.py index 1ac0b55..53c5f24 100644 --- a/pyrobolearn/robots/blackbird.py +++ b/pyrobolearn/robots/blackbird.py @@ -31,7 +31,7 @@ class Blackbird(BipedRobot): position=(0, 0, 1.2), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/blackbird/blackbird_biped.urdf'): self.height = 1.2 @@ -47,7 +47,7 @@ class Blackbird(BipedRobot): if fixed_base is None: fixed_base = False - super(Blackbird, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Blackbird, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'blackbird' # TODO: create constraints in pybullet diff --git a/pyrobolearn/robots/cartpole.py b/pyrobolearn/robots/cartpole.py index 3ac39ff..0a3183d 100644 --- a/pyrobolearn/robots/cartpole.py +++ b/pyrobolearn/robots/cartpole.py @@ -38,7 +38,7 @@ class CartPole(Robot): simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), - scaling=1., + scale=1., fixed_base=True, urdf=os.path.join(pybullet_data.getDataPath(), "cartpole.urdf"), num_links=1, @@ -54,7 +54,7 @@ class CartPole(Robot): if fixed_base is None: fixed_base = True - super(CartPole, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(CartPole, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'cartpole' # create dynamically other links if necessary diff --git a/pyrobolearn/robots/cassie.py b/pyrobolearn/robots/cassie.py index c69b3c0..eeb647d 100644 --- a/pyrobolearn/robots/cassie.py +++ b/pyrobolearn/robots/cassie.py @@ -36,7 +36,7 @@ class Cassie(BipedRobot): position=(0, 0, .8), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/cassie/cassie.urdf'): # check parameters if position is None: @@ -48,7 +48,7 @@ class Cassie(BipedRobot): if fixed_base is None: fixed_base = False - super(Cassie, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Cassie, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'cassie' # TODO: create constraints in pybullet diff --git a/pyrobolearn/robots/centauro.py b/pyrobolearn/robots/centauro.py index 998666b..ec758ed 100644 --- a/pyrobolearn/robots/centauro.py +++ b/pyrobolearn/robots/centauro.py @@ -30,7 +30,7 @@ class Centauro(WheeledRobot, QuadrupedRobot, BiManipulatorRobot): position=(0, 0, 1.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/centauro/centauro_stick.urdf' # centauro_stick.urdf, centauro_soft_hand.urdf, centauro_heri.urdf, # centauro_schunk_handL.urdf, centauro_schunk_hand.urdf @@ -45,7 +45,7 @@ class Centauro(WheeledRobot, QuadrupedRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Centauro, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling=scaling) + super(Centauro, self).__init__(simulator, urdf, position, orientation, fixed_base, scale=scale) self.name = 'centauro' self.necks = [self.get_link_ids(link) for link in ['neck_' + str(i) for i in range(1, 4)]] diff --git a/pyrobolearn/robots/cogimon.py b/pyrobolearn/robots/cogimon.py index c5068cb..78b71a9 100644 --- a/pyrobolearn/robots/cogimon.py +++ b/pyrobolearn/robots/cogimon.py @@ -28,7 +28,7 @@ class Cogimon(BipedRobot, BiManipulatorRobot): position=(0, 0, 1.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/cogimon/cogimon.urdf', lower_body=False): # cogimon_lower_body.urdf @@ -44,7 +44,7 @@ class Cogimon(BipedRobot, BiManipulatorRobot): if lower_body: urdf = os.path.dirname(__file__) + '/urdfs/cogimon/cogimon_lower_body.urdf' - super(Cogimon, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Cogimon, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'cogimon' self.waist = self.get_link_ids('DWL') if 'DWL' in self.link_names else None diff --git a/pyrobolearn/robots/coman.py b/pyrobolearn/robots/coman.py index 554672c..f3a1f14 100644 --- a/pyrobolearn/robots/coman.py +++ b/pyrobolearn/robots/coman.py @@ -29,7 +29,7 @@ class Coman(BipedRobot, BiManipulatorRobot): position=(0, 0, 0.5), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/coman/coman.urdf'): # check parameters if position is None: @@ -41,7 +41,7 @@ class Coman(BipedRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Coman, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Coman, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'coman' self.torso = [self.get_link_ids(link) for link in ['DWL', 'DWS', 'DWYTorso'] if link in self.link_names] diff --git a/pyrobolearn/robots/crab.py b/pyrobolearn/robots/crab.py index aeedf4f..cd350ce 100644 --- a/pyrobolearn/robots/crab.py +++ b/pyrobolearn/robots/crab.py @@ -28,7 +28,7 @@ class Crab(HexapodRobot): position=(0, 0, 0.12), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/crab/crab.urdf'): # check parameters if position is None: @@ -40,7 +40,7 @@ class Crab(HexapodRobot): if fixed_base is None: fixed_base = False - super(Crab, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Crab, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'crab' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/cubli.py b/pyrobolearn/robots/cubli.py index 368532e..05547ad 100644 --- a/pyrobolearn/robots/cubli.py +++ b/pyrobolearn/robots/cubli.py @@ -36,7 +36,7 @@ class Cubli(Robot): position=(0, 0, 0.5), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/cubli/cubli.urdf'): # check parameters if position is None: @@ -48,7 +48,7 @@ class Cubli(Robot): if fixed_base is None: fixed_base = False - super(Cubli, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Cubli, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'cubli' # disable each motor joint @@ -72,7 +72,7 @@ if __name__ == "__main__": scale = 1. # Warning: this does not scale the mass... position = [0., 0., np.sqrt(2) / 2. * scale + 0.001] orientation = [0.383, 0, 0, 0.924] - robot = Cubli(sim, position, orientation, scaling=scale) + robot = Cubli(sim, position, orientation, scale=scale) # print information about the robot robot.print_info() diff --git a/pyrobolearn/robots/darwin.py b/pyrobolearn/robots/darwin.py index 1a1975b..69ab94e 100644 --- a/pyrobolearn/robots/darwin.py +++ b/pyrobolearn/robots/darwin.py @@ -29,7 +29,7 @@ class Darwin(BipedRobot, BiManipulatorRobot): position=(0, 0, 0.34), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/darwin/darwin.urdf'): # check parameters if position is None: @@ -41,7 +41,7 @@ class Darwin(BipedRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Darwin, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Darwin, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'darwin' # self.torso = [self.get_link_ids(link) for link in ['DWL', 'DWS', 'DWYTorso'] if link in self.link_names] diff --git a/pyrobolearn/robots/edo.py b/pyrobolearn/robots/edo.py index d1f5cf1..5905a51 100644 --- a/pyrobolearn/robots/edo.py +++ b/pyrobolearn/robots/edo.py @@ -31,7 +31,7 @@ class Edo(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/edo/edo.urdf'): # check parameters if position is None: @@ -43,7 +43,7 @@ class Edo(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(Edo, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Edo, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'edo' diff --git a/pyrobolearn/robots/epuck.py b/pyrobolearn/robots/epuck.py index 1c5cb8f..01c377d 100644 --- a/pyrobolearn/robots/epuck.py +++ b/pyrobolearn/robots/epuck.py @@ -30,7 +30,7 @@ class Epuck(DifferentialWheeledRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/epuck/epuck.urdf'): # check parameters if position is None: @@ -42,7 +42,7 @@ class Epuck(DifferentialWheeledRobot): if fixed_base is None: fixed_base = False - super(Epuck, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Epuck, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'epuck' self.wheels = [self.get_link_ids(link) for link in ['left_wheel', 'right_wheel'] diff --git a/pyrobolearn/robots/f10_racecar.py b/pyrobolearn/robots/f10_racecar.py index 963183d..c7970d1 100644 --- a/pyrobolearn/robots/f10_racecar.py +++ b/pyrobolearn/robots/f10_racecar.py @@ -28,7 +28,7 @@ class F10Racecar(AckermannWheeledRobot): position=(0, 0, .1), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/f10_racecar/racecar.urdf'): # racecar_differential.urdf # check parameters if position is None: @@ -40,7 +40,7 @@ class F10Racecar(AckermannWheeledRobot): if fixed_base is None: fixed_base = False - super(F10Racecar, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(F10Racecar, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'racecar' self.wheels = [self.get_link_ids(link) for link in ['left_front_wheel', 'right_front_wheel', diff --git a/pyrobolearn/robots/fetch.py b/pyrobolearn/robots/fetch.py index db5f4da..3138843 100644 --- a/pyrobolearn/robots/fetch.py +++ b/pyrobolearn/robots/fetch.py @@ -29,7 +29,7 @@ class Fetch(WheeledRobot, ManipulatorRobot): position=(0, 0, 0.1), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/fetch/fetch.urdf'): # check parameters if position is None: @@ -41,7 +41,7 @@ class Fetch(WheeledRobot, ManipulatorRobot): if fixed_base is None: fixed_base = False - super(Fetch, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Fetch, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'fetch' diff --git a/pyrobolearn/robots/franka.py b/pyrobolearn/robots/franka.py index 069aa53..15fdff3 100644 --- a/pyrobolearn/robots/franka.py +++ b/pyrobolearn/robots/franka.py @@ -31,7 +31,7 @@ class Franka(ManipulatorRobot): simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), - scaling=1., + scale=1., fixed_base=True, urdf=os.path.dirname(__file__) + '/urdfs/franka/franka.urdf'): # check parameters @@ -44,7 +44,7 @@ class Franka(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(Franka, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Franka, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'franka' # self.disable_motor() diff --git a/pyrobolearn/robots/half_cheetah.py b/pyrobolearn/robots/half_cheetah.py index 6e08633..49130ce 100644 --- a/pyrobolearn/robots/half_cheetah.py +++ b/pyrobolearn/robots/half_cheetah.py @@ -27,7 +27,7 @@ class HalfCheetah(Robot): position=(-0.5, 0, 0.1), orientation=(0, 0.707, 0, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/mjcfs/half_cheetah.xml'): # check parameters if position is None: @@ -39,7 +39,7 @@ class HalfCheetah(Robot): if fixed_base is None: fixed_base = False - super(HalfCheetah, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(HalfCheetah, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'halfCheetah' diff --git a/pyrobolearn/robots/hand.py b/pyrobolearn/robots/hand.py index da0360d..45f2857 100644 --- a/pyrobolearn/robots/hand.py +++ b/pyrobolearn/robots/hand.py @@ -16,7 +16,6 @@ __status__ = "Development" class Hand(Robot): r"""Hand end-effector - """ def __init__(self, @@ -25,8 +24,8 @@ class Hand(Robot): position=(0, 0, 1.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1.): - super(Hand, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + scale=1.): + super(Hand, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.fingers = [] # list of fingers where each finger is a list of links/joints @@ -53,8 +52,8 @@ class TwoHand(Hand): position=(0, 0, 1.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1.): - super(TwoHand, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + scale=1.): + super(TwoHand, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.left_fingers = [] # list of ids in self.fingers self.right_fingers = [] # list of ids in self.fingers diff --git a/pyrobolearn/robots/hopper.py b/pyrobolearn/robots/hopper.py index c7e0633..82ce0b2 100644 --- a/pyrobolearn/robots/hopper.py +++ b/pyrobolearn/robots/hopper.py @@ -27,7 +27,7 @@ class Hopper(Robot): position=(-0.5, 0, 0.1), orientation=(0, 0.707, 0, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/mjcfs/hopper.xml'): # check parameters if position is None: @@ -39,7 +39,7 @@ class Hopper(Robot): if fixed_base is None: fixed_base = False - super(Hopper, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Hopper, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'hopper' diff --git a/pyrobolearn/robots/hubo.py b/pyrobolearn/robots/hubo.py index d328206..c0fc8b8 100644 --- a/pyrobolearn/robots/hubo.py +++ b/pyrobolearn/robots/hubo.py @@ -38,7 +38,7 @@ class Hubo(BipedRobot, BiManipulatorRobot, TwoHand): position=(0, 0, 1), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/hubo/hubo.urdf'): # check parameters if position is None: @@ -50,7 +50,7 @@ class Hubo(BipedRobot, BiManipulatorRobot, TwoHand): if fixed_base is None: fixed_base = False - super(Hubo, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Hubo, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'hubo' self.neck = [self.get_link_ids(link) for link in ['Body_Neck', 'Body_Head_Empty', 'Body_Head'] diff --git a/pyrobolearn/robots/humanoid.py b/pyrobolearn/robots/humanoid.py index 4c8edee..e55c136 100644 --- a/pyrobolearn/robots/humanoid.py +++ b/pyrobolearn/robots/humanoid.py @@ -28,7 +28,7 @@ class Humanoid(BipedRobot, BiManipulatorRobot): position=(-0.5, 0, 1.), orientation=(0, 0.707, 0, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/mjcfs/humanoid.xml'): # humanoid_symmetric.xml # check parameters if position is None: @@ -40,7 +40,7 @@ class Humanoid(BipedRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Humanoid, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Humanoid, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'humanoid' # self.waist = self.get_link_ids('DWL') if 'DWL' in self.link_names else None diff --git a/pyrobolearn/robots/husky.py b/pyrobolearn/robots/husky.py index 46758a3..2493454 100644 --- a/pyrobolearn/robots/husky.py +++ b/pyrobolearn/robots/husky.py @@ -32,7 +32,7 @@ class Husky(DifferentialWheeledRobot): position=(0, 0, .14), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/husky/husky.urdf'): # check parameters if position is None: @@ -44,7 +44,7 @@ class Husky(DifferentialWheeledRobot): if fixed_base is None: fixed_base = False - super(Husky, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Husky, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'husky' self.wheels = [self.get_link_ids(link) for link in ['front_left_wheel_link', 'front_right_wheel_link', diff --git a/pyrobolearn/robots/hyq.py b/pyrobolearn/robots/hyq.py index 52af7cb..5ffc3a4 100644 --- a/pyrobolearn/robots/hyq.py +++ b/pyrobolearn/robots/hyq.py @@ -30,7 +30,7 @@ class HyQ(QuadrupedRobot): position=(0, 0, .9), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/hyq/hyq.urdf'): # check parameters if position is None: @@ -42,7 +42,7 @@ class HyQ(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(HyQ, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(HyQ, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'hyq' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/hyq2max.py b/pyrobolearn/robots/hyq2max.py index 6d37682..f93f77e 100644 --- a/pyrobolearn/robots/hyq2max.py +++ b/pyrobolearn/robots/hyq2max.py @@ -34,7 +34,7 @@ class HyQ2Max(QuadrupedRobot): position=(0, 0, 0.8), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/hyq2max/hyq2max.urdf'): # check parameters if position is None: @@ -46,7 +46,7 @@ class HyQ2Max(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(HyQ2Max, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(HyQ2Max, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'hyq2max' self.height = 0.9 diff --git a/pyrobolearn/robots/icub.py b/pyrobolearn/robots/icub.py index 79cbee2..300ac5f 100644 --- a/pyrobolearn/robots/icub.py +++ b/pyrobolearn/robots/icub.py @@ -30,7 +30,7 @@ class ICub(BipedRobot, BiManipulatorRobot): position=(0, 0, 0.7), orientation=(0, 0, 1, 0), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/icub/icub-v2.5+.urdf'): # check parameters if position is None: @@ -42,7 +42,7 @@ class ICub(BipedRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(ICub, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling=scaling) + super(ICub, self).__init__(simulator, urdf, position, orientation, fixed_base, scale=scale) self.name = 'icub' self.head = self.get_link_ids('head') if 'head' in self.link_names else None diff --git a/pyrobolearn/robots/jaco.py b/pyrobolearn/robots/jaco.py index e29f8f2..4d6251f 100644 --- a/pyrobolearn/robots/jaco.py +++ b/pyrobolearn/robots/jaco.py @@ -29,7 +29,7 @@ class Jaco(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/jaco/jaco.urdf'): # check parameters if position is None: @@ -41,7 +41,7 @@ class Jaco(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(Jaco, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Jaco, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'jaco' diff --git a/pyrobolearn/robots/kr5.py b/pyrobolearn/robots/kr5.py index d2713a9..3903c24 100644 --- a/pyrobolearn/robots/kr5.py +++ b/pyrobolearn/robots/kr5.py @@ -31,7 +31,7 @@ class KR5(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/kuka/kr5/kr5.urdf'): # check parameters if position is None: @@ -43,7 +43,7 @@ class KR5(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(KR5, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(KR5, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'kr5' diff --git a/pyrobolearn/robots/kuka_iiwa.py b/pyrobolearn/robots/kuka_iiwa.py index 3e77f1b..62b3ca1 100644 --- a/pyrobolearn/robots/kuka_iiwa.py +++ b/pyrobolearn/robots/kuka_iiwa.py @@ -31,7 +31,7 @@ class KukaIIWA(ManipulatorRobot): simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), - scaling=1., + scale=1., fixed_base=True, urdf=os.path.dirname(__file__) + '/urdfs/kuka/kuka_iiwa/iiwa14.urdf'): # check parameters @@ -44,7 +44,7 @@ class KukaIIWA(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(KukaIIWA, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(KukaIIWA, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'kuka_iiwa' # self.disable_motor() diff --git a/pyrobolearn/robots/kuka_lwr.py b/pyrobolearn/robots/kuka_lwr.py index 60c1abb..61bdbf4 100644 --- a/pyrobolearn/robots/kuka_lwr.py +++ b/pyrobolearn/robots/kuka_lwr.py @@ -32,7 +32,7 @@ class KukaLWR(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/kuka/kuka_lwr/kuka.urdf'): # check parameters if position is None: @@ -44,7 +44,7 @@ class KukaLWR(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(KukaLWR, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(KukaLWR, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'kuka_lwr' diff --git a/pyrobolearn/robots/laikago.py b/pyrobolearn/robots/laikago.py index 2e24555..b0075a1 100644 --- a/pyrobolearn/robots/laikago.py +++ b/pyrobolearn/robots/laikago.py @@ -29,7 +29,7 @@ class Laikago(QuadrupedRobot): position=(0, 0, .5), orientation=(0.5, 0.5, 0.5, 0.5), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/laikago/laikago.urdf'): # check parameters if position is None: @@ -41,7 +41,7 @@ class Laikago(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(Laikago, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Laikago, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'laikago' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/legged_robot.py b/pyrobolearn/robots/legged_robot.py index 9e9cc84..7db8156 100644 --- a/pyrobolearn/robots/legged_robot.py +++ b/pyrobolearn/robots/legged_robot.py @@ -28,9 +28,9 @@ class LeggedRobot(Robot): in the standard regime are rhythmic movements. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1., + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1., foot_frictions=None): - super(LeggedRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling=scaling) + super(LeggedRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale=scale) # leg and feet ids self.legs = [] # list of legs where a leg is a list of links @@ -729,8 +729,8 @@ class BipedRobot(LeggedRobot): A biped robot is a robot which has 2 legs. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(BipedRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(BipedRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.left_leg_id = 0 self.right_leg_id = 1 @@ -766,8 +766,8 @@ class QuadrupedRobot(LeggedRobot): A quadruped robot is a robot which has 4 legs. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(QuadrupedRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(QuadrupedRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.left_front_leg_id = 0 self.right_front_leg_id = 1 @@ -825,8 +825,8 @@ class HexapodRobot(LeggedRobot): An hexapod robot is a robot which has 6 legs. """ - def __init__(self, simulator, urdf, position, orientation=None, fixed_base=False, scaling=1.): - super(HexapodRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position, orientation=None, fixed_base=False, scale=1.): + super(HexapodRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.left_front_leg_id = 0 self.right_front_leg_id = 1 diff --git a/pyrobolearn/robots/littledog.py b/pyrobolearn/robots/littledog.py index 5b144ac..03a3c3f 100644 --- a/pyrobolearn/robots/littledog.py +++ b/pyrobolearn/robots/littledog.py @@ -30,7 +30,7 @@ class LittleDog(QuadrupedRobot): position=(0, 0, 0.2), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/littledog/littleDog.urdf'): # check parameters if position is None: @@ -42,7 +42,7 @@ class LittleDog(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(LittleDog, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(LittleDog, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'littledog' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/manipulator.py b/pyrobolearn/robots/manipulator.py index 214ac41..2ba86da 100644 --- a/pyrobolearn/robots/manipulator.py +++ b/pyrobolearn/robots/manipulator.py @@ -26,8 +26,8 @@ class ManipulatorRobot(Robot): position=(0, 0, 0.), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1.): - super(ManipulatorRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + scale=1.): + super(ManipulatorRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.arms = [] # list of arms where an arm is a list of links self.hands = [] # list of end-effectors/hands @@ -124,8 +124,8 @@ class BiManipulatorRobot(ManipulatorRobot): """ def __init__(self, simulator, urdf, position=(0, 0, 1.5), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1.): - super(BiManipulatorRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + scale=1.): + super(BiManipulatorRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.left_arm_id = 0 self.left_hand_id = 0 diff --git a/pyrobolearn/robots/manipulator2d.py b/pyrobolearn/robots/manipulator2d.py index acefd2a..95e892f 100644 --- a/pyrobolearn/robots/manipulator2d.py +++ b/pyrobolearn/robots/manipulator2d.py @@ -28,7 +28,7 @@ class Manipulator2D(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/manipulator2d/manipulator2d.urdf'): # check parameters if position is None: @@ -40,7 +40,7 @@ class Manipulator2D(ManipulatorRobot): if fixed_base is None: fixed_base = False - super(Manipulator2D, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Manipulator2D, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'manipulator2d' diff --git a/pyrobolearn/robots/minitaur.py b/pyrobolearn/robots/minitaur.py index d63bd72..9a02708 100644 --- a/pyrobolearn/robots/minitaur.py +++ b/pyrobolearn/robots/minitaur.py @@ -33,7 +33,7 @@ class Minitaur(QuadrupedRobot): position=(0, 0, .3), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., couple_legs=True, foot_friction=1., urdf=os.path.dirname(__file__) + '/urdfs/minitaur/minitaur.urdf'): @@ -47,7 +47,7 @@ class Minitaur(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(Minitaur, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Minitaur, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'minitaur' self.base_height = 0.1638 diff --git a/pyrobolearn/robots/mkz.py b/pyrobolearn/robots/mkz.py index e0c5ae0..045d90c 100644 --- a/pyrobolearn/robots/mkz.py +++ b/pyrobolearn/robots/mkz.py @@ -32,7 +32,7 @@ class MKZ(AckermannWheeledRobot): position=(0, 0, .4), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/mkz/mkz.urdf'): # check parameters if position is None: @@ -44,7 +44,7 @@ class MKZ(AckermannWheeledRobot): if fixed_base is None: fixed_base = False - super(MKZ, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(MKZ, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'mkz' self.wheels = [self.get_link_ids(link) for link in ['wheel_fl', 'wheel_fr', 'wheel_rl', 'wheel_rr'] diff --git a/pyrobolearn/robots/morphex.py b/pyrobolearn/robots/morphex.py index 0260290..c1c925c 100644 --- a/pyrobolearn/robots/morphex.py +++ b/pyrobolearn/robots/morphex.py @@ -28,7 +28,7 @@ class Morphex(HexapodRobot): position=(0, 0, 0.2), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/morphex/morphex.urdf'): # check parameters if position is None: @@ -40,7 +40,7 @@ class Morphex(HexapodRobot): if fixed_base is None: fixed_base = False - super(Morphex, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Morphex, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'morphex' diff --git a/pyrobolearn/robots/nao.py b/pyrobolearn/robots/nao.py index 10b67c4..4dcbf07 100644 --- a/pyrobolearn/robots/nao.py +++ b/pyrobolearn/robots/nao.py @@ -27,7 +27,7 @@ class Nao(BipedRobot, BiManipulatorRobot, TwoHand): position=(0, 0, 0.35), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/nao/nao_v40.urdf'): # check parameters if position is None: @@ -39,7 +39,7 @@ class Nao(BipedRobot, BiManipulatorRobot, TwoHand): if fixed_base is None: fixed_base = False - super(Nao, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Nao, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'nao' self.neck = self.get_link_ids('Neck') if 'Neck' in self.link_names else None diff --git a/pyrobolearn/robots/opendog.py b/pyrobolearn/robots/opendog.py index bf1db8a..273fc7e 100644 --- a/pyrobolearn/robots/opendog.py +++ b/pyrobolearn/robots/opendog.py @@ -28,7 +28,7 @@ class OpenDog(QuadrupedRobot): position=(0, 0, .6), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/opendog/opendog.urdf'): # check parameters if position is None: @@ -40,7 +40,7 @@ class OpenDog(QuadrupedRobot): if fixed_base is None: fixed_base = False - super(OpenDog, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(OpenDog, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'opendog' self.legs = [[self.get_link_ids(link) for link in links if link in self.link_names] diff --git a/pyrobolearn/robots/pepper.py b/pyrobolearn/robots/pepper.py index 6c6cdbf..af22af7 100644 --- a/pyrobolearn/robots/pepper.py +++ b/pyrobolearn/robots/pepper.py @@ -34,7 +34,7 @@ class Pepper(WheeledRobot, BiManipulatorRobot): position=(0, 0, 0.9), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/pepper/pepper.urdf'): # check parameters if position is None: @@ -46,7 +46,7 @@ class Pepper(WheeledRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(Pepper, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling=scaling) + super(Pepper, self).__init__(simulator, urdf, position, orientation, fixed_base, scale=scale) self.name = 'pepper' # 2D Camera sensor @@ -55,16 +55,16 @@ class Pepper(WheeledRobot, BiManipulatorRobot): # Note that we divide width and height by 4 (otherwise the simulator is pretty slow) self.camera_top = CameraSensor(self.sim, self.id, 4, width=2560 / 4, height=1080 / 4, fovy=44.30, - near=0.3, far=100, refresh_rate=60) + near=0.3, far=100, rate=60) self.camera_bottom = CameraSensor(self.sim, self.id, 9, width=2560 / 4, height=1080 / 4, fovy=44.30, - near=0.3, far=100, refresh_rate=60) + near=0.3, far=100, rate=60) # 3D camera sensor # From [1]: "One 3D camera is located in the forehead. It provides image resolution up to 320x240 at # 20 frames per second. One ASUS Xtion 3D sensor is located behind the eyes. VFOV = 45 deg, HFOV = 58 deg, # focus = [80cm, 3.5m]." self.camera_depth = CameraSensor(self.sim, self.id, 6, width=320, height=240, fovy=45, near=0.3, far=3.5, - refresh_rate=120) + rate=120) self.cameras = [self.camera_top, self.camera_bottom, self.camera_depth] diff --git a/pyrobolearn/robots/phantomx.py b/pyrobolearn/robots/phantomx.py index f4f3cbe..0f8e1b9 100644 --- a/pyrobolearn/robots/phantomx.py +++ b/pyrobolearn/robots/phantomx.py @@ -27,7 +27,7 @@ class PhantomX(HexapodRobot): position=(0, 0, 0.2), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/phantomx/phantomx.urdf'): # check parameters if position is None: @@ -39,7 +39,7 @@ class PhantomX(HexapodRobot): if fixed_base is None: fixed_base = False - super(PhantomX, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(PhantomX, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'phantomx' diff --git a/pyrobolearn/robots/pleurobot.py b/pyrobolearn/robots/pleurobot.py index c515467..647d597 100644 --- a/pyrobolearn/robots/pleurobot.py +++ b/pyrobolearn/robots/pleurobot.py @@ -25,7 +25,7 @@ class Pleurobot(QuadrupedRobot, UUVRobot, USVRobot): [2] https://biorob.epfl.ch/pleurobot """ - def __init__(self, simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=False, scaling=1., + def __init__(self, simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=False, scale=1., urdf=os.path.dirname(__file__) + '/urdfs/pleurobot/pleurobot.urdf'): # check parameters if position is None: @@ -37,7 +37,7 @@ class Pleurobot(QuadrupedRobot, UUVRobot, USVRobot): if fixed_base is None: fixed_base = False - super(Pleurobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Pleurobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'pleurobot' diff --git a/pyrobolearn/robots/pr2.py b/pyrobolearn/robots/pr2.py index e5520d2..09226ee 100644 --- a/pyrobolearn/robots/pr2.py +++ b/pyrobolearn/robots/pr2.py @@ -29,7 +29,7 @@ class PR2(WheeledRobot, BiManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/pr2/pr2.urdf'): # check parameters if position is None: @@ -41,7 +41,7 @@ class PR2(WheeledRobot, BiManipulatorRobot): if fixed_base is None: fixed_base = False - super(PR2, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(PR2, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'pr2' diff --git a/pyrobolearn/robots/quadcopter.py b/pyrobolearn/robots/quadcopter.py index aead17d..d1fd3e7 100644 --- a/pyrobolearn/robots/quadcopter.py +++ b/pyrobolearn/robots/quadcopter.py @@ -68,7 +68,7 @@ class Quadcopter(RotaryWingUAV): position=(0, 0, 0.2), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/quadcopter/quadcopter.urdf'): # check parameters if position is None: @@ -80,7 +80,7 @@ class Quadcopter(RotaryWingUAV): if fixed_base is None: fixed_base = False - super(Quadcopter, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Quadcopter, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'quadcopter' self.gravity = 9.81 diff --git a/pyrobolearn/robots/rhex.py b/pyrobolearn/robots/rhex.py index de39ec7..21b1a84 100644 --- a/pyrobolearn/robots/rhex.py +++ b/pyrobolearn/robots/rhex.py @@ -34,7 +34,7 @@ class Rhex(HexapodRobot): position=(0, 0, 0.12), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/rhex/rhex.urdf'): # check parameters if position is None: @@ -46,7 +46,7 @@ class Rhex(HexapodRobot): if fixed_base is None: fixed_base = False - super(Rhex, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Rhex, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'rhex' self.legs = [[self.get_link_ids(link + str(idx))] for link, idx in zip(['leg'] * 6, range(1, 7)) diff --git a/pyrobolearn/robots/robot.py b/pyrobolearn/robots/robot.py index 310fbc1..aa56cf4 100644 --- a/pyrobolearn/robots/robot.py +++ b/pyrobolearn/robots/robot.py @@ -10,11 +10,12 @@ Dependencies: - `pyrobolearn.utils` """ +import os +import copy +import collections # import rbdl import numpy as np # import quaternion -import collections -import os from pyrobolearn.utils.transformation import * from pyrobolearn.robots.base import ControllableBody @@ -37,7 +38,7 @@ class Robot(ControllableBody): and has been implemented such that it is very generic. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1., *args, **kwargs): + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1., *args, **kwargs): """ Initialize the robot. @@ -47,7 +48,7 @@ class Robot(ControllableBody): position (np.float[3]): initial position. orientation (np.float[4]): initial orientation represented as a quaternion (x,y,z,w). fixed_base (bool, None): if True, the base of the robot will be fixed. - scaling (float): scaling factor. + scale (float): scaling factor. """ # check parameters if position is None: @@ -66,18 +67,23 @@ class Robot(ControllableBody): if urdf[-3:] == 'xml' or urdf[-4:] == 'mjcf': self.id = self.sim.load_mjcf(urdf)[0] # assume the first entity is the robot else: # if urdf[-4:] == 'urdf': - self.id = self.sim.load_urdf(urdf, position, orientation, use_fixed_base=fixed_base, scale=scaling) + self.id = self.sim.load_urdf(urdf, position, orientation, use_fixed_base=fixed_base, scale=scale) # self.sim.configure_debug_visualizer(self.sim.COV_ENABLE_RENDERING, 1) + # save the input parameters + self.urdf = urdf + self.fixed_base = fixed_base + self.scale = scale + # rescale if specified - if scaling != 1.0: + if scale != 1.0: # we rescale manually the mass and inertia matrices of each link for link in range(self.num_links): info = self.sim.get_dynamics_info(self.id, link) mass, local_inertia_diagonal = info[0], np.array(info[2]) - mass *= scaling**3 # because the density is unchanged when scaling - local_inertia_diagonal *= scaling**5 # 5 = 3+2; 3 is for the mass, and 2 is for the distance: I~mr^2 + mass *= scale ** 3 # because the density is unchanged when scaling + local_inertia_diagonal *= scale ** 5 # 5 = 3+2; 3 is for the mass, and 2 is for the distance: I~mr^2 self.sim.change_dynamics(self.id, link, mass=mass, local_inertia_diagonal=local_inertia_diagonal) # set robot properties @@ -138,14 +144,34 @@ class Robot(ControllableBody): # Gains self.kp, self.kd = None, None - def __repr__(self): - """ - Return the name of the class. + ############# + # Operators # + ############# - Returns: - str: name of the class + def __copy__(self): + """Return a shallow copy of the robot. This can be overridden in the child class.""" + return self.__class__(simulator=self.simulator, urdf=self.urdf, position=self.position, + orientation=self.orientation, fixed_base=self.fixed_base, scale=self.scale) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the robot. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass """ - return self.__class__.__name__ + simulator = copy.deepcopy(self.simulator, memo) + urdf = copy.deepcopy(self.urdf) + position = copy.deepcopy(self.position) + orientation = copy.deepcopy(self.orientation) + robot = self.__class__(simulator=simulator, urdf=urdf, position=position, orientation=orientation, + fixed_base=self.fixed_base, scale=self.scale) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = robot + + # return the copy + return robot ############## # Properties # @@ -248,7 +274,8 @@ class Robot(ControllableBody): Returns: bool: True if the robot has a floating base. """ - return self.floating_base + # return self.floating_base + return not self.fixed_base def has_fixed_base(self): """ @@ -257,7 +284,8 @@ class Robot(ControllableBody): Returns: bool: True if the robot has a fixed base. """ - return not self.has_floating_base() + # return not self.has_floating_base() + return self.fixed_base ####### # CoM # diff --git a/pyrobolearn/robots/rrbot.py b/pyrobolearn/robots/rrbot.py index caa58f8..c204982 100644 --- a/pyrobolearn/robots/rrbot.py +++ b/pyrobolearn/robots/rrbot.py @@ -28,7 +28,7 @@ class RRBot(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1, + scale=1, urdf=os.path.dirname(__file__) + '/urdfs/rrbot/rrbot.urdf'): # check parameters if position is None: @@ -40,7 +40,7 @@ class RRBot(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(RRBot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(RRBot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'rrbot' # set initial joint positions diff --git a/pyrobolearn/robots/sawyer.py b/pyrobolearn/robots/sawyer.py index 468bf97..a8813db 100644 --- a/pyrobolearn/robots/sawyer.py +++ b/pyrobolearn/robots/sawyer.py @@ -31,7 +31,7 @@ class Sawyer(ManipulatorRobot, WheeledRobot): position=(0, 0, 0.92), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/sawyer/sawyer.urdf'): # check parameters if position is None: @@ -43,7 +43,7 @@ class Sawyer(ManipulatorRobot, WheeledRobot): if fixed_base is None: fixed_base = True - super(Sawyer, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Sawyer, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'sawyer' self.head = self.get_link_ids('head') if 'head' in self.link_names else None diff --git a/pyrobolearn/robots/sea_hexapod.py b/pyrobolearn/robots/sea_hexapod.py index c27ebab..729b57f 100644 --- a/pyrobolearn/robots/sea_hexapod.py +++ b/pyrobolearn/robots/sea_hexapod.py @@ -27,7 +27,7 @@ class SEAHexapod(HexapodRobot): position=(0, 0, 0.15), orientation=(0, 0, 0.707, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/cmu_sea/hexapod.urdf'): # check parameters if position is None: @@ -39,7 +39,7 @@ class SEAHexapod(HexapodRobot): if fixed_base is None: fixed_base = False - super(SEAHexapod, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(SEAHexapod, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'sea_hexapod' diff --git a/pyrobolearn/robots/sea_snake.py b/pyrobolearn/robots/sea_snake.py index 8451e4e..14da777 100644 --- a/pyrobolearn/robots/sea_snake.py +++ b/pyrobolearn/robots/sea_snake.py @@ -27,7 +27,7 @@ class SEASnake(Robot): position=(-0.5, 0, 0.1), orientation=(0, 0.707, 0, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/cmu_sea/snake.urdf'): # check parameters if position is None: @@ -39,7 +39,7 @@ class SEASnake(Robot): if fixed_base is None: fixed_base = False - super(SEASnake, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(SEASnake, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'sea_snake' diff --git a/pyrobolearn/robots/sensors/camera.py b/pyrobolearn/robots/sensors/camera.py index b227776..6ce0f7b 100644 --- a/pyrobolearn/robots/sensors/camera.py +++ b/pyrobolearn/robots/sensors/camera.py @@ -56,7 +56,7 @@ class CameraSensor(LinkSensor): [4] http://learnwebgl.brown37.net/08_projections/projections_perspective.html """ - def __init__(self, simulator, body_id, link_id, width, height, position=None, orientation=None, refresh_rate=50, + def __init__(self, simulator, body_id, link_id, width, height, position=None, orientation=None, rate=50, target_position=None, distance=10., fovy=60, aspect=None, near=0.01, far=100., left=None, right=None, bottom=None, top=None): @@ -97,7 +97,7 @@ class CameraSensor(LinkSensor): bottom (float): bottom screen (canvas) coordinate top (float): top screen (canvas) coordinate """ - super(CameraSensor, self).__init__(simulator, body_id, link_id, position, orientation, refresh_rate) + super(CameraSensor, self).__init__(simulator, body_id, link_id, position, orientation, rate) self.width = width self.height = height diff --git a/pyrobolearn/robots/sensors/contact.py b/pyrobolearn/robots/sensors/contact.py index e0d6c1c..846a7ab 100644 --- a/pyrobolearn/robots/sensors/contact.py +++ b/pyrobolearn/robots/sensors/contact.py @@ -20,8 +20,8 @@ class ContactSensor(LinkSensor): This sensor return 1 if in contact with an object, and 0 otherwise. """ - def __init__(self, simulator, body_id, link_id, position, orientation, refresh_rate=1): - super(ContactSensor, self).__init__(simulator, body_id, link_id, position, orientation, refresh_rate) + def __init__(self, simulator, body_id, link_id, position, orientation, rate=1): + super(ContactSensor, self).__init__(simulator, body_id, link_id, position, orientation, rate) def get_contact_points(self): """Get the contact points. diff --git a/pyrobolearn/robots/sensors/force_torque.py b/pyrobolearn/robots/sensors/force_torque.py index fc9e440..40d1a0a 100644 --- a/pyrobolearn/robots/sensors/force_torque.py +++ b/pyrobolearn/robots/sensors/force_torque.py @@ -22,8 +22,8 @@ class ForceTorqueSensor(JointSensor): The F/T sensor allows to measure the forces and torques applied to it. """ - def __init__(self, simulator, body_id, joint_id, position, orientation, refresh_rate=1): - super(ForceTorqueSensor, self).__init__(simulator, body_id, joint_id, position, orientation, refresh_rate) + def __init__(self, simulator, body_id, joint_id, position, orientation, rate=1): + super(ForceTorqueSensor, self).__init__(simulator, body_id, joint_id, position, orientation, rate) self.sim.enable_joint_force_torque_sensor(body_id, joint_id, enableSensor=True) def _sense(self): diff --git a/pyrobolearn/robots/sensors/joints.py b/pyrobolearn/robots/sensors/joints.py index 48f3251..143409c 100644 --- a/pyrobolearn/robots/sensors/joints.py +++ b/pyrobolearn/robots/sensors/joints.py @@ -4,6 +4,7 @@ This mainly include encoders. """ +import copy from abc import ABCMeta, abstractmethod from pyrobolearn.robots.sensors.sensor import Sensor @@ -25,7 +26,7 @@ class JointSensor(Sensor): """ __metaclass__ = ABCMeta - def __init__(self, simulator, body_id, joint_id, position=None, orientation=None, refresh_rate=1): + def __init__(self, simulator, body_id, joint_id, position=None, orientation=None, rate=1): """Initialize the sensor. Args: @@ -34,9 +35,9 @@ class JointSensor(Sensor): joint_id (int): unique id of the joint position (vec3): local position of the sensor with respect to the given joint orientation (vec4): local orientation of the sensor with respect to the given joint - refresh_rate (int): number of steps to wait before acquisition of the next sensor value. + rate (int): number of steps to wait before acquisition of the next sensor value. """ - super(JointSensor, self).__init__(simulator, body_id, position, orientation, refresh_rate) + super(JointSensor, self).__init__(simulator, body_id, position, orientation, rate) self.joint_id = joint_id @property @@ -50,6 +51,28 @@ class JointSensor(Sensor): def _sense(self): raise NotImplementedError + def __copy__(self): + """Return a shallow copy of the sensor. This can be overridden in the child class.""" + return self.__class__(simulator=self.simulator, body_id=self.body_id, joint_id=self.joint_id, + position=self.local_position, orientation=self.local_orientation, rate=self.rate) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the sensor. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + simulator = copy.deepcopy(self.simulator, memo) + body_id = copy.deepcopy(self.body_id) + joint_id = copy.deepcopy(self.joint_id) + position = copy.deepcopy(self.local_position) + orientation = copy.deepcopy(self.local_orientation) + sensor = self.__class__(simulator=simulator, body_id=body_id, joint_id=joint_id, position=position, + orientation=orientation, rate=self.rate) + memo[self] = sensor + return sensor + + class Encoder(JointSensor): r"""Encoder joint sensor diff --git a/pyrobolearn/robots/sensors/links.py b/pyrobolearn/robots/sensors/links.py index 2953b2f..ad462dc 100644 --- a/pyrobolearn/robots/sensors/links.py +++ b/pyrobolearn/robots/sensors/links.py @@ -4,6 +4,7 @@ These include IMU, contact, Camera, and other sensors. """ +import copy from abc import ABCMeta, abstractmethod from pyrobolearn.utils.transformation import get_quaternion_product @@ -28,7 +29,7 @@ class LinkSensor(Sensor): __metaclass__ = ABCMeta - def __init__(self, simulator, body_id, link_id=None, position=None, orientation=None, refresh_rate=1): + def __init__(self, simulator, body_id, link_id=None, position=None, orientation=None, rate=1): """Initialize the sensor. Args: @@ -37,9 +38,9 @@ class LinkSensor(Sensor): link_id (int): unique id of the link position (vec3): local position of the sensor with respect to the given link orientation (vec4): local orientation of the sensor with respect to the given link - refresh_rate (int): number of steps to wait before acquisition of the next sensor value. + rate (int): number of steps to wait before acquisition of the next sensor value. """ - super(LinkSensor, self).__init__(simulator, body_id, position, orientation, refresh_rate) + super(LinkSensor, self).__init__(simulator, body_id, position, orientation, rate) self.link_id = link_id @property @@ -63,3 +64,24 @@ class LinkSensor(Sensor): @abstractmethod def _sense(self): raise NotImplementedError + + def __copy__(self): + """Return a shallow copy of the sensor. This can be overridden in the child class.""" + return self.__class__(simulator=self.simulator, body_id=self.body_id, link_id=self.link_id, + position=self.local_position, orientation=self.local_orientation, rate=self.rate) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the sensor. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + simulator = copy.deepcopy(self.simulator, memo) + body_id = copy.deepcopy(self.body_id) + link_id = copy.deepcopy(self.link_id) + position = copy.deepcopy(self.local_position) + orientation = copy.deepcopy(self.local_orientation) + sensor = self.__class__(simulator=simulator, body_id=body_id, link_id=link_id, position=position, + orientation=orientation, rate=self.rate) + memo[self] = sensor + return sensor diff --git a/pyrobolearn/robots/sensors/sensor.py b/pyrobolearn/robots/sensors/sensor.py index e4566ec..2fad3fc 100644 --- a/pyrobolearn/robots/sensors/sensor.py +++ b/pyrobolearn/robots/sensors/sensor.py @@ -8,6 +8,7 @@ simulation to reality. Also, note that some simulators are deterministic and thu add some noise to the returned sense value. The type of noise can also be selected at runtime. """ +import copy from abc import ABCMeta, abstractmethod import numpy as np @@ -33,7 +34,7 @@ class Sensor(object): # sensor attached to a link or joint """ __metaclass__ = ABCMeta - def __init__(self, simulator, body_id, position=None, orientation=None, refresh_rate=1): + def __init__(self, simulator, body_id, position=None, orientation=None, rate=1): """Initialize the sensor. Args: @@ -41,7 +42,7 @@ class Sensor(object): # sensor attached to a link or joint body_id (int): unique id of the body position (vec3): local position of the sensor with respect to the given link orientation (vec4): local orientation of the sensor with respect to the given link - refresh_rate (int): number of steps to wait before acquisition of the next sensor value. + rate (int): number of steps to wait before acquisition of the next sensor value. """ self.sim = simulator self.body_id = body_id @@ -54,12 +55,21 @@ class Sensor(object): # sensor attached to a link or joint orientation = [0., 0., 0., 1.] self.local_orientation = np.array(orientation) - self.rate = refresh_rate + self.rate = rate self.cnt = -1 # data from last acquisition self.data = None + ############## + # Properties # + ############## + + @property + def simulator(self): + """Return the simulator instance.""" + return self.sim + @property def position(self): """ @@ -80,9 +90,11 @@ class Sensor(object): # sensor attached to a link or joint @abstractmethod def _sense(self): + """Sense method to be implemented in the child class.""" raise NotImplementedError def sense(self): + """Get the next sensor value.""" self.cnt += 1 if self.cnt % self.rate == 0: self.data = self._sense() @@ -90,6 +102,39 @@ class Sensor(object): # sensor attached to a link or joint return self.data return self.data + ############# + # Operators # + ############# + # alias def __call__(self): + """Get the next sensor value.""" return self.sense() + + # def __repr__(self): + # """Return a representation string about the class for debugging and development.""" + # return self.__class__.__name__ + + def __str__(self): + """Return a readable string about the class.""" + return self.__class__.__name__ + + def __copy__(self): + """Return a shallow copy of the sensor. This can be overridden in the child class.""" + return self.__class__(simulator=self.simulator, body_id=self.body_id, position=self.local_position, + orientation=self.local_orientation, rate=self.rate) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the sensor. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + simulator = copy.deepcopy(self.simulator, memo) + body_id = copy.deepcopy(self.body_id) + position = copy.deepcopy(self.local_position) + orientation = copy.deepcopy(self.local_orientation) + sensor = self.__class__(simulator=simulator, body_id=body_id, position=position, orientation=orientation, + rate=self.rate) + memo[self] = sensor + return sensor diff --git a/pyrobolearn/robots/shadowhand.py b/pyrobolearn/robots/shadowhand.py index 9ddfeaf..b3f80c6 100644 --- a/pyrobolearn/robots/shadowhand.py +++ b/pyrobolearn/robots/shadowhand.py @@ -28,7 +28,7 @@ class ShadowHand(Hand): simulator, position=(0, 0, 0), orientation=(0, 0, 0.707, 0.707), - scaling=1., + scale=1., left=True, fixed_base=True): # check parameters @@ -48,7 +48,7 @@ class ShadowHand(Hand): orientation = (0, 0, 1, 0) urdf_path = os.path.dirname(__file__) + '/urdfs/shadowhand/right_hand.urdf' - super(ShadowHand, self).__init__(simulator, urdf_path, position, orientation, fixed_base, scaling) + super(ShadowHand, self).__init__(simulator, urdf_path, position, orientation, fixed_base, scale) self.name = 'shadow_hand' diff --git a/pyrobolearn/robots/softhand.py b/pyrobolearn/robots/softhand.py index 22e6b68..b995f3c 100644 --- a/pyrobolearn/robots/softhand.py +++ b/pyrobolearn/robots/softhand.py @@ -28,7 +28,7 @@ class SoftHand(Hand): simulator, position=(0, 0, 0), orientation=(0, 0, 0, 1), - scaling=1., + scale=1., left=True, fixed_base=True): # check parameters @@ -48,7 +48,7 @@ class SoftHand(Hand): orientation = (0, 0, 1, 0) urdf_path = os.path.dirname(__file__) + '/urdfs/softhand/right_hand.urdf' - super(SoftHand, self).__init__(simulator, urdf_path, position, orientation, fixed_base, scaling) + super(SoftHand, self).__init__(simulator, urdf_path, position, orientation, fixed_base, scale) self.name = 'soft_hand' diff --git a/pyrobolearn/robots/swimmer.py b/pyrobolearn/robots/swimmer.py index 38313ec..5e8f3fa 100644 --- a/pyrobolearn/robots/swimmer.py +++ b/pyrobolearn/robots/swimmer.py @@ -27,7 +27,7 @@ class Swimmer(Robot): position=(-0.5, 0, 0.1), orientation=(0, 0.707, 0, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/mjcfs/swimmer.xml'): # check parameters if position is None: @@ -39,7 +39,7 @@ class Swimmer(Robot): if fixed_base is None: fixed_base = False - super(Swimmer, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Swimmer, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'swimmer' diff --git a/pyrobolearn/robots/uav.py b/pyrobolearn/robots/uav.py index 1ecc2dc..232feb8 100644 --- a/pyrobolearn/robots/uav.py +++ b/pyrobolearn/robots/uav.py @@ -20,8 +20,8 @@ class UAVRobot(Robot): Vehicles/Robots that operate in the air. These are also called drones. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(UAVRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(UAVRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.propellers = [] # list of propellers id @@ -36,8 +36,8 @@ class FixedWingUAV(UAVRobot): """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(FixedWingUAV, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(FixedWingUAV, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) class RotaryWingUAV(UAVRobot): @@ -45,5 +45,5 @@ class RotaryWingUAV(UAVRobot): """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(RotaryWingUAV, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(RotaryWingUAV, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) diff --git a/pyrobolearn/robots/usv.py b/pyrobolearn/robots/usv.py index 5e009b8..b8284a9 100644 --- a/pyrobolearn/robots/usv.py +++ b/pyrobolearn/robots/usv.py @@ -20,5 +20,5 @@ class USVRobot(Robot): Vehicles/Robots that operate on the surface of water. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(USVRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(USVRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) diff --git a/pyrobolearn/robots/uuv.py b/pyrobolearn/robots/uuv.py index 3a75375..08566df 100644 --- a/pyrobolearn/robots/uuv.py +++ b/pyrobolearn/robots/uuv.py @@ -20,5 +20,5 @@ class UUVRobot(Robot): Vehicles/Robots that operate under water. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(UUVRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(UUVRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) diff --git a/pyrobolearn/robots/walker2d.py b/pyrobolearn/robots/walker2d.py index 6022dc1..82267a0 100644 --- a/pyrobolearn/robots/walker2d.py +++ b/pyrobolearn/robots/walker2d.py @@ -27,7 +27,7 @@ class Walker2D(Robot): position=(-0.5, 0, 0.1), orientation=(0, 0.707, 0, 0.707), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/mjcfs/walker2d.xml'): # check parameters if position is None: @@ -39,7 +39,7 @@ class Walker2D(Robot): if fixed_base is None: fixed_base = False - super(Walker2D, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Walker2D, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'walker2D' diff --git a/pyrobolearn/robots/walkman.py b/pyrobolearn/robots/walkman.py index 1a34032..a4cd42a 100644 --- a/pyrobolearn/robots/walkman.py +++ b/pyrobolearn/robots/walkman.py @@ -34,7 +34,7 @@ class Walkman(BipedRobot, BiManipulatorRobot): position=(0, 0, 1.14), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/walkman/walkman.urdf', lower_body=False): # 'walkman_lower_body.urdf' # check parameters @@ -49,7 +49,7 @@ class Walkman(BipedRobot, BiManipulatorRobot): if lower_body: urdf = os.path.dirname(__file__) + '/urdfs/walkman/walkman_lower_body.urdf' - super(Walkman, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Walkman, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'walkman' # Camera sensors: Two 2D Camera sensor (stereo-camera) @@ -57,16 +57,16 @@ class Walkman(BipedRobot, BiManipulatorRobot): # fovx = 1.3962634rad = 80 degrees, Gaussian noise = N(0, 0.007) # "left_camera_frame", self.left_camera = CameraSensor(self.sim, self.id, 11, width=800, height=800, fovy=80, near=0.02, far=300, - refresh_rate=30) # 11 + rate=30) # 11 self.right_camera = CameraSensor(self.sim, self.id, 13, width=800, height=800, fovy=80, near=0.02, far=300, - refresh_rate=30) # 13 + rate=30) # 13 # Laser (depth) sensor: Hokuyo sensor # link: "head_hokuyo_frame" # freq=40, samples = 720, angle = [-1.570796, 1.570796] rad, range = [0.10, 30.0] m # Gaussian noise: N(0.0, 0.01) self.depth_camera = CameraSensor(self.sim, self.id, 10, width=200, height=200, fovy=80, near=0.1, far=30.0, - refresh_rate=40) + rate=40) self.cameras = [self.left_camera, self.right_camera, self.depth_camera] diff --git a/pyrobolearn/robots/wam.py b/pyrobolearn/robots/wam.py index 9d5db38..0212d93 100644 --- a/pyrobolearn/robots/wam.py +++ b/pyrobolearn/robots/wam.py @@ -27,7 +27,7 @@ class WAM(ManipulatorRobot): position=(0, 0, 0), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/wam/wam.urdf'): # check parameters if position is None: @@ -39,7 +39,7 @@ class WAM(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(WAM, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(WAM, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'wam' # self.disable_motor() diff --git a/pyrobolearn/robots/wheeled_robot.py b/pyrobolearn/robots/wheeled_robot.py index d6ac54e..11e4e6e 100644 --- a/pyrobolearn/robots/wheeled_robot.py +++ b/pyrobolearn/robots/wheeled_robot.py @@ -22,8 +22,8 @@ class WheeledRobot(Robot): This type of robots has wheels. """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): - super(WheeledRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): + super(WheeledRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.wheels = [] self.wheel_directions = [] @@ -108,9 +108,9 @@ class DifferentialWheeledRobot(WheeledRobot): http://www.robotplatform.com/knowledge/Classification_of_Robots/wheel_control_theory.html """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): super(DifferentialWheeledRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, - scaling) + scale) class AckermannWheeledRobot(WheeledRobot): @@ -130,9 +130,9 @@ class AckermannWheeledRobot(WheeledRobot): http://www.robotplatform.com/knowledge/Classification_of_Robots/wheel_control_theory.html """ - def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scaling=1.): + def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.): super(AckermannWheeledRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, - scaling) + scale) self.steering = 0 # id of steering joint diff --git a/pyrobolearn/robots/youbot.py b/pyrobolearn/robots/youbot.py index 7486b98..5b83f2a 100644 --- a/pyrobolearn/robots/youbot.py +++ b/pyrobolearn/robots/youbot.py @@ -31,7 +31,7 @@ class YoubotBase(DifferentialWheeledRobot): position=(0, 0, 0.085), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/youbot/youbot_base_only.urdf'): # check parameters if position is None: @@ -43,7 +43,7 @@ class YoubotBase(DifferentialWheeledRobot): if fixed_base is None: fixed_base = False - super(YoubotBase, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(YoubotBase, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'youbot_base' # self.wheels = [self.get_link_ids(link) for link in ['left_wheel', 'right_wheel'] @@ -63,7 +63,7 @@ class KukaYoubotArm(ManipulatorRobot): position=(0, 0, 0.03), orientation=(0, 0, 0, 1), fixed_base=True, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/youbot/youbot_arm_only.urdf'): # check parameters if position is None: @@ -75,7 +75,7 @@ class KukaYoubotArm(ManipulatorRobot): if fixed_base is None: fixed_base = True - super(KukaYoubotArm, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(KukaYoubotArm, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'kuka_youbot_arm' @@ -91,7 +91,7 @@ class Youbot(ManipulatorRobot, DifferentialWheeledRobot): position=(0, 0, 0.085), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/youbot/youbot.urdf'): # check parameters if position is None: @@ -103,7 +103,7 @@ class Youbot(ManipulatorRobot, DifferentialWheeledRobot): if fixed_base is None: fixed_base = False - super(Youbot, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(Youbot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'youbot' # self.wheels = [self.get_link_ids(link) for link in ['left_wheel', 'right_wheel'] @@ -123,7 +123,7 @@ class YoubotDualArm(BiManipulatorRobot, DifferentialWheeledRobot): position=(0, 0, 0.085), orientation=(0, 0, 0, 1), fixed_base=False, - scaling=1., + scale=1., urdf=os.path.dirname(__file__) + '/urdfs/youbot/youbot_dual_arm.urdf'): # check parameters if position is None: @@ -135,7 +135,7 @@ class YoubotDualArm(BiManipulatorRobot, DifferentialWheeledRobot): if fixed_base is None: fixed_base = False - super(YoubotDualArm, self).__init__(simulator, urdf, position, orientation, fixed_base, scaling) + super(YoubotDualArm, self).__init__(simulator, urdf, position, orientation, fixed_base, scale) self.name = 'youbot_dual_arm' # self.wheels = [self.get_link_ids(link) for link in ['left_wheel', 'right_wheel'] diff --git a/pyrobolearn/states/state.py b/pyrobolearn/states/state.py index 870a5a6..b7730a4 100644 --- a/pyrobolearn/states/state.py +++ b/pyrobolearn/states/state.py @@ -5,10 +5,11 @@ This file defines the `State` class, which is returned by the environment, and g models such as policies/controllers, dynamic transition functions, value approximators, reward/cost function, and so on. """ -import numpy as np -import torch +import copy import collections # from abc import ABCMeta, abstractmethod +import numpy as np +import torch import gym from pyrobolearn.utils.data_structures.orderedset import OrderedSet @@ -828,11 +829,12 @@ class State(object): # Operators # ############# - def __repr__(self): + def __str__(self): + """Return a representation string about the object.""" if self._data is None: lst = [self.__class__.__name__ + '('] for state in self.states: - lst.append('\t' + state.__repr__() + ',') + lst.append('\t' + state.__str__() + ',') lst.append(')') return '\n'.join(lst) else: @@ -1012,8 +1014,9 @@ class State(object): def __sub__(self, other): """ Remove the other state(s) from the current state. - :param other: - :return: + + Args: + other (State): state to be removed. """ if not isinstance(other, State): raise TypeError("Expecting another state, instead got {}".format(type(other))) @@ -1029,7 +1032,7 @@ class State(object): Remove one or several states from the combined state. Args: - other: + other (State): state to be removed. """ if not isinstance(other, State): raise TypeError("Expecting another state, instead got {}".format(type(other))) @@ -1062,6 +1065,29 @@ class State(object): """ return self.fuse(other, axis=0) + def __copy__(self): + """Return a shallow copy of the state. This can be overridden in the child class.""" + return self.__class__(states=self.states, data=self._data, space=self._space, name=self.name, + window_size=self.window_size, axis=self.axis, ticks=self.ticks) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the state. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + states = [copy.deepcopy(state, memo) for state in self.states] + data = copy.deepcopy(self.window[0]) if self.has_data() else None + space = copy.deepcopy(self._space) + state = self.__class__(states=states, data=data, space=space, name=self.name, window_size=self.window_size, + axis=self.axis, ticks=self.ticks) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = state + + return state + # def __invert__(self): # """ # Return the diff --git a/pyrobolearn/tasks/reinforcement.py b/pyrobolearn/tasks/reinforcement.py index ad90c4d..291c5a6 100644 --- a/pyrobolearn/tasks/reinforcement.py +++ b/pyrobolearn/tasks/reinforcement.py @@ -2,8 +2,6 @@ """Define the reinforcement learning task. """ -import gym - from pyrobolearn.tasks.task import Task __author__ = "Brian Delhaisse" @@ -26,5 +24,6 @@ class RLTask(Task): [1] "Reinforcement Learning: An Introduction", Sutton and Barto, 2018 [2] "A Survey on Policy Search for Robotics", Deisenroth et al., 2013 """ + def __init__(self, environment, policies): super(RLTask, self).__init__(environment, policies) diff --git a/pyrobolearn/tasks/task.py b/pyrobolearn/tasks/task.py index e676b27..fc470a9 100644 --- a/pyrobolearn/tasks/task.py +++ b/pyrobolearn/tasks/task.py @@ -20,6 +20,7 @@ Dependencies: import collections import copy +import pickle import time from abc import ABCMeta from itertools import count @@ -231,6 +232,7 @@ class Task(object): """ Perform one step. """ + # if we need to render the environment if render: self.env.render() else: @@ -261,8 +263,50 @@ class Task(object): return self.policies[idx].model def save(self, filename): + """Save the storage on the disk.""" + pickle.dump(self, open(filename, 'wb')) + + @staticmethod + def load(filename): + """Load the storage from the disk.""" + return pickle.load(open(filename, 'r')) + + def rollout(self): # TODO pass + ############# + # Operators # + ############# + + def __repr__(self): + """Return a representation string about the reward function.""" + return self.__class__.__name__ + + def __str__(self): + """Return a string describing the reward function.""" + return self.__class__.__name__ + '(\n\tenvironment=' + str(self.environment) + ',\n\tpolicies=[' \ + + ',\n\t\t'.join([str(policy) for policy in self.policies]) + ']\n)' + + def __copy__(self): + """Return a shallow copy of the task. This can be overridden in the child class.""" + return self.__class__(environment=self.environment, policies=self.policies) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the task. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + environment = copy.deepcopy(self.environment, memo) + policies = [copy.deepcopy(policy, memo) for policy in self.policies] + task = self.__class__(environment=environment, policies=policies) + + # update the memodict (note that `copy.deepcopy` will automatically check this dictionary and return the + # reference if already present) + memo[self] = task + + return task + # alias Scenario = Task diff --git a/pyrobolearn/values/value.py b/pyrobolearn/values/value.py index a424894..7a59888 100644 --- a/pyrobolearn/values/value.py +++ b/pyrobolearn/values/value.py @@ -10,6 +10,7 @@ Dependencies: - `pyrobolearn.approximators` (and thus `pyrobolearn.models`) """ +import copy from abc import ABCMeta, abstractmethod import numpy as np import torch @@ -89,10 +90,37 @@ class ValueApproximator(object): """Predict the value.""" pass + ############# + # Operators # + ############# + + # def __repr__(self): + # """Return a representation string about the reward function.""" + # return self.__class__.__name__ + + def __str__(self): + """Return a string describing the reward function.""" + return self.__repr__() + def __call__(self, *args, **kwargs): """Predict the value.""" return self.evaluate(*args, **kwargs) + def __copy__(self): + """Return a shallow copy of the value approximator. This can be overridden in the child class.""" + return self.__class__(state=self.state) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the value approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) if isinstance(self.state, State) else copy.deepcopy(self.state) + value = self.__class__(state=state) + memo[self] = value + return value + class QValueApproximator(ValueApproximator): r"""Q-Value Approximator @@ -102,10 +130,22 @@ class QValueApproximator(ValueApproximator): __metaclass__ = ABCMeta def __init__(self, state, action): + """ + Initialize the value function approximator. + + Args: + state (State, np.array, torch.Tensor): state input + action (Action, np.array, torch.Tensor): action input (if discrete or continuous) or output (only if + discrete). + """ # super(QValueApproximator, self).__init__(state) ValueApproximator.__init__(self, state) self.action = action + ############## + # Properties # + ############## + @property def action(self): """Return the action instance.""" @@ -120,6 +160,26 @@ class QValueApproximator(ValueApproximator): raise TypeError("Expecting the action to be an Action, torch.Tensor, or np.ndarray.") self._action = action + ############# + # Operators # + ############# + + def __copy__(self): + """Return a shallow copy of the value approximator. This can be overridden in the child class.""" + return self.__class__(state=self.state, action=self.action) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the value approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) if isinstance(self.state, State) else copy.deepcopy(self.state) + action = copy.deepcopy(self.action, memo) if isinstance(self.action, Action) else copy.deepcopy(self.action) + value = self.__class__(state=state, action=action) + memo[self] = value + return value + class ParametrizedValue(ValueApproximator): r"""Parametrized (Learnable) Value Function Approximator @@ -273,10 +333,30 @@ class ParametrizedValue(ValueApproximator): self.value = self.model.predict(state, to_numpy=to_numpy, return_logits=True, set_output_data=False) return self.value + ############# + # Operators # + ############# + def __call__(self, state=None, to_numpy=False): """Predict the value.""" return self.evaluate(state=state, to_numpy=to_numpy) + def __copy__(self): + """Return a shallow copy of the value approximator. This can be overridden in the child class.""" + return self.__class__(state=self.state, model=self.model) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the value approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) if isinstance(self.state, State) else copy.deepcopy(self.state) + model = copy.deepcopy(self.model, memo) + value = self.__class__(state=state, model=model) + memo[self] = value + return value + # alias Value = ParametrizedValue @@ -453,10 +533,31 @@ class ParametrizedQValue(QValueApproximator): # ParametrizedValue, QValueApprox self.value = self.model.predict([state, action], to_numpy=to_numpy, return_logits=True, set_output_data=False) return self.value + ############# + # Operators # + ############# + def __call__(self, state=None, action=None, to_numpy=False): """Predict the value.""" return self.evaluate(state=state, action=action, to_numpy=to_numpy) + def __copy__(self): + """Return a shallow copy of the value approximator. This can be overridden in the child class.""" + return self.__class__(state=self.state, action=self.action, model=self.model) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the value approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) if isinstance(self.state, State) else copy.deepcopy(self.state) + action = copy.deepcopy(self.action, memo) if isinstance(self.action, Action) else copy.deepcopy(self.action) + model = copy.deepcopy(self.model, memo) + value = self.__class__(state=state, action=action, model=model) + memo[self] = value + return value + # alias QValue = ParametrizedQValue @@ -501,6 +602,10 @@ class ParametrizedQValueOutput(ParametrizedQValue): raise TypeError("Expecting the action to be an int, float, Action, torch.Tensor, or np.ndarray.") self._action = action + ########### + # Methods # + ########### + def evaluate(self, state=None, action=None, to_numpy=False): """Compute the output of the value function. @@ -523,10 +628,31 @@ class ParametrizedQValueOutput(ParametrizedQValue): self.value = self.model.predict(state, to_numpy=to_numpy, return_logits=True, set_output_data=False) return self.value + ############# + # Operators # + ############# + def __call__(self, state=None, action=None, to_numpy=False): """Predict the value.""" return self.evaluate(state=state, to_numpy=to_numpy) + def __copy__(self): + """Return a shallow copy of the value approximator. This can be overridden in the child class.""" + return self.__class__(state=self.state, action=self.action, model=self.model) + + def __deepcopy__(self, memo={}): + """Return a deep copy of the value approximator. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + state = copy.deepcopy(self.state, memo) if isinstance(self.state, State) else copy.deepcopy(self.state) + action = copy.deepcopy(self.action, memo) if isinstance(self.action, Action) else copy.deepcopy(self.action) + model = copy.deepcopy(self.model, memo) + value = self.__class__(state=state, action=action, model=model) + memo[self] = value + return value + # alias QValueOutput = ParametrizedQValueOutput diff --git a/pyrobolearn/worlds/world.py b/pyrobolearn/worlds/world.py index cb27f36..32a0767 100644 --- a/pyrobolearn/worlds/world.py +++ b/pyrobolearn/worlds/world.py @@ -6,13 +6,14 @@ Dependencies: - `pyrobolearn.simulators` """ -import numpy as np -import collections -import inspect -import multiprocessing import os -import cv2 +import copy +import inspect +import collections import time +import numpy as np +import cv2 + from pyrobolearn.simulators import Simulator from pyrobolearn.worlds.world_camera import WorldCamera @@ -71,6 +72,7 @@ class World(object): self.camera = WorldCamera(self.simulator) # keep track of the objects present in the world + # TODO: check what is already inside the simulator! self.robots = {} self.movable_bodies = {} # set() self.immovable_bodies = {} # set() @@ -226,8 +228,8 @@ class World(object): # Operators # ############# - def __repr__(self): - """Return a representation string of the object.""" + def __str__(self): + """Return a string describing the world.""" return self.__class__.__name__ def __contains__(self, item): @@ -246,6 +248,22 @@ class World(object): return (item in self.robots) or (item in self.movable_bodies) or (item in self.immovable_bodies) or \ (item in self.visual_objects) + def __copy__(self): # TODO: add the bodies in the copy + """Return a shallow copy of the world. This can be overridden in the child class.""" + return self.__class__(simulator=self.simulator, gravity=self.gravity) + + def __deepcopy__(self, memo={}): # TODO: add the bodies in the copy + """Return a deep copy of the world. This can be overridden in the child class. + + Args: + memo (dict): memo dictionary of objects already copied during the current copying pass + """ + simulator = copy.deepcopy(self.simulator, memo) + gravity = copy.deepcopy(self.gravity) + world = self.__class__(simulator=simulator, gravity=gravity) + memo[self] = world + return world + ########### # Methods # ###########