Skip to content
LIBRARY
Spherical.md

Spherical

Spherical joint: free rotation in all three directions (ball joint). Provides 3 rotational degrees of freedom at a single point. Extends PartialOrientation to support Euler angle, quaternion, or stateless modes via the orientation_state parameter:

  • Euler(): Euler angle state (3 angles)

  • Quaternion(): Quaternion state

  • None(): No state, orientation provided by connected components (default)

When orientation_state != None, the joint acts as root of the orientation tree. No torque is transmitted through the joint.

This component extends from PartialOrientation This component extends from Renderable

Usage

MultibodyComponents.Spherical(k=0.1, render=true, color=world_default_joint_color(), specular_coefficient=1.5, radius=world_default_joint_width())

Parameters:

NameDescriptionUnitsDefault value
orientation_stateOrientationState.Euler()
sequence[1, 2, 3]
statePriority10
w_rel_a_fixedfalse
z_rel_a_fixedfalse
k0.1
rendertrue
colorworld_defau...int_color()
specular_coefficient1.5
radiusRadius of the sphere in animationsworld_defau...int_width()

Connectors

  • frame_a - Frame3D is the fundamental 3D connector used for 6DOF motion. Most components have one or several Frame

connectors that can be connected together (Frame3D)

  • frame_b - Frame3D is the fundamental 3D connector used for 6DOF motion. Most components have one or several Frame

connectors that can be connected together (Frame3D)

Variables

NameDescriptionUnits
w_aAbsolute angular velocity of frame_a resolved in frame_arad/s
z_aAbsolute angular acceleration of frame_a resolved in frame_arad/s2
QUnit quaternion with [w,i,j,k]
Q_hatNon-unit quaternion with [w,i,j,k]
Q_hat_d
n_q
c_q
phiEuler anglesrad
phidrad/s
phiddrad/s2

Behavior

Dict{MIME{Symbol("text/plain")}, String} with 1 entry: MIME type text/plain => "Error displaying result"

Source

dyad
"""
Spherical joint: free rotation in all three directions (ball joint).
Provides 3 rotational degrees of freedom at a single point. Extends
`PartialOrientation` to support Euler angle, quaternion, or stateless modes
via the `orientation_state` parameter:
- `Euler()`: Euler angle state (3 angles)
- `Quaternion()`: Quaternion state
- `None()`: No state, orientation provided by connected components (default)
When `orientation_state != None`, the joint acts as root of the orientation tree.
No torque is transmitted through the joint.
"""
component Spherical
  extends PartialOrientation(orientation_state = OrientationState.Euler())
  extends Renderable(color = world_default_joint_color())
  frame_b = Frame3D() {}
  # Visualization shape
  shape = SphereShape(render = render, color = color, r = frame_a.r_0, R = transpose(frame_a.R), length = 2 * radius, width = 2 * radius, height = 2 * radius)
  structural parameter w_rel_a_fixed::Boolean = false
  structural parameter z_rel_a_fixed::Boolean = false
  "Radius of the sphere in animations"
  parameter radius::Real = world_default_joint_width()
relations
  # Position constraint: frames coincide
  frame_b.r_0 = frame_a.r_0
  # Torque: free rotation (zero torque transmitted)
  frame_a.tau = [0, 0, 0]
  frame_b.tau = [0, 0, 0]
  # w_a inherited from PartialOrientation is the relative angular velocity (w_rel) here
  switch orientation_state
    case None
      # No state: pure constraint
      frame_a.f = -resolve_relative(frame_b.f, frame_b, frame_a)
      if w_rel_a_fixed or z_rel_a_fixed
        w_a = angular_velocity2(ori(frame_b)) - resolve2(frame_b, angular_velocity1(ori(frame_a)))
      else
        w_a = zeros(3)
      end
    case Euler
      # Joint has state: frame_b is frame_a composed with the relative rotation.
      # Rrel from axes_rotations already carries the relative angular velocity.
      ori(frame_b) = absolute_rotation(ori(frame_a), Rrel)
      frame_a.f = -resolve1(Rrel, frame_b.f)
    case Quaternion
      # from_Q(Q) is only the relative rotation matrix; pair it with the
      # relative angular velocity w_a to form a full rotation object.
      ori(frame_b) = absolute_rotation(ori(frame_a), RM(from_Q(Q), w_a))
      frame_a.f = -resolve1(RM(from_Q(Q), w_a), frame_b.f)
  end
end
Flattened Source
dyad
"""
Spherical joint: free rotation in all three directions (ball joint).
Provides 3 rotational degrees of freedom at a single point. Extends
`PartialOrientation` to support Euler angle, quaternion, or stateless modes
via the `orientation_state` parameter:
- `Euler()`: Euler angle state (3 angles)
- `Quaternion()`: Quaternion state
- `None()`: No state, orientation provided by connected components (default)
When `orientation_state != None`, the joint acts as root of the orientation tree.
No torque is transmitted through the joint.
"""
component Spherical
  frame_a = Frame3D() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 0, "y1": 450, "x2": 100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  structural parameter orientation_state::OrientationState = OrientationState.None()
  structural parameter sequence::Integer[3] = [1, 2, 3]
  structural parameter statePriority::Integer = 10
  "Absolute angular velocity of frame_a resolved in frame_a"
  variable w_a::AngularVelocity(statePriority = case(orientation_state, OrientationState.Quaternion) ? 10 : 0, initial = case(orientation_state, OrientationState.Quaternion) ? [0.0, 0, 0] : missing)[3]
  "Absolute angular acceleration of frame_a resolved in frame_a"
  variable z_a::AngularAcceleration[3]
  "Unit quaternion with [w,i,j,k]"
  variable Q::Real[4] if case(orientation_state, OrientationState.Quaternion)
  "Non-unit quaternion with [w,i,j,k]"
  variable Q_hat::Real(statePriority = statePriority, initial = [1.0, 0, 0, 0])[4] if case(orientation_state, OrientationState.Quaternion)
  variable Q_hat_d::Real[4] if case(orientation_state, OrientationState.Quaternion)
  variable n_q::Real if case(orientation_state, OrientationState.Quaternion)
  variable c_q::Real if case(orientation_state, OrientationState.Quaternion)
  parameter k::Real = 0.1 if case(orientation_state, OrientationState.Quaternion)
  "Euler angles"
  variable phi::Angle(statePriority = statePriority, initial = [0, 0, 0])[3] if case(orientation_state, OrientationState.Euler)
  variable phid::AngularVelocity(statePriority = statePriority, initial = [0, 0, 0])[3] if case(orientation_state, OrientationState.Euler)
  variable phidd::AngularAcceleration[3] if case(orientation_state, OrientationState.Euler)
  structural variable Rrel::Native = case(orientation_state, OrientationState.Euler) ? axes_rotations(sequence, phi, phid) : (case(orientation_state, OrientationState.Quaternion) ? from_Q(Q) : nullrotation())
  parameter render::Boolean = true
  parameter color::Real[4] = [0.5, 0.5, 0.5, 1.0]
  parameter specular_coefficient::Real = 1.5
  frame_b = Frame3D() {}
  # Visualization shape
  shape = SphereShape(render = render, color = color, r = frame_a.r_0, R = transpose(frame_a.R), length = 2 * radius, width = 2 * radius, height = 2 * radius)
  structural parameter w_rel_a_fixed::Boolean = false
  structural parameter z_rel_a_fixed::Boolean = false
  "Radius of the sphere in animations"
  parameter radius::Real = world_default_joint_width()
relations
  z_a = der(w_a)
  switch orientation_state
    case Quaternion
      # guess Q_hat = Q # TODO: Ideally it should be possible to provide this guess, but due to dyad codegen to initial equations the guess propagation is not working correctly and ODEProblem gets stuck. Once the initial condition for Q codegens to __initial_conditions, we can start providing this guess again.
      n_q = dot(Q_hat, Q_hat)
      c_q = k * (1 - n_q)
      der(Q_hat) = Q_hat_d
      Q_hat_d = omega_matrix(w_a) * Q_hat / 2 + c_q * Q_hat
      Q = Q_hat / sqrt(n_q)
    case Euler
      phid = der(phi)
      phidd = der(phid)
      w_a = angular_velocity2(Rrel)
    case None
  end
  # Position constraint: frames coincide
  frame_b.r_0 = frame_a.r_0
  # Torque: free rotation (zero torque transmitted)
  frame_a.tau = [0, 0, 0]
  frame_b.tau = [0, 0, 0]
  # w_a inherited from PartialOrientation is the relative angular velocity (w_rel) here
  switch orientation_state
    case None
      # No state: pure constraint
      frame_a.f = -resolve_relative(frame_b.f, frame_b, frame_a)
      if w_rel_a_fixed or z_rel_a_fixed
        w_a = angular_velocity2(ori(frame_b)) - resolve2(frame_b, angular_velocity1(ori(frame_a)))
      else
        w_a = zeros(3)
      end
    case Euler
      # Joint has state: frame_b is frame_a composed with the relative rotation.
      # Rrel from axes_rotations already carries the relative angular velocity.
      ori(frame_b) = absolute_rotation(ori(frame_a), Rrel)
      frame_a.f = -resolve1(Rrel, frame_b.f)
    case Quaternion
      # from_Q(Q) is only the relative rotation matrix; pair it with the
      # relative angular velocity w_a to form a full rotation object.
      ori(frame_b) = absolute_rotation(ori(frame_a), RM(from_Q(Q), w_a))
      frame_a.f = -resolve1(RM(from_Q(Q), w_a), frame_b.f)
  end
metadata {}
end


Test Cases

No test cases defined.

  • Examples

  • Experiments

  • Analyses