Orientations and coordinate conventions
MultibodyComponents models mechanical systems as networks of frames (coordinate systems) attached to bodies and connected through joints. Understanding how frames relate to each other is essential for correctly setting up models, interpreting results and specifying initial conditions.
Planar mechanics (2D)
In 2D planar mechanics, all motion is confined to the
The Frame2D connector
Every planar component communicates through Frame2D connectors. A Frame2D carries six variables, all resolved in the world frame:
| Variable | Kind | Description |
|---|---|---|
x, y | Potential | Position of the frame origin |
phi | Potential | Orientation angle (rad) |
fx, fy | Flow | Cut-force components |
tau | Flow | Cut-torque about the |
The World component
Every planar model must contain exactly one World component. It defines:
The origin of the inertial reference frame at
with . Gravity, specified by a direction vector
n(default[0, -1], i.e. pointing downward) and a magnitudeg(default9.80665m/s²). AllBodycomponents automatically use the resulting gravitational acceleration.
The 2D rotation matrix
The standard 2D rotation matrix for an angle
Two functions are available for working with this matrix:
ori_2d(phi)returns, which transforms a vector from the world frame to the local frame: . get_rot(sol, frame, t)returns, which transforms a vector from the local frame to the world frame: .
How joints affect orientation
Orientations are additive through joints. When two frames are connected by a joint, the relation between their orientation angles is
This applies to:
Revolute:
, where is the (variable) joint coordinate. Positions are rigidly connected: , . FixedRotation:
, where is a fixed parameter. Also rigidly connects positions.
When a joint is at its zero coordinate (frame_a and frame_b coincide in both position and orientation.
Vectors in local vs. world frames
Some components specify vectors in the body-fixed (local) frame. For example, FixedTranslation(r = [1, 0]) specifies a displacement of 1 unit along the local frame_b ends up in the world frame, this vector is rotated by the current orientation of frame_a:
The Body component's position and velocity state variables (r, v, phi, w) are all expressed directly in the world frame, so no transformation is needed when reading them from a solution.
Example: a simple pendulum
The PendulumTest example connects a World, a Revolute joint, a FixedTranslation rod and a Body:
World.frame_b ─→ Revolute.frame_a
Revolute.frame_b ─→ FixedTranslation.frame_a
FixedTranslation.frame_b ─→ Body.frame_aWe can instantiate, simplify and simulate this model in Julia:
using ModelingToolkit, OrdinaryDiffEq
using MultibodyComponents
using MultibodyComponents: multibody
@named model = MultibodyComponents.PlanarMechanics.examples.PendulumTest()
ssys = multibody(model)
prob = ODEProblem(ssys, [], (0.0, 3.0))
sol = solve(prob, Tsit5())
import GLMakie
render(model, sol; filename = "pendulum.gif") # Use "pendulum.mp4" for a video fileThe revolute joint angle tells us the pendulum's orientation relative to the world. At revolute.phi is zero, so the rod extends horizontally (along the world r = [1, 0]). As the pendulum swings under gravity, the joint angle evolves:
sol(1.0, idxs = ssys.revolute.phi)-2.881411948155995The body's world-frame position is available directly:
sol(1.0, idxs = ssys.body.r)2-element Vector{Float64}:
-0.9663435065036469
-0.25725517961400923We can also extract a rotation matrix from any frame using get_rot:
R = get_rot(sol, ssys.rod.frame_b, 1.0)2×2 RotMatrix2{Float64} with indices SOneTo(2)×SOneTo(2):
-0.966344 -0.257255
0.257255 -0.966344This