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_state–OrientationState.Euler()
sequence–[1, 2, 3]
statePriority–10
initialize_orientation–true
w_rel_a_fixed–false
z_rel_a_fixed–false
k–0.1
render–true
color–world_defau...int_color()
specular_coefficient–1.5
radiusRadius of the sphere in animations–world_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() {
    "Dyad": {"placement": {"diagram": {"x1": 900, "x2": 1000, "y1": 450, "y2": 550}}}
  }
  # 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
  structural parameter initialize_orientation::Boolean = true
  "Absolute angular velocity of frame_a resolved in frame_a"
  variable w_a::AngularVelocity(statePriority = case(orientation_state, OrientationState.Quaternion) ? statePriority : 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 = initialize_orientation ? [1.0, 0, 0, 0] : missing)[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 = initialize_orientation ? [0, 0, 0] : missing)[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() {
    "Dyad": {"placement": {"diagram": {"x1": 900, "x2": 1000, "y1": 450, "y2": 550}}}
  }
  # 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.
      guess n_q = 1.0
      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