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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | bb343578be5e2fe7b3d767058725cbaab39bc3b05a7ce7335cf56aacac8867ce |
|
MD5 | e16490586cb01abfbf948deaed4fc5ed |
|
BLAKE2b-256 | 7dcfdc67e6df2eee2a0cbcf7f4088b78f4862a94da245c9489fab6f93ecec7d7 |
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | 349cef1319a77f226f27fc9e6f3b0a4ab4242f5d18982c0c9bdcbc2ee241f107 |
|
MD5 | 918a1473523ba594379ed6cbb1570cc1 |
|
BLAKE2b-256 | ea23b9b12d0aaf0f462db5373d39a6359ef4086cff73936076f2a31ada790e1a |