Cable
Cable / rope of total length l and mass m, modeled as n rigid segments connected by 3-DOF Spherical joints, matching the Multibody.jl Rope.
When end_joints = true (default), the topology uses n + 1 joints: frame_a → S₁ → link₁ → S₂ → … → Sₙ → linkₙ → Sₙ₊₁ → frame_b
When end_joints = false, only the n − 1 internal joints are kept and both ends attach rigidly to the adjacent frame: frame_a → link₁ → S₁ → link₂ → … → Sₙ₋₁ → linkₙ → frame_b
When iscut = true, the last segment is replaced by a SphericalSpherical rod that imposes a single distance constraint and carries only axial force. Use this whenever the cable closes a kinematic loop (both ends attached to frames that are already rooted, e.g. a mooring line between the world and a free-floating body): the rod terminates the chain without over-constraining the orientation, and the two spherical-like rod ends replace the joints that would otherwise sit on either side of the last link. Requires n >= 2. The resulting topologies are frame_a → S₁ → link₁ → … → Sₙ₋₁ → linkₙ₋₁ → rod → frame_b (end_joints = true) frame_a → link₁ → S₁ → … → Sₙ₋₂ → linkₙ₋₁ → rod → frame_b (end_joints = false)
Each Spherical joint uses quaternion state (Euler-angle state hits a structural-simplify cycle when chained — SciML/ModelingToolkit.jl#4608) and transmits no torque. The cable starts straight (along dir); seed an initial bend by setting a joint's non-unit quaternion Q_hat as an initial condition, e.g. a 0.9 rad bend about x on the first joint: cable.joints[1].Q_hat = [cos(0.45), sin(0.45), 0, 0].
This component extends from PartialTwoFrames This component extends from Renderable
Usage
MultibodyComponents.Cable(render=true, color=[0.5, 0.4, 0.4, 1], specular_coefficient=1.5, l=1, m=1, dir=[0, -1, 0], radius=0.05, r_segment=(l / n) * (dir / norm_(dir)), m_segment=m / n)
Parameters:
| Name | Description | Units | Default value |
|---|---|---|---|
initialize_orientation | Specify identity initial conditions for the joint orientations. Set to false when the cable closes a kinematic loop, where a straight cable is not a consistent initial condition, to let initialization solve for the joint orientations. | – | true |
orientation_state | – | Orientation...uaternion() | |
n | Number of rigid segments | – | 5 |
end_joints | Include Spherical joints at both ends (frame_a and frame_b) | – | true |
iscut | Terminate the frame_b end with a SphericalSpherical rod as the last segment; use when the cable closes a kinematic loop (requires n >= 2) | – | false |
nj | Number of joints | – | ifelse(iscu... 1, n - 1)) |
nl | Number of rigid links | – | ifelse(iscut, n - 1, n) |
render | – | true | |
color | – | [0.5, 0.4, 0.4, 1] | |
specular_coefficient | – | 1.5 | |
l | Total unstretched length of the rope | m | 1 |
m | Total mass of the rope (each segment gets m/n) | kg | 1 |
dir | Hanging direction; unit-normalized internally | – | [0, -1, 0] |
radius | Rendering radius of each segment | – | 0.05 |
Connectors
frame_a- Frame3D is the fundamental 3D connector used for 6DOF motion. Most components have one or severalFrame
connectors that can be connected together (Frame3D)
frame_b- Frame3D is the fundamental 3D connector used for 6DOF motion. Most components have one or severalFrame
connectors that can be connected together (Frame3D)
Behavior
using MultibodyComponents #hide
using ModelingToolkit #hide
@variables render #hide
@variables color #hide
@variables specular_coefficient #hide
@variables l #hide
@variables m #hide
@variables dir #hide
@variables radius #hide
@named sys = MultibodyComponents.Cable(render=render, color=color, specular_coefficient=specular_coefficient, l=l, m=m, dir=dir, radius=radius) #hide
let eqs = full_equations(sys); Base.length(eqs) > 25 ? nothing : eqs end #hide<< @example-block not executed in draft mode >>Source
"""
Cable / rope of total length `l` and mass `m`, modeled as `n` rigid
segments connected by 3-DOF `Spherical` joints, matching the
Multibody.jl `Rope`.
When `end_joints = true` (default), the topology uses `n + 1` joints:
`frame_a → S₁ → link₁ → S₂ → … → Sₙ → linkₙ → Sₙ₊₁ → frame_b`
When `end_joints = false`, only the `n − 1` internal joints are kept and
both ends attach rigidly to the adjacent frame:
`frame_a → link₁ → S₁ → link₂ → … → Sₙ₋₁ → linkₙ → frame_b`
When `iscut = true`, the last segment is replaced by a `SphericalSpherical`
rod that imposes a single distance constraint and carries only axial force.
Use this whenever the cable closes a kinematic loop (both ends attached to
frames that are already rooted, e.g. a mooring line between the world and a
free-floating body): the rod terminates the chain without over-constraining
the orientation, and the two spherical-like rod ends replace the joints that
would otherwise sit on either side of the last link. Requires `n >= 2`. The
resulting topologies are
`frame_a → S₁ → link₁ → … → Sₙ₋₁ → linkₙ₋₁ → rod → frame_b` (`end_joints = true`)
`frame_a → link₁ → S₁ → … → Sₙ₋₂ → linkₙ₋₁ → rod → frame_b` (`end_joints = false`)
Each `Spherical` joint uses quaternion state (Euler-angle state hits a
structural-simplify cycle when chained — SciML/ModelingToolkit.jl#4608) and
transmits no torque. The cable starts straight (along `dir`); seed an initial
bend by setting a joint's non-unit quaternion `Q_hat` as an initial condition,
e.g. a 0.9 rad bend about x on the first joint:
`cable.joints[1].Q_hat = [cos(0.45), sin(0.45), 0, 0]`.
"""
component Cable
extends PartialTwoFrames()
extends Renderable(color = [0.5, 0.4, 0.4, 1])
joints = [MultibodyComponents.Spherical(orientation_state = orientation_state, render = false, initialize_orientation = initialize_orientation) for i in 1:nj]
links = [MultibodyComponents.BodyShape(r = r_segment, m = m_segment, color = color, radius = radius) for i in 1:nl]
rod = MultibodyComponents.SphericalSpherical(r_0 = r_segment, has_mass = true, m = m_segment, radius = radius, color = color, render = render) if iscut
"Specify identity initial conditions for the joint orientations. Set to false when the cable closes a kinematic loop, where a straight cable is not a consistent initial condition, to let initialization solve for the joint orientations."
structural parameter initialize_orientation::Boolean = true
structural parameter orientation_state::MultibodyComponents.OrientationState = OrientationState.Quaternion()
"Number of rigid segments"
structural parameter n::Integer = 5
"Include Spherical joints at both ends (frame_a and frame_b)"
structural parameter end_joints::Boolean = true
"Terminate the frame_b end with a SphericalSpherical rod as the last segment; use when the cable closes a kinematic loop (requires n >= 2)"
structural parameter iscut::Boolean = false
"Number of joints"
final structural parameter nj::Integer = ifelse(iscut, ifelse(end_joints, n - 1, n - 2), ifelse(end_joints, n + 1, n - 1))
"Number of rigid links"
final structural parameter nl::Integer = ifelse(iscut, n - 1, n)
"Total unstretched length of the rope"
parameter l::Length = 1
"Total mass of the rope (each segment gets m/n)"
parameter m::Mass = 1
"Hanging direction; unit-normalized internally"
parameter dir::Real[3] = [0, -1, 0]
"Rendering radius of each segment"
parameter radius::Real = 0.05
final parameter r_segment::Length[3] = (l / n) * (dir / norm_(dir))
final parameter m_segment::Mass = m / n
relations
if iscut
if end_joints
connect(frame_a, joints[1].frame_a)
for i in 1:n - 2
connect(joints[i].frame_b, links[i].frame_a)
connect(links[i].frame_b, joints[i + 1].frame_a)
end
connect(joints[n - 1].frame_b, links[n - 1].frame_a)
connect(links[n - 1].frame_b, rod.frame_a)
connect(rod.frame_b, frame_b)
else
connect(frame_a, links[1].frame_a)
for i in 1:n - 2
connect(links[i].frame_b, joints[i].frame_a)
connect(joints[i].frame_b, links[i + 1].frame_a)
end
connect(links[n - 1].frame_b, rod.frame_a)
connect(rod.frame_b, frame_b)
end
else
if end_joints
connect(frame_a, joints[1].frame_a)
for i in 1:n
connect(joints[i].frame_b, links[i].frame_a)
connect(links[i].frame_b, joints[i + 1].frame_a)
end
connect(joints[nj].frame_b, frame_b)
else
connect(frame_a, links[1].frame_a)
for i in 1:nj
connect(links[i].frame_b, joints[i].frame_a)
connect(joints[i].frame_b, links[i + 1].frame_a)
end
connect(links[n].frame_b, frame_b)
end
end
metadata {
"Dyad": {
"icons": {"default": "dyad://MultibodyComponents/Cable.svg"},
"labels": [
{
"label": "$(instance)",
"x": 500,
"y": 150,
"rot": 0,
"attrs": {"font-size": "160"}
}
]
}
}
endFlattened Source
"""
Cable / rope of total length `l` and mass `m`, modeled as `n` rigid
segments connected by 3-DOF `Spherical` joints, matching the
Multibody.jl `Rope`.
When `end_joints = true` (default), the topology uses `n + 1` joints:
`frame_a → S₁ → link₁ → S₂ → … → Sₙ → linkₙ → Sₙ₊₁ → frame_b`
When `end_joints = false`, only the `n − 1` internal joints are kept and
both ends attach rigidly to the adjacent frame:
`frame_a → link₁ → S₁ → link₂ → … → Sₙ₋₁ → linkₙ → frame_b`
When `iscut = true`, the last segment is replaced by a `SphericalSpherical`
rod that imposes a single distance constraint and carries only axial force.
Use this whenever the cable closes a kinematic loop (both ends attached to
frames that are already rooted, e.g. a mooring line between the world and a
free-floating body): the rod terminates the chain without over-constraining
the orientation, and the two spherical-like rod ends replace the joints that
would otherwise sit on either side of the last link. Requires `n >= 2`. The
resulting topologies are
`frame_a → S₁ → link₁ → … → Sₙ₋₁ → linkₙ₋₁ → rod → frame_b` (`end_joints = true`)
`frame_a → link₁ → S₁ → … → Sₙ₋₂ → linkₙ₋₁ → rod → frame_b` (`end_joints = false`)
Each `Spherical` joint uses quaternion state (Euler-angle state hits a
structural-simplify cycle when chained — SciML/ModelingToolkit.jl#4608) and
transmits no torque. The cable starts straight (along `dir`); seed an initial
bend by setting a joint's non-unit quaternion `Q_hat` as an initial condition,
e.g. a 0.9 rad bend about x on the first joint:
`cable.joints[1].Q_hat = [cos(0.45), sin(0.45), 0, 0]`.
"""
component Cable
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
joints = [MultibodyComponents.Spherical(orientation_state = orientation_state, render = false, initialize_orientation = initialize_orientation) for i in 1:nj]
links = [MultibodyComponents.BodyShape(r = r_segment, m = m_segment, color = color, radius = radius) for i in 1:nl]
rod = MultibodyComponents.SphericalSpherical(r_0 = r_segment, has_mass = true, m = m_segment, radius = radius, color = color, render = render) if iscut
"Specify identity initial conditions for the joint orientations. Set to false when the cable closes a kinematic loop, where a straight cable is not a consistent initial condition, to let initialization solve for the joint orientations."
structural parameter initialize_orientation::Boolean = true
structural parameter orientation_state::MultibodyComponents.OrientationState = OrientationState.Quaternion()
"Number of rigid segments"
structural parameter n::Integer = 5
"Include Spherical joints at both ends (frame_a and frame_b)"
structural parameter end_joints::Boolean = true
"Terminate the frame_b end with a SphericalSpherical rod as the last segment; use when the cable closes a kinematic loop (requires n >= 2)"
structural parameter iscut::Boolean = false
"Number of joints"
final structural parameter nj::Integer = ifelse(iscut, ifelse(end_joints, n - 1, n - 2), ifelse(end_joints, n + 1, n - 1))
"Number of rigid links"
final structural parameter nl::Integer = ifelse(iscut, n - 1, n)
"Total unstretched length of the rope"
parameter l::Length = 1
"Total mass of the rope (each segment gets m/n)"
parameter m::Mass = 1
"Hanging direction; unit-normalized internally"
parameter dir::Real[3] = [0, -1, 0]
"Rendering radius of each segment"
parameter radius::Real = 0.05
final parameter r_segment::Length[3] = (l / n) * (dir / norm_(dir))
final parameter m_segment::Mass = m / n
relations
if iscut
if end_joints
connect(frame_a, joints[1].frame_a)
for i in 1:n - 2
connect(joints[i].frame_b, links[i].frame_a)
connect(links[i].frame_b, joints[i + 1].frame_a)
end
connect(joints[n - 1].frame_b, links[n - 1].frame_a)
connect(links[n - 1].frame_b, rod.frame_a)
connect(rod.frame_b, frame_b)
else
connect(frame_a, links[1].frame_a)
for i in 1:n - 2
connect(links[i].frame_b, joints[i].frame_a)
connect(joints[i].frame_b, links[i + 1].frame_a)
end
connect(links[n - 1].frame_b, rod.frame_a)
connect(rod.frame_b, frame_b)
end
else
if end_joints
connect(frame_a, joints[1].frame_a)
for i in 1:n
connect(joints[i].frame_b, links[i].frame_a)
connect(links[i].frame_b, joints[i + 1].frame_a)
end
connect(joints[nj].frame_b, frame_b)
else
connect(frame_a, links[1].frame_a)
for i in 1:nj
connect(links[i].frame_b, joints[i].frame_a)
connect(joints[i].frame_b, links[i + 1].frame_a)
end
connect(links[n].frame_b, frame_b)
end
end
metadata {
"Dyad": {
"icons": {"default": "dyad://MultibodyComponents/Cable.svg"},
"labels": [
{
"label": "$(instance)",
"x": 500,
"y": 150,
"rot": 0,
"attrs": {"font-size": "160"}
}
]
}
}
endTest Cases
No test cases defined.
Related
Examples
Experiments
Analyses
Tests