You Can Build this robot dog!
YouTube video: https://www.youtube.com/watch?v=2eKb_2N0SBI
simulation/ is a solid-node
model of the dog: the 9g CAD read into the machine it makes, driven by
the six servos DogCode.ino drives, with contracts that prove the
assembly. It sits on top of CAD/ and DogCode/ and edits neither.
pip install "solid-node[viewer]"
solid build # build and publish the model
solid test simulation/parts.py # build all 51 parts
solid test simulation/dog.py # the contracts (exact kernel)
solid test simulation/dog.py --faceted # the same, faster, for the loop
solid develop # drive it in a browser
solid snapshot -o dog.png --autocenter --viewallhardware.py and fasteners.py cannot be loaded on their own either:
a screw declares no default length, because the site it goes on supplies
it. Name a class and a length to build one —
solid test simulation/hardware.py:CapScrew --set length=20.
leg.py and chassis.py carry no companion of their own: a leg's swing
port is bound by the chassis half above it and a half's by the machine,
so neither is buildable alone and both are tested through
simulation/dog.py, whose companion holds every contract.
The first build derives one single-product STEP per solid from
CAD/dog02_9g.stp into simulation/parts/, which is ignored by Git: it
is a build product of a file the repository already carries. Run
python -m simulation.tools.split_step --force to redo it.
Six drivers, one per servo:
| driver | what it does | range |
|---|---|---|
front_left, front_right, back_left, back_right |
swings that leg, positive lifting the foot | 0 to 17° |
slide |
moves the two chassis halves fore and aft against each other | ±16 mm |
turn |
rotates them against each other about the joint centre | ±11° |
Zero on every driver is the CAD's own pose, standing on all four pads. The machine does not move on the timeline: it moves when you move a driver.
Six instructions replay the sketch's own poses — Rest, Lift A,
Lift B, Slide Fore, Slide Aft, Turn — each taking the sketch's
200 ms stepTime. walk forward is Lift A, Slide Fore, Rest,
Lift B, Slide Aft, Rest; a turn swaps the slide for the rotation.
Both sequences are run as scenario tests.
Ground up, if you want to read it in order:
simulation/tools/split_step.py— the CAD, one solid per file.simulation/parts.py— 51 nodes, one per printed piece, named for what each is on the machine.simulation/layout.py— every number measured off the CAD.simulation/tools/fastener_sites.py— the bores, read back as the joints they are.simulation/hardware.py— the M3 screws and nuts, drawn from catalogue dimensions.simulation/fasteners.py— where all thirty of them go.simulation/leg.py— the parallelogram leg.simulation/chassis.py— the two halves and what each carries.simulation/dog.py— the machine, its drivers and its instructions.
docs/measurements.md holds the readings; the design record is
openspec/changes/archive/.
The design draws no fasteners. It does draw the holes, and the holes say what goes in them: where two parts meet, the export drills 3.2 mm, 4.0 mm and 2.8 mm and nothing else — M3 close clearance, M3 loose clearance, and the pilot a thread-forming M3 is driven into. Around every 4.0 mm hole there is a 90° cone, which is a flat head asking to be let in.
So the model reads them back and fits thirty M3 screws, all of them derived from the CAD rather than typed in:
| where | how many | what |
|---|---|---|
| leg chassis pivots, knees and ankles | 20 | cap screw and hex nut, M3×16 to M3×30 |
| tower to joint cap | 2 | countersunk, M3×8 into a 2.8 mm pilot |
| tower head and both side panels to the spine | 6 | countersunk, M3×12 |
| battery holder to the lower plate | 2 | countersunk, M3×10 |
Nothing is drawn at a length nobody sells, and no screw sits flush: each head and nut stands 0.05 mm off the face it bears on, because two coincident faces cannot be asked whether they touch and the machine's load path hangs on the answer.
With the hardware in, the machine holds itself up — solid test proves
that the moving half, its two legs included, reaches the ground and
balances under gravity on frictionless contacts alone. It could not be
asked before: the links hung on nothing.
Simulating a design is the first time anyone asks it every question at
once, and dog02_9g answers some of them badly. None of this is fixed
here — the layer models the CAD as it is:
- The back-left leg is a stale copy. Its links span 39.9239 mm where the other three span 40.0000, its knee bores are 1.40 mm where theirs are 1.60 — an M3 pin that fits any other leg will not enter this one — its short link carries a duplicate bore 0.10 mm off the real one, and both its links sit inside the bracket they hang on, sharing 17.3 and 9.3 mm³ with it. That interference is what limits the machine: the other three legs swing 60° freely, this one fouls the chassis at 18°.
- No fasteners are drawn anywhere, and fitting them found four more
things:
- the back-left leg cannot take its own pins. Both its knees are bored 2.8 mm where the 3.2 mm pin they need would go, so all three of its knee screws stand 4.56 mm³ inside their own links. Four bores must be reamed before this leg goes together;
- the back-right leg's links are shaved by a true pin. They sit 0.105 mm off their own lower leg, so a pin centred on the lower leg cuts 0.015 mm³ from each, and 0.926 mm³ from the inner link, whose duplicate bore catches it along its whole length;
- two tower shelves are attached to nothing. Neither carries a bore of any size, and the nearest solid to either is 2.88 mm away — the left one sits 12.7 mm outboard of the panel it belongs to. They are the only two of the machine's 101 solids that the fasteners cannot reach;
- four 6 mm holes fit nothing. The tower panels share four of them at their corners and the head carries a fifth; no M3 fits one and the export puts nothing in them. They read as spacers left to the builder.
- One hole must stay empty. The joint cap and the lower plate share a 4 mm axis on the joint centre — the only site in the export whose two parts belong to different chassis halves. A screw there would lock the joint the machine walks on, so none is fitted; the hole reads as where the turn axis stands, and what stands in it the export does not draw.
- Nothing has running clearance. The two chassis plates that slide on each other, the cap that retains them, and four links on their pivots are all drawn touching at exactly 0.000000 mm. A printed machine needs a gap at each. The four legs are not even alike here: two touch their brackets, one has a gap, one interpenetrates.
- Two panels of the electronics tower are non-manifold at every tessellation, so their volume changes with precision — an open shell, not a solid.
- The export's parts are not printed pieces. 20 products hold 51
solids, three of them share the name
COMPOUND, four stray fragments of 0.1–1.5 mm³ sit on pivot bosses, and two of the four toe blocks are filed under a chassis product rather than under the leg they sit on. - The sketch's own comments contradict each other about which servo
drives which leg:
setup()and the offset block disagree, and the walk sequences disagree with both. What is unambiguous — and what the CAD confirms — is thatservo1/servo2drive one diagonal pair andservo3/servo4the other, one pair per chassis half.
Only dog02_9g is modelled. dog02_large exports each whole leg as one
product of eight solids, so its hip cannot be articulated without cutting
the geometry, which this layer does not do.