# LabProcesses This package contains an (programming- as well as connection-) interface to serve as a base for the implementation of lab-process software. The first example of an implementaiton of this interface is for the ball-and-beam process, which is used in Lab1 FRTN35: frequency response analysis of the beam. The lab is implemented in [BallAndBeam.jl](https://gitlab.control.lth.se/processes/BallAndBeam.jl), a package that makes use of `LabProcesses.jl` to handle the communication with the lab process and/or a simulated version thereof. This way, the code written for frequency response analysis of the beam can be run on another process implementing the same interface (or a simulated version) by changeing a single line of code :) ## Installation 1. Start julia by typing `julia` in a terminal, make sure the printed info says it's `v0.6+` running. If not, visit [julialang.org](https://julialang.org/downloads/) to get the latest release. 2. Install LabProcesses.jl using command `Pkg.clone("https://gitlab.control.lth.se/processes/LabProcesses.jl.git")` Lots of packages will now be installed, this will take some time. If this is your first time using Julia, you might have to run `julia> Pkg.init()` before you install any packages. ## How to implement a new process 1. Locate the file [interface.jl](https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface.jl). When the package is installed, you find its directory under `~/.julia/v0.6/LabProcesses/`, if not, run `julia> Pkg.dir("LabProcesses")` to locate the directory. (Alternatively, you can copy all definitions from [/interface_implementations/ballandbeam.jl](https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface_implementations/ballandbeam.jl) instead. Maybe it's easier to work from an existing implementaiton.) 2. Copy all function definitions. 3. Create a new file under `/interface_implementations` where you paste all the copied definitions and implement them. See [/interface_implementations/ballandbeam.jl](https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface_implementations/ballandbeam.jl) for an example. 4. Above all function implementations you must define the process type, e.g, ```julia struct BallAndBeam <: PhysicalProcess h::Float64 bias::Float64 end BallAndBeam() = BallAndBeam(0.01, 0.0) # Constructor with default value of sample time ``` Make sure you inherit from `PhysicalProcess` or `SimulatedProcess` as appropriate. This type must contains fields that hold information about everything that is relevant to a particular instance of the process. Different ballandbeam-process have different biases, hence this must be stored. A simulated process would have to keep track of its state etc. in order to implement the measure and control methods. See [Types in julia documentation](https://docs.julialang.org/en/stable/manual/types/#Composite-Types-1) for additional info regarding user defined types and (constructors)[https://docs.julialang.org/en/stable/manual/constructors/]. 5. Documentation of all interface functions is available in the file [interface_documentation.jl](https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface_documentation.jl) ## Control a process The interface `AbstractProcess` defines the functions `control(P, u)` and `measure(P)`. These functions can be used to implement your own control loops. A common loop with a feedback controller and a feedforward filter on the reference is implemented in the function [`run_control_2DOF`](@ref), where the user can supply $G_1$ and $G_4$ in the diagram below, with the process $P=G_2$. ![block diagram](docs/feedback4.png) The macro `@periodically` might come in handy if you want to implement your own loop. Consider the following example, in which the loop body will be run periodically with a sample time of `h` seconds. ```julia for (i,t) = enumerate(0:h:duration) @periodically h begin y[i] = measure(P) r[i] = reference(t) u[i] = calc_control(i,y,r) control(P, u[i]) end end ``` 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)`. Usage is demonstrated below, which is a simplified implementation of the block diagram above (transfer function- and signal names corresponds to the figure). ```julia stateG1 = init_sysfilter(G1) stateG4 = init_sysfilter(G4) function control(i) rf = sysfilter!(stateG4, G4, r) e = rf-y u = sysfilter!(stateG1, G1, 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). `TransferFunction` types can easily be converted to a `StateSpace` by `Gss = ss(Gtf)`. Continuous time systems can be discretized using `Gd = c2d(Gc, h)`. (The sample time of a process is available through `h = sampletime(P)`.)