L2 · DS-110

Joints & Degrees of Freedom

Define a joint between two parts and correctly state and realize its allowed motion - revolute vs. prismatic - exposing exactly one intended degree of freedom and no others.

01
Challenge

Try this first — before any explanation.

Two already-correctly-placed parts: a hinge_base welded to the world and a hinge_leaf whose knuckle is concentric with the base knuckle - a Ø6 pivot line runs through both along world Y. This CAD assembly has ZERO joints: nothing yet says the parts can move relative to each other; the DOF readout reads 0 and the leaf is rigid. For the first time the Bench exposes a physics model definition beside the CAD. Your job: declare ONE joint so the leaf rotates about the pivot line (world Y) and NOTHING else can move; the pivot passes through (30,0,12). You have not been taught joint types yet - guess a hinge, a slide, or a free joint and watch what the leaf does. Common first attempts: a free joint (leaf flops in all 6 DOF - too much), a slide joint (wrong motion), or the right hinge with the wrong axis (1 0 0 instead of 0 1 0, so it folds sideways). Each failure is diagnostic.

The Bench

The real Bench probes a MuJoCo-WASM sim for DOF. Here we emulate the kinematic referee in numpy: declare a joint (type, axis, range) and the grader applies a small test wrench in all six directions, measures which produce motion, and reports n_free, the realized axis, and the kind - exactly the sim-based DOF probe, not an XML parse. Edit only the joint declaration cell, gravity off, kinematic only.

0:\n axis = axis / np.linalg.norm(axis)\n trans_resp, rot_resp = [], []\n for d in TRANS_DIRS:\n if jtype == 'free':\n trans_resp.append(1.0)\n elif jtype == 'slide':\n trans_resp.append(abs(float(np.dot(d, axis)))) # only along axis\n else: # hinge / weld\n trans_resp.append(0.0)\n for d in ROT_DIRS:\n if jtype == 'free':\n rot_resp.append(1.0)\n elif jtype == 'hinge':\n rot_resp.append(abs(float(np.dot(d, axis)))) # only about axis\n else: # slide / weld\n rot_resp.append(0.0)\n return np.array(trans_resp), np.array(rot_resp), axis\n\nprint('probe() ready')","label":"Joint model - how a joint admits motion (given - do not edit)"},{"code":"# Add ONE joint so the leaf rotates about the pivot line (world Y) and NOTHING\n# else can move. Choose type ('hinge'=revolute, 'slide'=prismatic, 'free'=all 6),\n# the axis vector, and the range in degrees.\n#\n# in the full Bench this is inside .\n\njoint = {\n 'type': 'free', # <-- rotation, not translation, not all-6\n 'axis': [0, 0, 0], # <-- pivot runs along world Y\n 'pos': [30, 0, 12], # given: a point on the pivot line\n 'range': [0, 90], # degrees\n}\n\ntr, rot, axis = probe(joint)\nprint('translational response x,y,z :', np.round(tr, 3))\nprint('rotational response x,y,z :', np.round(rot, 3))","label":"YOUR JOB - declare ONE joint (edit this cell only)"},{"code":"# Sim-based DOF probe: count directions that moved, classify kind, check axis & range.\n\ntr, rot, axis = probe(joint)\nn_trans = int(np.sum(tr > MOTION_EPS))\nn_rot = int(np.sum(rot > MOTION_EPS))\nn_free = n_trans + n_rot\nkind = 'rotational' if n_rot >= n_trans else 'translational'\n\n# realized motion axis = the test direction that moved most; axis error vs target\nresp = rot if kind == 'rotational' else tr\nrealized = (ROT_DIRS if kind == 'rotational' else TRANS_DIRS)[int(np.argmax(resp))]\ncos = abs(float(np.dot(realized, TARGET_AXIS)))\naxis_err = float(np.degrees(np.arccos(np.clip(cos, -1, 1))))\n\nlo, hi = joint['range']\nrange_ok = (lo == 0 and hi == 90) # revolute spec: stop at 0 and 90 deg\n\nfails = []\nif n_free == 0:\n fails.append('FAIL: no free DOF detected - the leaf is welded. Add a joint inside the leaf body.')\nelif n_free > 1:\n fails.append(f'FAIL: leaf moves in {n_free} directions - that is a free joint, not a hinge. Use type=\"hinge\" to leave exactly 1 rotational DOF.')\nelif kind != 'rotational':\n fails.append('FAIL: leaf slides instead of rotating. Spec asked for rotation - change type from \"slide\" to \"hinge\".')\nelif axis_err > 1.0:\n fails.append(f'FAIL: hinge axis is {axis_err:.0f} deg off: leaf folds about the wrong axis, but the pivot runs along Y. Set axis=\"0 1 0\".')\nelif not range_ok:\n fails.append(f'FAIL: leaf rotates past 90 deg into the base. Add range=\"0 90\" to stop it.')\n\nprint(f'n_free={n_free} kind={kind} axis_err={axis_err:.1f}deg range=({lo},{hi})')\nif fails:\n print(fails[0])\nelse:\n print('PASS: exactly 1 rotational DOF about Y, range 0-90 enforced - that is a hinge.')","label":"Autograder - submit"}],"intro":"The real Bench probes a MuJoCo-WASM sim for DOF. Here we emulate the kinematic referee in numpy: declare a joint (type, axis, range) and the grader applies a small test wrench in all six directions, measures which produce motion, and reports n_free, the realized axis, and the kind - exactly the sim-based DOF probe, not an XML parse. Edit only the joint declaration cell, gravity off, kinematic only.","key":"design-simulation/joints-and-degrees-of-freedom","kind":"python","title":"Joints & Degrees of Freedom"}">
PYTHON · NUMPY · IN-BROWSER

Joints & Degrees of Freedom

The real Bench probes a MuJoCo-WASM sim for DOF. Here we emulate the kinematic referee in numpy: declare a joint (type, axis, range) and the grader applies a small test wrench in all six directions, measures which produce motion, and reports n_free, the realized axis, and the kind - exactly the sim-based DOF probe, not an XML parse. Edit only the joint declaration cell, gravity off, kinematic only.

02
Model

The idea, built visually.

Placing two parts touching does nothing. Either they are frozen, or - if you tell the sim they can move - they move in EVERY way. Neither is a hinge. A hinge is a promise about exactly ONE way to move. A rigid part floating in space has six degrees of freedom: three slides, three spins. 'Free' means all six. A machine is what you get when you take some away. A joint is a subtraction. A revolute - a hinge - leaves exactly one rotation about its axis and kills the other five. One degree of freedom. The axis is the whole identity of the joint: same type, different axis, completely different machine. Two joints do almost all of mechanism design. Revolute: one rotation - hinges, knuckles, wheels. Prismatic: one translation - drawers, pistons, linear slides. Both leave one DOF; the question is always rotation or translation, and about-or-along which axis. Our pivot runs along Y, so: revolute, axis zero-one-zero. Get the type right and the axis right and you have removed exactly the five degrees of freedom you meant to. That is a hinge. That is a mechanism.

▣ Stage animation: Cold open on the placed-but-jointless hinge; a replayed cursor tries to swing the leaf - it will not move, then in another take it flops freely in every direction. A free body shows its triad with six glowing arrows - three straight (translate x/y/z), three curved (rotate x/y/z) - and a counter ticks to DOF:6. A revolute joint snaps in as a glowing axis line; five arrows wither in warm-orange flashes, one curved arrow about the axis survives pulsing warm-teal, counter 6->1. Split-screen FLUX stills: a door hinge with vertical axis beside the SAME hinge rotated so its axis is horizontal and the door flaps like a cat-flap, captioned 'same type, different axis'. Revolute (curved arrow about an axis) sits beside prismatic (straight arrow along an axis, a drawer sliding). Finally on our hinge the pivot lights along Y, a revolute axis 0 1 0 snaps on and the leaf swings (DOF 1); a deliberately wrong axis 1 0 0 folds the leaf sideways through the base in a warm-orange collision flash.

03
Guided practice

Build it up, step by step.

Step A (worked, full scaffold): read a known-good DIFFERENT hinge - a lid hinged about world Z through (0,0,5): type='hinge' (revolute - one rotational DOF), axis='0 0 1' (the rotation axis), pos='0 0 5' (a point the axis passes through), range='0 120' (allowed motion, degrees). Each attribute mapped: type = which DOF survives, axis = its direction, pos = where the axis sits, range = how far it may travel. Step B (fill the attributes): structure given, values blank - type='' (rotation not translation), axis=' __ __' (pivot runs along world Y), pos='30 0 12' given, range='0 90'. Hints: (1) the leaf must turn not slide - which type leaves a rotational DOF?; (2) the pivot runs along world Y - which component is 1? Expected: type='hinge' axis='0 1 0'. Step C (independent, no scaffold): the spec changes to 'the leaf must SLIDE 25 mm along the pivot line to lock, not rotate.' You must recognize this demands a prismatic (slide) joint and realize it yourself: type='slide' axis='0 1 0' range='0 25'. If you pick hinge: this spec says slide, not turn - that is a translational DOF; use a prismatic (slide) joint along the same axis. By Step C you map an English motion requirement to joint type + axis with no template.

04
Feedback

How the Bench grades your run.

PASS WHEN PASS: the leaf probes to exactly one free DOF (n_free == 1), rotational, about the target axis (axis_err <= 1.0 deg), with range 0-90 enforced - a correct revolute hinge.

  • FAIL: leaf moves in 6 directions - that is a free joint, not a hinge. Use type="hinge" to leave exactly 1 rotational DOF.
  • FAIL: leaf slides instead of rotating. Spec asked for rotation - change type from "slide" to "hinge".
  • FAIL: hinge axis is 90 deg off: leaf folds about X, but the pivot runs along Y. Set axis="0 1 0".
  • FAIL: no free DOF detected - the leaf is welded. Add a joint inside the leaf body.
  • FAIL: leaf rotates past 90 deg into the base (collision at 118 deg). Add range="0 90" to stop it.
05
Retrieve & space

Bring back what you've already mastered.

  • (3.1 pose) Before a joint can be correct, the pivot must be placed right - the leaf knuckle bore must be concentric with the base bore. In the CAD, what translate put it there? Re-fuses placement with jointing.
  • (M2.1 driven dimensions) Make the joint range a parameter swing_deg and set the leaf clearance chamfer to a formula of it. Sweep swing_deg = 90, 120 - does collision-free still hold?
  • (3.2 itself, spaced +1 lesson) Given a drawer that must pull out 50 mm and not rotate: name the joint type, axis, and range - pure retrieval of revolute-vs-prismatic.
06
Mastery gate

What you must demonstrate to advance.

Submit a hinge model that probes to exactly one free DOF (n_free == 1), of the correct kind for the given spec, about the correct axis (axis_err <= 1.0 deg), with its range enforced and no self-collision through that range - passing BOTH the revolute spec AND the prismatic spec variant. Passing both proves you can map a stated motion requirement to the correct joint type + axis and verify the DOF empirically, not just assert it. 3.3 then unlocks.

07
Project

How this feeds your build.

The joint is the atom of every mechanism you will simulate. 3.3 chains four of these into a linkage; M4 puts mass and torque on exactly these joints; the M5 capstone is graded on a device whose motion is only as correct as its joints. A learner who can declare 'one DOF, this axis, this range, no collision' can build any mechanism in the course.