Skip to content

Commit fffb39d

Browse files
authored
Multibody validity check (#168)
* add multibody check function * use multibody check function * better world handling in examples and tests * remove hacky `W` world constructor * add support for planar mechanics in `multibody` * handle components without gui metadata
1 parent 3bf6cf6 commit fffb39d

33 files changed

+198
-178
lines changed

docs/src/examples/free_motion.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ eqs = [connect(world.frame_b, freeMotion.frame_a)
2222
systems = [world;
2323
freeMotion;
2424
body])
25-
ssys = structural_simplify(IRSystem(model))
25+
ssys = structural_simplify(multibody(model))
2626
2727
prob = ODEProblem(ssys, [], (0, 10))
2828
@@ -69,7 +69,7 @@ eqs = [connect(bar2.frame_a, world.frame_b)
6969
spring2,
7070
])
7171
model = complete(model)
72-
ssys = structural_simplify(IRSystem(model))
72+
ssys = structural_simplify(multibody(model))
7373
prob = ODEProblem(ssys, [
7474
collect(body.body.v_0 .=> 0);
7575
collect(body.body.w_a .=> 0);

docs/src/examples/gearbox.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ eqs = [connect(world.frame_b, gearConstraint.bearing)
4848
4949
@named model = ODESystem(eqs, t, systems = [world; systems])
5050
cm = complete(model)
51-
ssys = structural_simplify(IRSystem(model))
51+
ssys = structural_simplify(multibody(model))
5252
prob = ODEProblem(ssys, [
5353
D(cm.idealGear.phi_b) => 0
5454
cm.idealGear.phi_b => 0

docs/src/examples/gyroscopic_effects.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ connections = [
3838
3939
@named model = ODESystem(connections, t, systems = [world; systems])
4040
model = complete(model)
41-
ssys = structural_simplify(IRSystem(model))
41+
ssys = structural_simplify(multibody(model))
4242
4343
prob = ODEProblem(ssys, [model.world.g => 9.80665, model.revolute.w => 10], (0, 5))
4444

docs/src/examples/kinematic_loops.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ connections = [
7575
]
7676
@named fourbar = ODESystem(connections, t, systems = [world; systems])
7777
fourbar = complete(fourbar)
78-
ssys = structural_simplify(IRSystem(fourbar))
78+
ssys = structural_simplify(multibody(fourbar))
7979
prob = ODEProblem(ssys, [fourbar.j1.phi => 0.1], (0.0, 10.0))
8080
sol = solve(prob, FBDF(autodiff=true))
8181
@@ -146,7 +146,7 @@ connections = [connect(j2.frame_b, b2.frame_a)
146146
]
147147
@named fourbar2 = ODESystem(connections, t, systems = [world; systems])
148148
fourbar2 = complete(fourbar2)
149-
ssys = structural_simplify(IRSystem(fourbar2))
149+
ssys = structural_simplify(multibody(fourbar2))
150150
151151
prob = ODEProblem(ssys, [], (0.0, 1.4399)) # The end time is chosen to make the animation below appear to loop forever
152152
@@ -187,7 +187,7 @@ connections = [connect(j2.frame_b, b2.frame_a)
187187
188188
@named fourbar_analytic = ODESystem(connections, t, systems = [world; systems])
189189
fourbar_analytic = complete(fourbar_analytic)
190-
ssys_analytic = structural_simplify(IRSystem(fourbar_analytic))
190+
ssys_analytic = structural_simplify(multibody(fourbar_analytic))
191191
prob = ODEProblem(ssys_analytic, [], (0.0, 1.4399))
192192
sol2 = solve(prob, FBDF(autodiff=true)) # about 4x faster than the simulation above
193193
plot!(sol2, idxs=[j2.s]) # Plot the same coordinate as above

docs/src/examples/pendulum.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Pendulum--The "Hello World of multi-body dynamics"
2-
This beginners tutorial will start by modeling 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, i.e., the `world` component must be included in all models.
2+
This beginners tutorial will start by modeling 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, i.e., the `world` component must be included exactly once in all models, at the top level.
33

44
To start, we load the required packages
55
```@example pendulum
@@ -47,12 +47,16 @@ nothing # hide
4747
```
4848
The `ODESystem` is the fundamental model type in ModelingToolkit used for multibody-type models.
4949

50-
Before we can simulate the system, we must perform model compilation using [`structural_simplify`](@ref)
50+
Before we can simulate the system, we must perform model check using the function [`multibody`](@ref) and compilation using [`structural_simplify`](@ref)
5151
```@example pendulum
52-
ssys = structural_simplify(IRSystem(model))
52+
ssys = structural_simplify(multibody(model))
5353
```
5454
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. To simulate the pendulum, we require two state variables, one for angle and one for angular velocity, we can see above that these state variables have indeed been chosen.
5555

56+
```@docs
57+
multibody
58+
```
59+
5660
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.
5761
```@example pendulum
5862
D = Differential(t)
@@ -90,7 +94,7 @@ connections = [connect(world.frame_b, joint.frame_a)
9094
9195
@named model = ODESystem(connections, t, systems = [world, joint, body, damper])
9296
model = complete(model)
93-
ssys = structural_simplify(IRSystem(model))
97+
ssys = structural_simplify(multibody(model))
9498
9599
prob = ODEProblem(ssys, [damper.phi_rel => 1], (0, 10))
96100
@@ -123,7 +127,7 @@ connections = [connect(world.frame_b, joint.frame_a)
123127
124128
@named model = ODESystem(connections, t, systems = [world, joint, body_0, damper, spring])
125129
model = complete(model)
126-
ssys = structural_simplify(IRSystem(model))
130+
ssys = structural_simplify(multibody(model))
127131
128132
prob = ODEProblem(ssys, [], (0, 10))
129133
@@ -150,7 +154,7 @@ connections = [connect(world.frame_b, multibody_spring.frame_a)
150154
151155
@named model = ODESystem(connections, t, systems = [world, multibody_spring, root_body])
152156
model = complete(model)
153-
ssys = structural_simplify(IRSystem(model))
157+
ssys = structural_simplify(multibody(model))
154158
155159
defs = Dict(collect(root_body.r_0) .=> [0, 1e-3, 0]) # The spring has a singularity at zero length, so we start some distance away
156160
@@ -168,7 +172,7 @@ push!(connections, connect(multibody_spring.spring2d.flange_b, damper.flange_b))
168172
169173
@named model = ODESystem(connections, t, systems = [world, multibody_spring, root_body, damper])
170174
model = complete(model)
171-
ssys = structural_simplify(IRSystem(model))
175+
ssys = structural_simplify(multibody(model))
172176
prob = ODEProblem(ssys, defs, (0, 10))
173177
174178
sol = solve(prob, Rodas4(), u0 = prob.u0 .+ 1e-5 .* randn.())
@@ -186,11 +190,10 @@ This pendulum, sometimes referred to as a _rotary pendulum_, has two joints, one
186190
using ModelingToolkit, Multibody, JuliaSimCompiler, OrdinaryDiffEq, Plots
187191
import ModelingToolkitStandardLibrary.Mechanical.Rotational.Damper as RDamper
188192
import Multibody.Rotations
189-
W(args...; kwargs...) = Multibody.world
190193
191194
@mtkmodel FurutaPendulum begin
192195
@components begin
193-
world = W()
196+
world = World()
194197
shoulder_joint = Revolute(n = [0, 1, 0], axisflange = true)
195198
elbow_joint = Revolute(n = [0, 0, 1], axisflange = true, phi0=0.1)
196199
upper_arm = BodyShape(; m = 0.1, r = [0, 0, 0.6], radius=0.04)
@@ -218,7 +221,7 @@ end
218221
219222
@named model = FurutaPendulum()
220223
model = complete(model)
221-
ssys = structural_simplify(IRSystem(model))
224+
ssys = structural_simplify(multibody(model))
222225
223226
prob = ODEProblem(ssys, [model.shoulder_joint.phi => 0.0, model.elbow_joint.phi => 0.1], (0, 10))
224227
sol = solve(prob, Rodas4())
@@ -380,7 +383,7 @@ end
380383
end
381384
@named model = CartWithInput()
382385
model = complete(model)
383-
ssys = structural_simplify(IRSystem(model))
386+
ssys = structural_simplify(multibody(model))
384387
prob = ODEProblem(ssys, [model.cartpole.prismatic.s => 0.0, model.cartpole.revolute.phi => 0.1], (0, 10))
385388
sol = solve(prob, Tsit5())
386389
plot(sol, layout=4)
@@ -411,13 +414,13 @@ op = Dict([ # Operating point to linearize in
411414
cp.revolute.phi => 0 # Pendulum pointing upwards
412415
]
413416
)
414-
matrices, simplified_sys = linearize(IRSystem(cp), inputs, outputs; op)
417+
matrices, simplified_sys = linearize(multibody(cp), inputs, outputs; op)
415418
matrices
416419
```
417420
This gives us the matrices $A,B,C,D$ in a linearized statespace representation of the system. To make these easier to work with, we load the control packages and call `named_ss` instead of `linearize` to get a named statespace object instead:
418421
```@example pendulum
419422
using ControlSystemsMTK
420-
lsys = named_ss(IRSystem(cp), inputs, outputs; op) # identical to linearize, but packages the resulting matrices in a named statespace object for convenience
423+
lsys = named_ss(multibody(cp), inputs, outputs; op) # identical to linearize, but packages the resulting matrices in a named statespace object for convenience
421424
```
422425

423426
### LQR Control design
@@ -466,7 +469,7 @@ LQGSystem(args...; kwargs...) = ODESystem(observer_controller(lqg); kwargs...)
466469
end
467470
@named model = CartWithFeedback()
468471
model = complete(model)
469-
ssys = structural_simplify(IRSystem(model))
472+
ssys = structural_simplify(multibody(model))
470473
prob = ODEProblem(ssys, [model.cartpole.prismatic.s => 0.1, model.cartpole.revolute.phi => 0.35], (0, 10))
471474
sol = solve(prob, Tsit5())
472475
cp = model.cartpole
@@ -533,7 +536,7 @@ normalize_angle(x::Number) = mod(x+3.1415, 2pi)-3.1415
533536
end
534537
@named model = CartWithSwingup()
535538
model = complete(model)
536-
ssys = structural_simplify(IRSystem(model))
539+
ssys = structural_simplify(multibody(model))
537540
cp = model.cartpole
538541
prob = ODEProblem(ssys, [cp.prismatic.s => 0.0, cp.revolute.phi => 0.99pi], (0, 5))
539542
sol = solve(prob, Tsit5(), dt = 1e-2, adaptive=false)

docs/src/examples/prescribed_pose.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ using Test
3030
3131
t = Multibody.t
3232
D = Differential(t)
33-
W(args...; kwargs...) = Multibody.world
33+
3434
3535
n = [1, 0, 0]
3636
AB = 146.5 / 1000
@@ -131,7 +131,7 @@ end
131131
rod_radius = 0.02
132132
end
133133
@components begin
134-
world = W()
134+
world = World()
135135
mass = Body(m=ms, r_cm = 0.5DA*normalize([0, 0.2, 0.2*sin(t5)]))
136136
excited_suspension = ExcitedWheelAssembly(; rod_radius)
137137
prescribed_motion = Pose(; r = [0, 0.1 + 0.1sin(t), 0], R = RotXYZ(0, 0.5sin(t), 0))
@@ -144,7 +144,7 @@ end
144144
145145
@named model = SuspensionWithExcitationAndMass()
146146
model = complete(model)
147-
ssys = structural_simplify(IRSystem(model))
147+
ssys = structural_simplify(multibody(model))
148148
display([unknowns(ssys) diag(ssys.mass_matrix)])
149149
150150
defs = [

docs/src/examples/quad.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ function RotorCraft(; closed_loop = true, addload=true, L=nothing, outputs = not
191191
end
192192
model = RotorCraft(closed_loop=true, addload=true, pid=true)
193193
model = complete(model)
194-
ssys = structural_simplify(IRSystem(model))
194+
ssys = structural_simplify(multibody(model))
195195
# display(unknowns(ssys))
196196
op = [
197197
model.body.v_0[1] => 0;
@@ -234,7 +234,7 @@ op = [
234234
inputs .=> 1;
235235
] |> Dict
236236
237-
@time lsys = named_ss(IRSystem(quad), inputs, outputs; op)
237+
@time lsys = named_ss(multibody(quad), inputs, outputs; op)
238238
rsys = minreal(sminreal(lsys))
239239
C = rsys.C
240240
rank(C) >= rsys.nx || @warn "The output matrix C is not full rank"
@@ -265,7 +265,7 @@ L
265265
ModelingToolkit.get_iv(i::IRSystem) = i.t
266266
model = RotorCraft(; closed_loop=true, addload=true, L=-L, outputs) # Negate L for negative feedback
267267
model = complete(model)
268-
ssys = structural_simplify(IRSystem(model))
268+
ssys = structural_simplify(multibody(model))
269269
270270
op = [
271271
model.body.r_0[2] => 1e-3

docs/src/examples/robot.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The robot is a medium sized system with some 2000 equations before simplificatio
2121

2222
After simplification, the following states are chosen:
2323
```@example robot
24-
ssys = structural_simplify(IRSystem(robot))
24+
ssys = structural_simplify(multibody(robot))
2525
unknowns(ssys)
2626
```
2727

docs/src/examples/ropes_and_cables.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ connections = [connect(world.frame_b, rope.frame_a)
3131
@named stiff_rope = ODESystem(connections, t, systems = [world, body, rope])
3232
3333
stiff_rope = complete(stiff_rope)
34-
ssys = structural_simplify(IRSystem(stiff_rope))
34+
ssys = structural_simplify(multibody(stiff_rope))
3535
prob = ODEProblem(ssys, [], (0, 5))
3636
sol = solve(prob, Rodas4(autodiff=false))
3737
@test SciMLBase.successful_retcode(sol)
@@ -55,7 +55,7 @@ connections = [connect(world.frame_b, rope.frame_a)
5555
@named flexible_rope = ODESystem(connections, t, systems = [world, body, rope])
5656
5757
flexible_rope = complete(flexible_rope)
58-
ssys = structural_simplify(IRSystem(flexible_rope))
58+
ssys = structural_simplify(multibody(flexible_rope))
5959
prob = ODEProblem(ssys, [], (0, 8))
6060
sol = solve(prob, Rodas4(autodiff=false));
6161
@test SciMLBase.successful_retcode(sol)
@@ -88,7 +88,7 @@ connections = [connect(world.frame_b, fixed.frame_a, chain.frame_a)
8888
8989
@named mounted_chain = ODESystem(connections, t, systems = [systems; world])
9090
mounted_chain = complete(mounted_chain)
91-
ssys = structural_simplify(IRSystem(mounted_chain))
91+
ssys = structural_simplify(multibody(mounted_chain))
9292
prob = ODEProblem(ssys, [
9393
collect(chain.link_8.body.w_a) .=> [0,0,0];
9494
collect(chain.link_8.frame_b.r_0) .=> [x_dist,0,0];

docs/src/examples/sensors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ connections = [connect(world.frame_b, joint.frame_a)
3131
3232
@named model = ODESystem(connections, t,
3333
systems = [world, joint, body, torquesensor, forcesensor])
34-
ssys = structural_simplify(IRSystem(model))
34+
ssys = structural_simplify(multibody(model))
3535
3636
3737
D = Differential(t)

0 commit comments

Comments
 (0)