# Cube Arms: building the real machine

The simulation in this folder's parent is the digital twin of this machine: it loads the same part
models that go to the printer (`models/`), places them from the same description (`models/machine.json`)
and moves them the way the servos would. Everything here is generated by `build.py`.

**State of this design:** drawn from the manufacturers' dimension drawings and checked in software
only. Nothing has been printed or assembled yet. Print `servo_test_plate.stl` first and try it on a
real servo before printing the rest.

- `stl/` – printable parts, already lying in their print orientation, millimetres
- `step/` – the same parts as STEP for a CAD program
- `PARTS.md` – all lists: printed parts, bought parts, screws, cable lengths, servo numbers
- `cube-arms-parts.zip` – `stl/`, `step/` and the lists in one file
- `build.py` – the parametric source (CadQuery); every dimension is a named value at the top
- `check_fit.py`, `check.py`, `poses.json` – the software checks described below

## The machine in short

- Two identical hand units. Each slides on a printed rail (servo, 20-tooth pinion, rack: 94 mm per
  turn, 78 mm of travel), turns its wrist (servo, direct) and closes two fingers (servo, 24-tooth
  pinion, two racks: 6 mm per finger). The left unit's rail is horizontal at the cube's height, the
  top unit's rail is vertical on a two-piece tower. Both rails are behind the cube.
- A stand under the cube that one more servo drops by 20 mm.
- Seven Waveshare ST3215 bus servos, one bus adapter, a Raspberry Pi 5, a Camera Module 3 on a mast
  155 mm from the cube face, all on a 460 x 250 x 12 mm plywood board.
- Cube: 57 mm. The fingers reach 19 mm past the palm before they touch the cube, so that the palm of
  the holding hand stays outside the circle the other hand's fingers sweep when it turns a layer.

## Printing (Bambu Lab X2D)

- PETG for everything except the pads; TPU 95A for the four finger pads (`pad_a.stl`, print four).
  The pads can be printed on their own and glued on with contact adhesive, or co-printed with the
  second nozzle.
- 0.2 mm layers, 4 walls, 30 % gyroid infill; 6 walls and 50 % for `carriage`, `hand_body`,
  `finger_a`, `finger_b` and both pinions.
- Supports: needed for `carriage` (sleeve roof, wrist wall), `left_frame` and `tower_upper` (the rail
  overhangs its web by 9 mm on both sides) and `hand_body`. With the second nozzle, a support
  interface in a material that does not bond to PETG gives clean sliding faces.
- The sliding fit between rail and sleeve is drawn with 0.25 mm clearance per side. If it binds,
  sand the rail; if it rattles, a strip of thin tape inside the sleeve takes up the play.
- Largest parts on the bed: `tower_lower` 70 x 217 mm, `left_frame` 186 x 138 mm.

## Assembly

1. **Servo numbers first.** Out of the box every servo has ID 1. Connect one at a time to the bus
   adapter and set the IDs from the table in `PARTS.md` (1–3 left unit, 11–13 top unit, 21 stand)
   with Waveshare's servo tool. Set each to its middle position (2048) before fitting a horn.
2. **Heat-set inserts.** Two M3 inserts into the posts on the front of each `hand_body`, two M4
   inserts into `tower_upper` at the lap joint.
3. **Servos into their walls.** Each servo sits behind a 4.4 mm wall with its horn through the
   21 mm opening, held by four 2 x 6 mm self-tapping screws into the holes of its output face. The
   screw heads go into the counterbores so that pinions and racks pass over them.
   - `carriage`: slide servo behind the rail side, wrist servo between the two ribs.
   - `hand_body`: gripper servo, pushed in from the open side.
   - `stand_housing`: stand servo on the wall beside the column.
4. **Pinions.** A `pinion_m15` on the slide servos and the stand servo, a `pinion_m10` on the gripper
   servos: four M3 x 8 into the threaded holes of the horn, heads in the counterbores.
5. **Hands.** Bolt each `hand_body` onto the horn of its wrist servo from inside with four M3 x 6
   button head screws (before the gripper servo goes in). Lay `finger_a` and `finger_b` into the
   channel so that their racks mesh with the pinion at the same distance from the centre, then close
   the channel with `palm_cover` (two M3 x 8 countersunk).
6. **Rails.** Slide a carriage onto `left_frame` and one onto `tower_upper` from the end; turn the
   slide servo by hand until the pinion picks up the rack. Join `tower_lower` and `tower_upper` with
   two M4 x 20 and washers from behind.
7. **Board.** Mark the positions from the simulation (all coordinates are in `models/machine.json`,
   board corner to part) and fix `left_frame`, the tower, `stand_housing`, `camera_mast` and the two
   trays with the wood screws. Camera on the mast with four 2 x 6 mm screws, Pi and adapter on
   their trays.
8. **Stand.** Drop `stand_column` into the housing with the rack towards the pinion.

## Cables

- Chain 1: adapter → stand → left slide → left wrist → left gripper.
  Chain 2: adapter → top slide → top wrist → top gripper. Every servo has two sockets; the order of
  servos on a chain does not matter to the software.
- The 15 cm cables that come with the servos are too short for most runs; lengths are in `PARTS.md`.
- **Fixed runs** lie under the orange saddle clips on the board (one 3.5 x 12 wood screw each) and
  are tied at the slots in the left frame's leg, along the right side of the tower and on the mast.
- **Moving loops.** The cable to a carriage is tied at the last slot on the rail (fixed end) and at
  the tab behind the slide servo (moving end), with a free loop between them that is longest when
  the hand is at the cube. The cable to a gripper servo is tied at a rib of the carriage and at the
  slot on the back of the hand and winds round the wrist: the software never turns a wrist more than
  half a turn either way from straight.
- Power: 12 V supply → inline switch → barrel jack of the bus adapter. The Pi has its own supply.
  The two sides are connected only by the USB cable.

## What was checked, and what was not

- `python hardware/check_fit.py`: no bought part overlaps the printed part that holds it.
- `node tests/poses.mjs` records every pose of 60 complete jobs (92 states, 62 wrist turns, 42
  slides); `python hardware/check.py` puts the solid models into each of them, including the turning
  cube or layer, and finds no interference in 431 poses.
- `node tests/robot.mjs 100`: 100 of 100 scrambles solved by the planner, about 154 s each at the
  servos' no-load speed.
- **Not checked:** anything physical. In particular the depth of the screw holes in the servo case
  (the drawing does not give it), the exact outer diameter of the horn (19.2 or 19.95 mm depending
  on the drawing; the opening is 21 mm), how much play the printed rails have, whether the grip is
  firm enough to turn a layer, and colour recognition with a real camera. There is no software for
  the Raspberry Pi yet: the simulation runs in the browser only.

## Changing the design

Edit the values at the top of `build.py` and run

    pip install cadquery
    python hardware/build.py          # parts, models, machine.json, PARTS.md, zip
    node tests/poses.mjs && python hardware/check.py && python hardware/check_fit.py

The simulation picks the new models up on reload.
