-
-
Notifications
You must be signed in to change notification settings - Fork 2
Add Friction tutorial #19
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
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
853e826
build(docs): add deps
0eeee0d
docs: add friction tutorial
02041f1
refactor: [temp] add RealInput/RealOutput which works for size equal …
578a561
refactor: use RealInputArray and RealOutputArray
aa625f8
test: update lv tutorial
e7e702e
docs: import Test
4bac4dc
build: bump MTKStdLib compat
b70f2cc
docs: fix link in index.md
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,27 @@ | ||
[deps] | ||
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" | ||
Lux = "b2108857-7c20-44ae-9111-449ecde12c47" | ||
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" | ||
ModelingToolkitStandardLibrary = "16a59e39-deab-5bd0-87e4-056b12336739" | ||
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" | ||
OptimizationOptimisers = "42dfb2eb-d2b4-4451-abcd-913932933ac1" | ||
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" | ||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" | ||
SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226" | ||
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" | ||
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" | ||
ModelingToolkitNeuralNets = "f162e290-f571-43a6-83d9-22ecc16da15f" | ||
|
||
[compat] | ||
Documenter = "1.3" | ||
Lux = "0.5.32" | ||
ModelingToolkit = "9.9" | ||
ModelingToolkitStandardLibrary = "2.7" | ||
Optimization = "3.24" | ||
OptimizationOptimisers = "0.2.1" | ||
OrdinaryDiffEq = "6.74" | ||
Plots = "1" | ||
SciMLStructures = "1.1.0" | ||
StableRNGs = "1" | ||
SymbolicIndexingInterface = "0.3.15" | ||
ModelingToolkitNeuralNets = "1" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,29 @@ | ||
using ModelingToolkitNeuralNets | ||
using Documenter | ||
ENV["GKSwstype"] = "100" | ||
ENV["JULIA_DEBUG"] = "Documenter" | ||
|
||
cp("./docs/Manifest.toml", "./docs/src/assets/Manifest.toml", force = true) | ||
cp("./docs/Project.toml", "./docs/src/assets/Project.toml", force = true) | ||
|
||
DocMeta.setdocmeta!(ModelingToolkitNeuralNets, :DocTestSetup, :(using ModelingToolkitNeuralNets); recursive = true) | ||
DocMeta.setdocmeta!(ModelingToolkitNeuralNets, :DocTestSetup, | ||
:(using ModelingToolkitNeuralNets); recursive = true) | ||
|
||
makedocs(; | ||
modules = [ModelingToolkitNeuralNets], | ||
authors = "Sebastian Micluța-Câmpeanu <[email protected]> and contributors", | ||
sitename = "ModelingToolkitNeuralNets.jl", | ||
format = Documenter.HTML(; | ||
canonical = "https://SciML.github.io/ModelingToolkitNeuralNets.jl", | ||
edit_link = "main", | ||
assets = String[] | ||
), | ||
format = Documenter.HTML(assets = ["assets/favicon.ico"], | ||
canonical = "https://docs.sciml.ai/ModelingToolkitNeuralNets.jl/stable/"), | ||
clean = true, | ||
doctest = false, | ||
linkcheck = true, | ||
pages = [ | ||
"Home" => "index.md" | ||
"Home" => "index.md", | ||
"Tutorials" => [ | ||
"Friction Model" => "friction.md" | ||
], | ||
"API" => "api.md" | ||
] | ||
) | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# API | ||
|
||
```@docs | ||
NeuralNetworkBlock | ||
``` |
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
# Modeling Non Linear Friction Model using UDEs | ||
|
||
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. | ||
|
||
## Julia Environment | ||
|
||
First, lets import the required packages. | ||
|
||
```@example friction | ||
using ModelingToolkitNeuralNets | ||
using ModelingToolkit | ||
import ModelingToolkit.t_nounits as t | ||
import ModelingToolkit.D_nounits as Dt | ||
using ModelingToolkitStandardLibrary.Blocks | ||
using OrdinaryDiffEq | ||
using Optimization | ||
using OptimizationOptimisers: Adam | ||
using SciMLStructures | ||
using SciMLStructures: Tunable | ||
using SymbolicIndexingInterface | ||
using StableRNGs | ||
using Lux | ||
using Plots | ||
using Test #hide | ||
``` | ||
|
||
## Problem Setup | ||
|
||
Let's use the friction model presented in https://www.mathworks.com/help/simscape/ref/translationalfriction.html for generating data. | ||
|
||
```@example friction | ||
Fbrk = 100.0 | ||
vbrk = 10.0 | ||
Fc = 80.0 | ||
vst = vbrk / 10 | ||
vcol = vbrk * sqrt(2) | ||
function friction(v) | ||
sqrt(2 * MathConstants.e) * (Fbrk - Fc) * exp(-(v / vst)^2) * (v / vst) + | ||
Fc * tanh(v / vcol) | ||
end | ||
``` | ||
|
||
Next, we define the model - an object sliding in 1D plane with a constant force `Fu` acting on it and friction force opposing the motion. | ||
|
||
```@example friction | ||
function friction_true() | ||
@variables y(t) = 0.0 | ||
@constants Fu = 120.0 | ||
eqs = [ | ||
Dt(y) ~ Fu - friction(y) | ||
] | ||
return ODESystem(eqs, t, name = :friction_true) | ||
end | ||
``` | ||
|
||
Now that we have defined the model, we will simulate it from 0 to 0.1 seconds. | ||
|
||
```@example friction | ||
model_true = structural_simplify(friction_true()) | ||
prob_true = ODEProblem(model_true, [], (0, 0.1), []) | ||
sol_ref = solve(prob_true, Rodas4(); saveat = 0.001) | ||
``` | ||
|
||
Let's plot it. | ||
|
||
```@example friction | ||
scatter(sol_ref, label = "velocity") | ||
``` | ||
|
||
That was the velocity. Let's also plot the friction force acting on the object throughout the simulation. | ||
|
||
```@example friction | ||
scatter(sol_ref.t, friction.(first.(sol_ref.u)), label = "friction force") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could use the |
||
``` | ||
|
||
## Model Setup | ||
|
||
Now, we will try to 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 `RealInputArray` and `RealOutputArray` blocks. | ||
|
||
```@example friction | ||
function friction_ude(Fu) | ||
@variables y(t) = 0.0 | ||
@constants Fu = Fu | ||
@named nn_in = RealInputArray(nin = 1) | ||
@named nn_out = RealOutputArray(nout = 1) | ||
eqs = [Dt(y) ~ Fu - nn_in.u[1] | ||
y ~ nn_out.u[1]] | ||
return ODESystem(eqs, t, name = :friction, systems = [nn_in, nn_out]) | ||
end | ||
|
||
Fu = 120.0 | ||
model = friction_ude(Fu) | ||
|
||
chain = Lux.Chain( | ||
Lux.Dense(1 => 10, Lux.mish, use_bias = false), | ||
Lux.Dense(10 => 10, Lux.mish, use_bias = false), | ||
Lux.Dense(10 => 1, use_bias = false) | ||
) | ||
nn = NeuralNetworkBlock(1, 1; chain = chain, rng = StableRNG(1111)) | ||
|
||
eqs = [connect(model.nn_in, nn.output) | ||
connect(model.nn_out, nn.input)] | ||
|
||
ude_sys = complete(ODESystem(eqs, t, systems = [model, nn], name = :ude_sys)) | ||
sys = structural_simplify(ude_sys) | ||
``` | ||
|
||
## Optimization Setup | ||
|
||
We now setup the loss function and the optimization loop. | ||
|
||
```@example friction | ||
function loss(x, (prob, sol_ref, get_vars, get_refs)) | ||
new_p = SciMLStructures.replace(Tunable(), prob.p, x) | ||
new_prob = remake(prob, p = new_p, u0 = eltype(x).(prob.u0)) | ||
ts = sol_ref.t | ||
new_sol = solve(new_prob, Rodas4(), saveat = ts, abstol = 1e-8, reltol = 1e-8) | ||
loss = zero(eltype(x)) | ||
for i in eachindex(new_sol.u) | ||
loss += sum(abs2.(get_vars(new_sol, i) .- get_refs(sol_ref, i))) | ||
end | ||
if SciMLBase.successful_retcode(new_sol) | ||
loss | ||
else | ||
Inf | ||
end | ||
end | ||
|
||
of = OptimizationFunction{true}(loss, AutoForwardDiff()) | ||
|
||
prob = ODEProblem(sys, [], (0, 0.1), []) | ||
get_vars = getu(sys, [sys.friction.y]) | ||
get_refs = getu(model_true, [model_true.y]) | ||
x0 = reduce(vcat, getindex.((default_values(sys),), tunable_parameters(sys))) | ||
|
||
cb = (opt_state, loss) -> begin | ||
@info "step $(opt_state.iter), loss: $loss" | ||
return false | ||
end | ||
|
||
op = OptimizationProblem(of, x0, (prob, sol_ref, get_vars, get_refs)) | ||
res = solve(op, Adam(5e-3); maxiters = 10000, callback = cb) | ||
``` | ||
|
||
## Visualization of results | ||
|
||
We now have a trained neural network! We can check whether running the simulation of the model embedded with the neural network matches the data or not. | ||
|
||
```@example friction | ||
res_p = SciMLStructures.replace(Tunable(), prob.p, res) | ||
res_prob = remake(prob, p = res_p) | ||
res_sol = solve(res_prob, Rodas4(), saveat = sol_ref.t) | ||
@test first.(sol_ref.u)≈first.(res_sol.u) rtol=1e-3 #hide | ||
@test friction.(first.(sol_ref.u))≈(Fu .- first.(res_sol(res_sol.t, Val{1}).u)) rtol=1e-1 #hide | ||
``` | ||
|
||
Also, it would be interesting to check the simulation before the training to get an idea of the starting point of the network. | ||
|
||
```@example friction | ||
initial_sol = solve(prob, Rodas4(), saveat = sol_ref.t) | ||
``` | ||
|
||
Now we plot it. | ||
|
||
```@example friction | ||
scatter(sol_ref, idxs = [model_true.y], label = "ground truth velocity") | ||
plot!(res_sol, idxs = [sys.friction.y], label = "velocity after training") | ||
plot!(initial_sol, idxs = [sys.friction.y], label = "velocity before training") | ||
``` | ||
|
||
It matches the data well! Let's also check the predictions for the friction force and whether the network learnt the friction model or not. | ||
|
||
```@example friction | ||
scatter(sol_ref.t, friction.(first.(sol_ref.u)), label = "ground truth friction") | ||
plot!(res_sol.t, Fu .- first.(res_sol(res_sol.t, Val{1}).u), | ||
label = "friction from neural network") | ||
``` | ||
|
||
It learns the friction model well! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.