| name | topology |
| description | 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. |
| compatibility | pychrono >= 8.0 |
| metadata | {"domain":"mbs"} |
Skill: MBS Topology (Joints, Motors, Springs)
Purpose
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.
When to Use
- Two bodies must move relative to each other in a constrained way
- A body rotates about a fixed pivot
- A body slides along a fixed guide
- A linkage combines pivots, guides, motors, or springs
Part 1: Topology First
Rule 1: Decide the physical DOF before choosing the joint
For every connection, answer these questions in order:
- What relative motion is physically allowed: rotation, translation, or neither?
- About or along which axis does that motion occur?
- Is the constraint attached to ground or to another moving body?
Then choose the joint:
- Revolute: one relative rotation about a hinge axis
- Prismatic: one relative translation along a guide axis
- Motor: actuation only; it does not replace the geometric constraint
Do not infer joint type from a copied code pattern. Infer it from the mechanism.
Rule 2: Pivot + Motor needs BOTH links
When a body rotates about a fixed ground pivot and is actuated:
- Add a revolute for the geometric hinge
- Add a motor for the actuation
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)
motor.Initialize(rotating_body, ground, chrono.ChFramed(chrono.ChVector3d(0, 0, 0)))
sys.AddLink(motor)
Rule 3: Fixed guide means prismatic to GROUND
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)
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)
Rule 4: Fixed-guide rod-slider linkage uses revolute at rod-slider
When a connecting rod attaches to a slider that already moves on a fixed guide:
- Rod-Slider = Revolute
- Slider-Ground = Prismatic
Do not replace the rod-slider pin with a prismatic. That changes the mechanism.
Rule 5: Team convention for revolute links
Use chrono.ChLinkLockRevolute() as the default revolute implementation for these MBS skills and demos.
joint = chrono.ChLinkLockRevolute()
joint = chrono.ChLinkRevolute()
Rule 6: Fixed-guide slider linkage checklist
Before finalizing a crank-slider or similar fixed-guide mechanism, confirm all of these are present:
- rotating body-ground revolute at the pivot
- rotating body-ground motor if actuated
- rod-rotating body revolute
- rod-slider revolute
- slider-ground prismatic
Part 2: Frame Semantics
Rule 7: Revolute and prismatic frames are NOT interchangeable
Do not reuse a frame rotation from one joint type just because the point is the same.
- Revolute frame rotation defines the hinge axis
- Prismatic frame rotation defines the sliding axis
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.
Rule 8: Revolute frame must align to the intended hinge axis
For a revolute, the frame orientation must make the revolute axis coincide with the intended physical hinge axis.
- In many planar XY mechanisms, the hinge axis is world Z, so
chrono.QUNIT or an equivalent frame is common.
- In a different mechanism, the hinge axis may be X, Y, or an arbitrary direction; rotate the frame so the revolute axis matches that direction.
joint = chrono.ChLinkLockRevolute()
joint.Initialize(body_a, body_b, chrono.ChFramed(pivot_pos, chrono.QUNIT))
sys.AddLink(joint)
joint = chrono.ChLinkLockRevolute()
joint.Initialize(body_a, body_b, chrono.ChFramed(pivot_pos, chrono.Q_ROTATE_Z_TO_X))
sys.AddLink(joint)
Rule 9: Prismatic frame must map frame +Z onto the guide axis
ChLinkLockPrismatic uses the frame's local +Z as the sliding axis. Therefore:
- first identify the desired guide axis
- then rotate the frame so local +Z aligns with that guide axis
chrono.Q_ROTATE_Z_TO_X is only the common X-guide example, not a universal default.
joint = chrono.ChLinkLockPrismatic()
joint.Initialize(slider, ground, chrono.ChFramed(pos, chrono.Q_ROTATE_Z_TO_X))
sys.AddLink(joint)
joint = chrono.ChLinkLockPrismatic()
joint.Initialize(slider, ground, chrono.ChFramed(pos, rotation_z_to_axis_hat))
sys.AddLink(joint)
Rule 10: Use body-local frames when the attachment point belongs to each body
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)
Part 3: Springs and Supporting Rules
ChLinkTSDA
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)
Pure MBS: omit collision system
If the mechanism has no contact and is fully described by joints, springs, and motors, do not call sys.SetCollisionSystemType().
Constraint logging
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.
Pitfalls to Avoid
- Motor without revolute at a ground pivot
- Rod-slider prismatic in a fixed-guide linkage
- Missing slider-ground prismatic when a slider is supposed to move on a fixed guide
- Using
ChLinkRevolute instead of the team's ChLinkLockRevolute convention
- Copying
Q_ROTATE_Z_TO_X from a prismatic example into a revolute without checking the hinge axis
- Treating
Q_ROTATE_Z_TO_X as the only valid prismatic rotation instead of an X-axis example