.. index:: pair: page; PySim Engine .. _doxid-pysim_engine: PySim Engine ============ This engine is a special case engine of the :ref:`Python JSON Engine ` to link python APIs for different simulators (`OpenAI `__, `Mujoco `__, `OpenSim `__, and `Bullet `__) with NRP-core. The main difference between a PySim engine and a standard Python JSON engine is the way the Python APIs of several given simulators are called from a so-called :ref:`SimulatorManager ` interface. The :ref:`SimulatorManager ` packs different simulators into the same kinds of data interface to make it easier for using and combining different simulators in NRP_Core. As a whole, the PySim engine still runs a simulator model that specified the engine configuration and manages the synchronization and data exchange with other engines participating in the same experiment. In that sense, it is used in a manner very similar to the Python JSON Engine (please refer to this :ref:`guide ` for details on how to use it). Additionally, there are example experiments in the folder *examples/pysim_examples* that can be used as a reference for implementing experiments including this engine. Similarly to the Python JSON Engine, the engine behavior in each experiment is implemented by subclassing a Python class *PySimEngineScript* and overriding the hook methods: * ``:ref:`initialize() ``` : executed when the engine is initialized * ``:ref:`run_loop() ``` : executed when the engine is requested to advance its simulation (Same as EngineScript.runLoop of :ref:`Python JSON Engine `) * ``:ref:`shutdown() ``` : executed when the engine is requested to shutdown * ``:ref:`reset() ``` : executed when the engine is requested to reset. In this engine, several simulators (`OpenAI `__, `Mujoco `__, `OpenSim `__, and `Bullet `__) with python API are wrapped as functions in type of :ref:`Python JSON Engine ` can apply. It includes shutdown, reset, runLoop, and calling model properties and standardizes the simulation process. The simulation launch with heavy timing cost is executed in the background of the :ref:`initialize() ` function. In :ref:`run_loop() `, the interaction with the wrapped simulators is performed by an instance of the class :ref:`SimulatorManager `, which is stored in PySimEngineScript in the attribute self.sim_manager. This attribute must be "manually" called from the PySimEngineScript subclass in order to advance, modify or get information from the different simulators. As an example of this use, the script implementing PySimEngineScript in experiment examples/pysim_examples/opensim_control is listed below: .. ref-code-block:: cpp """ A Py_Sim Engine for simulation --> obtain information from simulation and send them to controller engine --> receive controller command to run the simulation """ from nrp_core.engines.py_sim import PySimEngineScript # The API of Opensim is shown in the following link: # https://simtk.org/api_docs/opensim/api_docs class Script(PySimEngineScript): def __init__(self): super().__init__() # To set the force of muscles, in arm_26, they are: # ['TRIlong', 'TRIlat', 'TRImed', 'BIClong', 'BICshort', 'BRA'] # The default color of muscle in the visualizer is blue. # Once the force of a muscle is not the default value, # the color of the muscle will be changed. # Using this phenomenon, the controlled muscles can be found in the visualizer # For example, if action= [0.5, 0.0, 0.0, 0.0, 0.0, 0.0], # the color of TRIlong will not be blue in shown screen self.action = [0.0] * 6 def :ref:`initialize `(self): print("OpensimEngine Server is initializing") print("Registering datapack --> sensors") self._registerDataPack("joints") self._setDataPack("joints", {"shoulder": 0, "elbow": 0}) self._registerDataPack("infos") self._setDataPack("infos", {"time": 0}) print("Registering datapack --> actuators") self._registerDataPack("control_cmd") def runLoop(self, timestep_ns): # Receive control data from TF self.action = self._getDataPack("control_cmd").get("act_list") reset_flag = self._getDataPack("control_cmd").get("reset") if reset_flag == 1: self.reset() else: # All Joints and Muscles can be found in the "*.osim" # Obtain the joint data from model "arm_26" # In arm_26, the joint set is [offset, r_shoulder, r_elbow] s_val = self.sim_manager.get_model_property("r_shoulder_elev", datapack_type="Joint") e_val = self.sim_manager.get_model_property("r_elbow_flex", datapack_type="Joint") # Send data to TF self._setDataPack("joints", {"shoulder": s_val, "elbow": e_val}) self._setDataPack("infos", {"time": self.sim_manager.get_sim_time()}) # Set muscles' force so to change joints self.sim_manager.run_step(self.action, timestep_ns) # To show components in the model changed by action # 1: To show components in a list # ctrl_list = self.sim_manager.theWorld.model.getControlsTable() # 2: To show components one by one # print(self.sim_manager.get_model_properties("Force")) def :ref:`reset `(self): print("Resetting Opensim simulation.") # Reset the value of set datapacks self._setDataPack("joints", {"shoulder": 0, "elbow": 0}) self._setDataPack("infos", {"time": 0}) # Reset simulation model self.sim_manager.:ref:`reset `() def :ref:`shutdown `(self): self.sim_manager.:ref:`shutdown `() print("Simulation engine is shutting down") .. _doxid-pysim_engine_1simulator_manager: SimulatorManager ~~~~~~~~~~~~~~~~ The SimulatorManager is a python class that packs different simulators (OpenSim, Mujoco, OpenAI,and Bullet) into the same interface and acts as a bridge to connect NRP-core and any of the supported simulators through the Python API. It processes requests for simulation initialization, reset, shutdown, run_step, and data retrieval. When instantiated, it loads the simulation model specified in the engine configuration. Additionally, the SimulatorManager receives action for the run step of the simulator and observation data requirement in a run loop of the pysim engine. The observation data covers a variety of commonly used data types of the supported simulator. And users can based on simulators' documents add more customized data types in the simulator APIs in "nrp_pysim_engines/nrp_pysim_engine/python". The following functions are provided by SimulatorManager to interact with the simulator: * ``run_step(action)`` : advances the simulation by the engine timestep as specified in the engine configuration. Takes as input an array of floats or dictionary. The length of input data must be equal to the number of controlled elements (such as muscles in Opensim) in the model. * ``:ref:`reset() ``` : resets the simulation * ``:ref:`shutdown() ``` : shutdowns the simulation * ``get_model_properties(p_type)`` : returns a list with the names of model's elements of the type specified by *p_type*. The latter can take multiple possible values. * OpenAI: "Property" (the names of the observed elements) * Bullet: "Body", "Joint", "Link" * Mujoco: "body", "joint", "geom", "site", "light", "camera", "actuator", "sensor", "tendon", and "mesh" * Opensim: "Joint" (the elements in the model ``JointSet``) and "Force" (the elements in the model ``ForceSet``) * ``get_model_all_properties(p_type)`` : returns a dictionary with the value of all elements of the type specified by *p_type*. The latter can take multiple possible values. * OpenAI: the same with ``p_type`` in ``get_model_properties`` * Bullet: the same with ``p_type`` in ``get_model_properties`` * Mujoco: supports all ``get_`` in `Mujoco Python API `__ * Opensim: the same with ``p_type`` in ``get_model_properties`` * ``get_model_property(p_name, p_type)`` : returns the observed value for required the element ``p_name``, and the ``p_type`` is the data type of the required element and is the same with ``p_type`` in ``get_model_all_properties`` * ``get_sim_time()`` : returns the simulation time in seconds .. _doxid-pysim_engine_1opensim_json_datapacks: DataPacks ~~~~~~~~~ Similarly to the Python JSON engine, the PySim engine supports a unique datapack type: *JsonDataPack*. Refer to this :ref:`section ` for more details. .. _doxid-pysim_engine_1engine_opensim_config_section: Engine Configuration Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The parameters for this engine are defined in the PySimEngine schema (listed :ref:`here `), which in turn is based on :ref:`EngineBase ` and :ref:`EngineJSON ` schemas, and thus inherits all parameters from them. To use the Python Simulator engine in an experiment, set ``EngineType`` to **"py_sim"**. * Parameters inherited from :ref:`EngineBase ` schema: ===================== =================================================================================================================================================== ====== ========================== ======== ===== Name Description Type Default Required Array ===================== =================================================================================================================================================== ====== ========================== ======== ===== EngineName Name of the engine string X EngineType Engine type. Used by string X EngineProcCmd Engine Process Launch command string EngineProcStartParams Engine Process Start Parameters string [] X EngineEnvParams Engine Process Environment Parameters string [] X EngineLaunchCommand object {"LaunchType":"BasicFork"} EngineTimestep Engine Timestep in seconds number 0.01 EngineCommandTimeout Engine Timeout (in seconds). It tells how long to wait for the completion of the engine runStep. 0 or negative values are interpreted as no timeout number 0.0 ===================== =================================================================================================================================================== ====== ========================== ======== ===== * Parameters inherited from :ref:`EngineJSON ` schema: ========================= =========== ====== ============== ======== ===== Name Description Type Default Required Array ========================= =========== ====== ============== ======== ===== ServerAddress string localhost:9002 RegistrationServerAddress Address string localhost:9001 ========================= =========== ====== ============== ======== ===== * Parameters specific to this engine type: ============== ========================================================== ====== ======= ======== ===== Name Description Type Default Required Array ============== ========================================================== ====== ======= ======== ===== PythonFileName Path to the Python script containing the engine definition string X WorldFileName Path to the file of simulation world string X Visualiser To show the simulation in visualizer or not bool false Simulator To call the python API for a special simulator string ============== ========================================================== ====== ======= ======== ===== .. _doxid-pysim_engine_1engine_opensim_schema: Schema ~~~~~~ As explained above, the schema used by the PySim engine inherits from :ref:`EngineBase ` and :ref:`EngineJSON ` schemas. A complete schema for the configuration of this engine is given below: .. ref-code-block:: cpp {"python_base" : { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Python Engine Base", "description": "Python Engine Base Configuration", "$id": "#PythonEngineBase", "allOf": [ { "$ref": "json://nrp-core/engines/engine_comm_protocols.json#/engine_json" }, { "properties": { "PythonFileName" : { "type": "string", "description": "Path to the python script containing the engine definition" } }, "required": ["PythonFileName"] } ] }, "python_json" : { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Python Json Engine", "description": "Python Json Engine Configuration", "$id": "#PythonJSONEngine", "allOf": [ { "$ref": "#/python_base" }, { "properties": { "EngineType": { "enum": ["python_json"] }, "ServerOptions" : { "type": "string", "default": "", "description": "Additional options that will be used by the server (gunicorn) on startup. The string should contain a Python dictionary in the following format - \"{'key1': value, 'key2': 'value_str'}\". The full list of options can be found at the official page - https://docs.gunicorn.org/en/stable/settings.html." } } } ] }, "python_grpc" : { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Python Grpc Engine", "description": "Python Grpc Engine Configuration", "$id": "#PythonGRPCEngine", "allOf": [ { "$ref": "#/python_base" }, { "properties": { "EngineType": { "enum": ["python_grpc"] } } } ] }, "py_sim" : { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Python Simulation Engine", "description": "A simulation engine for simulators offering a Python API.", "$id": "#PySim", "allOf": [ { "$ref": "#/python_base" }, { "properties": { "EngineType": { "enum": ["py_sim"] }, "ServerOptions" : { "type": "string", "default": "", "description": "Additional options that will be used by the server (gunicorn) on startup. The string should contain a Python dictionary in the following format - \"{'key1': value, 'key2': 'value_str'}\". The full list of options can be found at the official page - https://docs.gunicorn.org/en/stable/settings.html." }, "Simulator": { "enum": ["Opensim","OpenAI","Mujoco","Bullet"], "description": "The simulators that are supported" }, "WorldFileName": { "type": "string", "description": "Path to the file of simulation world" }, "Visualizer": { "type": "boolean", "default": false, "description": "To show the simulation in visualizer or not" } }, "required": ["Simulator", "WorldFileName"] } ] } }