# ControlSystems -- transfer functions with delay?

From the documentation:

`ControlSystems.tf` — Function.

`sys = tf(num, den, Ts=0), sys = tf(gain, Ts=0)`

→ Is it possible to define a time delay for functions `tf` and `zpk`?

I’m trying to “sell” Julia to a friend, and he currently works with a system that has a time delay.

→ If not possible, should I propose to use a Padé approximation?

The package has a `delay()` function. See examples here.

Thanks! That looks perfect – can also be used when the model is posed in the time delay :-).

Shouldn’t this be listed under Constructors? I didn’t find it in the documentation.

1 Like

Follow-up… `lsimplot` works without time delay, but doesn’t seem to work with time delay??

``````using Plots, ControlSystems, DataInterpolations
#
sys = tf(,[0.5,1])*tf(,[3,1])#*delay(2)
#
tval() = rand(range(2,10,length=5))
uval() = rand(range(-1,1,length=5))
#
Nchanges = 20
tvec = [tval() for i in 1:Nchanges] |> cumsum
uvec = [uval() for i in 1:Nchanges]
uinterp =  ConstantInterpolation(uvec,tvec)
u(x,t) = uinterp(t)
#
tfin = tvec[end]
plot(t->u(0,t),0,tfin,label="u_exp")
``````

gives the “experimental input”:

I can finally simulate the system with experiment:

``````tsim = range(0,tfin,length=200)
lsimplot(sys,u,tsim)
``````

Here I have started with default states at zero.

If I change the line `sys = tf(,[0.5,1])*tf(,[3,1])#*delay(2)` to `sys = tf(,[0.5,1])*tf(,[3,1])*delay(2)`, I can still do `stepplot` of the system, but `lsimplot` with input as above leads to zero output for all time…

Questions:

1. How can I make `lsim` work with time delay?
2. What initial states could I possibly choose when I have time-delay? Would it be reasonable to assume that `x0` refers to the states of the system without time delay, and that no states should be chosen for the (discretization of) time delay?

So one thing is that the `lsim` called for a delayed system seems to expect `u` that only depends on time, and if you send a function that takes two variables it assumes it is the inplace version `u(uout, t) = uout .= f(t)` and this would result in the `uout` sent in during the simulation to always be 0, which explains the output being 0.

So instead doing `u(t) = uinterp(u)` should solve the first problem I think.

This seems very confusing since it for non-delayed systems is `u(x,t)` as you used, so maybe something should be changed here.

For the states I’m pretty sure the internal states would be the same with a delayed input in this case, x’(t)=Ax(t)+Bu(t-tau), though that might depend on how delay is multiplied into the rest.

1 Like

Thanks for your suggestions and explanation (I didn’t understand it completely, though).

Do you mean I should do `u(t) = uinterp(t)`? Or do you mean `u(t) = uinterp(u)`? Remember: `uinterp` is an interpolation function with `t` as argument, not with `u` as argument.

If you mean that I should change `u(x,t) = uinterp(t)` to `u(t) = uinterp(t)`, then the statement:

``````lsimplot(sys,uinterp,tsim)
``````

should also work… but it doesn’t: it throws a lengthy error message.

It would indeed be confusing to the user if in `lsim`, `u` should have arguments `u(x,t)` when there is no time delay, and `u(t)` when there is time delay. If this is the case, then that would certainly be confusing.

Yeah I meant `u(t)=uinterp(t)` which works for me when copying your example. The reason that `lsimplot(sys, uinterp, tsim)` does not work is that `uinterp` is an AbstractVector type, and lsim checks the type of u and depending on type will handle it slightly differently. Since it is interpreted as a vector type it is called like a vector which causes an error. Not sure if this is intended, have never used the Interpolation types before so not sure how they interact with stuff.

Internally it looks like this

``````function lsim(sys::DelayLtiSystem{T,S}, u, t::AbstractArray{<:Real}; x0=fill(zero(T), nstates(sys)), alg=MethodOfSteps(Tsit5()), kwargs...) where {T,S}
# Make u! in-place function of u
u! = if isa(u, Number) || isa(u,AbstractVector) # Allow for u to be a constant number or vector
(uout, t) -> uout .= u
elseif DiffEqBase.isinplace(u, 2)               # If u is an inplace (more than 1 argument function)
u
else                                            # If u is a regular u(t) function
(out, t) -> (out .= u(t))
end
_lsim(sys, u!, t, x0, alg; kwargs...)
end
``````

where if it was handled under the last else clause it should have been fine to call it with uinterp, but as it is now it is caught by the first clause and errors. While the function `u(t)=uinterp(t)` will be hendled by the third clause and should hopefully work.

Yes it is weird as it is now, and I have opened an issue regarding this so will hopefully be looked over.

2 Likes

Thanks for following it up. The arguments for `u` is not documented in the documentation. And even if they were, I agree with you that they should be the same irrespective of a delay or not.

Also, it would make things clearer if the documentation states whether `x0` includes states for the delay approximation, or not. Probably `x0` should not include delay states… but I haven’t thought through what they should be if they are automatically chosen.

Another problem: named argument `x0` in `lsim` doesn’t work. So if I do `lsim(...; x0 = [1,1])`, I get an error message. However, if I do `lsim(...; x = [1,1])` then the simulation runs.

Could it be that the named argument has been changed from `x0` to `x` without updating the documentation?

1 Like

Checking `help?> lsim` in the repl shows me this information, though it seems like it is missing from the online docs. Have added that to the issue about lsim for delay system.

Looking at the code it seems x0 is the name and it does not include the delay states, the delay states seems to be initialized to zero if I understand correctly.

Does the simulation do anything different with `x=[1, 1]`? Because there is a catchall kwarg so I assume it is just the same as not sending anything in. The problem with the `x0=[1,1]` is that the dispatch is not perfectly made so it does not find the method since you make an int array, try `x0=[1.0, 1]` and it should work. Will also add this to the rest of the issues you found and hopefully it can be fixed soon.

2 Likes

Will test to use floats in `x0`. Problem is I set `x0 = zeros(2)`, and then setting `x0 = x0`, the code complained that the “symbol” didn’t find a zero, or something really weird.

Yeah, I got that too. If you try with only `lsim` and not `lsimplot` it seems to be slightly more reasonable error message, so I assume that is from the plottingrecepies being what they are (dark magic).

1 Like

You are right. The problem with keyword `x0` is only in `lsimplot`… And your interpretation of me using `x` is probably correct – that `lsimplot` just neglects it.

To sum up, I think there are the following two or three problems with `lsim`/`lsimplot`:

• The weird thing that `u` is supposed to have arguments `u(x,t)` when there is no delay, and that it is supposed to be `u(t)` when there is delay. This difference doesn’t make much sense to me, and in my view it is better if it is `u(x,t)` in both cases or `u(t)` in both cases.
• The problem of keyword argument `x0` giving error message in `lsimplot`, possibly due to a plot recipe problem.
• Some uncertainty about what `x0` means when there is a delay. Most likely, it means the finite states (of system without delay). The interesting thing is what value is chosen for the “delay states” when `x0` is specified to be different from zero. If the delay is assumed to be on the input, the delay states should probably have the same values as the initial time inputs, or something.
1 Like

Yeah, thanks for noting all this. Have linked this thread in the issue here, feel free to add stuff there also.

2 Likes