Skip to main content

Fym: An object-oriented simulator for dynamic systems

Project description

Fym

Fym is a general perpose dynamical simulator based on Python. The origin of Fym is a flight simulator that requires highly accurate integration (e.g. Runge-Kutta-Fehlberg method or simply rk45), and a set of components that interact each other. For the integration, Fym supports various Scipy integration methods in addition with own fixed-step solver such as rk4. Also, Fym has a novel structure that provides modular design of each component of systems, which is much simiar to Simulink.

The Fym project began with the development of accurate flight simulators that aerospace engineers could use with OpenAI Gym to study reinforcement learning. This is why the package name is Fym (Flight + Gym). Although it is now a general purpose dynamical simulator, many codes and ideas have been devised in the OpenAI Gym.

For more information, see:

Installation

There are two ways to install Fym.

Manual installation (recommended)

As Fym is the ongoing project, many changes are expected in the short time. We periodically upload stable versions to PyPi, but if you want to use the latest features of Fym development, we recommend installing Fym manually, as follows.

git clone https://github.com/fdcl-nrf/fym.git
cd fym
pip install -e .

Note that master branch contains all the latest features that can be used immediately as the default development branch.

Install with PyPi

If you want to install the most stable version of Fym uploaded in PyPi, you can do it.

pip install fym

Basic Usage

Simulation template

The basic usage of Fym is very similar to Simulink (conceptual only, of course). A simulation is executed through a following basic template.

env = Env()
env.reset()

while True:
    done = env.step()
    if done:
        break

env.close()

As you can see, this is the legacy of the OpenAI Gym.

Env is like a Blank Model in Simulink. Every setup including dynamics, simulation time-step, final time, integration method should be defined in this main class. The Env class is initialized with the following structure.

from fym.core import BaseEnv, BaseSystem

class Env(BaseEnv):
    def __init__(self):
        super().__init__(dt=0.01, max_t=10)

The arguments of super().__init__ establishes the integration method consisting of a time step (dt), a final time (max_t), etc.

Registration of BaseSystem

Now, you can add dynamical systems as follows. From now on, dynamical system means a system that requires an integration in the simulation, denoted by BaseSystem.

import numpy as np

class Env(BaseEnv):
    def __init__(self):
        super().__init__(dt=0.01, max_t=10)
        self.plant = BaseSystem(shape=(3, 2))
        self.load = BaseSystem(np.vstack((0, 0, -1)))
        self.actuator = BaseSystem()

There are three ways to initalize BaseSystem to the main Blank Model, Env. The first way is give it a shape as BaseSystem(shape=(3, 2)). This initializes the system with a numpy zeros with a shape (3, 2). Another way is directly give it an initial state as BaseSystem(np.vstack((0, 0, -1))). Finally, it can be initialized without any argument, where it's default initial state is a numpy zeros with a shape (1, 1).

States of BaseSystem

Because BaseSystem is a dynamical system, it has a state. The state is initialized with the registration of the instance in BaseEnv.__init__ method. It is basically a list or a numpy array with any shape. After the registration, states of each BaseSystem can be accessed in anywhere, with BaseSystem.state variable.

env = Env()
print(env.plant.state)

Setup the dynamics in BaseEnv.set_dot

Every dynamcal systems, i.e., BaseSystem, has its own dynamics, or a derivative. For example, there might be a stable linear system: ẋ = - x. Since we've define an initial value in BaseEnv.__init__ method, only defining the derivative completes the ordinary differential equation, as it is an initial value problem. All the derivatives should be defined in BaseEnv.set_dot method by simply assiging the derivative to BaseSystem.dot variable.

class Env(BaseEnv):
    """..."""
    def step(self, **action):
        *_, done = self.update(**action)
        return done

    def set_dot(self, t, **action):
        self.plant.dot = - self.plant.state
        """self.load.dot, self.actuator.dot, ... """

The method BaseEnv.step defines how the BaseEnv communicates with outer world as in the simulation template. The input and output is free to define, but there must be a self.update method, which will actually perform the integration. Fortunately, you don't need to define BaseEnv.update method. Everything complicated, such as integration, is done automatically by the Fym module.

Define the interaction APIs for BaseEnv

How Numerical Integration Works

The most important thing that you must be aware of is that how the numerical integration works. Fym implements a continuous-time ODE solver. Inside the BaseEnv.set_dot method, time t and BaseSystem.state's are varying continuously. Hence, if you want to design a continuous-time feedback controller, you must define it inside the BaseEnv.set_dot method.

class Env(BaseEnv):
    """..."""
    def set_dot(self, t):
        x = self.plant.state
        u = - K @ x  # a linear feedback input
        r = t > 1  # a Heviside function
        self.plant.dot = A @ x + B @ u + r

Define the BaseEnv.step method with ZoH inputs

There is another type of input that requires the zero-order hold (ZoH) which is useful for command signals or agent actions in reinforcement learning (of course, the command signal can be performed within the BaseEnv.set_dot method using a function of time). To conform to reinforcement learning practice (e.g., OpenAI Gym), one can define a step method and call update method with keyword arguments which are the ZoH inputs.

class Env(BaseEnv):
    """..."""
    def set_dot(self, t, action):
        """..."""

    def step(self, action):
        *_, done = self.update(action=action)
        """Construct next_obs, action, done, info etc."""
        return next_obs, action, done, info

In the above example, the key of ZoH input is action, and it must be set to an argument of set_dot method with the same key name. The update method returns three object: t_hist, ode_hist and done, although only the last done is useful for typical situations.

update method is actually do the numerical integration internally. Hence, after calling update, every states contained in the BaseEnv will be updated.

For the simulations that do not require the ZoH inputs, just call update and set_dot only with time t, like this:

class Env(BaseEnv):
    """..."""
    def set_dot(self, t):
        """..."""

    def step(self):
        *_, done = self.update()
        return done

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fym-1.3.0.tar.gz (33.8 kB view details)

Uploaded Source

Built Distribution

fym-1.3.0-py3-none-any.whl (36.5 kB view details)

Uploaded Python 3

File details

Details for the file fym-1.3.0.tar.gz.

File metadata

  • Download URL: fym-1.3.0.tar.gz
  • Upload date:
  • Size: 33.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/6.0.0 pkginfo/1.9.6 requests/2.29.0 requests-toolbelt/0.9.1 tqdm/4.65.0 CPython/3.11.4

File hashes

Hashes for fym-1.3.0.tar.gz
Algorithm Hash digest
SHA256 bb343578be5e2fe7b3d767058725cbaab39bc3b05a7ce7335cf56aacac8867ce
MD5 e16490586cb01abfbf948deaed4fc5ed
BLAKE2b-256 7dcfdc67e6df2eee2a0cbcf7f4088b78f4862a94da245c9489fab6f93ecec7d7

See more details on using hashes here.

File details

Details for the file fym-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: fym-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 36.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/6.0.0 pkginfo/1.9.6 requests/2.29.0 requests-toolbelt/0.9.1 tqdm/4.65.0 CPython/3.11.4

File hashes

Hashes for fym-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 349cef1319a77f226f27fc9e6f3b0a4ab4242f5d18982c0c9bdcbc2ee241f107
MD5 918a1473523ba594379ed6cbb1570cc1
BLAKE2b-256 ea23b9b12d0aaf0f462db5373d39a6359ef4086cff73936076f2a31ada790e1a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page