Skip to content

Commit f4a489d

Browse files
authored
Merge pull request #8 from JuliaComputing/reproducer
First working components
2 parents c2aa965 + 58b0150 commit f4a489d

File tree

13 files changed

+922
-1230
lines changed

13 files changed

+922
-1230
lines changed

Manifest.toml

Lines changed: 0 additions & 1082 deletions
This file was deleted.

Project.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
name = "Multibody"
22
uuid = "e1cad5d1-98ef-44f9-a79a-9ca4547f95b9"
3-
authors = ["Yingbo Ma <[email protected]> and contributors"]
3+
authors = ["JuliaHub Inc."]
44
version = "0.1.0"
55

66
[deps]
7+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
78
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
89
ModelingToolkitStandardLibrary = "16a59e39-deab-5bd0-87e4-056b12336739"
10+
Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
11+
SymbolicIR = "f7e81a98-b176-4533-a916-1afd54a23a03"
912

1013
[compat]
1114
ModelingToolkit = "8.30"
1215
ModelingToolkitStandardLibrary = "1.8"
16+
Rotations = "1.4"
17+
SymbolicIR = "0.1"
1318
julia = "1"
1419

1520
[extras]

docs/Manifest.toml

Lines changed: 0 additions & 103 deletions
This file was deleted.

docs/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
3+
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
4+
ModelingToolkitStandardLibrary = "16a59e39-deab-5bd0-87e4-056b12336739"
35
Multibody = "e1cad5d1-98ef-44f9-a79a-9ca4547f95b9"
6+
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"

docs/make.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ makedocs(;
1212
edit_link = nothing),
1313
pages = [
1414
"Home" => "index.md",
15+
"Tutorials" => [
16+
"Hello world: Pendulum" => "examples/pendulum.md",
17+
],
1518
])
1619

1720
deploydocs(;

docs/src/examples/pendulum.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Pendulum---The "Hello World of multi-body dynamics"
2+
This beginners tutorial will model a pendulum pivoted around the origin in the world frame. The world frame is a constant that lives inside the Multibody module, all multibody models are "grounded" in the same world. To start, we load the required packages
3+
```@example pendulum
4+
using ModelingToolkit
5+
using Multibody
6+
using OrdinaryDiffEq # Contains the ODE solver we will use
7+
using Plots
8+
```
9+
We then access the world frame and time variable from the Multibody module
10+
```@example pendulum
11+
t = Multibody.t
12+
world = Multibody.world
13+
```
14+
15+
## Modeling the pendulum
16+
Our simple pendulum will initially consist of a [`Body`](@ref) and a [`Revolute`](@ref) joint (the pivot joint). We construct these elements by calling their constructors
17+
```@example pendulum
18+
@named joint = Revolute(n = [0, 0, 1], isroot = true)
19+
@named body = Body(; m = 1, isroot = false, r_cm = [0.5, 0, 0])
20+
nothing # hide
21+
```
22+
The `n` argument to [`Revolute`](@ref) denotes the rotational axis of the joint, this vector must have `norm(n) == 1`. We also indicate that the revolute joint is the root of the kinematic tree, i.e., the potential states of the joint will serve as the state variables for the system.
23+
24+
The [`Body`](@ref) is constructed by providing its mass, `m`, and the vector `r_cm` from its first frame, `body.frame_a`, to the center of mass. Since the world by default has the gravity field pointing along the negative ``y`` axis, we place the center of mass along the ``x``-axis to make the pendulum swing back and forth. The body is not selected as the root of the kinematic tree, since we have a joint in this system, but if we had attached the body directly to, e.g., a spring, we could set the body to be the root and avoid having to introduce an "artificial joint".
25+
26+
To connect the components together, we create a vector of connections using the `connect` function. A joint typically has two frames, `frame_a` and `frame_b`. The first frame of the joint is attached to the world frame, and the body is attached to the second joint frame. The order of the connections is not important for ModelingToolkit, but it's good practice to follow some convention, here, we start at the world and progress outwards in the kinematic tree.
27+
```@example pendulum
28+
connections = [
29+
connect(world.frame_b, joint.frame_a)
30+
connect(joint.frame_b, body.frame_a)
31+
]
32+
nothing # hide
33+
```
34+
35+
With all components and connections defined, we can create an `ODESystem` like so:
36+
```@example pendulum
37+
@named model = ODESystem(connections, t, systems=[world, joint, body])
38+
nothing # hide
39+
```
40+
The `ODESystem` is the fundamental model type in ModelingToolkit used for multibody-type models.
41+
42+
Before we can simulate the system, we must perform model compilation using [`structural_simplify`](@ref)
43+
```@example pendulum
44+
ssys = structural_simplify(model, allow_parameter = false)
45+
```
46+
This results in a simplified model with the minimum required variables and equations to be able to simulate the system efficiently. This step rewrites all `connect` statements into the appropriate equations, and removes any redundant variables and equations.
47+
48+
We are now ready to create an `ODEProblem` and simulate it. We use the `Rodas4` solver from OrdinaryDiffEq.jl, and pass a dictionary for the initial conditions. We specify only initial condition for some variables, for those variables where no initial condition is specified, the default initial condition defined the model will be used.
49+
```@example pendulum
50+
D = Differential(t)
51+
defs = Dict(D(joint.phi) => 0, D(D(joint.phi)) => 0)
52+
prob = ODEProblem(ssys, defs, (0, 10))
53+
54+
sol = solve(prob, Rodas4())
55+
plot(sol, idxs = joint.phi)
56+
```
57+
The solution `sol` can be plotted directly if the Plots package is loaded. The figure indicates that the pendulum swings back and forth without any damping. To add damping as well, we could add a `Damper` from the `ModelingToolkitStandardLibrary.Mechanical.Rotational` module to the revolute joint. We do this below
58+
59+
## Adding damping
60+
To add damping to the pendulum such that the pendulum will eventually come to rest, we add a [`Damper`](@ref) to the revolute joint. The damping coefficient is given by `d`, and the damping force is proportional to the angular velocity of the joint. To add the damper to the revolute joint, we must create the joint with the keyword argument `useAxisFlange = true`, this adds two internal flanges to the joint to which you can attach components from the `ModelingToolkitStandardLibrary.Mechanical.Rotational` module. We then connect one of the flanges of the damper to the axis flange of the joint, and the other damper flange to the support flange which is rigidly attached to the world.
61+
```@example pendulum
62+
using ModelingToolkitStandardLibrary.Mechanical.Rotational
63+
64+
@named damper = Damper(d = 0.1)
65+
@named joint = Revolute(n = [0, 0, 1], isroot = true, useAxisFlange = true)
66+
67+
connections = [connect(world.frame_b, joint.frame_a)
68+
connect(damper.flange_b, joint.axis)
69+
connect(joint.support, damper.flange_a)
70+
connect(body.frame_a, joint.frame_b)]
71+
72+
@named model = ODESystem(connections, t, systems = [world, joint, body, damper])
73+
ssys = structural_simplify(model, allow_parameter = false)
74+
75+
76+
prob = ODEProblem(ssys, [damper.phi_rel => 1, D(joint.phi) => 0, D(D(joint.phi)) => 0],
77+
(0, 30))
78+
79+
80+
sol = solve(prob, Rodas4())
81+
plot(sol, idxs = collect(joint.phi))
82+
```
83+
This time we see that the pendulum loses energy and eventually comes to rest at the stable equilibrium point ``\pi / 2``.

src/Multibody.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
11
module Multibody
22

3+
using LinearAlgebra
34
using ModelingToolkit
5+
import ModelingToolkitStandardLibrary.Mechanical.Rotational
6+
47
const t = let
58
(@variables t)[1]
69
end
710
const D = Differential(t)
811

12+
"""
13+
at_variables_t(args)
14+
15+
Emulates the `@variables` macro but does never creates array variables. Also never introuces the variable names into the calling scope.
16+
"""
17+
function at_variables_t(args...; default = nothing)
18+
xs = Symbolics.variables(args...; T = Symbolics.FnType)
19+
xs = map(x -> x(t), xs)
20+
if default !== nothing
21+
xs = Symbolics.setdefaultval.(xs, default)
22+
end
23+
xs
24+
end
25+
26+
export Orientation, RotationMatrix
27+
include("orientation.jl")
28+
29+
export Frame
930
include("frames.jl")
1031

32+
include("interfaces.jl")
33+
34+
export World, world, FixedTranslation, Revolute, Body
35+
include("components.jl")
36+
37+
export Spring
38+
include("forces.jl")
39+
1140
end

0 commit comments

Comments
 (0)