\documentclass[12pt,a4paper]{article} \usepackage[a4paper,text={16.5cm,25.2cm},centering]{geometry} \usepackage{lmodern} \usepackage{amssymb,amsmath} \usepackage{graphicx} \usepackage{microtype} \usepackage{hyperref} \setlength{\parindent}{0pt} \setlength{\parskip}{1.2ex} \hypersetup { pdfauthor = { }, pdftitle={ }, colorlinks=TRUE, linkcolor=black, citecolor=blue, urlcolor=blue } \usepackage[T1]{fontenc} \usepackage{textcomp} \usepackage{upquote} \usepackage{listings} \usepackage{xcolor} \lstset{ basicstyle=\ttfamily\footnotesize, upquote=true, breaklines=true, keepspaces=true, showspaces=false, columns=fullflexible, showtabs=false, showstringspaces=false, escapeinside={(*@}{@*)}, extendedchars=true, } \newcommand{\HLJLt}[1]{#1} \newcommand{\HLJLw}[1]{#1} \newcommand{\HLJLe}[1]{#1} \newcommand{\HLJLeB}[1]{#1} \newcommand{\HLJLo}[1]{#1} \newcommand{\HLJLk}[1]{\textcolor[RGB]{148,91,176}{\textbf{#1}}} \newcommand{\HLJLkc}[1]{\textcolor[RGB]{59,151,46}{\textit{#1}}} \newcommand{\HLJLkd}[1]{\textcolor[RGB]{214,102,97}{\textit{#1}}} \newcommand{\HLJLkn}[1]{\textcolor[RGB]{148,91,176}{\textbf{#1}}} \newcommand{\HLJLkp}[1]{\textcolor[RGB]{148,91,176}{\textbf{#1}}} \newcommand{\HLJLkr}[1]{\textcolor[RGB]{148,91,176}{\textbf{#1}}} \newcommand{\HLJLkt}[1]{\textcolor[RGB]{148,91,176}{\textbf{#1}}} \newcommand{\HLJLn}[1]{#1} \newcommand{\HLJLna}[1]{#1} \newcommand{\HLJLnb}[1]{#1} \newcommand{\HLJLnbp}[1]{#1} \newcommand{\HLJLnc}[1]{#1} \newcommand{\HLJLncB}[1]{#1} \newcommand{\HLJLnd}[1]{\textcolor[RGB]{214,102,97}{#1}} \newcommand{\HLJLne}[1]{#1} \newcommand{\HLJLneB}[1]{#1} \newcommand{\HLJLnf}[1]{\textcolor[RGB]{66,102,213}{#1}} \newcommand{\HLJLnfm}[1]{\textcolor[RGB]{66,102,213}{#1}} \newcommand{\HLJLnp}[1]{#1} \newcommand{\HLJLnl}[1]{#1} \newcommand{\HLJLnn}[1]{#1} \newcommand{\HLJLno}[1]{#1} \newcommand{\HLJLnt}[1]{#1} \newcommand{\HLJLnv}[1]{#1} \newcommand{\HLJLnvc}[1]{#1} \newcommand{\HLJLnvg}[1]{#1} \newcommand{\HLJLnvi}[1]{#1} \newcommand{\HLJLnvm}[1]{#1} \newcommand{\HLJLl}[1]{#1} \newcommand{\HLJLld}[1]{\textcolor[RGB]{148,91,176}{\textit{#1}}} \newcommand{\HLJLs}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsa}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsb}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsc}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsd}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsdB}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsdC}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLse}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLsh}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsi}[1]{#1} \newcommand{\HLJLso}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLsr}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLss}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLssB}[1]{\textcolor[RGB]{201,61,57}{#1}} \newcommand{\HLJLnB}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLnbB}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLnfB}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLnh}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLni}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLnil}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLnoB}[1]{\textcolor[RGB]{59,151,46}{#1}} \newcommand{\HLJLoB}[1]{\textcolor[RGB]{102,102,102}{\textbf{#1}}} \newcommand{\HLJLow}[1]{\textcolor[RGB]{102,102,102}{\textbf{#1}}} \newcommand{\HLJLp}[1]{#1} \newcommand{\HLJLc}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLch}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLcm}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLcp}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLcpB}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLcs}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLcsB}[1]{\textcolor[RGB]{153,153,119}{\textit{#1}}} \newcommand{\HLJLg}[1]{#1} \newcommand{\HLJLgd}[1]{#1} \newcommand{\HLJLge}[1]{#1} \newcommand{\HLJLgeB}[1]{#1} \newcommand{\HLJLgh}[1]{#1} \newcommand{\HLJLgi}[1]{#1} \newcommand{\HLJLgo}[1]{#1} \newcommand{\HLJLgp}[1]{#1} \newcommand{\HLJLgs}[1]{#1} \newcommand{\HLJLgsB}[1]{#1} \newcommand{\HLJLgt}[1]{#1} \begin{document} \section{LabProcesses} \begin{verbatim} Depth = 3 \end{verbatim} 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 \href{https://gitlab.control.lth.se/processes/BallAndBeam.jl}{BallAndBeam.jl}, a package that makes use of \texttt{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 :) \subsection{Installation} \begin{itemize} \item[1. ] Start julia by typing \texttt{julia} in a terminal, make sure the printed info says it's \end{itemize} \texttt{v0.6+} running. If not, visit \href{https://julialang.org/downloads/}{julialang.org} to get the latest release. \begin{itemize} \item[2. ] Install LabProcesses.jl using command \texttt{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 \texttt{julia> Pkg.init()} before you install any packages. \end{itemize} \section{How to implement a new process} \subsubsection{1.} Locate the file \href{https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface.jl}{interface.jl}. When the package is installed, you find its directory under \texttt{{\sim}/.julia/v0.6/LabProcesses/}, if not, run \texttt{julia> Pkg.dir("LabProcesses")} to locate the directory. (Alternatively, you can copy all definitions from \href{https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface_implementations/ballandbeam.jl}{/interface\_implementations/ballandbeam.jl} instead. Maybe it's easier to work from an existing implementaiton.) \subsubsection{2.} Copy all function definitions. \subsubsection{3.} Create a new file under \texttt{/interface\_implementations} where you paste all the copied definitions and implement them. See \href{https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface_implementations/ballandbeam.jl}{/interface\_implementations/ballandbeam.jl} for an example. \subsubsection{4.} Above all function implementations you must define the process type, e.g, \begin{verbatim} struct BallAndBeam <: PhysicalProcess h::Float64 bias::Float64 end BallAndBeam() = BallAndBeam(0.01, 0.0) # Constructor with default value of sample time \end{verbatim} Make sure you inherit from \texttt{PhysicalProcess} or \texttt{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 \href{https://docs.julialang.org/en/stable/manual/types/#Composite-Types-1}{Types in julia documentation} for additional info regarding user defined types and (constructors)[https://docs.julialang.org/en/stable/manual/constructors/]. \subsubsection{5.} Documentation of all interface functions is available in the file \href{https://gitlab.control.lth.se/processes/LabProcesses.jl/blob/master/src/interface_documentation.jl}{interface\_documentation.jl} \section{How to control a process} The interface \texttt{AbstractProcess} defines the functions \texttt{control(P, u)} and \texttt{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 \href{@ref}{\texttt{run\_control\_2DOF}}, where the user can supply $G_1$ and $G_4$ in the diagram below, with the process $P=G_2$. \begin{figure} \centering \includegraphics{feedback4.png} \caption{block diagram} \end{figure} The macro \texttt{@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 \texttt{h} seconds. \begin{verbatim} for (i,t) = enumerate(0:h:duration) @periodically h begin y[i] = measure(P) r[i] = reference(t) u[i] = calc_control(y,r) control(P, u[i]) end end \end{verbatim} Often one finds the need to implement a stateful controller, i.e., a function that has a memory or state. To this end, the type \href{@ref}{\texttt{SysFilter}} is provided. This type is used to implement control loops where a signal is filtered through a dynamical system, i.e., \texttt{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 \texttt{SysFilter} objects are created, these objects can now be used as functions of an input, and return the filtered output. The \texttt{SysFilter} type takes care of updating and remembering the state of the system when called. \begin{verbatim} G1f = SysFilter(G1) G4f = SysFilter(G4) function calc_control(y,r) rf = G4f(r) e = rf-y u = G1f(e) end \end{verbatim} \texttt{G1} and \texttt{G4} must here be represented by \href{http://juliacontrol.github.io/ControlSystems.jl/latest/lib/constructors/#ControlSystems.ss}{\texttt{StateSpace}} types from \href{https://github.com/JuliaControl/ControlSystems.jl}{\texttt{ControlSystems.jl}}, e.g., \texttt{G1 = ss(A,B,C,D)}. \texttt{TransferFunction} types can easily be converted to a \texttt{StateSpace} by \texttt{Gss = ss(Gtf)}. Continuous time systems can be discretized using \texttt{Gd = c2d(Gc, h)[1]}. (The sample time of a process is available through \texttt{h = sampletime(P)}.) \section{How to implement a Simulated Process} \subsection{Linear process} This is very easy, just get a discrete time \texttt{StateSpace} model of your process (if you have a transfer function, \texttt{Gss = ss(Gtf)} will do the trick, if you have continuous time, \texttt{Gd = c2d(Gc,h)[1]} is your friend). You now have to implement the methods \texttt{control} and \texttt{measure} for your simulated type. The implementation for \texttt{BeamSimulator} is shown below \begin{verbatim} control(p::BeamSimulator, u) = p.Gf(u) measure(P) = vecdot(p.Gf.sys.C, p.Gf.state) \end{verbatim} The \texttt{control} method accepts a control signal (\texttt{u}) and propagates the system state (\texttt{p.Gf.state}) forward using the statespace model (\texttt{p.Gf.sys}) of the beam. The object \texttt{Gf} (of type \href{@ref}{\texttt{SysFilter}}) is familiar from the "Control" section above. What it does is essentially (simplified) \begin{verbatim} function Gf(input) sys = Gf.sys Gf.state .= sys.A*Gf.state + sys.B*input output = sys.C*Gf.state + sys.D*input end \end{verbatim} hence, it just performs one iteration of \begin{align} x' = Ax + Bu \end{align} \begin{align} y = Cx + Du \end{align} The \texttt{measure} method performs the computation \texttt{y = Cx}, the reason for the call to \texttt{vecdot} is that \texttt{vecdot} produces a scalar output, whereas \texttt{C*x} produces a 1-element \texttt{Matrix}. A scalar output is preferred in this case since the \texttt{Beam} is SISO. It should now be obvious which fields are required in the \texttt{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 \href{@ref}{\texttt{SysFilter}}, which handles filtering of a signal through an LTI system. The full type specification for \texttt{BeamSimulator} is given below \begin{verbatim} struct BeamSimulator <: SimulatedProcess h::Float64 Gf::SysFilter BeamSimulator() = new(0.01, SysFilter(beam_system, 0.01)) BeamSimulator(h::Real) = new(Float64(h), SysFilter(beam_system, h)) end \end{verbatim} It contains three fields and two inner constructors. The constructors initializes the system filter by creating a \href{@ref}{\texttt{SysFilter}}. The variable \texttt{beam\_system} is already defined outside the type specification. One of the constructors provides a default value for the sample time, in case the user is unsure about a reasonable value. \subsection{Non-linear process} Your first option is to linearize the process and proceed like above. Other options include \begin{itemize} \item[1. ] Make \texttt{control} perform forward Euler, i.e., \texttt{x[t+1] = x[t] + f(x[t],u[t])*h} for a general system model $x' = f(x,u); y = g(x,u)$ and sample time $h$. \item[2. ] Integrate the system model using some fancy method like Runge-Kutta. See \href{http://docs.juliadiffeq.org/stable/types/discrete_types.html}{DifferentialEquations.jl} for discrete-time solving of ODEs (don't be discouraged, this is almost as simple as forward Euler above). \end{itemize} \section{Exported functions and types} \begin{verbatim} Modules = [LabProcesses] Private = false Pages = ["LabProcesses.jl", "controllers.jl", "reference_generators.jl", "utilities.jl"] \end{verbatim} \section{Process interface specification} All processes must implement the following interface. See the existing implementations in the folder \texttt{interface\_implementations} for guidance. \begin{verbatim} Modules = [LabProcesses] Private = false Pages = ["interface_documentation.jl"] \end{verbatim} \section{Index} \begin{verbatim} \end{verbatim} \end{document}