You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/src/examples/pendulum.md
+40-53Lines changed: 40 additions & 53 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,7 +65,7 @@ D = Differential(t)
65
65
defs = Dict() # We may specify the initial condition here
66
66
prob = ODEProblem(ssys, defs, (0, 3.35))
67
67
68
-
sol = solve(prob, Rodas4())
68
+
sol = solve(prob, Tsit5())
69
69
plot(sol, idxs = joint.phi, title="Pendulum")
70
70
```
71
71
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
@@ -74,7 +74,7 @@ The solution `sol` can be plotted directly if the Plots package is loaded. The f
74
74
Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:
75
75
76
76
```@example pendulum
77
-
import GLMakie # GLMakie is another alternative, suitable for interactive plots
77
+
import GLMakie
78
78
Multibody.render(model, sol; filename = "pendulum.gif") # Use "pendulum.mp4" for a video file
@named model = System(connections, t, systems = [world, multibody_spring, root_body])
158
-
model = complete(model)
159
156
ssys = multibody(model)
160
157
161
-
defs = Dict(root_body.r_0 .=> [0, 1e-3, 0]) # The spring has a singularity at zero length, so we start some distance away
158
+
defs = Dict(collect(ssys.root_body.r_0) .=> [0, 1e-3, 0]) # The spring has a singularity at zero length, so we start some distance away
162
159
163
160
prob = ODEProblem(ssys, defs, (0, 10))
164
161
165
-
sol = solve(prob, Rodas4())
162
+
sol = solve(prob, Tsit5())
166
163
plot(sol, idxs = multibody_spring.r_rel_0[2], title="Mass-spring system without joint")
167
164
```
168
165
Here, we used a [`Multibody.Spring`](@ref) instead of connecting a `Translational.Spring` to a joint. The `Translational.Spring`, alongside other components from `ModelingToolkitStandardLibrary.Mechanical`, is a 1-dimensional object, whereas multibody components are 3-dimensional objects.
In the animation below, we visualize the path that the origin of the pendulum tip traces by providing the tip frame in a vector of frames passed to `traces`
Let's break down how to think about directions and orientations when building 3D mechanisms. In the example above, we started with the shoulder joint, this joint rotated around the gravitational axis, `n = [0, 1, 0]`. When this joint is positioned in joint coordinate `shoulder_joint.phi = 0`, its `frame_a` and `frame_b` will coincide. When the joint rotates, `frame_b` will rotate around the axis `n` of `frame_a`. The `frame_a` of the joint is attached to the world, so the joint will rotate around the world's `y`-axis:
246
241
247
242
```@example pendulum
248
-
get_rot(sol, model.shoulder_joint.frame_b, 0)
243
+
get_rot(sol, ssys.shoulder_joint.frame_b, 0)
249
244
```
250
245
we see that at time $t = 0$, we have no rotation of `frame_b` around the $y$ axis of the world (frames are always resolved in the world frame), but a second into the simulation, we have:
Here, the `frame_b` has rotated around the $y$ axis of the world (if you are not familiar with rotation matrices, we can ask for the rotation axis and angle)
This rotation axis and angle should correspond to the joint coordinate (the orientation described by an axis and an angle is invariant to a multiplication of both by -1)
261
256
```@example pendulum
262
-
sol(1, idxs=model.shoulder_joint.phi)
257
+
sol(1, idxs=ssys.shoulder_joint.phi)
263
258
```
264
259
265
260
!!! note "Convention"
@@ -271,25 +266,25 @@ Here, we made use of the function [`get_rot`](@ref), we will now make use of als
271
266
272
267
The next body is the upper arm. This body has an extent of `0.6` in the $z$ direction, as measured in its local `frame_a`
273
268
```@example pendulum
274
-
get_trans(sol, model.upper_arm.frame_b, 0)
269
+
get_trans(sol, ssys.upper_arm.frame_b, 0)
275
270
```
276
271
One second into the simulation, the upper arm has rotated around the $y$ axis of the world
277
272
```@example pendulum
278
-
rb1 = get_trans(sol, model.upper_arm.frame_b, 1)
273
+
rb1 = get_trans(sol, ssys.upper_arm.frame_b, 1)
279
274
```
280
275
281
-
If we look at the variable `model.upper_arm.r`, we do not see this rotation!
276
+
If we look at the variable `ssys.upper_arm.r`, we do not see this rotation!
282
277
```@example pendulum
283
-
arm_r = sol(1, idxs=model.upper_arm.r)
278
+
arm_r = sol(1, idxs=ssys.upper_arm.r)
284
279
```
285
280
The reason is that this variable is resolved in the local `frame_a` and not in the world frame. To transform this variable to the world frame, we may multiply with the rotation matrix of `frame_a` which is always resolved in the world frame:
286
281
```@example pendulum
287
-
get_rot(sol, model.upper_arm.frame_a, 1)*arm_r
282
+
get_rot(sol, ssys.upper_arm.frame_a, 1)*arm_r
288
283
```
289
284
We now get the same result has when we asked for the translation vector of `frame_b` above.
@@ -300,23 +295,23 @@ The next joint, the elbow joint, has the rotational axis `n = [0, 0, 1]`. This i
300
295
301
296
The lower arm is finally having an extent along the $y$-axis. At the final time when the pendulum motion has been fully damped, we see that the second frame of this body ends up with an $y$-coordinate of `-0.6`:
302
297
```@example pendulum
303
-
get_trans(sol, model.lower_arm.frame_b, 12)
298
+
get_trans(sol, ssys.lower_arm.frame_b, 12)
304
299
```
305
300
306
301
If we rotate the vector of extent of the lower arm to the world frame, we indeed see that the only coordinate that is nonzero is the $y$ coordinate:
The reason that the latter vector differs from `get_trans(sol, model.lower_arm.frame_b, 12)` above is that `get_trans(sol, model.lower_arm.frame_b, 12)` has been _translated_ as well. To both translate and rotate `model.lower_arm.r` into the world frame, we must use the full transformation matrix $T_W^A \in SE(3)$:
306
+
The reason that the latter vector differs from `get_trans(sol, ssys.lower_arm.frame_b, 12)` above is that `get_trans(sol, ssys.lower_arm.frame_b, 12)` has been _translated_ as well. To both translate and rotate `ssys.lower_arm.r` into the world frame, we must use the full transformation matrix $T_W^A \in SE(3)$:
312
307
313
308
```@example pendulum
314
-
r_A = sol(12, idxs=model.lower_arm.r)
309
+
r_A = sol(12, idxs=ssys.lower_arm.r)
315
310
r_A = [r_A; 1] # Homogeneous coordinates
316
311
317
-
get_frame(sol, model.lower_arm.frame_a, 12)*r_A
312
+
get_frame(sol, ssys.lower_arm.frame_a, 12)*r_A
318
313
```
319
-
the vector is now coinciding with `get_trans(sol, model.lower_arm.frame_b, 12)`.
314
+
the vector is now coinciding with `get_trans(sol, ssys.lower_arm.frame_b, 12)`.
320
315
321
316
322
317
## Control-design example: Pendulum on cart
@@ -336,12 +331,7 @@ using Plots
336
331
gray = [0.5, 0.5, 0.5, 1]
337
332
@component function Cartpole(; name, use_world = false)
338
333
systems = @named begin
339
-
if use_world
340
-
fixed = World()
341
-
else
342
-
# In case we wrap this model in an outer model below, we place the world there instead
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:
429
418
```@example pendulum
430
419
using ControlSystemsMTK
431
-
lsys = named_ss(multibody(cp), inputs, outputs; op) # identical to linearize, but packages the resulting matrices in a named statespace object for convenience
420
+
lsys = named_ss(cp, inputs, outputs; op) # identical to linearize, but packages the resulting matrices in a named statespace object for convenience
0 commit comments