Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Robust toolbox dev #192

Closed
wants to merge 66 commits into from
Closed
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
8f3e4c7
Initial commit of the H-infinity synthesis toolbox
mgreiff Oct 24, 2018
61ae0a3
Clean up dev and remove deprecated files
mgreiff Oct 24, 2018
61694cd
Include tools for discrete-time synthesis and some tests
mgreiff Nov 2, 2018
d312409
Enhancement - Initial commit of the tests for the H-infinity design, …
mgreiff Feb 27, 2019
909bec0
Enhancement - Include tests for stabilizability husing the Hautus Lem…
mgreiff Feb 27, 2019
b4b4e9c
Bugfix - Specify input types to handle method errors in the stabiliza…
mgreiff Feb 27, 2019
8d18f6b
Enhancement - Add method error tests for the stabilizability and dete…
mgreiff Feb 27, 2019
4cdcb12
Enhancement - Add check for detectability by the Hautus Lemma for usa…
mgreiff Feb 27, 2019
5a63b53
Enhancement - Check for method errors when checking detectability of …
mgreiff Feb 27, 2019
6de77ec
Enhancement - This test checks that the computations of the pseudoinv…
mgreiff Feb 27, 2019
153883d
Bugfix - Make sure to check that only abstract matrix types are used …
mgreiff Feb 27, 2019
6e70954
Enhancement - Add tests to verify that the pseudoinverse computation …
mgreiff Feb 27, 2019
0eaad04
Enhancement - Check that the method correctly reports that no pseudoi…
mgreiff Feb 27, 2019
3588cec
Enhancement - Add tests for the gamma-iterations, and speicically tes…
mgreiff Feb 27, 2019
3fc2b7c
Enhancement - Add tests to check that a solution is reported to be no…
mgreiff Feb 27, 2019
fe2f1ae
Enhancement - Add test set for the bilinear transformation
mgreiff Feb 27, 2019
f8db6b1
Test that the SS-type data is discretized correctly, and that moving …
mgreiff Feb 27, 2019
e7a4b51
Enhancement - Add tests for discretization of extended state-space da…
mgreiff Feb 27, 2019
3371fd3
Enhancement - Add test which runs the DC-motor example
mgreiff Feb 28, 2019
7457c26
Add test which verifies optimality in the DC-motor case
mgreiff Feb 28, 2019
7293cb7
Add test to check that the specifications are indeed satisfied by the…
mgreiff Feb 28, 2019
e863190
Encapsulate the DC motor example in a separate test set
mgreiff Feb 28, 2019
1a594fb
Enhancement - Add test which runs the MIT open courseware example
mgreiff Feb 28, 2019
17756e6
Enhancement - Add test to check optimality int the MIT open coursewar…
mgreiff Feb 28, 2019
8a2b895
Enhancement - Add check to see that the specifications are met with t…
mgreiff Feb 28, 2019
498df49
Enhancement - Add tests which run the quadtank example and make sure …
mgreiff Feb 28, 2019
7ad1ad7
Move examples fom the dev directory to the example directory
mgreiff Feb 28, 2019
26e397a
Enhancement - Check for optimality in the Quad-tank example H-infinit…
mgreiff Feb 28, 2019
d01b0a3
Enhancement - Check that the frequency-domain sspecifications are met…
mgreiff Feb 28, 2019
fe3dc87
Fix error throws in the specifications rewrite, include printstatemen…
mgreiff Feb 28, 2019
08067e3
Add tests to verify that the conversion in the hInf_partition is done…
mgreiff Feb 28, 2019
04eeee1
Add the hinfinity tests to the automatic testing
mgreiff Feb 28, 2019
c65c223
Parameterize Function - move hard coded numerical tolerance to be an …
mgreiff Feb 28, 2019
fb5fcc8
Remove Dead Code - remove unneeded variable epsilon
mgreiff Feb 28, 2019
3ba522b
Inline Variable - remove the flag variable and return a boolean direc…
mgreiff Feb 28, 2019
259dfda
Rename Variable - Rename the variable so that the controller is consi…
mgreiff Feb 28, 2019
4ffaefe
Inline Variable - Removed the gammasquared variable
mgreiff Feb 28, 2019
9785fca
Extract Variable - Extract exactly what is returned in the hInf_synth…
mgreiff Feb 28, 2019
a990295
Remove breaking underfined variable
mgreiff Feb 28, 2019
df01d94
Introduce Assertion - Make sure that the matrix which is to be factor…
mgreiff Feb 28, 2019
f25585e
Introduce Assertion - Assert that the matrix which is to be factorize…
mgreiff Feb 28, 2019
68f56b8
Extract Method - Extracted a method for checking positive definitenes…
mgreiff Feb 28, 2019
fbe6a5b
Bugfix with string concatenation in _assert_real_and_PSD()
mgreiff Feb 28, 2019
3aee485
Introduce Assertion - Throw an error when the conitions in _scale_mat…
mgreiff Feb 28, 2019
1e8d72f
Add test for the coordinate transform using the QR decomposition, ena…
mgreiff Mar 1, 2019
d16169e
Add SVD scaling as an option, although, I need to figure out why resu…
mgreiff Mar 2, 2019
9ef1a6e
Add tests for the SVD scaling
mgreiff Mar 2, 2019
88148e9
Remove dead code and include error exception when attempting to secom…
mgreiff Mar 2, 2019
2a0faf0
Add input error checks for the scaling function
mgreiff Mar 2, 2019
0ab6a67
Fix input types to all functions, edit docstrings
mgreiff Mar 2, 2019
dc3e3bc
Edit docstring with list of all functions which are to be tested
mgreiff Mar 2, 2019
f6e4f31
Add docstrings
mgreiff Mar 2, 2019
0e36097
Add recepie to visualize the H-infinity synthesis specifications agai…
mgreiff Mar 4, 2019
c5a2dee
Change the examples to use the recepies
mgreiff Mar 4, 2019
7ef5745
Remove deprecated development files
mgreiff Mar 4, 2019
f2de685
Remove deprecated utilities file
mgreiff Mar 4, 2019
37b4426
Include example pdf files of the controller synthesis
mgreiff Mar 4, 2019
5c8dcf6
Fix discrete time examples so that they use the specificationsplot fu…
mgreiff Mar 4, 2019
df3cc3c
Remove TODOs from the ARE solver
mgreiff Mar 4, 2019
ba7bfeb
Remove MIMO example primarily used for debuging
mgreiff Mar 4, 2019
eed2a82
Clean up bib-entry for the doyle/Glover paper
mgreiff Mar 4, 2019
2d0f99b
Removed pseudoinverse function
mgreiff Mar 4, 2019
9a0fa95
remove PDF files
mgreiff Mar 4, 2019
c4c49fa
Change naming convention to be consistent with https://docs.julialang…
mgreiff Mar 4, 2019
f1569c2
Change naming convention to be consistent with https://docs.julialang…
mgreiff Mar 4, 2019
8c2658b
Change name of all function to be compatible with the style guide
mgreiff Mar 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
docs/build/
docs/site/
.DS_Store
4 changes: 2 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Documenter, ControlSystems, Plots, LinearAlgebra, DSP
using Documenter, ControlSystems, Plots
import GR # Bug with world age in Plots.jl, see https://github.com/JuliaPlots/Plots.jl/issues/1047

include("src/makeplots.jl")
Expand All @@ -23,6 +23,6 @@ end
deploydocs(
repo = "github.com/JuliaControl/ControlSystems.jl.git",
latest = "master",
julia = "1.0",
julia = "0.6",
deps = myDeps
)
8 changes: 4 additions & 4 deletions docs/src/makeplots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Q = Matrix{Float64}(I,2,2)
R = Matrix{Float64}(I,1,1)
L = dlqr(A,B,Q,R) # lqr(sys,Q,R) can also be used

u(x,t) = -L*x .+ 1.5(t>=2.5)# Form control law (u is a function of t and x), a constant input disturbance is affecting the system from t≧2.5
u(x,t) = -L*x + 1.5(t>=2.5)# Form control law (u is a function of t and x), a constant input disturbance is affecting the system from t≧2.5
t=0:h:5
x0 = [1,0]
y, t, x, uout = lsim(sys,u,t,x0=x0)
Expand Down Expand Up @@ -71,7 +71,7 @@ Plots.savefig(plotsDir*"/ppgofplot.svg")

P1(s) = exp(-sqrt(s))
f1 = stabregionPID(P1,exp10.(range(-5, stop=1, length=1000))); Plots.savefig(plotsDir*"/stab1.svg")
P2 = s -> 100*(s+6).^2 ./(s.*(s+1).^2 .*(s+50).^2)
P2 = s -> 100*(s+6).^2./(s.*(s+1).^2.*(s+50).^2)
f2 = stabregionPID(P2,exp10.(range(-5, stop=2, length=1000))); Plots.savefig(plotsDir*"/stab2.svg")
P3 = tf(1,[1,1])^4
f3 = stabregionPID(P3,exp10.(range(-5, stop=0, length=1000))); Plots.savefig(plotsDir*"/stab3.svg")
Expand All @@ -84,15 +84,15 @@ f3 = stabregionPID(P3,exp10.(range(-5, stop=0, length=1000))); Plots.savefig(plo
P = tf([1.],[1., 1])
ζ = 0.5 # Desired damping
ws = exp10.(range(-1, stop=2, length=8)) # A vector of closed-loop bandwidths
kp = 2*ζ*ws.-1 # Simple pole placement with PI given the closed-loop bandwidth, the poles are placed in a butterworth pattern
kp = 2*ζ*ws-1 # Simple pole placement with PI given the closed-loop bandwidth, the poles are placed in a butterworth pattern
ki = ws.^2
pidplots(P,:nyquist,;kps=kp,kis=ki, ω= exp10.(range(-2, stop=2, length=500)))
Plots.savefig(plotsDir*"/pidplotsnyquist1.svg")
pidplots(P,:gof,;kps=kp,kis=ki, ω= exp10.(range(-2, stop=2, length=500)))
Plots.savefig(plotsDir*"/pidplotgof1.svg")

kp = range(-1, stop=1, length=8) # Now try a different strategy, where we have specified a gain crossover frequency of 0.1 rad/s
ki = sqrt.(1 .-kp.^2)./10
ki = sqrt.(1-kp.^2)./10
pidplots(P,:nyquist,;kps=kp,kis=ki)
Plots.savefig(plotsDir*"/pidplotsnyquist2.svg")
pidplots(P,:gof,;kps=kp,kis=ki)
Expand Down
Binary file added example/example_DC_clgain.pdf
Binary file not shown.
Binary file added example/example_DC_specifications.pdf
Binary file not shown.
Binary file added example/example_MIT_clgain.pdf
Binary file not shown.
Binary file added example/example_MIT_specifications.pdf
Binary file not shown.
Binary file added example/example_tank_clgain.pdf
Binary file not shown.
Binary file added example/example_tank_specifications.pdf
Binary file not shown.
Binary file added example/example_tank_stepresponse.pdf
Binary file not shown.
64 changes: 64 additions & 0 deletions example/hinf_example_DC.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Plots
using ControlSystems
"""
This is a simple SISO example with a pole in the origin, corresponding to the
DC servos used in the Lund laboratories. It serves to exeplify how the syntheis
can be done for simple SISO systems, and also demonstrates how we chan verify
if the problem is feasible to solve using the ARE method.

The example can be set to visualize and save plots using the variables
MakePlots - true/false (true if plots are to be generated, false for testing)
SavePlots - true/false (true if plots are to be saved, false for testing)
"""
MakePlots, SavePlots = false, false

# Define the process
Gtrue = tf([11.2], [1, 0.12,0])

# Sensitivity weight function
M, wB, A = 1.5, 20.0, 1e-8
WS = tf([1/M, wB],[1, wB*A])

# Output sensitivity weight function
WU = ss(1)

# Complementary sensitivity weight function
WT = []

# Form the P in the LFT F_l(P,C) as a partitioned state-space object
P = hInf_partition(Gtrue, WS, WU, WT)

# Check if the system is feasible for synythesis
flag = hInf_assumptions(P)

# Since it is not, modify the plant desciption
epsilon = 1e-5
G = tf([11.2], [1, 0.12]) * tf([1], [1, epsilon])

# Form the P in the LFT Fl(P,C) as a partitioned state-space object
P = hInf_partition(G, WS, WU, WT)

# Check if the problem is feasible
flag = hInf_assumptions(P)

# Synthesize the H-infinity optimal controller
flag, C, gamma = hInf_synthesize(P)

# Extract the transfer functions defining some signals of interest
Pcl, S, CS, T = hInf_signals(P, G, C)

## Plot the specifications
if MakePlots
specificationplot([S, CS, T], [WS, WU, WT], gamma)
if SavePlots
savefig("example_DC_specifications.pdf")
end
end

## Plot the closed loop gain from w to z
if MakePlots
specificationplot(Pcl, gamma; s_labels=["\$\\sigma(P_{cl}(j\\omega))\$"], w_labels=["\$\\gamma\$"])
if SavePlots
savefig("example_DC_clgain.pdf")
end
end
60 changes: 60 additions & 0 deletions example/hinf_example_DC_discrete.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Plots
using ControlSystems
"""
This is a simple SISO example with a pole in the origin, corresponding to the
DC servos used in the Lund laboratories. It serves to exeplify how the syntheis
can be done for simple SISO systems, and also demonstrates how we chan verify
if the problem is feasible to solve using the ARE method.

The example can be set to visualize and save plots using the variables
MakePlots - true/false (true if plots are to be generated, false for testing)
SavePlots - true/false (true if plots are to be saved, false for testing)
"""
MakePlots = true

# Define the process
ts = 0.01
epsilon = 1e-5
Gd = ss(c2d(tf([11.2], [1, 0.12]) * tf([1], [1, epsilon]), ts))

# Sensitivity weight function
M, wB, A = 1.5, 20.0, 1e-8
WS = tf([1/M, wB],[1, wB*A])

# Output sensitivity weight function
WU = ss(1)

# Complementary sensitivity weight function
WT = []

# Create continuous time approximation of the process
Gc = hInf_bilinear_z2s(ss(Gd))

# Form the P in the LFT Fl(P,C) as a partitioned state-space object
Pc = hInf_partition(Gc, WS, WU, WT)

# Check if the problem is feasible
flag = hInf_assumptions(Pc)

# Synthesize the H-infinity optimal controller
flag, Cc, gamma = hInf_synthesize(Pc)

# Extract the transfer functions defining some signals of interest, but do so
# using discrete equivalent of the continuous time objects Pc, Cc and Gc
PclD, SD, CSD, TD = hInf_signals(
hInf_bilinear_s2z(Pc, ts),
hInf_bilinear_s2z(Gc, ts),
hInf_bilinear_s2z(Cc, ts)
)

# This solution is a bit hacky and should be revised
Pcl = ss(PclD.A, PclD.B, PclD.C, PclD.D, ts)
S = ss(SD.A, SD.B, SD.C, SD.D, ts)
CS = ss(CSD.A, CSD.B, CSD.C, CSD.D, ts)
T = ss(TD.A, TD.B, TD.C, TD.D, ts)

# Visualize results
if MakePlots
specificationplot([S, CS, T], [WS, WU, WT], gamma)
specificationplot(Pcl, gamma; s_labels=["\$\\sigma(P_{cl}(j\\omega))\$"], w_labels=["\$\\gamma\$"])
end
58 changes: 58 additions & 0 deletions example/hinf_example_MIT.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using ControlSystems
using Plots

"""
This is a simple SISO example which was used for debugging the implementation,
as this exact example was use in the lecture notes of the "Principles of Optimal
Control" cours of the MIT OpenCourseWare [1], where the choice of weighting
functions and dynamics gave rise to an H-infinity optimal cotnroller with a
γ of approximately 1.36, where, in our case, we get a controller at 0.93

[1] https://ocw.mit.edu/courses/aeronautics-and-astronautics/16-323-principles-of-optimal-control-spring-2008/lecture-notes/lec15.pdf

The example can be set to visualize and save plots using the two variables
ShowPlots - true/false (true if plots are to be generated, false for testing)
filename - Set to string if files are to be saved, otherwise set a empty list
"""
MakePlots, SavePlots = false, false

# Define the process
G = tf([200], [0.025,1.0025,10.1,1])

# Sensitivity weight function
M, wB, A = 1.5, 10, 1e-4
WS = tf([1/M, wB],[1, wB*A])

# Output sensitivity weight function
WU = ss(0.1)

# Complementary sensitivity weight function
WT = []

# Form augmented P dynamics in state-space
P = hInf_partition(G, WS, WU, WT)

# Check that the assumptions are satisfied
flag = hInf_assumptions(P)

# Synthesize the H-infinity optimal controller
flag, C, gamma = hInf_synthesize(P)

# Extract the transfer functions defining some signals of interest
Pcl, S, CS, T = hInf_signals(P, G, C)

## Plot the specifications
if MakePlots
specificationplot([S, CS, T], [ss(WS), WU, WT], gamma)
if SavePlots
savefig("example_MIT_specifications.pdf")
end
end

## Plot the closed loop gain from w to z
if MakePlots
specificationplot(Pcl, gamma; s_labels=["\$\\sigma(P_{cl}(j\\omega))\$"], w_labels=["\$\\gamma\$"])
if SavePlots
savefig("example_MIT_clgain.pdf")
end
end
60 changes: 60 additions & 0 deletions example/hinf_example_MIT_discrete.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using ControlSystems
using Plots

"""
This is a simple SISO example which was used for debugging the implementation,
as this exact example was use in the lecture notes of the "Principles of Optimal
Control" cours of the MIT OpenCourseWare [1], however, this example is in discrete time

[1] https://ocw.mit.edu/courses/aeronautics-and-astronautics/16-323-principles-of-optimal-control-spring-2008/lecture-notes/lec15.pdf

The example can be set to visualize and save plots using the two variables
MakePlots - true/false (true if plots are to be generated, false for testing)
SavePlots - true/false (true if plots are to be saved, false for testing)
"""
MakePlots = true

# Define the process
ts = 0.005
Gd = hInf_bilinear_s2z(ss(tf([200], [0.025,1.0025,10.1,1])),ts)

# Sensitivity weight function
M, wB, A = 1.5, 10, 1e-4
WS = tf([1/M, wB],[1, wB*A])

# Output sensitivity weight function
WU = ss(0.1)

# Complementary sensitivity weight function
WT = []

# Create continuous time approximation of the process
Gc = hInf_bilinear_z2s(ss(Gd))

# Form augmented P dynamics in state-space
Pc = hInf_partition(Gc, WS, WU, WT)

# Check that the assumptions are satisfied
flag = hInf_assumptions(Pc)

# Synthesize the H-infinity optimal controller
flag, Cc, gamma = hInf_synthesize(Pc)

# Extract the transfer functions defining some signals of interest, but do so
# using discrete equivalent of the continuous time objects Pc, Cc and Gc
PclD, SD, CSD, TD = hInf_signals(
hInf_bilinear_s2z(Pc, ts),
hInf_bilinear_s2z(Gc, ts),
hInf_bilinear_s2z(Cc, ts)
)

Pcl = ss(PclD.A, PclD.B, PclD.C, PclD.D, ts)
S = ss(SD.A, SD.B, SD.C, SD.D, ts)
CS = ss(CSD.A, CSD.B, CSD.C, CSD.D, ts)
T = ss(TD.A, TD.B, TD.C, TD.D, ts)

# Visualize results
if MakePlots
specificationplot([S, CS, T], [WS, WU, WT], gamma)
specificationplot(Pcl, gamma; s_labels=["\$\\sigma(P_{cl}(j\\omega))\$"], w_labels=["\$\\gamma\$"])
end
99 changes: 99 additions & 0 deletions example/hinf_example_tank.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using ControlSystems
using Plots
using LinearAlgebra
"""
This is a simple SISO example with integrator dynamics corresponding to the
quad tank process in the lab.

The example can be set to visualize and save plots using the variables
MakePlots - true/false (true if plots are to be generated, false for testing)
SavePlots - true/false (true if plots are to be saved, false for testing)
"""
MakePlots, SavePlots = false, false

# Define the proces parameters
k1, k2, kc, g = 3.33, 3.35, 0.5, 981
A1, A3, A2, A4 = 28, 28, 32, 32
a1, a3, a2, a4= 0.071, 0.071, 0.057, 0.057
h01, h02, h03, h04 = 12.4, 12.7, 1.8, 1.4
T1, T2 = (A1/a1)*sqrt(2*h01/g), (A2/a2)*sqrt(2*h02/g)
T3, T4 = (A3/a3)*sqrt(2*h03/g), (A4/a4)*sqrt(2*h04/g)
c1, c2 = (T1*k1*kc/A1), (T2*k2*kc/A2)
gamma1, gamma2 = 0.7, 0.6

# Define the process dynamics
A = [-1/T1 0 A3/(A1*T3) 0;
0 -1/T2 0 A4/(A2*T4);
0 0 -1/T3 0;
0 0 0 -1/T4];
B = [gamma1*k1/A1 0;
0 gamma2*k2/A2;
0 (1-gamma2)*k2/A3;
(1-gamma1)*k1/A4 0 ];
C = [kc 0 0 0;
0 kc 0 0];
D = zeros(2,2)
G = ss(A,B,C,D);

# Sensitivity weight function
WSelement = 100*tf([1,1],[1000,1])
WS = [WSelement 0; 0 WSelement]
iWSelement = 1/WSelement
iWS = [iWSelement 0; 0 iWSelement]

# Output sensitivity weight function
WUelement = 5*tf([1,1],[0.1,1])
WUelement = ss(0.01)
WU = [WUelement 0; 0 WUelement]
iWUelement = 1/WUelement
iWU = [iWUelement 0; 0 iWUelement]

# Complementary sensitivity weight function
WTelement = tf([10,0.1],[1,1])
WT = [WTelement 0; 0 WTelement]
iWTelement = 1/WTelement
iWT = [iWTelement 0; 0 iWTelement]

# Form augmented P dynamics in state-space
P = hInf_partition(G, WS, WU, WT)

# Check that the assumptions are satisfied
flag = hInf_assumptions(P)

# Synthesize the H-infinity optimal controller
flag, C, gamma = hInf_synthesize(P)

Pcl, S, CS, T = hInf_signals(P, G, C)

## Plot the specifications
# TODO figure out why I get segmentation errors when using ss instead of tf for
# the weighting functions, makes no sense at all
if MakePlots
specificationplot([S, CS, T], [WSelement, 0.01, WTelement], gamma)
if SavePlots
savefig("example_tank_specifications.pdf")
end
end

## Plot the closed loop gain from w to z
# TODO figure out why the legends don't seem to work in this case
if MakePlots
specificationplot(Pcl, gamma; s_labels=["\$\\sigma(P_{cl}(j\\omega))\$"], w_labels=["\$\\gamma\$"])
if SavePlots
savefig("example_tank_clgain.pdf")
end
end

if MakePlots
times = [i for i in range(0, stop=300, length=10000)]
stepy, stept, stepx = step(T, times)
pStep1=plot(stept, stepy[:,1,1], color = :blue, w=2, label="\$u_1\\rightarrow y_1\$")
pStep2=plot(stept, stepy[:,1,2], color = :blue, w=2, label="\$u_1\\rightarrow y_2\$", ylims = (-0.5,1.1))
pStep3=plot(stept, stepy[:,2,1], color = :blue, w=2, label="\$u_2\\rightarrow y_1\$", ylims = (-0.5,1.1))
pStep4=plot(stept, stepy[:,2,2], color = :blue, w=2, label="\$u_2\\rightarrow y_2\$")
l = @layout [ a b c d ]
plt=plot(pStep1, pStep2, pStep3, pStep4, layout=l, size=(1000,250))
if SavePlots
savefig("example_tank_stepresponse.pdf")
end
end
Loading