DocTestSetup = quote
using Controlz
end
consider the linear, time-invariant system:
with:
- input
$U(s)=\mathcal{L}[u(t)]$ - output
$Y(s)=\mathcal{L}[y(t)]$
the output for an input is characterized by a transfer function
here,
a data structure, TransferFunction
, represents a transfer function.
for example, consider the transfer function
constructor 1. we can construct
g = (5 * s + 1) / (s ^ 2 + 4 * s + 5) # construction method 1
# output
5.0*s + 1.0
---------------------
1.0*s^2 + 4.0*s + 5.0
constructor 2. alternatively, we can construct a TransferFunction
using the coefficients associated with the powers of
g = TransferFunction([5, 1], [1, 4, 5]) # construction method 2
# output
5.0*s + 1.0
---------------------
1.0*s^2 + 4.0*s + 5.0
!!! note
under the hood, s == TransferFunction([1, 0], [1])
.
as rational functions associated with a time delay, each TransferFunction
data structure has a numerator
, denominator
, and time_delay
attribute. access as follows:
g.numerator
# output
Polynomial(1.0 + 5.0*s)
g.denominator
# output
Polynomial(5.0 + 4.0*s + 1.0*s^2)
g.time_delay
# output
0.0
g.numerator
and g.denominator
are Polynomial
s from Polynomials.jl.
to construct a transfer function with a time delay, such as
θ = 2.0 # time delay
g = 3 / (2 * s + 1) * exp(-θ * s) # construction method 1
g = TransferFunction([3], [2, 1], θ) # construction method 2
# output
3.0
----------- e^(-2.0*s)
2.0*s + 1.0
we can write any transfer function
the scalar factor
for example, consider:
g = zeros_poles_k([-1/5], [-2 + im, -2 - im], 5.0, time_delay=0.0) # construction method 3
# output
5.0*s + 1.0
---------------------
1.0*s^2 + 4.0*s + 5.0
im
is the imaginary number
g = (5 * s + 1) / (s ^ 2 + 4 * s + 5)
z, p, k = zeros_poles_k(g)
# output
([-0.2], ComplexF64[-2.0 - 1.0im, -2.0 + 1.0im], 5.0)
add +
, subject -
, multiply *
, and divide /
transfer functions.
g₁ = 3 / (s + 2)
g₂ = 1 / (s + 4)
g_product = g₁ * g₂
# output
3.0
---------------------
1.0*s^2 + 6.0*s + 8.0
g_sum = g₁ + g₂
# output
4.0*s + 14.0
---------------------
1.0*s^2 + 6.0*s + 8.0
for example, to evaluate
g = 4 / (s + 2)
evaluate(g, - 2 + im)
# output
0.0 - 4.0im
compute the zero-frequency gain of a transfer function
g = (5 * s + 1) / (s ^ 2 + 4 * s + 5)
zero_frequency_gain(g)
# output
0.2
the zero-frequency gain is the ratio of the steady state output value to the steady state input value (e.g., consider a step input). note that the zero-frequency gain could be infinite or zero, which is why we do not have a function to construct a transfer function from its zeros, poles, and zero-frequency gain.
compute the poles, zeros, and zero-frequency gain of a transfer function all at once as follows:
g = (5 * s + 5) / (s ^ 2 + 4 * s + 5)
z, p, gain = zeros_poles_gain(g)
# output
([-1.0], ComplexF64[-2.0 - 1.0im, -2.0 + 1.0im], 1.0)
cancel pairs of identical poles and zeros in a transfer function as follows:
# define g(s) = s * (s+1) / ((s+3) * s * (s+1) ^ 2)
g = TransferFunction([1, 1, 0], [1, 5, 7, 3, 0])
pole_zero_cancellation(g) # 1 / ((s+3) * (s+1))
# output
1.0
---------------------
1.0*s^2 + 4.0*s + 3.0
under the hood, pole_zero_cancellation
compares all pairs of poles and zeros to look for identical pairs via isapprox
. after removing identical pole-zero pairs, we reconstruct the transfer function from the remaining poles and zeros---in addition to its k-factor. we ensure that the coefficients in the resulting rational function are real.
!!! note pole-zero cancellation is done automatically when multiplying, dividing, adding, and subtracting transfer functions, as illustrated below.
g = s * (s+1) / ((s+3) * s * (s+1) ^ 2)
# output
1.0
---------------------
1.0*s^2 + 4.0*s + 3.0
we can find the apparent order of the polynomials in the numerator and denominator of the rational function comprising the transfer function:
g = (s + 1) / ((s + 2) * (s + 3))
system_order(g)
# output
(1, 2)
in the closed loop below,
compute the critical frequency, gain crossover frequency, gain margin, and phase margin of a closed loop control system with open-loop transfer function g_ol
with gain_phase_margins
. for example, consider:
g_ol = 2 * exp(-s) / (5 * s + 1)
margins = gain_phase_margins(g_ol)
# output
-- gain/phase margin info--
critical frequency ω_c [rad/time]: 1.68868
gain crossover frequency ω_g [rad/time]: 0.34641
gain margin: 4.25121
phase margin: 1.74798
access the attributes of margins
via:
margins.ω_c # critical freq. (radians / time)
margins.ω_g # gain crossover freq. (radians / time)
margins.gain_margin # gain margin
margins.phase_margin # phase margin (radians)
# output
1.74798494087942
easily construct:
K = 2.0
τ = 3.0
g = first_order_system(K, τ)
# output
2.0
-----------
3.0*s + 1.0
compute time constant:
g = 10 / (6 * s + 2)
time_constant(g)
# output
3.0
easily construct:
K = 1.0
τ = 2.0
ξ = 0.1
g = second_order_system(K, τ, ξ)
# output
1.0
---------------------
4.0*s^2 + 0.4*s + 1.0
compute time constant, damping coefficient:
τ = time_constant(g)
# output
2.0
ξ = damping_coefficient(g)
# output
0.1
to represent a closed-loop transfer function, we use a special transfer function type, ClosedLoopTransferFunction
.
this is only necessary when time delays are involved, but it works for when time delays are not involved as well.
using block diagram algebra, we find the closed-loop transfer functions that relate changes in the output
we construct these two closed-loop transfer functions as gr
and gs
as follows.
# PI controller transfer function
pic = PIController(1.0, 2.0)
gc = TransferFunction(pic)
# process, sensor dynamics
gu = 2 / (4 * s + 1) * exp(-0.5 * s)
gm = 1 / (s + 1) * exp(-0.1 * s)
gd = 6 / (6 * s + 1)
# open-loop transfer function
g_ol = gc * gu * gm
# closed-loop transfer function for regulator response
gr = ClosedLoopTransferFunction(gd, g_ol)
# output
closed-loop transfer function.
top
-------
1 + g_ol
top =
6.0
-----------
6.0*s + 1.0
g_ol =
4.0*s + 2.0
-------------------------- e^(-0.6*s)
8.0*s^3 + 10.0*s^2 + 2.0*s
# closed-loop transfer function for servo response
gs = ClosedLoopTransferFunction(gc * gu, g_ol)
# output
closed-loop transfer function.
top
-------
1 + g_ol
top =
4.0*s + 2.0
--------------- e^(-0.5*s)
8.0*s^2 + 2.0*s
g_ol =
4.0*s + 2.0
-------------------------- e^(-0.6*s)
8.0*s^3 + 10.0*s^2 + 2.0*s
TransferFunction
ClosedLoopTransferFunction
zero_frequency_gain
zeros_poles_gain
zeros_poles_k
pole_zero_cancellation
evaluate
proper
strictly_proper
characteristic_polynomial
zpk_form
system_order
first_order_system
second_order_system
time_constant
damping_coefficient
gain_phase_margins