Skip to content

Latest commit

 

History

History
110 lines (61 loc) · 12.5 KB

Readme.md

File metadata and controls

110 lines (61 loc) · 12.5 KB

Simulink Gym

A wrapper for using Simulink models as Gym environments

This wrapper establishes the Gymnasium environment interface for Simulink models by deriving a simulink_gym.SimulinkEnv subclass from gymnasium.Env.

This wrapper uses Gymnasium version 1.0.0 enabling easy usage with established RL libraries such as Stable-Baselines3 or rllib.

How it Works

This section gives a broad description of the functionality under the hood. For detailed instructions on how to wrap a simulink model, see below.

The wrapper is based on adding TCP/IP communication between a Simulink model running in a background instance of MATLAB Simulink and a Python wrapper class implementing the Gymnasium interface.

The TCP/IP communication is established via respective Simulink blocks and matching communication sockets. The Simulink blocks are provided by the Simulink block library included in this project. The input block receives the input action, which triggers the simulation of the next time step. At the end of this time step, the output block sends the output data (i.e., the observation) back to the wrapper. Check out the Readme of the Simulink block library for more information.

The wrapper provides the necessary methods to create this derived environment without the user having to implement the TCP/IP communication. Similar to the usual Gymnasium environment implementations, the user only has to define the action and observation/state space as well as the individual reset and step methods.

Note

Initializing an environment object takes a few seconds due to the starting of MATLAB in the background and the creation of the simulation object (SimulationInput object). Also, the first reset(...) takes substantially longer than any consecutive reset(...).

Setup

This package is controlling Simulink models in MATLAB from Python through the MATLAB engine for Python, which is a Python package provided by MATLAB. It is recommended to use a MATLAB version > R2022b since until MATLAB version R2022b, this Python package was not available for simple installation via pip from PyPI. Instead, it had to be installed manually from source and, therefore, it could not be added as a dependency for automatic installation (e.g., in requirements.txt or pyproject.toml). If you need instruction on how to install Simulink Gym and the MATLAB engine for Python for a version <= R2022b, see an older version of this project.

The provided Simulink blocks for connecting the Simulink model to this wrapper use the Simulink Instrument Control Toolbox. Therefore, this toolbox has to be installed in MATLAB/Simulink as a dependency.

Using uv, follow the following steps to install Simulink Gym into your environment or project.

# Install Simulink Gym into some environment:
uv pip install git+https://github.com/johbrust/simulink_gym.git
# Or add it as a dependency to a Python project:
uv add git+https://github.com/johbrust/simulink_gym.git

Extras

This package also provides example implementations using the Simulink wrapper (including example training scripts for DQN and PPO agents for the cart pole implementation in Simulink). To try them out, it is recommended to clone the repository and install from source by executing uv sync --all-extras to install the extra packages required by the examples.

Simulink Gym Block Library

Shipped with this package comes a custom Simulink block library for setting up the interface on the model side. Checkout the respective Readme for more information about setup and usage.

How to Wrap a Simulink Model

In order to use a Simulink model with this wrapper the model has to be prepared accordingly. This includes preparing the Simulink model file (.slx) to be wrapped and writing a wrapper class for the model with SimulinkEnv as its base class.

Prepare the Simulink Model File

For the communication with the wrapper the TCP/IP blocks provided by the Simulink Gym block library have to be added and setup accordingly.

Setting parameter values of the model through the wrapper can be done in two different ways, which has consequences for the model creation process. The first possibility is to directly set block parameter values through SimulinkEnv.set_block_parameter(...). The block parameters can be set to any value and changed later through the wrapper. A second way would be to define a variable in the model workspace and set the block parameter to this variable. The workspace variable then can be changed for changing the block parameter through SimulinkEnv.set_workspace_variable(...).

Note

Model workspace variables are the recommended way to make general block settings, like step sizes, available for the wrapper. For creating a model workspace variable, you can use the Model Explorer, which can be opened with CTRL + H from the Simulink model editor.

Check Model Debugging for information on how to debug the Simulink model while using this wrapper.

Preparing the Environment File

The second part of the environment definition is to create an environment class derived from the SimulinkEnv base class.

This derived class has to define the action and observation space as well as the reset(...) and step(...) methods specific for the environment.

Action and Observation Space

While the action space is defined simply by, e.g., self.action_space = gymnasium.spaces.Discrete(2), the observation space definition needs additional information about the corresponding blocks or workspace variables in the Simulink model. This is due to the fact that the wrapper needs to be able to set these values, e.g., while resetting the environment. For this, the wrapper provides the Observation and Observations classes. For an example definition of an observation space, check the cart pole example implementations in Simulink and Simscape which set initial values directly through the block parameter values (Simulink implementation) or through workspace variables (Simscape implementation).

The Observations object of the environment is a list-like object with the order of its Observation entries matching the concatenation order of the observation signals in the Simulink model (e.g., through the mux block).

Since observation values are reset after an episode, information about the corresponding blocks or workspace variable have to be provided. For block parameters, the wrapper can access these through the path of the block value which is given by the template <model name>/<subsystem 0>/.../<subsystem n>/<block name>/<parameter name> for a block buried in n subsystems.

Warning

Block parameter names don't always match the description in the block mask! Therefore, get the correct parameter name from the Simulink documentation and not from the mask!

Reset and Step Methods

The provided _reset() method is to be called in the reset() method of the derived environment class. This takes care of resetting the Simulink simulation. The derived class therefore only has to implement environment specific reset behavior like resampling of the initial state or only parts of it. Again, see the cart pole example for an example usage.

The basic stepping functionality is provided by the wrapper's sim_step(...) method which should be called in the step(...) method of the derived environment definition class (see, e.g., step(...) method of the cart pole example).

Running the Simulink Model

After everything is set up just use the defined environment like any other Gymnasium environment. See the notebook of the cart pole Simulink implementation for an example usage.

Model Debugging

For debugging the Simulink model in combination with the wrapper, the model_debug flag is provided. Set this to True in the super().__init__(...) call (example usage here) in your derived environment class and start your environment. This tells the wrapper to not start a thread with a MATLAB instance running the simulation in the background. Instead, you have to manually start the simulation model in the Simulink GUI once the environment object is instantiated and reset initially (executing state = env.reset() will cause the program to wait for the connection). You can then access the Simulink model's internal signals through the Simulink GUI for easy debugging.

The best way to stop the simulation is by executing env.stop_simulation().

End of Episode

An environment complying with the Gymnasium interface returns the terminated flag when the episode is finished. The Simulink simulation returns an empty TCP/IP message after the simulation stopped (i.e., when the simulation has run for the defined duration). But this is only sent after the last simulation step (i.e., at time t_end + 1). Therefore, the termination of the simulation can only be detected one time step after the terminal state was already reached. Keep this in mind, when using the data from the environment, since the terminal state will be present two times! As a workaround, simply drop the last data point from the trajectory!

Known Issues

The known issues below could not be fixed due to the lack of knowledge about the exact cause. Despite these known issues, there are fixes known to avoid these issues, which are given with each issue.

Important

If you encounter issues not listed below, please create a new issue or even a pull request if you also already found the fix!

  • It sometimes can be observed that after a while two sets of output data are received from the Simulink model when only one action was sent. It is assumed that this is causes by some timing issues of the TCP/IP communication in combination with the update order of the model.

    Fix: All occurrences of this issue could be mitigated by ensuring a certain block execution order of the Simulink model. There are different possibilities to achieve this:

    1. Set the priorities of the TCP/IP In and TCP/IP Out blocks to 1 and 2, respectively. Simulink then tries to come up with a block execution order according to these priorities. Unfortunately, setting these priorities does not guarantee that such a block execution order is possible.
    2. Introduce additional signals in the Simulink model to enforce a certain block execution order. E.g., add a signal of the incoming action to some (dummy) blocks close before the TCP/IP Out block.