Skip to content

Commit c1330c6

Browse files
author
Sathvik Bhagavan
committed
docs: add friction tutorial
1 parent b88bbd4 commit c1330c6

File tree

5 files changed

+193
-6
lines changed

5 files changed

+193
-6
lines changed

docs/make.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ makedocs(;
1010
modules = [ModelingToolkitNeuralNets],
1111
authors = "Sebastian Micluța-Câmpeanu <[email protected]> and contributors",
1212
sitename = "ModelingToolkitNeuralNets.jl",
13-
format = Documenter.HTML(;
14-
canonical = "https://SciML.github.io/ModelingToolkitNeuralNets.jl",
15-
edit_link = "main",
16-
assets = String[]
17-
),
13+
format = Documenter.HTML(assets = ["assets/favicon.ico"],
14+
canonical = "https://docs.sciml.ai/ModelingToolkitNeuralNets.jl/stable/"),
15+
clean = true,
16+
doctest = false,
17+
linkcheck = true,
1818
pages = [
19-
"Home" => "index.md"
19+
"Home" => "index.md",
20+
"Tutorials" => [
21+
"Friction Model" => "friction.md"
22+
],
23+
"API" => "api.md"
2024
]
2125
)
2226

docs/src/api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# API
2+
3+
```@docs
4+
NeuralNetworkBlock
5+
```

docs/src/assets/favicon.ico

1.36 KB
Binary file not shown.

docs/src/assets/logo.png

26 KB
Loading

docs/src/friction.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Modeling Non Linear Friction Model using UDEs
2+
3+
Friction between moving bodies is not trivial to model. There have been idealised linear models which are not always useful in complicated systems. There have been many theories and non linear models which we can use, but they are not perfect. The aim of this tutorial to use Universal Differential Equations to showcase how we can embed a neural network to learn an unknown non linear friction model.
4+
5+
## Julia Environment
6+
7+
First, lets import the required packages.
8+
9+
```@example friction
10+
using UDEComponents
11+
using ModelingToolkit
12+
import ModelingToolkit.t_nounits as t
13+
import ModelingToolkit.D_nounits as Dt
14+
using ModelingToolkitStandardLibrary.Blocks
15+
using OrdinaryDiffEq
16+
using Optimization
17+
using OptimizationOptimisers: Adam
18+
using SciMLStructures
19+
using SciMLStructures: Tunable
20+
using SymbolicIndexingInterface
21+
using StableRNGs
22+
using Lux
23+
using Plots
24+
```
25+
26+
## Problem Setup
27+
28+
Lets use the friction model presented in https://www.mathworks.com/help/simscape/ref/translationalfriction.html for generating data.
29+
30+
```@example friction
31+
Fbrk = 100.0
32+
vbrk = 10.0
33+
Fc = 80.0
34+
vst = vbrk / 10
35+
vcol = vbrk * sqrt(2)
36+
function friction(v)
37+
sqrt(2 * MathConstants.e) * (Fbrk - Fc) * exp(-(v / vst)^2) * (v / vst) +
38+
Fc * tanh(v / vcol)
39+
end
40+
```
41+
42+
Lets define the model - an object sliding in 1D plane with a constant force `Fu` acting on it and friction force opposing the motion.
43+
44+
```@example friction
45+
function friction_true()
46+
@variables y(t) = 0.0
47+
@constants Fu = 120.0
48+
eqs = [
49+
Dt(y) ~ Fu - friction(y)
50+
]
51+
return ODESystem(eqs, t, name = :friction_true)
52+
end
53+
```
54+
55+
Now that we have defined the model, lets simulate it from 0 to 1 seconds.
56+
57+
```@example friction
58+
model_true = structural_simplify(friction_true())
59+
prob_true = ODEProblem(model_true, [], (0, 0.1), [])
60+
sol_ref = solve(prob_true, Rodas4(); saveat = 0.001)
61+
```
62+
63+
Lets plot it.
64+
65+
```@example friction
66+
scatter(sol_ref, label = "velocity")
67+
```
68+
69+
That was the velocity. Lets also plot the friction force acting on the object throughout the simulation.
70+
71+
```@example friction
72+
scatter(sol_ref.t, friction.(first.(sol_ref.u)), label = "friction force")
73+
```
74+
75+
## Model Setup
76+
77+
Now, lets learn the same friction model using a neural network. We will use [`NeuralNetworkBlock`](@ref) to define neural network as a component. The input of the neural network is the velocity and the output is the friction force. We connect the neural network with the model using `RealInput` and `RealOutput` blocks.
78+
79+
```@example friction
80+
function friction_ude(Fu)
81+
@variables y(t) = 0.0
82+
@constants Fu = Fu
83+
@named nn_in = UDEComponents.RealInput2(nin = 1)
84+
@named nn_out = UDEComponents.RealOutput2(nout = 1)
85+
eqs = [Dt(y) ~ Fu - nn_in.u[1]
86+
y ~ nn_out.u[1]]
87+
return ODESystem(eqs, t, name = :friction, systems = [nn_in, nn_out])
88+
end
89+
90+
Fu = 120.0
91+
model = friction_ude(Fu)
92+
93+
chain = Lux.Chain(
94+
Lux.Dense(1 => 10, Lux.mish, use_bias = false),
95+
Lux.Dense(10 => 10, Lux.mish, use_bias = false),
96+
Lux.Dense(10 => 1, use_bias = false)
97+
)
98+
nn = NeuralNetworkBlock(1, 1; chain = chain, rng = StableRNG(1111))
99+
100+
eqs = [connect(model.nn_in, nn.output)
101+
connect(model.nn_out, nn.input)]
102+
103+
ude_sys = complete(ODESystem(eqs, t, systems = [model, nn], name = :ude_sys))
104+
sys = structural_simplify(ude_sys)
105+
```
106+
107+
## Optimization Setup
108+
109+
We now setup the loss function and the optimization loop.
110+
111+
```@example friction
112+
function loss(x, (prob, sol_ref, get_vars, get_refs))
113+
new_p = SciMLStructures.replace(Tunable(), prob.p, x)
114+
new_prob = remake(prob, p = new_p, u0 = eltype(x).(prob.u0))
115+
ts = sol_ref.t
116+
new_sol = solve(new_prob, Rodas4(), saveat = ts, abstol = 1e-8, reltol = 1e-8)
117+
loss = zero(eltype(x))
118+
for i in eachindex(new_sol.u)
119+
loss += sum(abs2.(get_vars(new_sol, i) .- get_refs(sol_ref, i)))
120+
end
121+
if SciMLBase.successful_retcode(new_sol)
122+
loss
123+
else
124+
Inf
125+
end
126+
end
127+
128+
of = OptimizationFunction{true}(loss, AutoForwardDiff())
129+
130+
prob = ODEProblem(sys, [], (0, 0.1), [])
131+
get_vars = getu(sys, [sys.friction.y])
132+
get_refs = getu(model_true, [model_true.y])
133+
x0 = reduce(vcat, getindex.((default_values(sys),), tunable_parameters(sys)))
134+
135+
cb = (opt_state, loss) -> begin
136+
@info "step $(opt_state.iter), loss: $loss"
137+
return false
138+
end
139+
140+
op = OptimizationProblem(of, x0, (prob, sol_ref, get_vars, get_refs))
141+
res = solve(op, Adam(5e-3); maxiters = 10000, callback = cb)
142+
```
143+
144+
## Visualization of results
145+
146+
We now have a trained neural network! Lets check whether running the simulation of the model embedded with the neural network matches the data or not.
147+
148+
```@example friction
149+
res_p = SciMLStructures.replace(Tunable(), prob.p, res)
150+
res_prob = remake(prob, p = res_p)
151+
res_sol = solve(res_prob, Rodas4(), saveat = sol_ref.t)
152+
@test first.(sol_ref.u)≈first.(res_sol.u) rtol=1e-3 #hide
153+
@test friction.(first.(sol_ref.u))≈(Fu .- first.(res_sol(res_sol.t, Val{1}).u)) rtol=1e-1 #hide
154+
```
155+
156+
Also, lets check the simulation before the training as well to get an idea of the starting point of the network.
157+
158+
```@example friction
159+
initial_sol = solve(prob, Rodas4(), saveat = sol_ref.t)
160+
```
161+
162+
Lets plot it.
163+
164+
```@example friction
165+
scatter(sol_ref, idxs = [model_true.y], label = "ground truth velocity")
166+
plot!(res_sol, idxs = [sys.friction.y], label = "velocity after training")
167+
plot!(initial_sol, idxs = [sys.friction.y], label = "velocity before training")
168+
```
169+
170+
It matches the data well! Lets also check the friction force and whether the network learnt the friction model or not.
171+
172+
```@example friction
173+
scatter(sol_ref.t, friction.(first.(sol_ref.u)), label = "ground truth friction")
174+
plot!(res_sol.t, Fu .- first.(res_sol(res_sol.t, Val{1}).u),
175+
label = "friction from neural network")
176+
```
177+
178+
It learns the friction model well!

0 commit comments

Comments
 (0)