Plain
This example builds a double pendulum on a rail entirely in code and uses it as a guided tour of the SuperDex Physics articulated actor API: building the joint/link chain, introspecting its topology, reading forward-kinematics state, manipulating the state directly, modeling joints live, actuating without a controller, reading the end-effector Jacobian, and controlling contact.
Source: examples/example_articulations_double_pendulum_on_rail.py
The scene is a single articulated actor — a serial 4-joint / 4-link chain welded to the ceiling:
ceiling -[Hard]-> RailHousing -[Prismatic]-> Cart -[Revolute]-> UpperArm -[Spherical]-> LowerArm
A cart slides on a horizontal prismatic rail; hanging from it is a double pendulum (a revolute upper hinge and a spherical lower joint) whose tip strikes a ball resting on the ground. It has 5 DoFs (Prismatic 1 + Revolute 1 + Spherical 3; the Hard root contributes 0).
An articulated actor has a fixed joint topology and is more efficient and robust than an equivalent set of constraints. Prefer it whenever the set of joints does not change at runtime. If you need to add or remove joints at runtime, model the joints as constraints instead — see the Constraints example.
Implementation
Building the Articulated Chain
An articulated actor is described by parallel joints[] and links[] arrays, where joints[i] is the inbound joint of links[i] and parent_link = i - 1 forms a serial chain. joint.parent_link_from_joint places the joint frame in its parent link's frame; link.parent_joint_from_link places the link body relative to its inbound joint frame.
params = sdp.ArticulatedActorParams(name="DoublePendulumOnRail")
params.world_from_root = sdp.TransformRT(translation=[0, 0.75, 0])
params.joints = [
# Root weld: fixes the rail housing to the world (0 DoFs).
sdp.ArticulatedJointParams(name="CeilingWeld", type=sdp.ArticulatedJointType.HARD),
# Horizontal rail with soft limits, viscous friction, and armature inertia.
sdp.ArticulatedJointParams(
name="Rail",
type=sdp.ArticulatedJointType.PRISMATIC,
axis=[1, 0, 0],
min_limit=[-0.2, 0, 0], # scalar limit times axis
max_limit=[0.2, 0, 0],
limit_stiffness=250.0,
limit_damping=8.8,
friction=sdp.ArticulatedJointFrictionParams(viscous=0.018),
inertia=0.125,
),
# Upper pendulum hinge (revolute about Z) and lower ball joint (spherical).
sdp.ArticulatedJointParams(name="UpperSwing", type=sdp.ArticulatedJointType.REVOLUTE, axis=[0, 0, -1]),
sdp.ArticulatedJointParams(name="LowerSwing", type=sdp.ArticulatedJointType.SPHERICAL),
]
params.links = [
# RailHousing, Cart, UpperArm, LowerArm — each a Box collider with a
# parent_link, a parent_joint_from_link offset, a layer, and a density.
# (See the example source for the exact geometry.)
# ...
]
articulation = scene.create_articulated_actor(params)
# Seed the swing: a small rail drift plus an upper-hinge kick.
articulation.set_articulated_joint_velocities(velocities=[0.3, 4.2, 0, 0, 0])