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.

Full code is available here.

real_engine

Engine-specific can be created by adding a method to an Object, e.g. 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. Also, we will be using the engine states that we have created here. Finally, we will construct an 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 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 eagerx_dcsc_setups.pendulum.real. During the import, the engine nodes of this module are registered and therefore we can use the make() and make() methods with the IDs to create these nodes (e.g. PendulumOutput).