Skip to content

Commit 8a181b8

Browse files
authored
add point gravity model and space tutorial (#79)
* add point gravity model and space tutorial * include space in make
1 parent b90f804 commit 8a181b8

File tree

4 files changed

+132
-3
lines changed

4 files changed

+132
-3
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ makedocs(;
3131
"Kinematic loops" => "examples/kinematic_loops.md",
3232
"Industrial robot" => "examples/robot.md",
3333
"Ropes, cables and chains" => "examples/ropes_and_cables.md",
34+
"Bodies in space" => "examples/space.md",
3435
],
3536
"3D rendering" => "rendering.md",
3637
])

docs/src/examples/space.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Bodies in space
2+
3+
The default gravity model in Multibody.jl computes the gravitational acceleration according to
4+
```math
5+
a = g n
6+
```
7+
where ``g`` and ``n`` are properties of the `world`. The default values for these parameters are `g = 9.81` and `n = [0, -1, 0]`.
8+
9+
This example demonstrates how to use the _point gravity_ model, in which the gravitational acceleration is always pointing towards the origin, with a magnitude determined by ``\mu`` and ``r``
10+
```math
11+
-\dfrac{\mu}{r^T r} \dfrac{r}{||r||}
12+
```
13+
where ``\mu`` is the gravity field constant (defaults to 3.986004418e14 for Earth) and ``r`` is the distance of a body from the origin.
14+
15+
In this example, we set ``\mu = 1``, `point_gravity = true` and let two masses orbit an invisible attractor at the origin.
16+
17+
```@example SPACE
18+
using Multibody
19+
using ModelingToolkit
20+
using Plots
21+
using JuliaSimCompiler
22+
using OrdinaryDiffEq
23+
24+
t = Multibody.t
25+
D = Differential(t)
26+
W() = Multibody.world
27+
28+
@mtkmodel PointGrav begin
29+
@components begin
30+
world = W()
31+
body1 = Body(
32+
m=1,
33+
I_11=0.1,
34+
I_22=0.1,
35+
I_33=0.1,
36+
r_0=[0,0.6,0],
37+
isroot=true,
38+
v_0=[1,0,0])
39+
body2 = Body(
40+
m=1,
41+
I_11=0.1,
42+
I_22=0.1,
43+
I_33=0.1,
44+
r_0=[0.6,0.6,0],
45+
isroot=true,
46+
v_0=[0.6,0,0])
47+
end
48+
end
49+
@named model = PointGrav()
50+
model = complete(model)
51+
ssys = structural_simplify(IRSystem(model))
52+
defs = [
53+
model.world.mu => 1
54+
model.world.point_gravity => true # The gravity model is selected here
55+
collect(model.body1.w_a) .=> 0
56+
collect(model.body2.w_a) .=> 0
57+
58+
]
59+
prob = ODEProblem(ssys, defs, (0, 5))
60+
sol = solve(prob, Rodas4())
61+
plot(sol)
62+
```
63+
64+
65+
```@example SPACE
66+
import CairoMakie
67+
Multibody.render(model, sol, filename = "space.gif")
68+
nothing # hide
69+
```
70+
![space](space.gif)
71+
72+
73+
## Turning gravity off
74+
To simulate without gravity, or with a gravity corresponding to that of another celestial body, set the value of ``g`` or ``\mu`` appropriately for the chosen gravity model.

src/components.jl

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,23 @@ end
3838
"""
3939
World(; name, render=true)
4040
"""
41-
@component function World(; name, render=true)
41+
@component function World(; name, render=true, point_gravity=false)
4242
# World should have
4343
# 3+3+9+3 // r_0+f+R.R+τ
4444
# - (3+3) // (f+t)
4545
# = 12 equations
4646
@named frame_b = Frame()
4747
@parameters n[1:3]=[0, -1, 0] [description = "gravity direction of world"]
4848
@parameters g=9.81 [description = "gravitational acceleration of world"]
49+
@parameters mu=3.986004418e14 [description = "Gravity field constant [m³/s²] (default = field constant of earth)"]
4950
@parameters render=render
51+
@parameters point_gravity = point_gravity
5052
O = ori(frame_b)
5153
eqs = Equation[collect(frame_b.r_0) .~ 0;
5254
O ~ nullrotation()
5355
# vec(D(O).R .~ 0); # QUESTION: not sure if I should have to add this, should only have 12 equations according to modelica paper
5456
]
55-
ODESystem(eqs, t, [], [n; g; render]; name, systems = [frame_b])
57+
ODESystem(eqs, t, [], [n; g; mu; point_gravity; render]; name, systems = [frame_b])
5658
end
5759

5860
"""
@@ -61,7 +63,17 @@ The world component is the root of all multibody models. It is a fixed frame wit
6163
const world = World(; name = :world)
6264

6365
"Compute the gravity acceleration, resolved in world frame"
64-
gravity_acceleration(r) = GlobalScope(world.g) * GlobalScope.(world.n) # NOTE: This is hard coded for now to use the the standard, parallel gravity model
66+
function gravity_acceleration(r)
67+
inner_gravity(GlobalScope(world.point_gravity), GlobalScope(world.mu), GlobalScope(world.g), GlobalScope.(collect(world.n)), collect(r))
68+
end
69+
70+
function inner_gravity(point_gravity, mu, g, n, r)
71+
# This is slightly inefficient, producing three if statements, one for each array entry. The function registration for array-valued does not work properly so this is a workaround for now. Hitting, among other problems, https://github.com/SciML/ModelingToolkit.jl/issues/2808
72+
gvp = -(mu/(r'r))*(r/_norm(r))
73+
gvu = g * n
74+
ifelse.(point_gravity==true, gvp, gvu)
75+
end
76+
6577

6678
@component function Fixed(; name, r = [0, 0, 0])
6779
systems = @named begin frame_b = Frame() end
@@ -221,6 +233,7 @@ Representing a body with 3 translational and 3 rotational degrees-of-freedom.
221233
phi0 = zeros(3),
222234
phid0 = zeros(3),
223235
r_0 = 0,
236+
v_0 = 0,
224237
radius = 0.05,
225238
v_0 = 0,
226239
w_a = 0,

test/runtests.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,47 @@ sol = solve(prob, Rodas4())
147147
@test sol(1, idxs=powersensor.power.u) -1.94758 atol=1e-2
148148
end
149149

150+
# ==============================================================================
151+
## Point gravity ======================
152+
# ==============================================================================
153+
@mtkmodel PointGrav begin
154+
@components begin
155+
world = W()
156+
body1 = Body(
157+
m=1,
158+
I_11=0.1,
159+
I_22=0.1,
160+
I_33=0.1,
161+
r_0=[0,0.6,0],
162+
isroot=true,
163+
v_0=[1,0,0])
164+
body2 = Body(
165+
m=1,
166+
I_11=0.1,
167+
I_22=0.1,
168+
I_33=0.1,
169+
r_0=[0.6,0.6,0],
170+
isroot=true,
171+
v_0=[0.6,0,0])
172+
end
173+
end
174+
@named model = PointGrav()
175+
model = complete(model)
176+
ssys = structural_simplify(IRSystem(model))
177+
defs = [
178+
model.world.mu => 1
179+
model.world.point_gravity => true
180+
collect(model.body1.w_a) .=> 0
181+
collect(model.body2.w_a) .=> 0
182+
183+
]
184+
prob = ODEProblem(ssys, defs, (0, 5))
185+
sol = solve(prob, Rodas4())
186+
187+
@test sol(5, idxs=model.body2.r_0) [0.7867717, 0.478463, 0] atol=1e-1
188+
# plot(sol)
189+
190+
150191
# ==============================================================================
151192
## Simple pendulum from Modelica "First Example" tutorial ======================
152193
# ==============================================================================

0 commit comments

Comments
 (0)