Skip to content

Commit cd7741c

Browse files
authored
Merge pull request #905 from JuliaControl/thiran
add Thiran approximation of fractional-delay systems
2 parents 70dd235 + 5e27355 commit cd7741c

File tree

7 files changed

+55
-13
lines changed

7 files changed

+55
-13
lines changed

docs/make.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ makedocs(modules=[ControlSystems, ControlSystemsBase],
2222
format = Documenter.HTML(prettyurls = haskey(ENV, "CI")),
2323
sitename="ControlSystems.jl",
2424
pagesonly = true,
25-
draft = false,
25+
draft = true,
2626
pages=[
2727
"Home" => "index.md",
2828
"Introductory guide" => Any[
@@ -39,7 +39,7 @@ makedocs(modules=[ControlSystems, ControlSystemsBase],
3939
"Properties of delay systems" => "examples/delay_systems.md",
4040
"Automatic differentiation" => "examples/automatic_differentiation.md",
4141
"Tune a controller using experimental data" => "examples/tuning_from_data.md",
42-
"Analysis of hybrid continuous/discrete systems" => "examples/zoh.md",
42+
"Analysis of sampled-data (continuous/discrete) systems" => "examples/zoh.md",
4343
],
4444
"Functions" => Any[
4545
"Constructors" => "lib/constructors.md",

docs/src/examples/delay_systems.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ For a more advanced example using time delays, see the [Smith predictor](@ref) t
3232
Time-delay systems are numerically challenging to simulate, if you run into problems, please open an issue with a reproducing example. The [`lsim`](@ref), [`step`](@ref) and [`impulse`](@ref) functions accept keyword arguments that are passed along to the ODE integrator, this can be used to both select integration method and to tweak the integrator options. The documentation for solving delay-differential equations is available [here](https://diffeq.sciml.ai/latest/types/dde_types/) and [here](https://diffeq.sciml.ai/latest/tutorials/dde_example/).
3333

3434
## Estimation of delay
35-
See the companion tutorial in ControlSystemIdentification.jl on [Delay estimation](file:///home/fredrikb/.julia/dev/ControlSystemIdentification/docs/build/examples/delayest.html). This tutorial covers the both the detection of the presence of a delay, and estimation of models for systems with delays.
35+
See the companion tutorial in ControlSystemIdentification.jl on [Delay estimation](file:///home/fredrikb/.julia/dev/ControlSystemIdentification/docs/build/examples/delayest.html). This tutorial covers the both the detection of the presence of a delay, and estimation of models for systems with delays.
36+
37+
## Approximation and discretization of delays
38+
Delay systems may be approximated as rational functions by means of [Padé approximation](https://en.wikipedia.org/wiki/Pad%C3%A9_approximant) using the function [`pade`](@ref). Pure continuous-time delays can also be discretized using the function [`thiran`](@ref). Continuous-time models with internal delays can be discretized using [`c2d`](@ref), provided that the delay is an integer multiple of the sampling time (fractional delays are not yet supported by [`c2d`](@ref)).

docs/src/examples/zoh.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Next, we discretize this system using the standard [`c2d`](@ref) function, which
1616
```math
1717
Z(s) = \dfrac{1 - e^{-T_s s}}{s}
1818
```
19+
[^CCS]: Åström, K. J., & Wittenmark, B. (1997). Computer-Controlled Systems: Theory and Design.
1920

2021
```@example zoh
2122
Ts = 1 # Sample interval
@@ -113,7 +114,7 @@ The discrete-time controller ``C(z)`` is a basic PI controller
113114
Ts = 1
114115
C = pid(0.01,10; Ts, Tf = 1/100, state_space=true)
115116
```
116-
If we choose option 1. in this case, we get a discretized plant that has a very poor frequency-response fit to the original continuous-time plant, making frequency-domain analysis difficult
117+
If we choose option 1, we get a discretized plant that has a very poor frequency-response fit to the original continuous-time plant, making frequency-domain analysis difficult
117118
```@example zoh
118119
Pd = c2d(P, Ts)
119120
Ld = Pd*C
@@ -146,13 +147,11 @@ plot(lsim(PS, disturbance, 0:0.22:3500), lab="Continuous disturbance response")
146147
plot!(lsim(PSd, disturbance, 3500), lab="Discrete disturbance response")
147148
hline!([abs(freqresp(PS, ω)[])], l=(:black, :dash), lab="Predicted freq. response amplitude", legend=:bottomleft, fmt=:png)
148149
```
149-
The continuous-time analysis eventually settles at a periodic output that matches the amplitude predicted by the continuous-time frequency response. However, the discrete-time simulation yields, as expected, a very poor result. Noticeable in the simulation is the appearance of a beat frequency, which is expected due to the modulation introduced by sampling.[^CCS]
150+
The continuous-time analysis eventually settles at a periodic output that matches the amplitude predicted by the continuous-time frequency response. However, the discrete-time simulation yields, as expected, a very poor result. Noticeable in the simulation is the appearance of a beat frequency, which is expected due to the modulation introduced by sampling. [^CCS]
150151

151152
## Caveats
152153
- The exact output of the system that was translated from discrete to continuous time is not going to be accurate, so transient properties of the hybrid system cannot be accurately assessed using this kind of analysis.
153-
- Interpretation of frequency-responses for sampled-data systems must be done with care. The modulation introduced by sampling implies the creating of additional frequencies in the output. For an input with frequency ``\omega``, the output will contain all frequencies ``\omega ± \omega_s k`` where ``\omega_s`` is the sampling frequency and ``k`` is an integer.[^CCS]
154+
- Interpretation of frequency-responses for sampled-data systems must be done with care. The modulation introduced by sampling implies the creating of additional frequencies in the output. For an input with frequency ``\omega``, the output will contain all frequencies ``\omega ± \omega_s k`` where ``\omega_s`` is the sampling frequency and ``k`` is an integer. [^CCS]
154155

155156
## References
156157
Learn more about sampled-data systems and zero-order hold sampling in chapter 7 of the reference below.
157-
[^CCS]: Åström, K. J., & Wittenmark, B. (1997). Computer-Controlled Systems: Theory and Design.
158-

docs/src/man/creating_systems.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ Sample Time: 0.1 (seconds)
123123
Discrete-time transfer function model
124124
```
125125

126+
127+
## Converting between continuous and discrete time
128+
A continuous-time system represents differential equations or a transfer function in the [Laplace domain](https://en.wikipedia.org/wiki/Laplace_transform), while a discrete-time system represents difference equations or a transfer function in the [Z-domain](https://en.wikipedia.org/wiki/Z-transform).
129+
130+
The functions [`c2d`](@ref) and [`d2c`](@ref) implement sampling/discretization of continuous-time systems and the inverse mapping from discrete-time to continuous-time systems.
131+
126132
## Delay Systems
127133
The constructor [`delay`](@ref) creates a pure delay, which may be connected to a system by multiplication:
128134
```julia
@@ -138,7 +144,7 @@ L = 1.2 # Delay time
138144
tf(1, [1, 1]) * exp(-L*s)
139145
```
140146

141-
Padé approximations of delays can be created using [`pade`](@ref).
147+
Padé approximations of delays can be created using [`pade`](@ref). Models with delays can be discretized using [`c2d`](@ref), currently, only delays that are integer multiples of the sample time are supported. Pure fractional delays can be approximately discretized using the function [`thiran`](@ref).
142148

143149
A tutorial on delay systems is available here:
144150
```@raw html

lib/ControlSystemsBase/src/ControlSystemsBase.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export LTISystem,
104104
# delay systems
105105
delay,
106106
pade,
107+
thiran,
107108
nonlinearity,
108109
# demo systems
109110
ssrand,

lib/ControlSystemsBase/src/delay_systems.jl

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,16 @@ end
3838

3939
"""
4040
delayd_ss(τ, Ts)
41-
Discrete-time statespace realization of a delay τ sampled with period Ts,
42-
i.e. of z^-N where N = τ / Ts.
4341
44-
τ must be a multiple of Ts.
42+
Discrete-time statespace realization of a delay ``τ`` sampled with period ``T_s``,
43+
i.e. of ``z^{-N}`` where ``N = τ / T_s.``
44+
45+
``τ`` must be a multiple of ``T_s``. See [`thiran`](@ref) for approximate discretization of fractional delays.
4546
"""
4647
function delayd_ss::Number, Ts::Number)
4748
n = round(Int, τ / Ts)
4849
if !- n*Ts 0)
49-
error("The delay τ must be a multiple of the sample time Ts")
50+
error("The delay τ must be a multiple of the sample time Ts, use the function `thiran` to approximately discretize fractional delays.")
5051
end
5152
ss(diagm(1 => ones(n-1)), [zeros(n-1,1); 1], [1 zeros(1,n-1)], 0, Ts)
5253
end
@@ -128,6 +129,8 @@ const PADE_Q_COEFFS = [[2, 1],
128129
pade(τ::Real, N::Int)
129130
130131
Compute the `N`th order Padé approximation of a time-delay of length `τ`.
132+
133+
See also [`thiran`](@ref) for discretization of delays.
131134
"""
132135
function pade::Real, N::Int)
133136
if !(1 <= N <= 10); error("Order of Padé approximation must be between 1 and 10. Got $N."); end
@@ -149,3 +152,25 @@ function pade(G::DelayLtiSystem, N)
149152
X = append(ss(pade(τ,N)) for τ in G.Tau) # Perhaps append should be renamed blockdiag
150153
return lft(G.P.P, X)
151154
end
155+
156+
"""
157+
thiran(τ::Real, Ts)
158+
159+
Discretize a potentially fractional delay ``τ`` as a Thiran all-pass filter with sample time `Ts`.
160+
161+
The Thiran all-pass filter gives an a maximally flat group delay.
162+
163+
If ``τ`` is an integer multiple of ``Ts``, the Thiran all-pass filter reduces to ``z^{-τ/Ts}``.
164+
165+
Ref: T. I. Laakso, V. Valimaki, M. Karjalainen and U. K. Laine, "Splitting the unit delay [FIR/all pass filters design]," in IEEE Signal Processing Magazine, vol. 13, no. 1, 1996.
166+
"""
167+
function thiran::Real, Ts)
168+
D = τ/Ts
169+
N = ceil(Int, D)
170+
a = ones(N+1)
171+
for k = 1:N
172+
P = prod((D-N+n) / (D-N+n+k) for n in 0:N)
173+
a[k+1] = (-1)^k * binomial(N, k) * P # Eq 86 in reference
174+
end
175+
tf(reverse(a), a, Ts)
176+
end

lib/ControlSystemsBase/test/test_delayed_systems.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ P_wb = DemoSystems.woodberry()
229229

230230
@test freqresp(pade(feedback(eye_(2), P_wb), 3), Ω) freqresp(feedback(eye_(2), P_wb), Ω) atol=1e-4
231231

232+
# test thiran
233+
for Ts = [1, 1.1]
234+
z = tf('z', Ts)
235+
@test thiran(2Ts, Ts) == 1/z^2
236+
end
237+
238+
@test thiran(pi, 1) tf([-0.00031815668236122736, 0.0042438423976339556, -0.03424682772398137, 0.8290601401044773, 1.0], [1.0, 0.8290601401044773, -0.03424682772398137, 0.0042438423976339556, -0.00031815668236122736], 1)
239+
232240

233241
# test automatic frequency selection
234242
mag, phase, w = bode(DemoSystems.lag()*delay(1))

0 commit comments

Comments
 (0)