Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
02368c7
fix(docs): reduce gifs size
AlessandriniAntoine Feb 27, 2026
3b7797b
fix(gui): file path browser as posix
AlessandriniAntoine Feb 28, 2026
e95b44b
fix(gui): gui browse fallback on windows
AlessandriniAntoine Feb 28, 2026
8b2cad5
fix(sofa): sofa path resilient on windows
AlessandriniAntoine Feb 28, 2026
1047aa7
fix(core): path expander
AlessandriniAntoine Mar 1, 2026
8dfd401
fix(gui): windows quit shortcut
AlessandriniAntoine Mar 3, 2026
f63d28f
fix(gui): delete with backspace$
AlessandriniAntoine Mar 3, 2026
0ebfd2c
fix(windows): test
AlessandriniAntoine Mar 9, 2026
21f900a
fix(gui): sofa windows utf compatibility
AlessandriniAntoine Mar 9, 2026
1265d4e
fix(gui): ask to load project if current is dirty
AlessandriniAntoine Mar 10, 2026
96da0e9
fix(gui): renaming block make project dirty
AlessandriniAntoine Mar 10, 2026
b5f85ac
fix(gui): enable ctr-q on ubuntu
AlessandriniAntoine Mar 10, 2026
edd0c44
fix(gui): block dialog modification do not impact connection
AlessandriniAntoine Mar 10, 2026
41e2657
fix(docs): add windows command tutorial SOFA
AlessandriniAntoine Mar 10, 2026
b30b77c
fix(sofa): runsofa message error on keys
AlessandriniAntoine Mar 10, 2026
96fc4e2
fix(sofa): print in terminal and detect step error
AlessandriniAntoine Mar 10, 2026
f772bc1
fix(sofa): send problem to gui
AlessandriniAntoine Mar 10, 2026
f92d187
fix(sofa): send problem to gui
AlessandriniAntoine Mar 10, 2026
84dc0bb
fix(docs): adapt sofa tuto to v25.12
AlessandriniAntoine Mar 10, 2026
0e71686
fix(docs): tuto specify sofa version
AlessandriniAntoine Mar 10, 2026
fc07d25
fix(docs): one sofa scene for all version
AlessandriniAntoine Mar 11, 2026
3d94104
fix(docs): remove hardware from tuto
AlessandriniAntoine Mar 11, 2026
23c97d2
add(docs): update readme tutorial
AlessandriniAntoine Mar 11, 2026
1650e9e
fix(docs): update readme example
AlessandriniAntoine Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 50 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# pySimBlocks

A deterministic block-diagram simulation framework for discrete-time modeling, co-simulation and research prototyping in Python.
A deterministic block-diagram simulation framework for discrete-time modeling,
co-simulation and research prototyping in Python.

pySimBlocks allows you to build, configure, and execute discrete-time systems using either:
pySimBlocks allows you to build, configure, and execute discrete-time systems
using either:

- A pure Python API
- A graphical editor (PySide6)
- YAML project configuration
- Optional SOFA and hardware integration

![pySimBlocks graphical editor](./docs/User_Guide/images/gui_example.png)

## Features

- Block-based modeling (Simulink-like)
Expand Down Expand Up @@ -36,89 +40,90 @@ cd pySimBlocks
pip install .
```

## First Steps
## Getting Started

### Quick Example

The following example models a damped harmonic oscillator:

$$ \ddot{x} +0.5\dot{x} +2x = 0 $$
The following example models a simple first-order low-pass filter, defined by
the difference equation:

The continuous-time equation is implemented using explicit forward Euler
discretization through discrete integrator blocks with a fixed time step.
$$ y[k] = \alpha x[k] + (1-\alpha) y[k-1] $$

The system is assembled explicitly from discrete-time operators.
It can be implemented in pySimBlocks using the following code:

```python
from pySimBlocks import Model, Simulator, SimulationConfig, PlotConfig
from pySimBlocks.blocks.operators import Gain, Sum, DiscreteIntegrator
from pySimBlocks.blocks.operators import Gain, Sum, Delay
from pySimBlocks.blocks.sources import WhiteNoise
from pySimBlocks.project.plot_from_config import plot_from_config

# 1. Create the blocks
v = DiscreteIntegrator("v", initial_state=5)
x = DiscreteIntegrator("x", initial_state=2.)
damping = Gain(name="damping", gain=0.5)
stiffness = Gain(name="stiffness", gain=2)
sum = Sum(name="sum", signs="--")
noise = WhiteNoise(name="noise", std=1.0)
delay = Delay(name="delay")
filtered = Sum("filtered", signs="++")
alpha_gain = Gain(name="alpha", gain=0.1)
complement = Gain(name="complement", gain=0.9)

# 2. Build the model
model = Model("Example")
for block in [v, x, damping, stiffness, sum]:
for block in [noise, delay, filtered, alpha_gain, complement]:
model.add_block(block)

model.connect("v", "out", "x", "in")
model.connect("v", "out", "damping", "in")
model.connect("x", "out", "stiffness", "in")
model.connect("damping", "out", "sum", "in1")
model.connect("stiffness", "out", "sum", "in2")
model.connect("sum", "out", "v", "in")
model.connect("noise", "out", "alpha", "in")
model.connect("delay", "out", "complement", "in")
model.connect("alpha", "out", "filtered", "in1")
model.connect("complement", "out", "filtered", "in2")
model.connect("filtered", "out", "delay", "in")

# 3. Create the simulator
# 3. Simulate the model
sim_cfg = SimulationConfig(dt=0.05, T=30.)
sim = Simulator(model, sim_cfg)
logs = sim.run(logging=["noise.outputs.out", "filtered.outputs.out"])

# 4. Run the simulation
logs = sim.run(logging=[
"x.outputs.out",
"v.outputs.out",
]
)

# 5. Plot the results
# 4. Plot the results
plot_cfg = PlotConfig([
{"title": "Position and Velocity",
"signals": ["x.outputs.out", "v.outputs.out"],},
{"title": "Noisy signal vs Filtered",
"signals": ["noise.outputs.out", "filtered.outputs.out"],},
])
plot_from_config(logs, plot_cfg)
```

The simulated position and velocity exhibit the expected damped oscillatory behavior.
The resulting plot should look like this:

![Damped oscillator simulation](./docs/User_Guide/images/quick_example.png)
![Noise filter simulation](./docs/User_Guide/images/quick_example.png)

See [examples/quick_start/oscillator.py](./examples/quick_start/oscillator.py)
See [examples/quick_start/filter.py](./examples/quick_start/filter.py)
to run the example yourself.

### Graphical Editor

The same model can be constructed visually using the graphical editor:

![GUI Example](./docs/User_Guide/images/gui_example.png)
The exact same model can be constructed visually using the graphical editor (as
shown in the image above of this README).

To open the graphical editor, run:
```bash
pysimblocks examples/quick_start/gui
```

The quick-start GUI project is stored in a single
`examples/quick_start/gui/project.yaml` file.
[examples/quick_start/gui/project.yaml](./examples/quick_start/gui/project.yaml) file.

### Learning Resources

### Tutorials

See the [Getting Started Guide](./docs/User_Guide/getting_started.md) for
tutorials on building your first simulation with pySimBlocks.
#### Tutorials

### Examples
Three step-by-step tutorials are available detailed in the
[guide](./docs/User_Guide/getting_started.md):

| | Tutorial | Description |
|---|---|---|
| 1 | [Python API](./docs/User_Guide/tutorial_1_python.md) | Build a closed-loop PI control system in pure Python |
| 2 | [GUI](./docs/User_Guide/tutorial_2_gui.md) | Build the same system visually with the graphical editor |
| 3 | [SOFA](./docs/User_Guide/tutorial_3_sofa.md) | Replace the plant with a SOFA physics simulation |


#### Other Examples

A collection of basic and advanced examples is available in the
[examples](./examples) directory, including:
Expand All @@ -134,11 +139,7 @@ See [examples/README.md](./examples/README.md) for an overview.

### License

pySimBlocks is LGPL.

LGPL refers to the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
pySimBlocks is licensed under [LGPL-3.0-or-later](./LICENSE.md).

---
© 2026 Université de Lille & INRIA – Licensed under LGPL-3.0-or-later
6 changes: 3 additions & 3 deletions docs/User_Guide/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ You will learn:
- How to run a discrete-time simulation
- How to log and visualize results

[Start Tutorial 1 — Python API](tutorial_1_python.md)
-> [Start Tutorial 1 — Python API](tutorial_1_python.md)
After this tutorial: You can build and run a basic closed-loop model from code.

## 2. First Simulation (GUI)
Expand All @@ -34,7 +34,7 @@ You will learn:
- How to configure blocks and simulation settings
- How to save and export the project

[Start Tutorial 2 — GUI](tutorial_2_gui.md)
-> [Start Tutorial 2 — GUI](tutorial_2_gui.md)
After this tutorial: You can create and manage equivalent models in the GUI.

## 3. SOFA Coupling
Expand All @@ -50,5 +50,5 @@ You will learn:
- How to run co-simulations
- How to use SOFA's GUI for inspection and debugging

[Start Tutorial 3 — SOFA Coupling](tutorial_3_sofa.md)
-> [Start Tutorial 3 — SOFA Coupling](tutorial_3_sofa.md)
After this tutorial: You can run a pySimBlocks + SOFA co-simulation workflow.
Binary file modified docs/User_Guide/images/gui_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/quick_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_2-block_dialog.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_2-connections.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_2-drag_drop.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_2-plots.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_2-setting.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_3-run_psb_master.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_3-run_sofa_master.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_3-sofa_block.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/User_Guide/images/tutorial_3-sofa_gui_enhanced.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 5 additions & 6 deletions docs/User_Guide/tutorial_1_python.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def main():
start_time=0.5
)
sum = Sum(name="error", signs="+-")
pid = Pid(name="pid", controller="PI", Kp=Kp, Ki=Ki)
pid = Pid(name="PID", controller="PI", Kp=Kp, Ki=Ki)
system = LinearStateSpace(name="system", A=A, B=B, C=C, x0=x0)

# -------------------------------------------------------
Expand All @@ -86,8 +86,8 @@ def main():

model.connect("ref", "out", "error", "in1")
model.connect("system", "y", "error", "in2")
model.connect("error", "out", "pid", "e")
model.connect("pid", "u", "system", "u")
model.connect("error", "out", "PID", "e")
model.connect("PID", "u", "system", "u")

# -------------------------------------------------------
# 3. Create the simulator
Expand All @@ -102,7 +102,7 @@ def main():
# -------------------------------------------------------
logs = sim.run(logging=[
"ref.outputs.out",
"pid.outputs.u",
"PID.outputs.u",
"system.outputs.y"
]
)
Expand All @@ -111,7 +111,7 @@ def main():
# 5. Extract logged data
# -------------------------------------------------------
t = sim.get_data("time")
u = sim.get_data("pid.outputs.u").squeeze()
u = sim.get_data("PID.outputs.u").squeeze()
r = sim.get_data("ref.outputs.out").squeeze()
y = sim.get_data("system.outputs.y").squeeze()

Expand Down Expand Up @@ -187,5 +187,4 @@ This simple example is the foundation for more advanced use cases,
including:
- [GUI modeling](./tutorial_2_gui.md),
- [SOFA integration](./tutorial_3_sofa.md),
- Hardware implementation.

7 changes: 4 additions & 3 deletions docs/User_Guide/tutorial_2_gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ Modify the following parameters:
| PID | Ki | 2. |
| Sum | signs | +- |

Renaming a block updates its label in the `Diagram View`. In [Tutorial 1](./tutorial_1_python.md), the blocks were named `ref`, `error`, `pid`, and `system`.
Renaming a block updates its label in the `Diagram View`. In
[Tutorial 1](./tutorial_1_python.md), the blocks were named `ref`, `error`, `PID`, and `system`.

All required parameters must be defined before running the simulation.

Expand All @@ -104,7 +105,7 @@ All required parameters must be defined before running the simulation.
Next, configure the simulation settings:
- Sample time: 0.01 s
- Duration: 5 s
- Signals to log: system y, pid u, and ref output
- Signals to log: system y, PID u, and ref output

Click the `Settings` button in the `Toolbar`. A dialog opens with multiple panels. One is for the simulation.

Expand Down Expand Up @@ -186,4 +187,4 @@ After modifying the model, save the project and export a new `run.py` file.
Run it from the command line to verify that the exported script reproduces the same behavior.

This tutorial demonstrates how to build and execute a model visually.
The next tutorials extend this approach to [SOFA integration](./tutorial_3_sofa.md) and real-time execution.
The next tutorials extend this approach to [SOFA integration](./tutorial_3_sofa.md).
Loading
Loading