Skip to content

Commit

Permalink
P, PI, PID objects
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonEnsemble committed Dec 25, 2019
1 parent e9fa270 commit f273581
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 135 deletions.
Binary file added docs/src/FOPTD_step_response.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions docs/src/assets/full_feedback_control_system.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
\documentclass{standalone} % run wit shell escape

\usepackage{tikz}
\usetikzlibrary{arrows}
\usepackage{verbatim}

\usepackage{tikz}
\usepackage{color}
\definecolor{honeydew}{rgb}{0.94, 1.0, 0.94}
\definecolor{ivory}{rgb}{1.0, 1.0, 0.94}


\usetikzlibrary{shapes,arrows}
\begin{document}


\tikzstyle{block} = [draw, fill=honeydew!70, rectangle, line width=0.5mm,
minimum height=3em, minimum width=5em]
\tikzstyle{sum} = [draw, fill=ivory!20, circle, node distance=1cm, line width=0.5mm]
\tikzstyle{input} = [coordinate]
\tikzstyle{output} = [coordinate]
\tikzstyle{pinstyle} = [pin edge={to-,thick,black}]

\tikzset{
circ/.style={draw, circle, fill=ivory!70}
}


% The block diagram code is probably more verbose than necessary
\begin{tikzpicture}[auto, node distance=1cm,>=latex']
\node [block, node distance=1.5cm, label=process] (Gp) {$g_p(s)$};
\node [block, left of=Gp, node distance=3cm, label=controller] (Gc) {$g_c(s)$};
\node [circ, , line width=0.5mm, label={[label distance=-4.75mm]270:$-$},label={[label distance=-4.75mm]180:$+$}, minimum size=8mm, left of=Gc, node distance=2.5cm] (comparator) {};
\node [input, node distance=2cm, left of=comparator] (Ysp) {};
\node [circ, line width=0.5mm, label={[label distance=-4.75mm]90:$+$},label={[label distance=-4.75mm]180:$+$}, minimum size=8mm, right of=Gp, node distance=2.5cm] (sum) {};
% \node [sum, right of=process, node distance=2.5cm] (sum) {};
% \node [block, right of=controller, pin={[pinstyle]above:D},
% node distance=3cm] (system) {System};
\node [block, above of=sum, node distance=1.75cm, label={[label distance=0cm]180:process}] (Gd) {$g_d(s)$};
\node [input, name=DV, above of=Gd, node distance=1.5cm] {};
\node [output, name=output, right of=sum, node distance=1.5cm] {};
\node [below of=comparator, node distance=1.5cm] (ptbelow) {};
\draw [->, line width=0.5mm] (Gc) -- node[name=h] {} (Gp);
\node [block, below of=h, node distance=1.75cm, label=sensor] (Gm) {$g_m(s)$};

% \draw [->] (input) -- node[name=u] {$u$} (system);
% \node [output, right of=system] (output) {};
%
% \draw [draw,->] (input) -- system {$r$} (sum);

\draw [->, line width=0.5mm] (Gc) -- node {$U(s)$} (Gp);
\draw [->, line width=0.5mm] (Gp) -- node {} (sum);
\draw [->, line width=0.5mm] (Gd) -- node {} (sum);
\draw [->, line width=0.5mm] (sum) -- node[name=y] {$Y(s)$} (output);
\draw [->, line width=0.5mm] (DV) -- node {$D(s)$} (Gd);
\draw [->, line width=0.5mm] (comparator) -- node {$E(s)$} (Gc);
\draw [->, line width=0.5mm] (Ysp) -- node {$Y_{sp}(s)$} (comparator);
\draw [->, line width=0.5mm] (y) |- (Gm);
\draw [->, line width=0.5mm] (Gm) -| node {$Y_m(s)$} (comparator);

% \draw [->] (system) -- node [name=y] {$y$}(output);
\end{tikzpicture}

\end{document}
5 changes: 3 additions & 2 deletions docs/src/assets/pdf_to_trim_png.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
convert -density 300 simple_servo.pdf simple_servo.png
convert simple_servo.png -trim simple_servo.png
muhfile="full_feedback_control_system"
convert -density 300 $muhfile.pdf $muhfile.png
convert $muhfile.png -trim $muhfile.png
36 changes: 26 additions & 10 deletions docs/src/controls.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,33 @@

we build upon `simulate` to simulate feedback and feedforward control systems.

## PI, PID controller transfer functions
## P, PI, PID controller transfer functions

we express PID controller transfer functionas in the form:
we express PID controller transfer functions in the form:

$$g_c(s)=K_c \bigl[1+\frac{\tau_I}{s}+\tau_D s \frac{1}{\tau_D \alpha s + 1}\bigr]$$
$$g_c(s)=K_c \bigl[1+\frac{1}{\tau_I}+\tau_D s \frac{1}{\tau_D \alpha s + 1}\bigr]$$

where $\alpha$ characterizes the derivative filter. this controller function function governs the controller output in response to the input error signal.

To facilitate constructing PID controller transfer functions:
To construct P, PI, or PID controllers:

```julia
Kc = 2.0 # controller gain
pc = PController(Kc) # P-controller with given Kc

τI = 1.0 # integral time constant
gc = PI(Kc, τI) # PI-controller transfer function
pic = PIController(Kc, τI) # PI-controller with given Kc, τI

τD = 0.1 # derivative time constant
gc = PID(Kc, τI, τD, α=0.0) # PID-controller transfer function with derivative filter α
pidc = PIDController(Kc, τI, τD, α=0.0) # PID-controller with given Kc, τI, τD. keyword argument is derivative filter α
```

to construct controller transfer functions $g_c(s)$ from the P, PI, or PID controller parameters:

and take an error signal as input and output the controller output (affecting the manipulated variable).
```julia
pic = PIController(2.0, 1.0)
gc = TransferFunction(pic) # (2s+2) / s
```

## servo response of a simple control system

Expand All @@ -33,7 +39,8 @@ with block diagram algebra, we can use [`simulate`](@ref) to simulate a control
as an example, we specify $g_c(s)$ as a PI controller and $g_p(s)$ as a first-order system. the latter describes how the process responds to inputs. the input to the process here is provided by the controller.

```julia
gc = PI(1.0, 1.0) # controller transfer function
pic = PIController(1.0, 1.0)
gc = TransferFunction(pic) # controller transfer function
gp = 3 / (4 * s + 1) # process transfer function
```

Expand Down Expand Up @@ -74,8 +81,17 @@ finally, we can plot `y`, `ysp`, and `u` against `t` to visualize the response o

![](simple_servo_response.png)

also plotted separately is the contribution of the controller output by the P- and I- components of the PI controller, obtained via:
```julia
U_Paction = Kc * E # P-action
U_Iaction = Kc * τI / s * E # I-action

t, u_Paction = simulate(U_Paction, final_time)
t, u_Iaction = simulate(U_Iaction, final_time)
```

```@docs
PID
PI
PController
PIController
PIDController
```
9 changes: 9 additions & 0 deletions docs/src/sim.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ t, y = simulate(Y, 50.0) # simulate until t = 50

we can then plot the `y` array versus the `t` array:

```julia
viz_response(t, y, plot_title="SO underdamped step response")
```

![](SO_underdamped_step_response.png)

## response of a first-order plus time delay system to a unit step input
Expand All @@ -33,6 +37,8 @@ U = 1 / s # step input
Y = g * U

t, y = simulate(Y, 15.0) # simulate until t = 15

viz_response(t, y, plot_title="FOPTD step response")
```

![](FOPTD_step_response.png)
Expand All @@ -48,7 +54,10 @@ we can numerically invert $\dfrac{s^2-a^2}{(s^2+a^2)^2}$ as follows:
```julia
a = π
U = (s^2 - a^2) / (s^2 + a^2) ^ 2

t, u = simulate(U, 8.0, nb_time_points=300) # simulate until t=8, use 300 time points for high resolution

viz_response(t, u, plot_title=L"inverting an input $U(s)$", plot_ylabel=L"$u(t)$")
```

the `nb_time_points` argument gives a `t` array with higher resolution for plotting the response.
Expand Down
Binary file added docs/src/simple_servo_response.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/tcosat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
353 changes: 248 additions & 105 deletions examples/examples.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/Controlz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ PyPlot.matplotlib.style.use(normpath(joinpath(pathof(Controlz), "..", "hipster.m

include("tf.jl")
include("systems.jl")
include("show.jl")
include("sim.jl")
include("viz.jl")
include("controls.jl")
include("show.jl")

export
# tf.jl
Expand All @@ -25,5 +25,5 @@ export
# viz.jl
viz_response, viz_poles_and_zeros, nyquist_diagram, root_locus, bode_plot,
# controlz.jl
PI, PID
PController, PIController, PIDController
end
70 changes: 55 additions & 15 deletions src/controls.jl
Original file line number Diff line number Diff line change
@@ -1,35 +1,75 @@
@doc raw"""
gc = PID(Kc, τI, τD, α=0.0)
pc = PController(Kc)
Construct Proportional-Integral-Derivative (PID) controller transfer function, $g_c(s)$.
Construct a Proportional (P) controller by specifying the controller gain defined under the following transfer function representation:
$$g_c(s)=K_c \bigl[1+\frac{\tau_I}{s}+\tau_D s \frac{1}{\alpha \tau_D s + 1}\bigr]$$
$$g_c(s)=K_c$$
# Arguments
* `Kc::Float64`: controller gain
# Example
```julia
pc = PController(1.0) # specify P controller gain
gc = TransferFunction(pc) # construct transfer function with this P-controller gain
```
"""
mutable struct PController
Kc::Float64
end

TransferFunction(pc::PController) = TransferFunction([pc.Kc], [1.0])

@doc raw"""
pic = PIController(Kc, τI)
Construct a Proportional-Integral (PI) controller by specifying the controller gain and integral time constant defined under the following transfer function representation:
$$g_c(s)=K_c \bigl[1+\frac{1}{\tau_I s}\bigr]$$
# Arguments
* `Kc::Float64`: controller gain
* `τI::Float64`: integral time constant
* `τD::Float64`: derivative time constant
* `α::Float64`: derivative filter
# Returns
PID controller transfer function `gc::TransferFunction` that takes error signal as input and outputs the manipulated variable.
# Example
```julia
pic = PIController(1.0, 3.0) # specify PI controller params
gc = TransferFunction(pic) # construct transfer function with these PI-controller params
```
"""
function PID(Kc::Float64, τI::Float64, τD::Float64; α::Float64=0.0)
return Kc * (1 + τI / s + τD * s /* τD * s + 1))
mutable struct PIController
Kc::Float64
τI::Float64
end

TransferFunction(pic::PIController) = pic.Kc * (1 + 1 / (pic.τI * s))

mutable struct PIDController
Kc::Float64
τI::Float64
τD::Float64
α::Float64
end

@doc raw"""
gc = PI(Kc, τI)
pidc = PIDController(Kc, τI, τD, α=0.0)
Construct Proportional-Integral (PI) controller transfer function, $g_c(s)$.
Construct a Proportional-Integral-Derivative (PID) controller by specifying the controller gain, integral time constant, derivative time constant, and derivative filter defined under the following transfer function representation:
$$g_c(s)=K_c \bigl[1+\frac{\tau_I}{s}\bigr]$$
$$g_c(s)=K_c \bigl[1+\frac{1}{\tau_I s}+\tau_D s \frac{1}{\alpha \tau_D s + 1}\bigr]$$
# Arguments
* `Kc::Float64`: controller gain
* `τI::Float64`: integral time constant
* `τD::Float64`: derivative time constant
* `α::Float64`: derivative filter
# Returns
PI controller transfer function `gc::TransferFunction` that takes error signal as input and outputs the manipulated variable.
# Example
```julia
pidc = PIDController(1.0, 3.0, 0.1) # specify PID controller params
gc = TransferFunction(pidc) # construct transfer function with these PID-controller params
```
"""
PI(Kc::Float64, τI::Float64) = Kc * (1 + τI / s)
PIDController(Kc::Float64, τI::Float64, τD::Float64; α::Float64=0.0) = PIDController(Kc, τI, τD, α)

TransferFunction(pidc::PIDController) = pidc.Kc * (1 + 1.0 / (pidc.τI * s) + pidc.τD * s / (pidc.α * pidc.τD * s + 1))
2 changes: 1 addition & 1 deletion src/hipster.mplstyle
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ text.color : (0.3, 0.3, 0.3)
axes.labelcolor : (0.3, 0.3, 0.3)
xtick.color : (0.3, 0.3, 0.3)
ytick.color : (0.3, 0.3, 0.3)
axes.prop_cycle : cycler('color', [(0.2, 0.8, 0.2), (0.12, 0.56, 1.0), (0.9765625, 0.5, 0.4453125), (0.57421875, 0.4375, 0.85546875)])
axes.prop_cycle : cycler('color', [(0.2, 0.8, 0.2), (0.12, 0.56, 1.0), (0.9765625, 0.5, 0.4453125), (0.57421875, 0.4375, 0.85546875), (0.64, 0.64, 0.64)])
19 changes: 19 additions & 0 deletions src/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ function Base.show(io::IO, tf::TransferFunction)
print(io, bottom)
return nothing
end

function Base.show(io::IO, pc::PController)
println(io, "P controller")
println(io, "\tcontroller gain Kc = ", pc.Kc)
end

function Base.show(io::IO, pic::PIController)
println(io, "PI controller")
println(io, "\tcontroller gain Kc = ", pic.Kc)
println(io, "\tintegral time constant τI = ", pic.τI)
end

function Base.show(io::IO, pidc::PIDController)
println(io, "PID controller")
println(io, "\tcontroller gain Kc = ", pidc.Kc)
println(io, "\tintegral time constant τI = ", pidc.τI)
println(io, "\tderivative time constant τD = ", pidc.τD)
println(io, "\tderivative filter α = ", pidc.α)
end
6 changes: 6 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,9 @@ end
y_truth[t .< 0.0] .= 0.0
@test isapprox(y_truth, y, rtol=0.001)
end

@testset "testing Controls" begin
@test isapprox(TransferFunction(PIController(1.3, 2.0)), 1.3 * (1+1/(2*s)))
@test isapprox(TransferFunction(PIDController(1.3, 2.0, 0.0)), 1.3 * (1+1/(2*s)))
@test isapprox(TransferFunction(PIDController(1.3, 2.0, 0.1)), 1.3 * (1+1/(2*s) + 0.1*s))
end

2 comments on commit f273581

@SimonEnsemble
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request updated: JuliaRegistries/General/7102

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if Julia TagBot is installed, or can be done manually through the github interface, or via:

git tag -a v0.1.0 -m "<description of version>" f27358190cd142c6de53f29b61d36cb4756d3d28
git push origin v0.1.0

Please sign in to comment.