Skip to content

Commit 4a13a9c

Browse files
authored
add an example of gyroscopic effects (#88)
* add an example of gyroscopic effects * set explicit world.g * explicit g again
1 parent f425e87 commit 4a13a9c

File tree

7 files changed

+75
-13
lines changed

7 files changed

+75
-13
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ makedocs(;
3535
"Ropes, cables and chains" => "examples/ropes_and_cables.md",
3636
"Swing" => "examples/swing.md",
3737
"Bodies in space" => "examples/space.md",
38+
"Gyroscopic effects" => "examples/gyroscopic_effects.md",
3839
],
3940
"Rotations and orientation" => "rotations.md",
4041
"3D rendering" => "rendering.md",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Gyroscopic effects
2+
3+
![animation](gyro.gif)
4+
5+
In this example, we demonstrate how a rotating body creates a reaction torque when its axis of rotation is changed.
6+
7+
The system consists of a pendulum suspended in a spherical joint, a joint without any rotational constraints. The tip of the pendulum is a cylinder that is rotating around a revolute joint in its center. When the pendulum swings, the rotation axis of the rotating tip is changed, this causes the entire pendulum to rotate around the axis through the pendulum rod.
8+
9+
10+
```@example spring_mass_system
11+
using Multibody
12+
using ModelingToolkit
13+
using Plots
14+
using JuliaSimCompiler
15+
using OrdinaryDiffEq
16+
17+
t = Multibody.t
18+
D = Differential(t)
19+
world = Multibody.world
20+
21+
systems = @named begin
22+
spherical = Spherical(state=true, radius=0.02, color=[1,1,0,1])
23+
body1 = BodyCylinder(r = [0.25, 0, 0], diameter = 0.05, isroot=false, quat=false)
24+
rot = FixedRotation(; n = [0,1,0], angle=deg2rad(45))
25+
revolute = Revolute(n = [1,0,0], radius=0.06, color=[1,0,0,1])
26+
trans = FixedTranslation(r = [-0.1, 0, 0])
27+
body2 = BodyCylinder(r = [0.2, 0, 0], diameter = 0.1, color=[0,0,0.5,1])
28+
end
29+
30+
connections = [
31+
connect(world.frame_b, spherical.frame_a)
32+
connect(spherical.frame_b, body1.frame_a)
33+
connect(body1.frame_b, rot.frame_a)
34+
connect(rot.frame_b, revolute.frame_a)
35+
connect(revolute.frame_b, trans.frame_a)
36+
connect(trans.frame_b, body2.frame_a)
37+
]
38+
39+
@named model = ODESystem(connections, t, systems = [world; systems])
40+
model = complete(model)
41+
ssys = structural_simplify(IRSystem(model))
42+
43+
prob = ODEProblem(ssys, [model.world.g => 9.80665, model.revolute.w => 10], (0, 5))
44+
45+
sol = solve(prob, Rodas5P(), abstol=1e-6, reltol=1e-6)
46+
@assert SciMLBase.successful_retcode(sol)
47+
using Test # hide
48+
@test sol(5, idxs=collect(model.body2.r_0[1:3])) ≈ [-0.0357364, -0.188245, 0.02076935] atol=1e-3 # hide
49+
# plot(sol, idxs=collect(model.body2.r_0)) # hide
50+
51+
import CairoMakie
52+
Multibody.render(model, sol; x=1, z=1, filename = "gyro.gif") # Use "gyro.mp4" for a video file
53+
nothing # hide
54+
```
55+
56+
![animation](gyro.gif)
57+
58+
Try setting `model.revolute.w => 0` and plot this variable using `plot(sol, idxs=model.revolute.w)` and you will notice that the swinging of the pendulum induces a rotation around this joint, even if it has no rotational velocity from the start.

docs/src/examples/kinematic_loops.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ connections = [
6161
]
6262
@named fourbar = ODESystem(connections, t, systems = [world; systems])
6363
m = structural_simplify(IRSystem(fourbar))
64-
prob = ODEProblem(m, [], (0.0, 30.0))
64+
prob = ODEProblem(m, [world.g => 9.81], (0.0, 30.0))
6565
sol = solve(prob, Rodas4(autodiff=false))
6666
6767
plot(sol, idxs = [j1.phi, j2.phi, j3.phi])
@@ -72,7 +72,6 @@ using Test
7272
@test SciMLBase.successful_retcode(sol)
7373
@test sol(sol.t[end], idxs=j3.phi) % 2pi ≈ π/2 atol=0.3 # Test the the "pendulum" is hanging almost straight down after sufficient time has passed
7474
@test sol(sol.t[end], idxs=j2.phi) % 2pi ≈ -π/2 atol=0.3
75-
7675
```
7776

7877

src/Multibody.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ Create a 3D animation of a multibody system
2727
2828
# Camera control
2929
The following keyword arguments are available to control the camera pose:
30-
_ `x = 5`
31-
_ `y = 0`
32-
_ `z = 5`
33-
_ `lookat = [0,0,0]`: a three-vector of coordinates indicating the point at which the camera looks.
30+
- `x = 2`
31+
- `y = 0.5`
32+
- `z = 2`
33+
- `lookat = [0,0,0]`: a three-vector of coordinates indicating the point at which the camera looks.
3434
- `up = [0,1,0]`: A vector indicating the direction that is up.
3535
"""
3636
function render end

src/components.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ end
4545
# = 12 equations
4646
@named frame_b = Frame()
4747
@parameters n[1:3]=[0, -1, 0] [description = "gravity direction of world"]
48-
@parameters g=9.81 [description = "gravitational acceleration of world"]
48+
@parameters g=9.80665 [description = "gravitational acceleration of world"]
4949
@parameters mu=3.986004418e14 [description = "Gravity field constant [m³/s²] (default = field constant of earth)"]
5050
@parameters render=render
5151
@parameters point_gravity = point_gravity
@@ -58,7 +58,7 @@ end
5858
end
5959

6060
"""
61-
The world component is the root of all multibody models. It is a fixed frame with a parallel gravitational field and a gravity vector specified by the unit direction `world.n` (defaults to [0, -1, 0]) and magnitude `world.g` (defaults to 9.81).
61+
The world component is the root of all multibody models. It is a fixed frame with a parallel gravitational field and a gravity vector specified by the unit direction `world.n` (defaults to [0, -1, 0]) and magnitude `world.g` (defaults to 9.80665).
6262
"""
6363
const world = World(; name = :world)
6464

@@ -151,7 +151,7 @@ Fixed translation followed by a fixed rotation of `frame_b` with respect to `fra
151151
- `sequence`: DESCRIPTION
152152
- `angle`: Angle of rotation around `n`, given in radians
153153
"""
154-
@component function FixedRotation(; name, r, n = [1, 0, 0], sequence = [1, 2, 3], isroot = false,
154+
@component function FixedRotation(; name, r=[0, 0, 0], n = [1, 0, 0], sequence = [1, 2, 3], isroot = false,
155155
angle, n_x = [1, 0, 0], n_y = [0, 1, 0])
156156
norm(n) 1 || error("n must be a unit vector")
157157
@named frame_a = Frame()
@@ -581,6 +581,8 @@ end
581581
@structural_parameters begin
582582
r = [1, 0, 0]
583583
r_shape = [0, 0, 0]
584+
isroot = false
585+
quat = false
584586
end
585587

586588
@parameters begin
@@ -608,6 +610,7 @@ end
608610
density = 7700, [
609611
description = "Density of cylinder (e.g., steel: 7700 .. 7900, wood : 400 .. 800)",
610612
]
613+
color[1:4] = purple, [description = "Color of cylinder in animations"]
611614
end
612615
begin
613616
radius = diameter/2
@@ -641,7 +644,7 @@ end
641644
frame_a = Frame()
642645
frame_b = Frame()
643646
frameTranslation = FixedTranslation(r = r)
644-
body = Body(; m, r_cm, I_11 = I[1,1], I_22 = I[2,2], I_33 = I[3,3], I_21 = I[2,1], I_31 = I[3,1], I_32 = I[3,2])
647+
body = Body(; m, r_cm, I_11 = I[1,1], I_22 = I[2,2], I_33 = I[3,3], I_21 = I[2,1], I_31 = I[3,1], I_32 = I[3,2], isroot, quat)
645648
end
646649

647650
@equations begin

test/runtests.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ eqs = [connect(world.frame_b, freeMotion.frame_a)
708708
ssys = structural_simplify(IRSystem(model))
709709
@test length(unknowns(ssys)) == 12
710710

711-
prob = ODEProblem(ssys, [collect(body.w_a .=> [0, 0, 0]); collect(body.v_0 .=> [0, 0, 0]); ], (0, 10))
711+
prob = ODEProblem(ssys, [world.g=>9.81; collect(body.w_a .=> [0, 0, 0]); collect(body.v_0 .=> [0, 0, 0]); ], (0, 10))
712712

713713
sol = solve(prob, Rodas4())
714714
doplot() && plot(sol, idxs = body.r_0[2], title = "Free falling body")
@@ -732,7 +732,7 @@ eqs = [connect(world.frame_b, freeMotion.frame_a)
732732
ssys = structural_simplify(IRSystem(model))
733733
@test length(unknowns(ssys)) == 12
734734

735-
prob = ODEProblem(ssys, [collect(body.w_a .=> [0, 1, 0]); collect(body.v_0 .=> [0, 0, 0]); ], (0, 10))
735+
prob = ODEProblem(ssys, [world.g=>9.81; collect(body.w_a .=> [0, 1, 0]); collect(body.v_0 .=> [0, 0, 0]); ], (0, 10))
736736

737737
sol = solve(prob, Rodas4())
738738
doplot() && plot(sol, idxs = body.r_0[2], title = "Free falling body")
@@ -752,6 +752,7 @@ ssys = structural_simplify(IRSystem(model))
752752
@test length(unknowns(ssys)) == 12
753753

754754
prob = ODEProblem(ssys, [
755+
world.g=>9.81;
755756
collect(body.w_a .=> [0, 1, 0]);
756757
collect(body.v_0 .=> [0, 0, 0]);
757758
], (0, 10))

test/test_orientation_getters.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ end
3232
model = complete(model)
3333
ssys = structural_simplify(IRSystem(model))
3434

35-
prob = ODEProblem(ssys, [model.shoulder_joint.phi => 0.0, model.elbow_joint.phi => 0.1], (0, 12))
35+
prob = ODEProblem(ssys, [model.shoulder_joint.phi => 0.0, model.elbow_joint.phi => 0.1, model.world.g => 9.81], (0, 12))
3636
sol = solve(prob, Rodas4())
3737

3838

0 commit comments

Comments
 (0)