Joint topology, kinematic constraints, and elastic links for MBS. Covers revolute, prismatic, motors, springs, axis-aware joint frames, and correct topology for mechanisms with ground pivots and fixed guides.
Connect bodies with kinematic constraints, motors, and springs without mixing up topology or frame semantics. The core rule is: identify the physical degree-of-freedom axis first, then choose joint type and frame orientation so the joint axis matches that physical intent.
For quaternion and rotation construction details, see ../quaternions/SKILL.md. For planar XY body-orientation conventions, see ../body_creation/SKILL.md.
For every connection, answer these questions in order:
Then choose the joint:
Do not infer joint type from a copied code pattern. Infer it from the mechanism.
When a body rotates about a fixed ground pivot and is actuated:
joint_pivot = chrono.ChLinkLockRevolute()
frame_origin = chrono.ChFramed(chrono.ChVector3d(0, 0, 0), chrono.QUNIT)
joint_pivot.Initialize(rotating_body, ground, frame_origin)
sys.AddLink(joint_pivot)
motor = chrono.ChLinkMotorRotationTorque()
motor.Initialize(rotating_body, ground, frame_origin)
motor.SetTorqueFunction(chrono.ChFunctionSine(amplitude, frequency))
sys.AddLink(motor)
# WRONG: motor only
motor.Initialize(rotating_body, ground, chrono.ChFramed(chrono.ChVector3d(0, 0, 0)))
sys.AddLink(motor)
If a body slides along a fixed rail, the prismatic belongs between the slider and ground, not between the slider and another moving body.
guide_axis = chrono.ChVector3d(1, 0, 0) # example only
guide_pos = chrono.ChVector3d(guide_x, guide_y, guide_z)
frame_prismatic = chrono.ChFramed()
frame_prismatic.SetPos(guide_pos)
frame_prismatic.SetRot(guide_rotation_that_maps_frame_Z_to_guide_axis)
joint_slider_ground = chrono.ChLinkLockPrismatic()
joint_slider_ground.Initialize(slider, ground, frame_prismatic)
sys.AddLink(joint_slider_ground)
When a connecting rod attaches to a slider that already moves on a fixed guide:
Do not replace the rod-slider pin with a prismatic. That changes the mechanism.
Use chrono.ChLinkLockRevolute() as the default revolute implementation for these MBS skills and demos.
# CORRECT
joint = chrono.ChLinkLockRevolute()
# WRONG for this skill family
joint = chrono.ChLinkRevolute()
Before finalizing a crank-slider or similar fixed-guide mechanism, confirm all of these are present:
Do not reuse a frame rotation from one joint type just because the point is the same.
If you change a joint from prismatic to revolute, recompute the frame orientation from the hinge axis requirement. Do not keep the old guide-axis rotation by default.
For a revolute, the frame orientation must make the revolute axis coincide with the intended physical hinge axis.
chrono.QUNIT or an equivalent frame is common.# Common planar case: hinge about world Z
joint = chrono.ChLinkLockRevolute()
joint.Initialize(body_a, body_b, chrono.ChFramed(pivot_pos, chrono.QUNIT))
sys.AddLink(joint)
# WRONG: copied from a prismatic guide example without checking hinge axis
joint = chrono.ChLinkLockRevolute()
joint.Initialize(body_a, body_b, chrono.ChFramed(pivot_pos, chrono.Q_ROTATE_Z_TO_X))
sys.AddLink(joint)
ChLinkLockPrismatic uses the frame's local +Z as the sliding axis. Therefore:
chrono.Q_ROTATE_Z_TO_X is only the common X-guide example, not a universal default.
# Example: guide along global X
joint = chrono.ChLinkLockPrismatic()
joint.Initialize(slider, ground, chrono.ChFramed(pos, chrono.Q_ROTATE_Z_TO_X))
sys.AddLink(joint)
# Example: guide along arbitrary axis_hat
joint = chrono.ChLinkLockPrismatic()
joint.Initialize(slider, ground, chrono.ChFramed(pos, rotation_z_to_axis_hat))
sys.AddLink(joint)
For linkages such as crank-rod or rod-slider, the body-local Initialize(body1, body2, True, frame1, frame2) form is preferred because each marker is defined in the body it belongs to.
joint = chrono.ChLinkLockRevolute()
joint.Initialize(body1, body2, True, frame1_local_to_body1, frame2_local_to_body2)
sys.AddLink(joint)
spring = chrono.ChLinkTSDA()
spring.Initialize(body1, body2, True,
chrono.ChVector3d(0, 0, 0),
chrono.ChVector3d(-1, 0, 0))
spring.SetRestLength(rest_length)
spring.SetSpringCoefficient(spring_k)
spring.SetDampingCoefficient(damping_c)
sys.AddLink(spring)
If the mechanism has no contact and is fully described by joints, springs, and motors, do not call sys.SetCollisionSystemType().
If sys.GetConstraintViolation() is not directly usable in your logging path, log a derived scalar or 0.0; do not block the whole simulation on debug logging.
ChLinkRevolute instead of the team's ChLinkLockRevolute conventionQ_ROTATE_Z_TO_X from a prismatic example into a revolute without checking the hinge axisQ_ROTATE_Z_TO_X as the only valid prismatic rotation instead of an X-axis example