Often one finds the need to implement a stateful controller, i.e., a function
that has a memory or state. To this end, the function[`sysfilter`](@ref) is
provided. This function is used to implement control loops where a signal is
filtered through a dynamical system, i.e., `U(z) = C(z)E(z)`.
that has a memory or state. To this end, the type[`SysFilter`](@ref) is
provided. This type is used to implement control loops where a signal is
filtered through a dynamical system, i.e., `U(z) = G1(z)E(z)`.
Usage is demonstrated below, which is a simplified implementation of the block
diagram above (transfer function- and signal names corresponds to the figure).
First two `SysFilter` objects are created, these objects can now be used as
functions of an input, and return the filtered output. The `SysFilter` type takes
care of updating remembering the state of the system when called.
```julia
stateG1=init_sysfilter(G1)
stateG4=init_sysfilter(G4)
G1f=SysFilter(G1)
G4f=SysFilter(G4)
function control(i)
rf=sysfilter!(stateG4,G4,r)
rf=G4f(r)
e=rf-y
u=sysfilter!(stateG1,G1,e)
u=G1f(e)
end
```
`G1` and `G4` must here be represented by [`StateSpace`](http://juliacontrol.github.io/ControlSystems.jl/latest/lib/constructors/#ControlSystems.ss) types from [`ControlSystems.jl`](https://github.com/JuliaControl/ControlSystems.jl).
...
...
@@ -93,19 +96,18 @@ This is very easy, just get a discrete time `StateSpace` model of your process
You now have to implement the methods `control` and `measure` for your simulated type.
The implementation for `BeamSimulator` is shown below
```julia
function control(p::BeamSimulator,u)
sysfilter!(p.state,p.sys,u)
end
measure(P)=vecdot(p.sys.C,p.state)
control(p::BeamSimulator,u)=p.Gf(u)
measure(P)=vecdot(p.Gf.sys.C,p.Gf.state)
```
The `control` method accepts a control signal (`u`) and propagates the system state
(`p.state`) forward using the statespace model (`p.sys`) of the beam. The function
[`sysfilter!`](@ref) is familiar from the "Control" section above. What it does
is essentially
(`p.Gf.state`) forward using the statespace model (`p.Gf.sys`) of the beam. The object
[`Gf::SysFilter`](@ref) is familiar from the "Control" section above. What it does
is essentially (simplified)
```julia
function sysfilter!(state,sys,input)
state.=sys.A*state+sys.B*input
output=sys.C*state+sys.D*input
function Gf(input)
sys=Gf.sys
Gf.state.=sys.A*Gf.state+sys.B*input
output=sys.C*Gf.state+sys.D*input
end
```
hence, it just performs one iteration of
...
...
@@ -125,19 +127,20 @@ It should now be obvious which fields are required in the `BeamSimulator` type.
It must know which sample time it has been discretized with, as well as its
discrete-time system model. It must also remember the current state of the system.
This is not needed in a physical process since it kind of remembers its own state.
The system model and its state is conveniently covered by the type [`SysFilter`](@ref),
which handles filtering of a signal through an LTI system.
The full type specification for `BeamSimulator` is given below
```julia
struct BeamSimulator<:SimulatedProcess
h::Float64
state::Vector{Float64}# states defined by the file define_beam_system