A Python library for modeling, analyzing, and simulating feedback control loops.
Project description
loopkit
A Python library for modeling, analyzing, and simulating feedback control loops, with a focus on scientific and engineering applications such as laser locking, electronics, and precision instrumentation.
🔧 What is loopkit?
loopkit provides an object-oriented framework for building and analyzing control loops as directed graphs of signal-processing components. It supports:
- Modular design of control systems using Python classes and NumPy/Scipy tools
- Construction of linear time-invariant (LTI) models via symbolic transfer functions or tabulated data
- Frequency-domain and time-domain simulation of system behavior
- Visualization and analysis of loop stability, gain, noise propagation, and performance
- Extensibility through subclassing and dynamic graph generation
The library is designed with scientific control systems in mind—especially applications in optics, interferometry, and laser metrology.
🧩 Key Features
- Component-based modeling: Each system block is a
Componentwith defined transfer functions, inputs, and outputs. - Flexible interconnection: Loops are defined by connecting components into directed graphs, not just serial chains.
- Simulation support: Built-in methods to simulate step responses, noise propagation, and open/closed-loop behavior.
- Plotting utilities: Visualize Bode plots, noise budgets, signal flow graphs, and more.
- Extensible: Easily define custom blocks, auto-generate loop structures, and integrate with hardware configurations.
📦 Installation
pip install loopkit
For block diagram generation (loop.block_diagram()), install the optional dependencies:
pip install loopkit[diagram]
Note: pytikz is required for block diagrams but is not on PyPI. Install it from the allefeld fork:
pip install git+https://github.com/allefeld/pytikz.git
🚀 Quick Start
Example
import numpy as np
import matplotlib.pyplot as plt
from loopkit.component import Component
from loopkit.components import PIControllerComponent
from loopkit.loop import LOOP
import loopkit.loopmath as lm
# Define loop parameters
sps = 80e6 # Loop update frequency in Hz
frfr = np.logspace(np.log10(1e0), np.log10(40e6), int(1e5))[:-1] # Frequency array (Hz)
# Define Plant using Laplace-domain string (auto-discretized)
w_n = 2 * np.pi * 1e6 # 10 kHz resonance
zeta = 2.0 # damping ratio
plant = Component("Plant", sps=sps, tf=f"{w_n**2} / (s**2 + {2*zeta*w_n}*s + {w_n**2})", domain='s')
# Define Sensor using z-domain string (explicit difference equation)
sensor = Component("Sensor", sps=sps, tf="(0.391 + 0.391*z**-1) / (1 - 0.218*z**-1)", domain='z')
# Compute the P-servo log2 gain from a dB value
p_log2_gain = lm.db_to_log2_gain(15)
# Compute the integrator log2 gains for a certain cross-over frequency with the P-servo
i_log2_gain = lm.gain_for_crossover_frequency(p_log2_gain, sps, 1e5, kind='I')
# Define PI controller component with those gains
controller = PIControllerComponent("Controller", sps=sps, Kp=p_log2_gain, Ki=i_log2_gain)
# Build the loop
loop = LOOP(sps, [plant, sensor, controller], name="My Loop")
ugf, margin = lm.get_margin(loop.Gf(f=frfr), frfr, deg=True, unwrap_phase=True, interpolate=True) # compute UGF and phase margin
print(f"Unity gain frequency = {ugf:.4e} Hz; Phase margin = {margin:.4f} degrees")
# Visualize block diagram
loop.block_diagram(dpi=150)
# Bode plot of open-loop gain
ax = loop.bode_plot(frfr)
ax[0].axvline(x=ugf, ls='--', c='gray')
ax[1].axvline(x=ugf, ls='--', c='gray')
plt.show()
# Nyquist plot of open-loop gain
ax = loop.nyquist_plot(frfr, which='G', logy=True, logx=True, critical_point=True)
plt.show()
🧪 Specialized Loop Implementations
The loopkit.loops subpackage provides pre-built loop models:
PLL: Digital phase-locked loop model.MokuLaserLock: Laser frequency lock model for Moku hardware (heterodyne phase-locking).NPROLaserLock: Composite model for NPRO laser frequency stabilization using PZT and temperature control loops with digital PLL (phasemeter) readout.LaserLockPZT,LaserLockTemp: Building blocks for laser lock subsystems.
from loopkit.loops import PLL, MokuLaserLock, NPROLaserLock
📚 Documentation
In-depth API documentation, tutorials, and example notebooks coming soon.
💡 Design Philosophy
- Favor explicit, graph-based design over implicit signal paths
- Maintain clarity between loop structure and numerical simulation
- Support rapid prototyping of complex control systems
- Blend symbolic (transfer function) and numeric (data-driven) components
👥 Contributing
Contributions are welcome! We are especially interested in:
- New component definitions (hardware models, filters, sensors)
- Visualization tools for loop inspection
- Test coverage and validation scripts
- Real-world loop configurations from physics labs
📜 License
BSD 3-Clause License.
🛰 Authors & Acknowledgments
Developed by Miguel Dovale, based on an initial implementation by Kohei Yamamoto.
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
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file loopkit-1.0.1.tar.gz.
File metadata
- Download URL: loopkit-1.0.1.tar.gz
- Upload date:
- Size: 137.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
abcf0039a2adf0d6116ce72f679a66b6d0c6fb47f70811360781e7efd4440f2d
|
|
| MD5 |
78dbd380526278290e064f29c70f5e6d
|
|
| BLAKE2b-256 |
74143280fb37a55d27d7d2e676eb3ab4349e3e8650b2198b07b88af3bf4f0f2e
|
File details
Details for the file loopkit-1.0.1-py3-none-any.whl.
File metadata
- Download URL: loopkit-1.0.1-py3-none-any.whl
- Upload date:
- Size: 111.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60657619692727d22efcb5ba4566cc82b126317bb011985eb9612099fbc228e1
|
|
| MD5 |
2a9a57de6bf3dfad0e192e81406ad661
|
|
| BLAKE2b-256 |
2e6eba08ed20fdd1c23abeef6e29092e0acb9162d5570a75146103c88cd8a19a
|