Skip to content

Commit

Permalink
add linear system in state space representation
Browse files Browse the repository at this point in the history
  • Loading branch information
Dany-L committed May 10, 2023
1 parent 5ee9ea6 commit b4841fa
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 537 deletions.
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Numeric Simulator for state space models
Simulating ordinary differential equations that have a state space representation with external input. For the integration of continuous systems `scipy.integrate.solve_ivp` is used. Discrete systems are iterated with a fixed step size.
It simulates ordinary differential equations with a state space representation and external input. `scipy.integrate.solve_ivp` integrates continuous systems. Discrete systems are iterated with a fixed step size.

State space description, given an initial condition $x(0)$ and a fixed time horizon $T$, for discrete system with a step size $\eta$

Expand Down Expand Up @@ -37,13 +37,13 @@ $$\dot{x}(t) = f(x(t), u(t)),\qquad y(t) = g(x(t), u(t))$$
```

## Simulation
The continuous simulator will evaluate the function depending on the method chosen, the input sequence is always discrete with a constant distance of `step_size`. The output sequence has the same step size as the input sequence, therefore the result of the continuous integrator is resampled (or evaluated) to the `step_size`.
The continuous simulator will evaluate the function depending on the method chosen. The input sequence is always discrete with a constant distance of `step_size`. The output sequence has the same step size as the input sequence. Therefore the result of the continuous integrator is resampled (or evaluated) to the `step_size`.

## Systems
The systems that can be simulated with `statesim` are described by nonlinear differential equations. Each system has a nonlinear symbolic expression that can be used for simulation and is considered to be the ground truth data. From the symbolic nonlinear expressions, linearizations can be derived and evaluated at an equilibrium point with `SymPy`.
The systems that can be simulated with `statesim` are described by nonlinear differential equations. Each system has a nonlinear symbolic expression that can be used for simulation and is considered the ground truth data. From the nonlinear symbolic expressions, linearizations can be derived and evaluated at an equilibrium point with `SymPy`.

Currently, the following systems are implemented
- **CartPole**: Zero input, the initial state is around the upright position of the pole (Barto AG, Sutton RS, Anderson CW. Neuronlike adaptive elements that can solve difficult learning control problems. IEEE transactions on systems, man, and cybernetics. 1983 Sep(5):834-46.)
- **CartPole**: Zero input, the initial state is around the upright position of the pole (Barto AG, Sutton RS, Anderson CW. Neuronlike adaptive elements that can solve difficult learning control problems. IEEE Transactions on systems, man, and cybernetics. 1983 Sep(5):834-46.)
![Cartpole](/img/state_cartpole.png)

- **Coupled mass spring damper system**: states of 4 coupled masses
Expand All @@ -52,15 +52,19 @@ Currently, the following systems are implemented
- **Inverted pendulum with torque input**: Zero input, the initial state is around the upright position of the pole
![Pendulum](/img/state_pend.png)


## Input sequences
Random input sequences can be generated to excite the system.

Currently, the following types of input generation are implemented:
- **Random Static**: Static inputs that jump to another static value after a random time from a given interval.
![input sequence](/img/input.png)

## Generate data for a continuous linear system
To generate a dataset for a continuous linear system, you can use the script `run_datagen_linear.py`. This will use the matrices defined in `config/linear.json`. The following output is generated for a double integrator with Gaussian measurement noise. The input sequence is a random static sequence in the range $u \in [-1, 1]$
![output double integrator](/img/output_linear.png)

## Example
In `scripts/notebooks` some examples of how to use `statesim` are shown in *jupyter notebooks*. To generate `.csv` files from the simulations the script `scripts/run_cartpole_datagen.py` shows this for the cartpole example. The configuration can also be external as a `.json` file with the fields:
In `scripts/notebooks`, some examples of how to use `statesim` are shown in *jupyter notebooks*. To generate `.csv` files from the simulations, the script `scripts/run_cartpole_datagen.py` offers this for the cart pole example. The configuration can also be external as a `.json` file with the fields:
```json
{
"result_directory": "str, Directory where the .csv files will be stored, must exist",
Expand All @@ -69,8 +73,9 @@ In `scripts/notebooks` some examples of how to use `statesim` are shown in *jupy
"K": "int, Number of samples",
"T": "float, Simulation end time start is always 0, e.g. [0, T]",
"step_size": "float, step between two measurements",
"input": "InputConfig, configuration for generating random input sequences",
"input": "Optional: InputGeneratorConfig, configuration for generating random input sequences, if not defined there will be no input",
"system": "SystemConfig, specific configuration for the system to be simulated",
"simulator": "SimulatorConfig, configuration for the simulator, requires an initial state"
"measurement_noise": "Optional: NoiseConfig, configuration for measurement noise, if not defined there will be no measurement noise"
}
```
15 changes: 8 additions & 7 deletions config/msd.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@
"base_name": "initial_state-0",
"seed": 2023,
"K": 200,
"T": 300,
"step_size": 0.05,
"input": {
"T": 1000,
"step_size": 0.5,
"input_generator": {
"type": "random_static_input",
"u_max": 0.2,
"u_min": -0.2,
"interval_min": 20,
"interval_max": 100
"interval_min": 10,
"interval_max": 40
},
"system": {
"name": "CoupledMsd",
"nx": 8,
"ny": 1,
"nu": 1,
"C": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
"C": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]],
"xbar": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"ubar": [0.0],
"N": 4,
Expand All @@ -28,6 +29,6 @@
"measurement_noise": {
"type": "gaussian",
"mean": 0.0,
"std": 0.02
"std": 0.03
}
}
2 changes: 1 addition & 1 deletion scripts/create_train_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

config = SplitConfig.parse_obj(
{
'raw_data_directory': '/Users/jack/pendulum/initial_state_0_K-100_T-20/raw',
'raw_data_directory': '/Users/jack/mass-spring-damper/initial_state-0_K-200_T-1000/raw',
'train_split': 0.6,
'validation_split': 0.1,
'seed': 2023,
Expand Down
8 changes: 4 additions & 4 deletions scripts/notebooks/input_sequence.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"metadata": {},
"outputs": [],
"source": [
"from statesim.generate.input import generate_random_static_input\n",
"from statesim.generate.input import random_static_input\n",
"import numpy as np\n",
"from statesim.simulator import ContinuousSimulator\n",
"from statesim.model.statespace import Nonlinear\n",
Expand All @@ -39,7 +39,7 @@
"T = 20\n",
"eta = 0.01\n",
"N = int(T / eta)\n",
"us = generate_random_static_input(\n",
"us = random_static_input(\n",
" N=N, nu=1, amplitude_range=(-10.0, 10.0), frequency_range=(50, 100)\n",
")\n",
"us = [np.array([[u]]) for u in np.zeros(N)]"
Expand Down Expand Up @@ -118,7 +118,7 @@
}
],
"source": [
"us = generate_random_static_input(\n",
"us = random_static_input(\n",
" N=N, nu=1, amplitude_range=(-1, 1), frequency_range=(50, 100)\n",
")\n",
"# us = [np.array([[u]]) for u in np.zeros(N)]\n",
Expand Down Expand Up @@ -192,7 +192,7 @@
"T = 200\n",
"eta = 0.05\n",
"N = int(T / eta)\n",
"us = generate_random_static_input(\n",
"us = random_static_input(\n",
" N=N, nu=1, amplitude_range=(-2, 2), frequency_range=(50, 150)\n",
")\n",
"# us = [np.array([[u]]) for u in np.zeros(N)]\n",
Expand Down
4 changes: 2 additions & 2 deletions scripts/notebooks/l2_stability.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"import cvxpy as cp\n",
"import numpy as np\n",
"from statesim.system.cartpole import CartPole\n",
"from statesim.generate.input import generate_random_static_input\n",
"from statesim.generate.input import random_static_input\n",
"from statesim.system.inverted_pendulum import InvertedPendulum\n",
"from statesim.system.coupled_msd import CoupledMsd\n",
"from statesim.model.statespace import Lure\n",
Expand Down Expand Up @@ -691,7 +691,7 @@
" step_size=step_size,\n",
")\n",
"u = [np.array([[u]]) for u in np.zeros(shape=(N, 1))]\n",
"u = generate_random_static_input(\n",
"u = random_static_input(\n",
" N=N, nu=1, amplitude_range=(-10.0, 10.0), frequency_range=(50, 150)\n",
")\n",
"x0 = np.zeros(shape=(2 * nx, 1))\n",
Expand Down
43 changes: 17 additions & 26 deletions scripts/notebooks/linearization.ipynb

Large diffs are not rendered by default.

138 changes: 0 additions & 138 deletions scripts/run_datagen.py

This file was deleted.

37 changes: 27 additions & 10 deletions src/statesim/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from typing import Optional, List, Dict, Union, Literal


CSV_FILE_NAME = 'simulation'


class SplitConfig(BaseModel):
raw_data_directory: str
train_split: float
Expand All @@ -11,26 +14,38 @@ class SplitConfig(BaseModel):
split_filenames: Optional[Dict]


class InputConfig(BaseModel):
class InputGeneratorConfig(BaseModel):
type: str
u_min: float
u_max: float
interval_min: float
interval_max: float


class SystemConfig(BaseModel):
class LinearSystemConfig(BaseModel):
name: str
A: List[List[float]]
B: List[List[float]]
C: List[List[float]]
D: List[List[float]]
nx: Optional[int]
ny: Optional[int]
nu: Optional[int]


class NonlinearSystemConfig(BaseModel):
name: str
A: Optional[List]
B: Optional[List]
C: List[float]
A: Optional[List[List[float]]]
B: Optional[List[List[float]]]
C: List[List[float]]
xbar: List[float]
ubar: List[float]
nx: int
ny: int
nu: int


class CartPoleConfig(SystemConfig):
class CartPoleConfig(NonlinearSystemConfig):
g: float
m_c: float
m_p: float
Expand All @@ -39,14 +54,14 @@ class CartPoleConfig(SystemConfig):
mu_p: float


class PendulumConfig(SystemConfig):
class PendulumConfig(NonlinearSystemConfig):
g: float
m_p: float
length: float
mu_p: float


class CoupledMsdConfig(SystemConfig):
class CoupledMsdConfig(NonlinearSystemConfig):
N: int
k: List[float]
c: List[float]
Expand All @@ -71,7 +86,9 @@ class GenerateConfig(BaseModel):
K: int
T: float
step_size: float
input: InputConfig
system: Union[CartPoleConfig, PendulumConfig, CoupledMsdConfig]
input_generator: Optional[InputGeneratorConfig]
system: Union[
LinearSystemConfig, CartPoleConfig, PendulumConfig, CoupledMsdConfig
]
simulator: Optional[SimulatorConfig]
measurement_noise: Optional[NoiseConfig]
Loading

0 comments on commit b4841fa

Please sign in to comment.