Skip to content

Commit 74fa946

Browse files
authored
Merge pull request #23 from OpenSourceAWE/linearize
Linearize the system to analyze the stability
2 parents 945d4ba + eef9523 commit 74fa946

File tree

9 files changed

+476
-13
lines changed

9 files changed

+476
-13
lines changed

Project.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ authors = ["Uwe Fechner <[email protected]> and contributors"]
44
version = "0.5.3"
55

66
[deps]
7-
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
87
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
98
FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41"
109
KiteUtils = "90980105-b163-44e5-ba9f-8b1c83bb0533"
@@ -32,6 +31,7 @@ NLsolve = "4.5.1"
3231
NOMAD = "2.4.1"
3332
Parameters = "0.12.3"
3433
Pkg = "1.10, 1.11"
34+
RobustAndOptimalControl = "0.4.39"
3535
StaticArrays = "1.9.13"
3636
Statistics = "1.10, 1.11.1"
3737
StructTypes = "1.10, 1.11"
@@ -44,9 +44,11 @@ julia = "1.10, 1.11"
4444
[extras]
4545
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
4646
ControlPlots = "23c2ee80-7a9e-4350-b264-8e670f12517c"
47+
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
48+
RobustAndOptimalControl = "21fd56a4-db03-40ee-82ee-a87907bee541"
4749
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
4850
NOMAD = "02130f1c-4665-5b79-af82-ff1385104aa0"
4951
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
5052

5153
[targets]
52-
test = ["Test", "ControlPlots", "Documenter", "Aqua", "NOMAD"]
54+
test = ["Test", "ControlPlots", "ControlSystemsBase", "Documenter", "Aqua", "NOMAD", "RobustAndOptimalControl"]

examples/menu.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ options = ["test_winchcontroller = include(\"test_winchcontroller.jl\")",
66
"autotune_ = include(\"autotune.jl\")",
77
"test_tuned_winchcontroller = include(\"test_tuned_winchcontroller.jl\")",
88
"plot_power = include(\"plot_power.jl\")",
9+
"stability_ufc = include(\"stability_ufc.jl\")",
910
"help = WinchControllers.help()",
1011
"quit"]
1112

examples/stability_ufc.jl

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Linearize the closed loop system consisting of the winch, kite and upper force controller.
2+
3+
using Pkg
4+
if ! ("ControlPlots" keys(Pkg.project().dependencies))
5+
using TestEnv; TestEnv.activate()
6+
using Test
7+
end
8+
using WinchControllers, WinchModels, KiteUtils, ControlPlots, ControlSystemsBase, FiniteDiff, RobustAndOptimalControl
9+
import FiniteDiff: finite_difference_jacobian
10+
11+
if isfile("data/system_tuned.yaml")
12+
set = load_settings("system_tuned.yaml")
13+
else
14+
set = load_settings("system.yaml")
15+
end
16+
wcs = WCSettings(dt=0.02)
17+
update(wcs)
18+
wcs.test = true
19+
20+
winch = Winch(wcs, set)
21+
22+
@info "Testing stability of the upper force controller (UFC) with the winch and kite system."
23+
24+
# find equilibrium speed
25+
function find_equilibrium_speed(winch, set_speed, force, n=10000)
26+
last_v_act = 0.0
27+
for v_set in range(0.0, 2*set_speed, n)
28+
lim_speed = minimum([v_set, set_speed])
29+
set_force(winch, force)
30+
set_v_set(winch, lim_speed)
31+
v_act = get_speed(winch)
32+
on_timer(winch)
33+
if v_set > 0 && abs(v_act - last_v_act) < 1e-6
34+
return v_act
35+
end
36+
last_v_act = v_act
37+
end
38+
set_v_set(winch, set_speed)
39+
on_timer(winch)
40+
@error "Failed to find equilibrium speed"
41+
end
42+
43+
function motor_dynamics(x, u)
44+
# x: state vector, e.g., [v_act]
45+
# u: input vector, e.g., [v_set, force]
46+
v_act = x[1]
47+
v_set, force = u[1], u[2]
48+
acc = calc_acceleration(winch.wm, v_act, force; set_speed = v_set)
49+
return [acc]
50+
end
51+
52+
function calc_force(v_wind, v_ro)
53+
(v_wind - v_ro)^2 * 4000.0 / 16.0
54+
end
55+
56+
# create the upper force controller
57+
# Tf​ can typically be chosen as Ti/NT for a PI controller and Td/N for a PID controller, and N is commonly in the range 2 to 20.
58+
# Td = wcs.df_high / wcs.pf_high Kd/Kp
59+
function upper_force_controller(wcs)
60+
# wcs: WCSettings object
61+
Td = wcs.df_high / wcs.pf_high
62+
N = wcs.nf_high
63+
Tf = Td / N
64+
C = pid(wcs.pf_high, wcs.if_high, wcs.df_high; form=:parallel, filter_order=1, state_space=true, Tf)
65+
return C
66+
end
67+
68+
function system_dynamics(x, u)
69+
# x: state vector, e.g., [v_act]
70+
# u: input vector, e.g., [v_set, v_wind]
71+
v_act = x[1]
72+
v_set, v_wind = u[1], u[2]
73+
force = calc_force(v_wind, v_act)
74+
acc = calc_acceleration(winch.wm, v_act, force; set_speed = v_set)
75+
return [acc]
76+
end
77+
78+
function linearize(winch, v_set, v_wind)
79+
force = calc_force(v_wind, v_set)
80+
v_act = find_equilibrium_speed(winch, v_set, force)
81+
x0 = [v_act] # State at operating point
82+
u0 = [v_set, v_wind] # Input at operating point
83+
A = finite_difference_jacobian(x -> system_dynamics(x, u0), x0)
84+
B = finite_difference_jacobian(u -> system_dynamics(x0, u), u0)
85+
C = [1.0]
86+
D = [0.0 0.0]
87+
force = calc_force(v_wind, v_act)
88+
siso_sys = ss(A, B[:, 1], C, D[:, 1]) * force/v_act
89+
end
90+
91+
function open_loop_system(winch, v_set, v_wind)
92+
# Create the open loop system with the upper force controller
93+
C = upper_force_controller(winch.wcs)
94+
sys = linearize(winch, v_set, v_wind)
95+
# sys_open = feedback(C * sys, 1.0; sign=-1)
96+
return C * sys
97+
end
98+
99+
function margins()
100+
margins = []
101+
for v_wind in range(1, 9, length=9)
102+
local v_set, dm
103+
v_set = 0.57*v_wind
104+
sys = open_loop_system(winch, v_set, v_wind)
105+
dm = diskmargin(sys)
106+
push!(margins, dm.margin)
107+
end
108+
min_margin = minimum(margins)
109+
if min_margin < 0.3
110+
@error "System is unstable with a minimum margin of: $min_margin"
111+
elseif min_margin < 0.5
112+
@warn "System is marginally stable with a minimum margin of: $min_margin"
113+
else
114+
@info "System is stable with a minimum margin of: $(round(min_margin, digits=2))"
115+
end
116+
return margins
117+
end
118+
margins()
119+
120+
for v_wind in range(7.5, 9, length=3)
121+
global sys
122+
local v_set
123+
v_set = 0.57*v_wind
124+
sys = open_loop_system(winch, v_set, v_wind)
125+
bode_plot(sys; from=0.76, to=2.85, title="System with UFC, v_wind=7.5..9 m/s")
126+
end
127+
nothing

mwes/mwe_02.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function motor_dynamics(x, u)
4747
return [acc]
4848
end
4949

50-
v_set = 8.0
50+
v_set = 4.0
5151
force = 4000.0
5252
v_act = find_equilibrium_speed(winch, v_set, force)
5353

mwes/mwe_03.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function motor_dynamics(x, u)
4949
return [acc]
5050
end
5151

52-
v_set = 8.0
52+
v_set = 4.0
5353
force = 4000.0
5454
v_act = find_equilibrium_speed(winch, v_set, force)
5555

@@ -68,4 +68,4 @@ D = [0.0 0.0]
6868
sys = ss(A, B, C, D)
6969
sys_new = ss(A, B[:, 1], C, D[:, 1])
7070

71-
bode_plot(sys_new; to=2, title="Linearized Winch, F=$force N")
71+
bode_plot(sys_new; from=0.76, to=2.85, title="Linearized Winch, F=$force N")

mwes/mwe_04.jl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ function linearize(winch, v_set, force)
6262
end
6363

6464
v_set = 4.0
65-
for force in range(300.0, 3800.0, length=10)
66-
@info "Linearizing for force: $force N"
67-
sys_new = linearize(winch, v_set, force)
68-
# @info "System: $sys_new"
69-
# @info "Eigenvalues: $(eigvals(sys_new))"
70-
bode_plot(sys_new; from=0.76, to=2.85, title="Linearized Winch, F=300..3800 N")
71-
end
65+
# for force in range(300.0, 3800.0, length=10)
66+
# @info "Linearizing for force: $force N"
67+
# sys_new = linearize(winch, v_set, force)
68+
# # @info "System: $sys_new"
69+
# # @info "Eigenvalues: $(eigvals(sys_new))"
70+
# bode_plot(sys_new; from=0.76, to=2.85, title="Linearized Winch, F=300..3800 N")
71+
# end
7272

73-
# bode_plot(sys_new; to=2, title="Linearized Winch, F=$force N")
73+
sys_new = linearize(winch, v_set, 1000.0)
74+
bode_plot(sys_new; from=0.76, to=2.85, title="Linearized Winch, F=1000 N")

mwes/mwe_05.jl

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Linearize the system consisting of the winch and kite
2+
# input: set_speed
3+
# output: speed
4+
5+
using Pkg
6+
if ! ("ControlPlots" keys(Pkg.project().dependencies))
7+
using TestEnv; TestEnv.activate()
8+
using Test
9+
end
10+
using WinchControllers, WinchModels, KiteUtils, ControlPlots, ControlSystemsBase, FiniteDiff
11+
import FiniteDiff: finite_difference_jacobian
12+
13+
if isfile("data/system_tuned.yaml")
14+
set = load_settings("system_tuned.yaml")
15+
else
16+
set = load_settings("system.yaml")
17+
end
18+
wcs = WCSettings(dt=0.02)
19+
update(wcs)
20+
wcs.test = true
21+
22+
winch = Winch(wcs, set)
23+
24+
# find equilibrium speed
25+
function find_equilibrium_speed(winch, set_speed, force, n=10000)
26+
last_v_act = 0.0
27+
for v_set in range(0.0, 2*set_speed, n)
28+
lim_speed = minimum([v_set, set_speed])
29+
set_force(winch, force)
30+
set_v_set(winch, lim_speed)
31+
v_act = get_speed(winch)
32+
on_timer(winch)
33+
if v_set > 0 && abs(v_act - last_v_act) < 1e-6
34+
return v_act
35+
end
36+
last_v_act = v_act
37+
end
38+
set_v_set(winch, set_speed)
39+
on_timer(winch)
40+
@error "Failed to find equilibrium speed"
41+
end
42+
43+
function motor_dynamics(x, u)
44+
# x: state vector, e.g., [v_act]
45+
# u: input vector, e.g., [v_set, force]
46+
v_act = x[1]
47+
v_set, force = u[1], u[2]
48+
acc = calc_acceleration(winch.wm, v_act, force; set_speed = v_set)
49+
return [acc]
50+
end
51+
52+
function calc_force(v_wind, v_ro)
53+
(v_wind - v_ro)^2 * 4000.0 / 16.0
54+
end
55+
56+
function system_dynamics(x, u)
57+
# x: state vector, e.g., [v_act]
58+
# u: input vector, e.g., [v_set, v_wind]
59+
v_act = x[1]
60+
v_set, v_wind = u[1], u[2]
61+
force = calc_force(v_wind, v_act)
62+
acc = calc_acceleration(winch.wm, v_act, force; set_speed = v_set)
63+
return [acc]
64+
end
65+
66+
function linearize(winch, v_set, v_wind)
67+
force = calc_force(v_wind, v_set)
68+
v_act = find_equilibrium_speed(winch, v_set, force)
69+
x0 = [v_act] # State at operating point
70+
u0 = [v_set, v_wind] # Input at operating point
71+
A = finite_difference_jacobian(x -> system_dynamics(x, u0), x0)
72+
B = finite_difference_jacobian(u -> system_dynamics(x0, u), u0)
73+
C = [1.0]
74+
D = [0.0 0.0]
75+
force = calc_force(v_wind, v_act)
76+
siso_sys = ss(A, B[:, 1], C, D[:, 1]) * force/v_act
77+
end
78+
79+
for v_wind in range(1, 9, length=9)
80+
local v_set, sys_new
81+
v_set = 0.57*v_wind
82+
@info "Linearizing for v_wind: $v_wind m/s, v_ro: $(round(v_set, digits=2)) m/s"
83+
sys = linearize(winch, v_set, v_wind)
84+
bode_plot(sys; from=0.76, to=2.85, title="Linearized System, v_wind=1..9 m/s")
85+
end

0 commit comments

Comments
 (0)