A Python library for discretizing continuous-time transfer functions with time delays
Project description
delay_sys
A Python library for discretizing continuous-time transfer functions with time delays.
Overview
delay_sys provides utilities for converting continuous-time control systems to discrete-time while incorporating deadtime delays. The library features a DelayTransferFunction class that extends python-control's TransferFunction to natively handle time delays.
Installation
Using pip
pip install delay_sys
Using uv (recommended for development)
git clone https://github.com/jamestjsp/delay_sys.git
cd delay_sys
uv sync
For development installation with pip:
git clone https://github.com/jamestjsp/delay_sys.git
cd delay_sys
pip install -e .
Command-line Interface
The package includes an educational demonstration showing the impact of time delays on feedback control systems:
# Run the demonstration
uv run delay_sys
This will:
- Test multiple delay scenarios (0s, 0.2s, 0.5s, 1.0s) with a first-order plant
- Show how system order increases with delay: Order = Original + (Delay/Sampling_time)
- Demonstrate stability challenges as delays increase
- Plot comprehensive analysis including:
- Step responses for different delays
- System order vs delay time
- Maximum pole magnitude vs delay
- Relationship between sampling time and delay complexity
- Provide practical guidance for controlling delayed systems
Key Learning Points:
- Large delays create very high-order discrete systems
- Stability becomes more challenging with many delay poles
- Proper sampling time selection is crucial for delayed systems
Quick Start
Using DelayTransferFunction directly
import control as ct
import delay_sys
# Create a continuous-time transfer function and discretize
G = ct.tf([1], [1, 2, 1]) # 1/(s^2 + 2s + 1)
G_discrete = ct.sample_system(G, 0.1, method='zoh')
# Create with deadtime
Gd = delay_sys.DelayTransferFunction(
num=G_discrete.num[0][0],
den=G_discrete.den[0][0],
dt=0.1,
deadtime=0.3
)
print(Gd)
Using helper functions
from delay_sys.delayed_tf import create_fopdt
# Create FOPDT system directly
Gd = create_fopdt(
gain=2.0,
time_constant=1.5,
dt=0.1,
deadtime=0.3
)
print(Gd) # Shows transfer function with deadtime info
API Reference
DelayTransferFunction
A transfer function class with explicit time delay support.
Parameters:
num(list of float): Numerator coefficients of the base transfer functionden(list of float): Denominator coefficients of the base transfer functiondt(float): The sampling period for the discrete-time system. Must be > 0deadtime(float, optional): The deadtime in the same units asdt. Defaults to 0.0
Attributes:
deadtime(float): The deadtime value used in the system
Examples
Basic Discretization
import control as ct
import delay_sys
# First-order system
G = ct.tf([2], [1, 3])
G_discrete = ct.sample_system(G, 0.1, method='zoh')
Gd = delay_sys.DelayTransferFunction(
G_discrete.num[0][0],
G_discrete.den[0][0],
dt=0.1
)
print(f"Deadtime: {Gd.deadtime}") # Access deadtime attribute
With Deadtime
# Add 0.5 second deadtime
Gd_delayed = delay_sys.DelayTransferFunction(
G_discrete.num[0][0],
G_discrete.den[0][0],
dt=0.1,
deadtime=0.5
)
print(Gd_delayed) # Shows transfer function with deadtime info
Different Discretization Methods
# Using Tustin (bilinear) method
G_tustin = ct.sample_system(G, 0.1, method='tustin')
Gd_tustin = delay_sys.DelayTransferFunction(
G_tustin.num[0][0],
G_tustin.den[0][0],
dt=0.1
)
Direct DelayTransferFunction Usage
# Create a first-order system with deadtime
fopdt_system = delay_sys.DelayTransferFunction(
num=[0.095], # Numerator coefficients
den=[1, -0.819], # Denominator coefficients
dt=0.1, # 100ms sampling
deadtime=0.25 # 250ms deadtime
)
# Use with python-control functions
t, y = ct.step_response(fopdt_system)
System Analysis
import numpy as np
import matplotlib.pyplot as plt
from delay_sys.delayed_tf import create_fopdt
# Create system with deadtime using helper
Gd = create_fopdt(gain=1.0, time_constant=1.0, dt=0.1, deadtime=0.3)
# Analyze stability margins
gm, pm, wg, wp = ct.margin(Gd)
print(f"Gain margin: {gm:.2f}, Phase margin: {pm:.2f}°")
# Plot frequency response
w = np.logspace(-2, 2, 100)
ct.bode(Gd, w)
plt.show()
PID Control with Delayed Systems
import control as ct
from delay_sys.delayed_tf import create_fopdt
# Create delayed plant
plant = create_fopdt(gain=2.0, time_constant=1.5, dt=0.1, deadtime=0.3)
# Create PID controller (discrete form)
# PID: Kp + Ki*dt/(z-1) + Kd*(z-1)/z
Kp, Ki, Kd = 1.0, 0.2, 0.05
pid_num = [Kp + Ki*0.1 + Kd, -Kp + Ki*0.1 - 2*Kd, Kd]
pid_den = [1, -1, 0]
pid = ct.tf(pid_num, pid_den, dt=0.1)
# Create closed-loop system
open_loop = pid * plant
closed_loop = ct.feedback(open_loop, 1)
# Analyze performance
t, y = ct.step_response(closed_loop)
gm, pm, _, _ = ct.margin(open_loop)
print(f"Gain margin: {gm:.2f}, Phase margin: {pm:.1f}°")
Features
- Native deadtime support:
DelayTransferFunctionclass with explicit deadtime handling - Multiple discretization methods: Supports all methods available in
python-control - Integer delay implementation: Automatically rounds deadtime to nearest integer number of samples
- Full compatibility: Works seamlessly with
python-controlfunctions - Input validation: Comprehensive error checking for invalid parameters
- SISO systems: Currently supports Single-Input Single-Output systems
Requirements
- Python >= 3.8
- control >= 0.9.0
- numpy >= 1.19.0
- matplotlib >= 3.0.0
Development
Using uv (recommended)
# Clone and setup
git clone https://github.com/jamestjsp/delay_sys.git
cd delay_sys
# Install dependencies and run
uv run delay_sys
# Run tests
uv run pytest
# Create FOPDT system example
uv run python -c "from delay_sys.delayed_tf import create_fopdt; sys = create_fopdt(gain=2.0, time_constant=1.0, dt=0.1, deadtime=0.3); print(sys)"
Traditional development setup
pip install -e .
python -m delay_sys
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Delay Considerations
When working with systems that have significant delays:
Sampling Time Selection
# For a system with 1s delay:
# Too fine sampling (1ms) → 1000 delay poles → Very high order
# Better sampling (50ms) → 20 delay poles → Manageable
delay = 1.0 # seconds
dt_fine = 0.001 # 1000 poles!
dt_good = 0.05 # 20 poles - much better
Managing Large Delays
from delay_sys.delayed_tf import create_fopdt
# Example: Plant with significant delay
plant = create_fopdt(gain=1.0, time_constant=2.0, dt=0.1, deadtime=1.5)
# Check system complexity
delay_samples = int(1.5 / 0.1) # 15 additional poles
print(f"System has {delay_samples} delay poles")
# For very large delays, consider:
# - Smith Predictor control
# - Model Predictive Control (MPC)
# - Feedforward compensation
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 delay_sys-0.1.1.tar.gz.
File metadata
- Download URL: delay_sys-0.1.1.tar.gz
- Upload date:
- Size: 167.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04f648eb4af41f96b1a13a6144635ad82ea448676e2f7a29a24fa731dc0e3b10
|
|
| MD5 |
0dd8fe1b91f3a9c028a7e3ca15b67477
|
|
| BLAKE2b-256 |
01d94c9333b3f83e5e5d99a66d4b8ecdbf038241402ca157b221478a00797cfe
|
Provenance
The following attestation bundles were made for delay_sys-0.1.1.tar.gz:
Publisher:
python-publish.yml on jamestjsp/delay_sys
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delay_sys-0.1.1.tar.gz -
Subject digest:
04f648eb4af41f96b1a13a6144635ad82ea448676e2f7a29a24fa731dc0e3b10 - Sigstore transparency entry: 255752734
- Sigstore integration time:
-
Permalink:
jamestjsp/delay_sys@75af386deaf12541b3bd5d563ec916d6d12cd73e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/jamestjsp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@75af386deaf12541b3bd5d563ec916d6d12cd73e -
Trigger Event:
release
-
Statement type:
File details
Details for the file delay_sys-0.1.1-py3-none-any.whl.
File metadata
- Download URL: delay_sys-0.1.1-py3-none-any.whl
- Upload date:
- Size: 8.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1627ead2a0e8b689c7bc5fb6853041550879bb001b0c5b816b3d89c8f04ae76c
|
|
| MD5 |
e3c88b17548c48ecfc9c40a3f7852983
|
|
| BLAKE2b-256 |
8268a0da130ecc5881c0291b74bac2d0c458c95c6c0a9cc765d457b446899b64
|
Provenance
The following attestation bundles were made for delay_sys-0.1.1-py3-none-any.whl:
Publisher:
python-publish.yml on jamestjsp/delay_sys
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delay_sys-0.1.1-py3-none-any.whl -
Subject digest:
1627ead2a0e8b689c7bc5fb6853041550879bb001b0c5b816b3d89c8f04ae76c - Sigstore transparency entry: 255752739
- Sigstore integration time:
-
Permalink:
jamestjsp/delay_sys@75af386deaf12541b3bd5d563ec916d6d12cd73e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/jamestjsp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@75af386deaf12541b3bd5d563ec916d6d12cd73e -
Trigger Event:
release
-
Statement type: