update simulators

This commit is contained in:
Brian Delhaisse
2019-09-03 06:40:32 +02:00
parent 9161c7568b
commit 5b201702f6
8 changed files with 388 additions and 38 deletions
+8 -6
View File
@@ -3,7 +3,7 @@
This is the main interface that communicates with the PyBullet simulator [1]. By defining this interface, it allows to
decouple the PyRoboLearn framework from the simulator. It also converts some data types to the ones required by
PyBullet. For instance, some methods in PyBullet do not accepts numpy arrays but only lists. The interface provided
PyBullet. For instance, some methods in PyBullet do not accept numpy arrays but only lists. The interface provided
here makes the necessary conversions.
The signature of each method defined here are inspired by [1,2] but in accordance with the PEP8 style guide [3].
@@ -80,12 +80,13 @@ class Bullet(Simulator):
Erwin Coumans and Yunfei Bai, 2017/2018
"""
def __init__(self, render=True, **kwargs):
def __init__(self, render=True, num_instances=1, **kwargs):
"""
Initialize PyBullet simulator.
Initialize the PyBullet simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
num_instances (int): number of simulator instances.
**kwargs (dict): optional arguments (this is not used here).
"""
super(Bullet, self).__init__(render=render, **kwargs)
@@ -746,9 +747,10 @@ class Bullet(Simulator):
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
position (np.array[float[3]]): create the base of the object at the specified position in world space
coordinates [x,y,z].
orientation (np.array[float[4]]): create the base of the object at the specified orientation as world
space quaternion [x,y,z,w].
use_maximal_coordinates (int): Experimental. By default, the joints in the URDF file are created using the
reduced coordinate method: the joints are simulated using the Featherstone Articulated Body algorithm
(btMultiBody in Bullet 2.x). The useMaximalCoordinates option will create a 6 degree of freedom rigid
+13 -4
View File
@@ -95,7 +95,15 @@ class Dart(Simulator):
- [2] PEP8: https://www.python.org/dev/peps/pep-0008/
"""
def __init__(self, render=True, dt=0.001, **kwargs):
def __init__(self, render=True, num_instances=1, dt=0.001, **kwargs):
"""
Initialize the Dart simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
num_instances (int): number of simulator instances.
**kwargs (dict): optional arguments (this is not used here).
"""
super(Dart, self).__init__(render, **kwargs)
# dart = {'collision': ['BulletCollisionDetector', 'BulletCollisionGroup', 'CollisionDetector',
@@ -521,9 +529,10 @@ class Dart(Simulator):
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
position (np.array[float[3]]): create the base of the object at the specified position in world space
coordinates [x,y,z].
orientation (np.array[float[4]]): create the base of the object at the specified orientation as world
space quaternion [x,y,z,w].
use_fixed_base (bool): force the base of the loaded object to be static
scale (float): scale factor to the URDF model.
+2
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env python
"""Define the Nvidia FleX Simulator API.
DEPRECATED: See `isaac.py`.
This is the main interface that communicates with the FleX simulator [1]. By defining this interface, it allows to
decouple the PyRoboLearn framework from the simulator. It also converts some data types to the ones required by
FleX.
+10 -2
View File
@@ -15,7 +15,7 @@ References:
[1] Gazebo: http://gazebosim.org/
"""
# TODO
# TODO: create Gazebo wrapper or use ROS to communicate...
from pyrobolearn.simulators.simulator import Simulator
@@ -36,6 +36,14 @@ class Gazebo(Simulator):
[1] Gazebo: http://gazebosim.org/
"""
def __init__(self, render=True):
def __init__(self, render=True, num_instances=1, **kwargs):
r"""
Initialize the Gazebo Simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
num_instances (int): number of simulator instances.
**kwargs (dict): optional arguments (this is not used here).
"""
super(Gazebo, self).__init__(render=render)
raise NotImplementedError
+319 -11
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env python
"""Define the Isaac SDK simulator API.
This is the main interface that communicates with the Isaac SDK simulator [1]. By defining this interface, it allows to
decouple the PyRoboLearn framework from the simulator.
This is the main interface that communicates with the Isaac SDK/Gym simulator [1]. By defining this interface, it
allows to decouple the PyRoboLearn framework from the simulator. It also converts some data types to the ones required
by Isaac Gym. For instance, some methods in `isaacgym` do not accept numpy arrays but only `gymapi` data types such
as `gymapi.Vec3`, `gymapi.Quat`, and others. The interface provided here makes the necessary conversions.
The signature of each method defined here are inspired by [2] but in accordance with the PEP8 style guide [3].
Parts of the documentation for the methods have been copied-pasted from [2] for completeness purposes.
@@ -11,15 +13,25 @@ Dependencies in PRL:
* `pyrobolearn.simulators.simulator.Simulator`
References:
[1] Isaac SDK: https://developer.nvidia.com/isaac-sdk
[2] PyBullet Quickstart Guide: https://docs.google.com/document/d/10sXEhzFRSnvFcl3XxNGhnD4N2SedqwdAvK3dsihxVUA
[3] PEP8: https://www.python.org/dev/peps/pep-0008/
- [1] Isaac SDK: https://developer.nvidia.com/isaac-sdk
- [2] PyBullet Quickstart Guide: https://docs.google.com/document/d/10sXEhzFRSnvFcl3XxNGhnD4N2SedqwdAvK3dsihxVUA
- [3] PEP8: https://www.python.org/dev/peps/pep-0008/
- [4] Isaac gym slides:
https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9918-isaac-gym.pdf
"""
# TODO: waiting for its release at the end of March
# TODO: waiting for its release in September
import os
import time
import numpy as np
from collections import OrderedDict
try:
import isaacgym
from isaacgym import gymapi
except ImportError as e:
raise ImportError(str(e) + "\nTry to install `Isaac Gym`!")
from pyrobolearn.simulators.simulator import Simulator
@@ -43,11 +55,307 @@ class Isaac(Simulator):
operate and cooperate with humans." [1]
References:
[1] https://developer.nvidia.com/isaac-sdk
[2] https://www.nvidia.com/en-au/deep-learning-ai/industries/robotics/
[3] "GPU-Accelerated Robotic Simulation for Distributed Reinforcement Learning", Liang et al., 2018
- [1] https://developer.nvidia.com/isaac-sdk
- [2] https://www.nvidia.com/en-au/deep-learning-ai/industries/robotics/
- [3] "GPU-Accelerated Robotic Simulation for Distributed Reinforcement Learning", Liang et al., 2018
- [4] Slides: https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9918-isaac-gym.pdf
"""
def __init__(self, render=True, **kwargs):
def __init__(self, render=True, num_instances=1, **kwargs):
"""
Initialize Isaac gym simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
num_instances (int): number of simulator instances.
**kwargs (dict): optional arguments (this is not used here).
"""
super(Isaac, self).__init__(render=render)
raise NotImplementedError
# define variables
self.gym = gymapi.acquire_gym()
self.sim = self.gym.create_sim()
self.params = gymapi.SimParams()
self.gym.get_sim_params(self.sim, self.params)
spacing = 10
lower, upper = gymapi.Vec3(-spacing, -spacing, 0.), gymapi.Vec3(spacing, spacing, spacing)
self.env = self.gym.create_env(self.sim, lower, upper)
self.viewer = None
self.dt = 1./60
self.num_substeps = 2
if render: # gui
self.viewer = self.gym.create_viewer(None, 1920, 1080)
# keep track of loaded bodies
self.bodies = OrderedDict() # {body_id: Body}
self._body_cnt = 0
##################
# Static methods #
##################
@staticmethod
def simulate_gas_dynamics():
"""Return True if the simulator can simulate gases."""
return False
@staticmethod
def simulate_liquid_dynamics():
"""Return True if the simulator can simulate liquids."""
return True # using Flex
@staticmethod
def simulate_fluid_dynamics():
"""Return True if the simulator can simulate fluids (gases and liquids)."""
return Simulator.simulate_gas_dynamics() and Simulator.simulate_liquid_dynamics()
@staticmethod
def simulate_soft_bodies():
"""Return True if the simulator can simulate soft bodies."""
return True # using Flex
@staticmethod
def has_middleware_communication_layer():
"""Return True if the simulator has a middleware communication layer (like ROS, YARP, etc)."""
return False
@staticmethod
def supports_dynamic_loading():
"""Return True if the simulator supports the dynamic loading of models."""
return True
@staticmethod
def supports_acceleration():
"""Return True if the simulator provides acceleration (dynamic) information (such as joint accelerations, link
Cartesian accelerations, etc). If not, the `Robot` class will have to implement these using finite
difference."""
return False
@staticmethod
def supports_sensors(sensor_type=None):
"""Return True if the simulator provides supports for the specified sensor."""
return False
@staticmethod
def supports_urdf():
"""Return True if we can use URDFs."""
return True
@staticmethod
def supports_light():
"""Return True if we can define and access to the lights in the simulator."""
return False
@staticmethod
def supports_depth_image():
"""Return True if we can get depth images from the simulator."""
return False
@staticmethod
def supports_segmentation_images():
"""Return True if we can get segmentation images from the simulator."""
return False
@staticmethod
def supports_visualization():
"""Return True if there is a graphical user interface (GUI)."""
return True
@staticmethod
def supports_interactive_gui():
"""Return True if the simulator has an interactive GUI."""
return False
@staticmethod
def supports_mousekeyboard_events():
"""Return True if the simulator allows to capture mouse and keyboard events."""
return False
@staticmethod
def supports_visual_objects():
"""Return True if we can simulate objects that do not have collision shapes."""
return False
@staticmethod
def supports_plugins():
"""Return True if we can use plugins."""
return False
@staticmethod
def supports_constraints(constraint_type):
"""Return True if we can support the specified constraint type."""
return False
@staticmethod
def supports_realtime():
"""Return True if the simulator supports real-time (meaning we don't need to step manually in the simulator).
Note that if we can step in the simulator, we can use threads to simulate the real-time. So the return value
should always be True."""
return True
@staticmethod
def supports_ray_casting():
"""Return True if the simulator supports ray casting."""
return False
@staticmethod
def can_step():
"""Return True if we can step manually in the simulator."""
return True
@staticmethod
def can_load_heightmap():
"""Return True if the simulator can load a heightmap."""
return False
###########
# Methods #
###########
##############
# Simulators #
##############
def step(self, sleep_time=0):
"""Perform a step in the simulator, and sleep the specified amount of time.
Args:
sleep_time (float): amount of time to sleep after performing one step in the simulation.
"""
# step the simulation
self.gym.simulate(self.sim, self.dt, self.num_substeps)
self.gym.fetch_results(self.sim, True)
# update the viewer
if self.viewer is not None:
if self.gym.query_viewer_has_closed(self.viewer):
exit()
self.gym.step_graphics(self.sim)
self.gym.draw_viewer(self.viewer, self.sim, True)
# wait for dt to elapse in real-time.
# This synchronizes the physics simulation with the rendering rate.
self.gym.sync_frame_time(self.sim)
# time.sleep(sleep_time)
def render(self, enable=True):
"""Render the simulation.
Args:
enable (bool): If True, it will render the simulator by enabling the GUI.
"""
self._render = enable
if self._render:
if self.viewer is None:
self.viewer = self.gym.create_viewer(None, 1920, 1080)
else:
if self.viewer is not None:
pass # close the viewer set viewer to None
def get_physics_properties(self):
"""Get the physics engine parameters.
Returns:
dict: dictionary containing the physics simulator properties.
"""
properties = dict()
gravity = self.params.gravity # return gymapi.Vec3
gravity = np.array([gravity[0], gravity[1], gravity[2]])
properties['gravity'] = gravity
properties['solver_type'] = self.params.solver_type
properties['num_outer_iterations'] = self.params.num_outer_iterations
properties['num_inner_iterations'] = self.params.num_inner_iterations
properties['relaxation'] = self.params.relaxation
properties['warm_start'] = self.params.warm_start
properties['num_substeps'] = self.num_substeps
return properties
def set_physics_properties(self, solver_type=None, num_outer_iterations=None, num_inner_iterations=None,
relaxation=None, warm_start=None, num_substeps=None, *args, **kwargs):
"""Set the physics engine parameters."""
if solver_type is not None:
self.params.solver_type = int(solver_type)
if num_outer_iterations is not None:
self.params.num_outer_iterations = int(num_outer_iterations)
if num_inner_iterations is not None:
self.params.num_inner_iterations = int(num_inner_iterations)
if relaxation is not None:
self.params.relaxation = float(relaxation)
if warm_start is not None:
self.params.warm_start = float(warm_start)
if num_substeps is not None:
self.num_substeps = int(num_substeps) if num_substeps >= 1 else 1
def get_gravity(self):
"""Return the gravity set in the simulator."""
gravity = self.params.gravity # return gymapi.Vec3
return np.asarray([gravity[0], gravity[1], gravity[2]])
def set_gravity(self, gravity=(0, 0, -9.81)):
"""Set the gravity in the simulator with the given acceleration.
By default, there is no gravitational force enabled in the simulator.
Args:
gravity (list, tuple of 3 floats): acceleration in the x, y, z directions.
"""
gravity = gymapi.Vec3(*gravity)
self.params.gravity = gravity
self.gym.set_sim_params(self.sim, self.params)
######################################
# Loading URDFs, SDFs, MJCFs, meshes #
######################################
def load_urdf(self, filename, position, orientation, use_fixed_base=0, scale=1.0, *args, **kwargs):
"""Load a URDF file in the simulator.
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (np.array[float[3]]): create the base of the object at the specified position in world space
coordinates [x,y,z].
orientation (np.array[float[4]]): create the base of the object at the specified orientation as world
space quaternion [x,y,z,w].
use_fixed_base (bool): force the base of the loaded object to be static
scale (float): scale factor to the URDF model.
Returns:
int (non-negative): unique id associated to the load model.
"""
dirname = os.path.dirname(filename)
filename = os.path.basename(filename)
name = ''.join(filename.split('.')[:-1])
asset = self.gym.load_asset(dirname, filename)
position = gymapi.Vec3(*position)
orientation = gymapi.Quat(*orientation)
pose = gymapi.Transform(position, orientation)
self.gym.create_actor(self.env, asset, pose, name)
self._body_cnt += 1
self.bodies[self._body_cnt] = name
return self._body_cnt
def load_mjcf(self, filename, scaling=1., *args, **kwargs):
"""Load a Mujoco file in the simulator.
Args:
filename (str): a relative or absolute path to the MJCF file on the file system of the physics server.
scaling (float): scale factor for the object
Returns:
list(int): list of object unique id for each object loaded
"""
dirname = os.path.dirname(filename)
filename = os.path.basename(filename)
name = ''.join(filename.split('.')[:-1])
asset = self.gym.load_asset(dirname, filename)
self.gym.create_actor(self.env, asset, name)
self._body_cnt += 1
self.bodies[self._body_cnt] = name
return self._body_cnt
+9 -7
View File
@@ -209,16 +209,17 @@ class Mujoco(Simulator):
- [3] DeepMind Control Suite: https://github.com/deepmind/dm_control/tree/master/dm_control/mujoco
"""
def __init__(self, render=True, load_at_the_end=False):
def __init__(self, render=True, num_instances=1, load_at_the_end=False):
"""
Initialize the MuJoCo simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server (i.e. in a headless
mode, i.e. without a GUI).
mode, i.e. without a GUI).
num_instances (int): number of simulator instances.
load_at_the_end (bool): if True, it will load at the end all the models that have been "loaded" in the
simulator. The reason is that the MuJoCo simulator does not allow to load dynamically models into the
world.
simulator. The reason is that the MuJoCo simulator does not allow to load dynamically models into the
world.
"""
super(Mujoco, self).__init__(render=render)
@@ -562,9 +563,10 @@ class Mujoco(Simulator):
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
position (np.array[float[3]]): create the base of the object at the specified position in world space
coordinates [x,y,z].
orientation (np.array[float[4]]): create the base of the object at the specified orientation as world
space quaternion [x,y,z,w].
use_fixed_base (bool): force the base of the loaded object to be static
scale (float): scale factor to the URDF model.
+14 -4
View File
@@ -41,6 +41,7 @@ except ImportError as e:
from pyrobolearn.simulators.simulator import Simulator
from pyrobolearn.utils.decorator import keyboard_interrupt
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2019, PyRoboLearn"
__credits__ = ["RaiSim (ETHz, Hwangbo, Kang, Lee)", "Brian Delhaisse (raisimpy + PRL)"]
@@ -68,7 +69,15 @@ class Raisim(Simulator):
- [6] RaiSimPy - A Python wrapper for Raisim: https://github.com/robotlearn/raisimpy
"""
def __init__(self, render=True, **kwargs):
def __init__(self, render=True, num_instances=1, **kwargs):
"""
Initialize the Raisim simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
num_instances (int): number of simulator instances.
**kwargs (dict): optional arguments (this is not used here).
"""
super(Raisim, self).__init__(render, **kwargs)
# create world
@@ -487,9 +496,10 @@ class Raisim(Simulator):
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
position (np.array[float[3]]): create the base of the object at the specified position in world space
coordinates [x,y,z].
orientation (np.array[float[4]]): create the base of the object at the specified orientation as world
space quaternion [x,y,z,w].
use_fixed_base (bool): force the base of the loaded object to be static
scale (float): scale factor to the URDF model.
+13 -4
View File
@@ -182,7 +182,15 @@ class Simulator(object):
URDF_USE_SELF_COLLISION_EXCLUDE_PARENT = 16
URDF_USE_SELF_COLLISION_INCLUDE_PARENT = 8192
def __init__(self, render=True, **kwargs):
def __init__(self, render=True, num_instances=1, **kwargs):
r"""
Initialize the Simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
num_instances (int): number of simulator instances.
**kwargs (dict): optional arguments (this is not used here).
"""
self._render = render
self.real_time = False
self.kwargs = kwargs
@@ -560,9 +568,10 @@ class Simulator(object):
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
position (np.array[float[3]]): create the base of the object at the specified position in world space
coordinates [x,y,z].
orientation (np.array[float[4]]): create the base of the object at the specified orientation as world
space quaternion [x,y,z,w].
use_fixed_base (bool): force the base of the loaded object to be static
scale (float): scale factor to the URDF model.