Skip to content
LIBRARY
SphericalSpherical.md

SphericalSpherical

Joint with two spherical-like ends connected by a fixed-length rod.

Imposes a single distance constraint: |frame_b.r_0 - frame_a.r_0| = ||r_0||. The rod can optionally carry a point mass at its midpoint (has_mass = true, mass given bym). Highly recommended for closed kinematic loops — it's much more efficient than twoSpherical joints plus a body.

  • r_0: nominal vector from frame_a to frame_b. Defines rod_length and the initial rod direction. Must be non-zero.

  • has_mass: include a point mass at the rod midpoint (structural).

  • m: rod mass (only used when has_mass = true).

  • kinematic_constraint: when true, use the rotation-matrix-aware form of the rod position (helps the symbolic loop solver); when false, the simpler frame_b.r_0 - frame_a.r_0 form.

This component extends from PartialTwoFrames This component extends from Renderable

Usage

MultibodyComponents.SphericalSpherical(render=true, color=[1, 1, 0, 1], specular_coefficient=1.5, r_0=[1, 0, 0], m=1, radius=0.1, rod_length=norm_(r_0))

Parameters:

NameDescriptionUnitsDefault value
has_massfalse
kinematic_constrainttrue
constraint_residue_externalWhen true, the length constraint is replaced by an externally supplied constraint_residue (set by an enclosing assembly joint to solve the loop analytically).false
point_gravityUse a point-gravity field pointing towards the world origin (structural; see Body)false
rendertrue
color[1, 1, 0, 1]
specular_coefficient1.5
r_0Nominal vector from frame_a to frame_bm[1, 0, 0]
mMass of point at rod midpointkg1
radiusRendering radius of the rod cylinder0.1

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
f_rodConstraint force in the rod direction (positive on frame_a when from a to b)
rRod_0Position vector from frame_a to frame_b, resolved in worldm
rRod_aPosition vector from frame_a to frame_b, resolved in frame_am
eRod_aUnit vector from frame_a to frame_b, resolved in frame_a
r_cm_0Position of rod midpoint, resolved in worldm
v_cm_0Velocity of rod midpoint, resolved in worldm/s
f_cm_aInertial force at midpoint, resolved in frame_a
f_cm_eComponent of f_cm_a along the rod
f_b_a1Force at frame_b without rod constraint force, resolved in frame_a
constraint_residueConstraint residue: length constraint by default, or rod force when external

Behavior

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

Source

dyad
"""
Joint with two spherical-like ends connected by a fixed-length rod.

Imposes a single distance constraint: |frame_b.r_0 - frame_a.r_0| = ||r_0||.
The rod can optionally carry a point mass at its midpoint
(`has_mass = true`, mass given by `m`). Highly recommended for closed
kinematic loops — it's much more efficient than two `Spherical` joints
plus a body.

- `r_0`: nominal vector from frame_a to frame_b. Defines `rod_length` and
  the initial rod direction. Must be non-zero.
- `has_mass`: include a point mass at the rod midpoint (structural).
- `m`: rod mass (only used when `has_mass = true`).
- `kinematic_constraint`: when true, use the rotation-matrix-aware form of
  the rod position (helps the symbolic loop solver); when false, the
  simpler `frame_b.r_0 - frame_a.r_0` form.
"""
component SphericalSpherical
  extends PartialTwoFrames()
  extends Renderable(color = [1, 1, 0, 1])
  shape = CylinderShape(render = render, color = color, r = frame_a.r_0, R = transpose(frame_a.R), length_direction = eRod_a, length = rod_length, width = 2 * radius, height = 2 * radius)
  structural parameter has_mass::Boolean = false
  structural parameter kinematic_constraint::Boolean = true
  "When true, the length constraint is replaced by an externally supplied `constraint_residue` (set by an enclosing assembly joint to solve the loop analytically)."
  structural parameter constraint_residue_external::Boolean = false
  "Nominal vector from frame_a to frame_b"
  parameter r_0::Length[3] = [1, 0, 0]
  "Mass of point at rod midpoint"
  parameter m::Mass = 1 if has_mass
  "Rendering radius of the rod cylinder"
  parameter radius::Real = 0.1
  final parameter rod_length::Real = norm_(r_0)
  "Constraint force in the rod direction (positive on frame_a when from a to b)"
  variable f_rod::Real
  "Position vector from frame_a to frame_b, resolved in world"
  variable rRod_0::Position[3]
  "Position vector from frame_a to frame_b, resolved in frame_a"
  variable rRod_a::Position[3]
  "Unit vector from frame_a to frame_b, resolved in frame_a"
  variable eRod_a::Real[3]
  "Position of rod midpoint, resolved in world"
  variable r_cm_0::Position[3] if has_mass
  "Velocity of rod midpoint, resolved in world"
  variable v_cm_0::Velocity[3] if has_mass
  "Inertial force at midpoint, resolved in frame_a"
  variable f_cm_a::Real[3] if has_mass
  "Component of f_cm_a along the rod"
  variable f_cm_e::Real[3] if has_mass
  "Force at frame_b without rod constraint force, resolved in frame_a"
  variable f_b_a1::Real[3]
  "Constraint residue: length constraint by default, or rod force when external"
  variable constraint_residue::Real
  "Use a point-gravity field pointing towards the world origin (structural; see Body)"
  structural parameter point_gravity::Boolean = false
relations
  if kinematic_constraint
    rRod_0 = transpose(frame_b.R) * (frame_b.R * frame_b.r_0) - transpose(frame_a.R) * (frame_a.R * frame_a.r_0)
  else
    rRod_0 = frame_b.r_0 - frame_a.r_0
  end
  rRod_a = resolve2(frame_a.R, rRod_0)
  eRod_a = rRod_a / rod_length
  # constraint_residue is pinned to 0. When constraint_residue_external is false
  # the length constraint below provides the second equation. When true, the
  # parent assembly (e.g. JointSSP/JointSSR) supplies
  # `constraint_residue = f_rod - <rod force>` instead, solving the loop analytically.
  constraint_residue = 0
  if !constraint_residue_external
    constraint_residue = dot(rRod_0, rRod_0) - rod_length ^ 2
  end
  frame_a.tau = [0, 0, 0]
  frame_b.tau = [0, 0, 0]
  if has_mass
    r_cm_0 = frame_a.r_0 + rRod_0 / 2
    v_cm_0 = der(r_cm_0)
    f_cm_a = m * resolve2(frame_a.R, der(v_cm_0) - gravity_acceleration(r_cm_0, point_gravity))
    f_cm_e = dot(f_cm_a, eRod_a) * eRod_a
    frame_a.f = (f_cm_a - f_cm_e) / 2 + f_rod * eRod_a
    f_b_a1 = (f_cm_a + f_cm_e) / 2
    frame_b.f = resolve_relative(f_b_a1 - f_rod * eRod_a, frame_a.R, frame_b.R)
  else
    f_b_a1 = [0, 0, 0]
    frame_a.f = f_rod * eRod_a
    frame_b.f = -resolve_relative(frame_a.f, frame_a.R, frame_b.R)
  end
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MultibodyComponents/SphericalSpherical.svg"},
    "labels": [
      {
        "label": "$(instance)",
        "x": 500,
        "y": 200,
        "rot": 0,
        "attrs": {"font-size": "160"}
      }
    ]
  }
}
end
Flattened Source
dyad
"""
Joint with two spherical-like ends connected by a fixed-length rod.

Imposes a single distance constraint: |frame_b.r_0 - frame_a.r_0| = ||r_0||.
The rod can optionally carry a point mass at its midpoint
(`has_mass = true`, mass given by `m`). Highly recommended for closed
kinematic loops — it's much more efficient than two `Spherical` joints
plus a body.

- `r_0`: nominal vector from frame_a to frame_b. Defines `rod_length` and
  the initial rod direction. Must be non-zero.
- `has_mass`: include a point mass at the rod midpoint (structural).
- `m`: rod mass (only used when `has_mass = true`).
- `kinematic_constraint`: when true, use the rotation-matrix-aware form of
  the rod position (helps the symbolic loop solver); when false, the
  simpler `frame_b.r_0 - frame_a.r_0` form.
"""
component SphericalSpherical
  frame_a = Frame3D() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -50, "y1": 450, "x2": 50, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  frame_b = Frame3D() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 950, "y1": 450, "x2": 1050, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  parameter render::Boolean = true
  parameter color::Real[4] = [0.5, 0.5, 0.5, 1.0]
  parameter specular_coefficient::Real = 1.5
  shape = CylinderShape(render = render, color = color, r = frame_a.r_0, R = transpose(frame_a.R), length_direction = eRod_a, length = rod_length, width = 2 * radius, height = 2 * radius)
  structural parameter has_mass::Boolean = false
  structural parameter kinematic_constraint::Boolean = true
  "When true, the length constraint is replaced by an externally supplied `constraint_residue` (set by an enclosing assembly joint to solve the loop analytically)."
  structural parameter constraint_residue_external::Boolean = false
  "Nominal vector from frame_a to frame_b"
  parameter r_0::Length[3] = [1, 0, 0]
  "Mass of point at rod midpoint"
  parameter m::Mass = 1 if has_mass
  "Rendering radius of the rod cylinder"
  parameter radius::Real = 0.1
  final parameter rod_length::Real = norm_(r_0)
  "Constraint force in the rod direction (positive on frame_a when from a to b)"
  variable f_rod::Real
  "Position vector from frame_a to frame_b, resolved in world"
  variable rRod_0::Position[3]
  "Position vector from frame_a to frame_b, resolved in frame_a"
  variable rRod_a::Position[3]
  "Unit vector from frame_a to frame_b, resolved in frame_a"
  variable eRod_a::Real[3]
  "Position of rod midpoint, resolved in world"
  variable r_cm_0::Position[3] if has_mass
  "Velocity of rod midpoint, resolved in world"
  variable v_cm_0::Velocity[3] if has_mass
  "Inertial force at midpoint, resolved in frame_a"
  variable f_cm_a::Real[3] if has_mass
  "Component of f_cm_a along the rod"
  variable f_cm_e::Real[3] if has_mass
  "Force at frame_b without rod constraint force, resolved in frame_a"
  variable f_b_a1::Real[3]
  "Constraint residue: length constraint by default, or rod force when external"
  variable constraint_residue::Real
  "Use a point-gravity field pointing towards the world origin (structural; see Body)"
  structural parameter point_gravity::Boolean = false
relations
  if kinematic_constraint
    rRod_0 = transpose(frame_b.R) * (frame_b.R * frame_b.r_0) - transpose(frame_a.R) * (frame_a.R * frame_a.r_0)
  else
    rRod_0 = frame_b.r_0 - frame_a.r_0
  end
  rRod_a = resolve2(frame_a.R, rRod_0)
  eRod_a = rRod_a / rod_length
  # constraint_residue is pinned to 0. When constraint_residue_external is false
  # the length constraint below provides the second equation. When true, the
  # parent assembly (e.g. JointSSP/JointSSR) supplies
  # `constraint_residue = f_rod - <rod force>` instead, solving the loop analytically.
  constraint_residue = 0
  if !constraint_residue_external
    constraint_residue = dot(rRod_0, rRod_0) - rod_length ^ 2
  end
  frame_a.tau = [0, 0, 0]
  frame_b.tau = [0, 0, 0]
  if has_mass
    r_cm_0 = frame_a.r_0 + rRod_0 / 2
    v_cm_0 = der(r_cm_0)
    f_cm_a = m * resolve2(frame_a.R, der(v_cm_0) - gravity_acceleration(r_cm_0, point_gravity))
    f_cm_e = dot(f_cm_a, eRod_a) * eRod_a
    frame_a.f = (f_cm_a - f_cm_e) / 2 + f_rod * eRod_a
    f_b_a1 = (f_cm_a + f_cm_e) / 2
    frame_b.f = resolve_relative(f_b_a1 - f_rod * eRod_a, frame_a.R, frame_b.R)
  else
    f_b_a1 = [0, 0, 0]
    frame_a.f = f_rod * eRod_a
    frame_b.f = -resolve_relative(frame_a.f, frame_a.R, frame_b.R)
  end
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MultibodyComponents/SphericalSpherical.svg"},
    "labels": [
      {
        "label": "$(instance)",
        "x": 500,
        "y": 200,
        "rot": 0,
        "attrs": {"font-size": "160"}
      }
    ]
  }
}
end


Test Cases

No test cases defined.

  • Examples

  • Experiments

  • Analyses