eager-dev/eagerx

View on GitHub
docs/_shared/pendulum_real.rst

Summary

Maintainability
Test Coverage
.. _engine_specific_real_engine:
****************************
Engine-Specific (RealEngine)
****************************

Having defined the agnostic parameters of the *Pendulum*, we can now specify the engine-specific implementations.
In this case, we will create an implementation for the `RealEngine <https://github.com/eager-dev/eagerx_reality>`_.

`Full code is available here. <https://github.com/eager-dev/eagerx_dcsc_setups/blob/master/eagerx_dcsc_setups/pendulum/objects.py>`_

real_engine
###########

Engine-specific can be created by adding a method to an :class:`~eagerx.core.entities.Object`, e.g. :func:`~eagerx.core.entities.Object.example_engine`.
In this case, we create an implementation for the *RealEngine*, to be able to perform experiments with the real system.
For this, we use the `engine nodes that we have created here <https://github.com/eager-dev/eagerx_dcsc_setups/blob/master/eagerx_dcsc_setups/pendulum/real/engine_nodes.py>`_.
Also, we will be using the `engine states that we have created here <https://github.com/eager-dev/eagerx_dcsc_setups/blob/master/eagerx_dcsc_setups/pendulum/real/engine_states.py>`_.
Finally, we will construct an :class:`~eagerx.core.graph_engine.EngineGraph` with the created nodes.

::

  @staticmethod
  @register.engine(entity_id, RealEngine)  # This decorator pre-initializes engine implementation with default object_params
  def real_engine(spec: ObjectSpec, graph: EngineGraph):
      """Engine-specific implementation (RealEngine) of the object."""
      # Import any object specific entities for this engine
      import eagerx_dcsc_setups.pendulum.real  # noqa # pylint: disable=unused-import

      # Couple engine states
      spec.RealEngine.states.model_state = EngineState.make("RandomActionAndSleep", sleep_time=1.0, repeat=1)

      # Create sensor engine nodes
      obs = EngineNode.make("PendulumOutput", "pendulum_output", rate=spec.sensors.pendulum_output.rate, process=0)
      applied = EngineNode.make("ActionApplied", "applied", rate=spec.sensors.action_applied.rate, process=0)
      image = EngineNode.make(
          "CameraRender",
          "image",
          camera_idx=spec.config.camera_index,
          shape=spec.config.render_shape,
          rate=spec.sensors.image.rate,
          process=0,
      )

      # Create actuator engine nodes
      action = EngineNode.make("PendulumInput", "pendulum_input", rate=spec.actuators.pendulum_input.rate, process=0)

      # Connect all engine nodes
      graph.add([obs, applied, image, action])
      graph.connect(source=obs.outputs.pendulum_output, sensor="pendulum_output")
      graph.connect(source=action.outputs.action_applied, target=applied.inputs.action_applied, skip=True)
      graph.connect(source=applied.outputs.action_applied, sensor="action_applied")
      graph.connect(source=image.outputs.image, sensor="image")
      graph.connect(actuator="pendulum_input", target=action.inputs.pendulum_input)

.. note::
  Mind the use of the :func:`~eagerx.core.register.engine` decorator, which creates the link to the corresponding engine.
  Therefore, the name of the *real_engine* method is irrelevant, i.e. the link to the *RealEngine* is defined by the aforementioned decorator.
  Also note that we are importing :class:`eagerx_dcsc_setups.pendulum.real`.
  During the import, the engine nodes of this module are registered and therefore we can use the :func:`~eagerx.core.entities.EngineNode.make` and :func:`~eagerx.core.entities.EngineState.make` methods with the IDs to create these nodes (e.g. PendulumOutput).