Skip to content
LIBRARY
tests.RevoluteConstraintTest.md

tests.RevoluteConstraintTest

Revolute Constraint (closed loop with a spring)

A freely-moving body is connected to the world by the implicit RevoluteConstraint at one end and pulled back to a fixed world point by a Spring at the other end, forming a closed loop. The constraint removes five degrees of freedom, leaving a single rotation about n = [0, 0, 1], so the body swings about the constraint axis under the spring and gravity loads.

The free body carries its own (absolute) Euler body coordinates as states. The orientation is given concrete initial conditions: phi = [0, 0, 0.5] and phid = [0, 0, 0]. The two tilt angles start at zero (on the constraint manifold, where n is invariant) and the free angle about n starts at 0.5, so the body begins tilted by half a radian and then swings under the loads.

Pinning the orientation makes the initialization fully determined. Leaving the angles free (initial missing, with the free angle settled only by a guess) yields an under-determined, structurally singular initialization whose least-squares solve is unreliable on some solver stacks (intermittent InitialFailure), so concrete initial conditions are used instead.

The Euler sequence is [1, 2, 3] so that the free rotation about n = [0, 0, 1] is carried by the last angle, phi[3]. State selection keeps the last array component as the continuous state, so this aligns the selected state with the unconstrained degree of freedom.

Usage

MultibodyComponents.tests.RevoluteConstraintTest()

Behavior

Source

dyad
"""
# Revolute Constraint (closed loop with a spring)

A freely-moving body is connected to the world by the implicit
`RevoluteConstraint` at one end and pulled back to a fixed world point by a
`Spring` at the other end, forming a closed loop. The constraint removes five
degrees of freedom, leaving a single rotation about `n = [0, 0, 1]`, so the
body swings about the constraint axis under the spring and gravity loads.

The free body carries its own (absolute) Euler body coordinates as states. The
orientation is given concrete initial conditions: `phi = [0, 0, 0.5]` and
`phid = [0, 0, 0]`. The two tilt angles start at zero (on the constraint
manifold, where `n` is invariant) and the free angle about `n` starts at `0.5`,
so the body begins tilted by half a radian and then swings under the loads.

Pinning the orientation makes the initialization fully determined. Leaving the
angles free (`initial missing`, with the free angle settled only by a guess)
yields an under-determined, structurally singular initialization whose
least-squares solve is unreliable on some solver stacks (intermittent
`InitialFailure`), so concrete initial conditions are used instead.

The Euler sequence is `[1, 2, 3]` so that the free rotation about `n = [0, 0, 1]`
is carried by the last angle, `phi[3]`. State selection keeps the last array
component as the continuous state, so this aligns the selected state with the
unconstrained degree of freedom.
"""
example component RevoluteConstraintTest
  world = MultibodyComponents.World() {}
  constraint = MultibodyComponents.RevoluteConstraint(n = [0, 0, 1]) {}
  body = MultibodyComponents.Body(m = 1, r_cm = [0.2, 0, 0], I_11 = 0.1, I_22 = 0.1, I_33 = 0.1, orientation_state = OrientationState.Euler(), sequence = [1, 2, 3], phi(initial = [0, 0, 0.5]), phid(initial = [0, 0, 0]), r_0(initial = missing), v_0(initial = missing)) {}
  rod = MultibodyComponents.FixedTranslation(r = [0.4, 0, 0], render = false) {}
  spring = MultibodyComponents.Spring(c = 20, s_unstretched = 0) {}
  mount = MultibodyComponents.FixedTranslation(r = [0.8, 0, 0.3], render = false) {}
relations
  connect(world.frame_b, constraint.frame_a) {}
  connect(constraint.frame_b, body.frame_a, rod.frame_a) {}
  connect(rod.frame_b, spring.frame_b) {}
  connect(world.frame_b, mount.frame_a) {}
  connect(mount.frame_b, spring.frame_a) {}
end
Flattened Source
dyad
"""
# Revolute Constraint (closed loop with a spring)

A freely-moving body is connected to the world by the implicit
`RevoluteConstraint` at one end and pulled back to a fixed world point by a
`Spring` at the other end, forming a closed loop. The constraint removes five
degrees of freedom, leaving a single rotation about `n = [0, 0, 1]`, so the
body swings about the constraint axis under the spring and gravity loads.

The free body carries its own (absolute) Euler body coordinates as states. The
orientation is given concrete initial conditions: `phi = [0, 0, 0.5]` and
`phid = [0, 0, 0]`. The two tilt angles start at zero (on the constraint
manifold, where `n` is invariant) and the free angle about `n` starts at `0.5`,
so the body begins tilted by half a radian and then swings under the loads.

Pinning the orientation makes the initialization fully determined. Leaving the
angles free (`initial missing`, with the free angle settled only by a guess)
yields an under-determined, structurally singular initialization whose
least-squares solve is unreliable on some solver stacks (intermittent
`InitialFailure`), so concrete initial conditions are used instead.

The Euler sequence is `[1, 2, 3]` so that the free rotation about `n = [0, 0, 1]`
is carried by the last angle, `phi[3]`. State selection keeps the last array
component as the continuous state, so this aligns the selected state with the
unconstrained degree of freedom.
"""
example component RevoluteConstraintTest
  world = MultibodyComponents.World() {}
  constraint = MultibodyComponents.RevoluteConstraint(n = [0, 0, 1]) {}
  body = MultibodyComponents.Body(m = 1, r_cm = [0.2, 0, 0], I_11 = 0.1, I_22 = 0.1, I_33 = 0.1, orientation_state = OrientationState.Euler(), sequence = [1, 2, 3], phi(initial = [0, 0, 0.5]), phid(initial = [0, 0, 0]), r_0(initial = missing), v_0(initial = missing)) {}
  rod = MultibodyComponents.FixedTranslation(r = [0.4, 0, 0], render = false) {}
  spring = MultibodyComponents.Spring(c = 20, s_unstretched = 0) {}
  mount = MultibodyComponents.FixedTranslation(r = [0.8, 0, 0.3], render = false) {}
relations
  connect(world.frame_b, constraint.frame_a) {}
  connect(constraint.frame_b, body.frame_a, rod.frame_a) {}
  connect(rod.frame_b, spring.frame_b) {}
  connect(world.frame_b, mount.frame_a) {}
  connect(mount.frame_b, spring.frame_a) {}
metadata {}
end


Test Cases

No test cases defined.

  • Examples

  • Experiments

  • Analyses