-
-
Notifications
You must be signed in to change notification settings - Fork 40
Home
Note
Actively maintained — August 2026. Every page has been checked against the current bdsim release, code samples verified to actually run, and cross-links repaired.
This Python package enables modelling and simulation of continuous-time, discrete-time or hybrid dynamic systems. Systems are conceptualized in block diagram form, but represented in terms of Python class and method calls. Unlike Simulink or LabView we write Python code rather than drawing boxes and wires. Wires can communicate any Python type such as scalars, lists, dicts, NumPy arrays, objects, and functions.
We first sketch the dynamic system we want to simulate as a block diagram, for example this simple first-order system

which we can express concisely with bdsim as (see bdsim/examples/eg1.py
#!/usr/bin/env python3
import bdsim
sim = bdsim.BDSim() # setup run-time environment
bd = sim.blockdiagram() # create a new block diagram
# define the blocks
demand = bd.STEP(T=1, pos=(0,0), name='demand')
sum = bd.SUM('+-', pos=(1,0))
gain = bd.GAIN(10, pos=(1.5,0))
plant = bd.LTI_SISO(0.5, [2, 1], name='plant', pos=(3,0))
scope = bd.SCOPE(styles=['k', 'r--'], pos=(4,0))
# connect the blocks
bd.connect(demand, sum[0], scope[1])
bd.connect(plant, sum[1])
bd.connect(sum, gain)
bd.connect(gain, plant)
bd.connect(plant, scope[0])
bd.compile() # check the diagram
sim.report(bd) # list all blocks and wires
out = sim.run(bd, 5) # simulate for 5swhich is just 16 executable lines of code.
The red block annotations in the diagram are the names of blocks, and have become names of instances of object that represent those blocks. The blocks can also have names which are used in diagnostics and as labels in plots.
In bdsim all wires are point to point, a one-to-many connection is implemented by many wires.
Ports are designated using Python indexing and slicing notation, for example sum[0]. Whether it is an input or output port depends on context. Blocks are connected by connect(from, to_1, to_2, ...) so an index on the first argument refers to an output port, while on the second (or subsequent) arguments refers to an input port. If a port has only a single port then no index is required.
The simulation results are return in a struct-like container object
>>> out
t = ndarray:float64 (220,)
x = ndarray:float64 (220, 1)
xnames = ['plant:x_0'] (list)
which contains an array of time values, an array of state values, and a list of the names of the state variables. See Simulation results for the full picture, including watched signals and discrete/clocked state.
More details on this Wiki about:
This wiki covers tutorials, worked examples, and "under the hood" mechanics. For the full class and method API reference, see the Sphinx documentation.
Copyright (c) Peter Corke 2020-
- Home
- API reference (Sphinx)
- Block catalog
- Control Systems Magazine article
- Adding blocks to your model
- Block path
- Connecting blocks
- Compiling
- Running
- Watching a simulation variable
- Simulation results
- Runtime options
- Environment variables
- Discrete-time blocks
- Subsystems
- Figures
- Notebook animation
- Animation and movies
- PID control
- Coding patterns
- Block methods and attributes
- Time stepping: integration, animation & events
- Blocks, wires and plugs
- Graphics blocks
- Evaluation
- Runtimes and simulator state
- Creating a new block
- Related packages
Under development on feat/realtime branch, planned for release before end of 2026.