Skip to content

Commit 593da8a

Browse files
committed
Fixed documentation?
1 parent b8ed263 commit 593da8a

File tree

10 files changed

+279
-1941
lines changed

10 files changed

+279
-1941
lines changed

DiffEqFlux.gif

507 KB
Loading

assets/DiffEqFlux.gif

507 KB
Loading

assets/simple_coulomb.png

13.5 KB
Loading

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ makedocs(;
99
"Home" => "index.md",
1010
"Quick Start" => "quickstart.md",
1111
"Examples" => [
12+
"examples/DiffEqFlux.md",
1213
"examples/coulomb_control.md",
1314
"examples/ODE_jac.md",
14-
"examples/DiffEqFlux.md",
1515
],
1616
"API" => "api.md",
1717
],

docs/src/assets/DiffEqFlux.gif

507 KB
Loading
64.7 KB
Loading

docs/src/assets/simple_coulomb.png

13.5 KB
Loading

docs/src/examples/DiffEqFlux.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Neural ODEs with DiffEqFlux
2+
Let's see how easy it is to make dense neural ODE layers from scratch.
3+
Flux is used here just for the `glorot_uniform` function and the `ADAM` optimizer.
4+
5+
This example is taken from https://diffeqflux.sciml.ai/dev/Flux/.
6+
7+
```julia
8+
using ComponentArrays
9+
using OrdinaryDiffEq
10+
using Plots
11+
using UnPack
12+
13+
using DiffEqFlux: sciml_train
14+
using Flux: glorot_uniform, ADAM
15+
using Optim: LBFGS
16+
```
17+
18+
```julia
19+
u0 = Float32[2.; 0.]
20+
datasize = 30
21+
tspan = (0.0f0, 1.5f0)
22+
```
23+
24+
First, let's make a function that creates dense neural layer components. It is similar to `Flux.Dense`, except it doesn't handle the activation function. We'll do that separately.
25+
```julia
26+
dense_layer(in, out) = ComponentArray(W=glorot_uniform(out, in), b=zeros(out))
27+
```
28+
29+
Now we create the truth data.
30+
```julia
31+
function trueODEfunc(du, u, p, t)
32+
true_A = [-0.1 2.0; -2.0 -0.1]
33+
du .= ((u.^3)'true_A)'
34+
end
35+
t = range(tspan[1], tspan[2], length = datasize)
36+
prob = ODEProblem(trueODEfunc, u0, tspan)
37+
ode_data = Array(solve(prob, Tsit5(), saveat = t))
38+
```
39+
40+
We can define a neural ODE function.
41+
```julia
42+
function dudt(u, p, t)
43+
@unpack L1, L2 = p
44+
return L2.W * tanh.(L1.W * u.^3 .+ L1.b) .+ L2.b
45+
end
46+
47+
prob = ODEProblem(dudt, u0, tspan)
48+
```
49+
50+
Our parameter vector will be a `ComponentArray` that holds the ODE initial conditions and the dense neural layers. This enables it to pass through the solver as a flat array while giving us the convenience of struct-like access to the components.
51+
```julia
52+
layers = (L1=dense_layer(2, 50), L2=dense_layer(50, 2))
53+
θ = ComponentArray(u=u0, p=layers)
54+
```
55+
56+
Now let's define prediction and loss functions as well as a callback function to observe training
57+
```julia
58+
predict_n_ode(θ) = Array(solve(prob, Tsit5(), u0=θ.u, p=θ.p, saveat=t))
59+
60+
function loss_n_ode(θ)
61+
pred = predict_n_ode(θ)
62+
loss = sum(abs2, ode_data .- pred)
63+
return loss, pred
64+
end
65+
loss_n_ode(θ)
66+
67+
cb = function (θ, loss, pred; doplot=false)
68+
display(loss)
69+
# plot current prediction against data
70+
pl = scatter(t, ode_data[1,:], label = "data")
71+
scatter!(pl, t, pred[1,:], label = "prediction")
72+
display(plot(pl))
73+
return false
74+
end
75+
```
76+
77+
And now let's train!
78+
```julia
79+
cb(θ, loss_n_ode(θ)...)
80+
81+
data = Iterators.repeated((), 1000)
82+
83+
res1 = sciml_train(loss_n_ode, θ, ADAM(0.05); cb=cb, maxiters=100)
84+
cb(res1.minimizer, loss_n_ode(res1.minimizer)...; doplot=true)
85+
86+
res2 = sciml_train(loss_n_ode, res1.minimizer, LBFGS(); cb=cb)
87+
cb(res2.minimizer, loss_n_ode(res2.minimizer)...; doplot=true)
88+
```
89+
![](../assets/DiffEqFlux.gif)
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Control of a sliding block
2+
```julia
3+
using ComponentArrays
4+
using DifferentialEquations
5+
using Interact: @manipulate
6+
using Parameters: @unpack
7+
using Plots
8+
```
9+
## Problem Setup
10+
```julia
11+
const g = 9.80665
12+
13+
maybe_apply(f::Function, x, p, t) = f(x, p, t)
14+
maybe_apply(f, x, p, t) = f
15+
16+
# Applies functions of form f(x,p,t) to be applied and passed in as inputs
17+
function simulator(func; kwargs...)
18+
simfun(dx, x, p, t) = func(dx, x, p, t; map(f->maybe_apply(f, x, p, t), (;kwargs...))...)
19+
simfun(x, p, t) = func(x, p, t; map(f->maybe_apply(f, x, p, t), (;kwargs...))...)
20+
return simfun
21+
end
22+
23+
softsign(x) = tanh(1e3x)
24+
```
25+
## Component Functions
26+
### A sliding block with two different friction models
27+
```julia
28+
# Sliding block with viscous friction
29+
function viscous_block!(D, vars, p, t; u=0.0)
30+
@unpack m, c, k = p
31+
@unpack v, x = vars
32+
33+
D.x = v
34+
D.v = (-c*v + k*(u-x))/m
35+
return x
36+
end
37+
38+
# Sliding block with coulomb friction
39+
function coulomb_block!(D, vars, p, t; u=0.0)
40+
@unpack m, μ, k = p
41+
@unpack v, x = vars
42+
43+
D.x = v
44+
a = -μ*g*softsign(v) + k*(u-x)/m
45+
D.v = abs(a)<1e-3 && abs(v)<1e-3 ? -10v : a #deadzone to help the simulation
46+
return x
47+
end
48+
```
49+
### PID feedback control
50+
```julia
51+
function PID_controller!(D, vars, p, t; err=0.0, v=0.0)
52+
@unpack kp, ki, kd = p
53+
@unpack x = vars
54+
55+
D.x = ki*err
56+
return x + kp*err + kd*v
57+
end
58+
59+
function feedback_sys!(D, components, p, t; ref=0.0)
60+
@unpack ctrl, plant = components
61+
62+
u = p.ctrl.fun(D.ctrl, ctrl, p.ctrl.params, t; err=ref-plant.x, v=-plant.v)
63+
return p.plant.fun(D.plant, plant, p.plant.params, t; u=u)
64+
end
65+
66+
step_input(;time=1.0, mag=1.0) = (x,p,t) -> t>time ? mag : 0
67+
sine_input(;mag=1.0, period=10.0) = (x,p,t) -> mag*sin(t*2π/period)
68+
69+
# Equivalent viscous damping coefficient taken from:
70+
# https://engineering.purdue.edu/~deadams/ME563/lecture2010.pdf
71+
visc_equiv(μ, N, ω, mag) = 4*μ*N/*ω*mag)
72+
```
73+
## Open-Loop Response
74+
To see the open-loop response of the coulomb system, let's set the input to ```5``` and plot
75+
the results.
76+
```julia
77+
const tspan = (0.0, 30.0)
78+
const m = 50.0
79+
const μ = 0.1
80+
const k = 50.0
81+
82+
p = (m=m, μ=μ, k=k)
83+
ic = ComponentArray(v=0, x=0)
84+
85+
ODEProblem(simulator(coulomb_block!, u=5), ic, tspan, p) |> solve |> plot
86+
```
87+
![](../assets/simple_coulomb.png)
88+
89+
## Closed-Loop Response
90+
For the closed-loop response, let's make an interactive GUI. Since we are using
91+
```ComponentArray```s, we don't have to change anything about our plant model to incorporate
92+
it in the overall system simulation.
93+
```julia
94+
p = (
95+
ctrl = (
96+
params = (kp=13, ki=12, kd=5),
97+
fun = PID_controller!,
98+
),
99+
plant = (
100+
params = plant_p,
101+
fun = coulomb_block!,
102+
),
103+
)
104+
105+
ic = ComponentArray(ctrl=(;x=0), plant=plant_ic)
106+
107+
sol = ODEProblem(simulator(feedback_sys!, ref=10), ic, tspan, p) |> solve
108+
plot(sol, vars=3)
109+
```
110+
111+
```julia
112+
## Interactive GUI for switching out plant models and varying PID gains
113+
@manipulate for kp in 0:0.01:15,
114+
ki in 0:0.01:15,
115+
kd in 0:0.01:15,
116+
damping in Dict(
117+
"Coulomb" => coulomb_block!,
118+
"Viscous" => viscous_block!,
119+
),
120+
reference in Dict(
121+
"Sine" => sine_input,
122+
"Step" => step_input,
123+
),
124+
magnitude in 0:0.01:10, # pop-pop!
125+
period in 1:0.01:30,
126+
plot_v in false
127+
128+
# Inputs
129+
tspan = (0.0, 30.0)
130+
131+
ctrl_fun = PID_controller!
132+
# plant_fun = coulomb_block!
133+
134+
ref = if reference==sine_input
135+
reference(period=period, mag=magnitude)
136+
else
137+
reference(mag=magnitude)
138+
end
139+
140+
m = 50.0
141+
μ = 0.1
142+
ω = 2π/period
143+
c = 4*μ*m*g/*ω*magnitude) # Viscous equivalent damping
144+
k = 50.0
145+
146+
plant_p = (m=m, μ=μ, c=c, k=k) # We'll just put everything for both models in here
147+
ctrl_p = (kp=kp, ki=ki, kd=kd)
148+
149+
plant_ic = (v=0, x=0)
150+
ctrl_ic = (;x=0)
151+
152+
153+
154+
# Set up and solve
155+
sys_p = (
156+
ctrl = (
157+
params = ctrl_p,
158+
fun = ctrl_fun,
159+
),
160+
plant = (
161+
params = plant_p,
162+
fun = damping,
163+
),
164+
)
165+
sys_ic = ComponentArray(ctrl=ctrl_ic, plant=plant_ic)
166+
sys_fun = ODEFunction(simulator(feedback_sys!, ref=ref), syms=[:u, :v, :x])
167+
sys_prob = ODEProblem(sys_fun, sys_ic, tspan, sys_p)
168+
169+
sol = solve(sys_prob, Tsit5())
170+
171+
172+
# Plot
173+
t = tspan[1]:0.1:tspan[2]
174+
lims = magnitude*[-1, 1]
175+
plotvars = plot_v ? [3, 2] : [3]
176+
strip = plot(t, ref.(0, 0, t), ylim=1.2lims, label="r(t)")
177+
plot!(strip, sol, vars=plotvars)
178+
phase = plot(ref.(0, 0, t), map(x->x.plant.x, sol(t).u),
179+
xlim=lims,
180+
ylim=1.2lims,
181+
legend=false,
182+
xlabel="r(t)",
183+
ylabel="x(t)",
184+
)
185+
plot(strip, phase, layout=(2, 1), size=(700, 800))
186+
187+
end
188+
```
189+
![](../assets/coulomb_control.png)

0 commit comments

Comments
 (0)