The turret program
The user: "keep a good agent running 24/7 making our turret something we can manufacture, fully validated and awesome with nerf balls." This file is that agent's charter, checklist and log. Every turret agent reads it first, works the next open items, ticks what it proved (with the evidence), and appends to the log before it stops.
The goal
A foam-ball turret (Nerf Rival-size balls) for Gyropup that:
- can be built: every made part printable on a 256 mm bed in the materials we use (PA6-CF structure, PETG, TPU),
in a sensible orientation, with every bought part a real part number (McMaster-Carr through
tools/mcmaster_cad.py, or a named vendor part) and an assembly order a person can follow; - is validated: each claim below backed by a simulation, a calculation with its inputs, or a bench test plan;
- is awesome with Nerf balls: it feeds every time, fires accurately at a kid-safe speed, sounds and looks great, and is easy to reload and to service.
Hard rules: the barrel never points above horizontal (kid safety, docs/head-payload.md); muzzle speed at or under 70 fps (21.3 m/s) for balls; nothing spins or feeds unless the safety line (the bench pendant now, the radio stop on the robot) is held; private fleet details stay out of public repos.
The speed cap is held by hardware, not by firmware (coordinator, 2026-10-27): the flywheel ESCs are fed only from the
fixed-output 12 V regulator, never from 24 V, and at every connector the 12 V flywheel pins and the 24 V pins are keyed
so they cannot be swapped. The ceiling goes as motor KV x rail voltage x tyre diameter, so a flywheel motor, rail or
tyre may change only when the physics agent's ceiling map (sim/turret_launch3d.py ceiling_map: 100 % throttle, a
+5 % KV motor, a +4 % rail, runout and tyre growth) keeps the fastest ball at or under 66 fps on the cartridge as
built. The Snail 2305 is 875 rpm/V; a drone 2306 at 1700-2600 rpm/V on 12 V would throw far over 70 fps, so a drone
motor is never a drop-in replacement: its rail comes down in proportion (about 6 V at 1700 rpm/V) and the map is
rerun before it is fitted.
Parts rule (the user, docs/decisions.yaml D40): every part is off the shelf with a listed price and a buy link (McMaster-Carr first; Digi-Key, Mouser, SparkFun, Adafruit, Pololu; the makers' stores for motors and servos), or 3D printed, or ordered to our drawing at an instant online price (SendCutSend for laser-cut, bent or machined sheet and plate; Xometry or JLCCNC for CNC; JLCPCB or PCBWay for boards). Nothing that needs a quote. The plan's choices D1-D10 are decided (D41, docs/turret-plan.md section 1): continuous pan; the SparkFun ROB-13065 ring on the bench and the robot; the Feather M4 CAN; the servo bus across the ring; one fixed 12 V regulator as the speed ceiling; two ring circuits kept for a camera; the line cuts the wheels and the dial in hardware, pan and tilt stay live and capped at 100 deg/s; a phase-short brake with brake-on-stop; a 66 fps chronograph trip. The turret stands on a 30 mm printed riser on the top deck.
Where it sits
- On the robot (body not designed yet): behind the VLP-16 lidar on the top deck, firing over it (user, 2026-10-26). The ball path and the barrel must clear the lidar at every allowed tilt, or a pan-dependent depression limit applies.
- On the bench:
cad/turret_stand.py(24 V in, fuse, switch, USB-CAN, hold-to-run pendant). - Interface to the robot: the TM pattern (4 x M5 on 80 x 80, 2 x Ø5 dowels, Ø45 cable hole) and the DT12 connector.
Validation checklist
Tick with a link to the evidence (file, picture, result JSON). Unticked = open.
Ball and launch
- The ball: Rival-size foam ball diameter, mass and its spread from real products (sourced); the bore, chute and
dial clearances checked against the spread. Evidence: docs/turret-physics.md section 2, exports/turret/launch/ball.json;
the bore study (section 6) made the channel after the nip and the barrel ball + 2.0 (cad/turret.py
bore_out). - Flywheel launch model: wheel speed, crush, contact time, slip → muzzle speed; at or under 70 fps at the motors'
set speed with margin, and how the speed varies with ball size and wear. Evidence (v0, the X2216 880KV on Ø34,
D42): docs/turret-physics.md section 4: 52 fps nominal at 8,855 rpm, the population 48.9 +- 5.0 fps; the fastest
swept ball at 100 % on a +5 % motor and a +4 % rail 67.6 fps (2.3 under the cap; exports/turret/launch/launch3d.json);
on this cage's 53.0 mm spacing Ø36 would reach 70.7, so
TYRE_SETprints Ø34 only. - Trajectory and accuracy: drop and drift at 1-5 m, spin (matched wheels: none), dispersion budget. Evidence: docs/turret-physics.md sections 6-7, exports/turret/flight/flight.json.
- Recoil per shot and per burst, on the robot (feeds sim/turret.py and the head-payload budget). Evidence: docs/turret-physics.md section 8 (0.030 N s per shot, 1.2 mrad of aim).
Feed
- Hopper → dial → chute → wheels: no jam in any pan/tilt pose (gravity direction at -25 deg tilt), double-feed impossible, the dial parks safe with the motor off. v0 (the STS3215 feed, docs/turret-physics.md section 3): parked-safe passes powered and unpowered; nothing sticks in the chute; on a fixed period 3/s level and 2.5/s at -25 deg feed without a double (a hung ball at -25 deg and 3/s came down with the next ball), about 1 step in 8 empty with rigid balls. The firmware's answer: each step paced on gate 1, a dither instead of a step when no ball arrives (docs/turret-firmware.md). Open until bench test 5.1 counts it with real foam.
- Rate of fire: dial speed vs flywheel recovery between shots. Evidence (v0, the STS3215 feed): docs/turret-physics.md section 3: a step settles in 232-246 ms; 3 balls/s level and 2.5 at -25 deg on a fixed period, so the firmware paces each step on gate 1 (docs/turret-firmware.md); the X2216's wheels lose 1.3 % a shot and recover between shots at every rate.
From the turret-on-Orin-box scene (coordinator, 2026-10-27; cad/turret_box.py on main)
- Hardware speed ceiling that holds at 100 % throttle (safety, highest priority): at full speed on 24 V the flywheels would throw 123-140 fps. The design answer: the ESCs only on the fixed 12 V regulator's output and Ø34 tyres, so the motors' no-load speed caps the ball; the chronograph trip is the backup. Evidence (v0): docs/turret-physics.md section 4 (67.6 fps the fastest swept ball with the X2216); the bench keeps every ball under 66 fps by test 4.1 (no-load at most 11,180 rpm on the rail before any ball: docs/turret-bench-tests.md); the 12 V regulator is the parts list's el_flywheel_regulator (Pololu 2885).
- ARM release is not instant: a dropped ball still fires ~96 ms after ARM is released and the wheels coast ~1.4 s. The design answer (D9) is the phase-short brake, not a gate: six FETs short the motors' phases to GND 5-14 ms after the line drops (through the FETs and the winding's inductance: tau 10.2 ms at low speed, 5 % speed ~48 ms after release, nothing left before a dropped ball reaches the nip at 96 ms), plus AM32's brake_on_stop, which the firmware triggers first. Evidence: docs/turret-physics.md section 5; docs/turret-wiring.md 3.3; bench test 4.7.
- Command to muzzle: ~340 ms with the servo-stepped dial in v0 (the step ~235 ms, the drop and chute ~90 ms, the nip and bore ~12 ms: docs/turret-physics.md section 3). Pre-staging a ball is not in v0; the firmware leads its shots by the measured latency.
- Firing backwards: at pan 140 deg the muzzle clears the deck's rear corner by 3 mm, where the operator stands.
Add a pan keep-out behind and check the muzzle's clearance at every pan and tilt (a general obstacle check,
as for the lidar). Evidence: cad/turret_lidar.py is now that general check (obstacles as cylinders and boxes,
the ball path at the slowest stray-ball speed);
--scene boxfinds every pose clear of the deck and the antennas and keeps firing out within 60 deg of straight back (exports/turret/lidar/box.jsonkeep_out_fire_pan_deg; docs/turret-firmware.md section 5). - Torque caps: the tilt holds with a 15x margin and slewing is speed-limited (~100x torque margin). Cap both servos' torque in firmware to protect the printed gear and fingers (the pinion is the weak tooth: see the FEA).
- Box-specific, for cad/turret_box.py when the turret's base changes: the antennas collide at full depression, the stack tips at a 10.6 N push, the shroud costs ~7 % of the fan's airflow (est).
Structure and motion
- Strength of every printed load path (pan ring and gear teeth, yoke, cradle, cartridge, barrel mount) under slewing, recoil, a drop of the robot and handling, with the cad-agent FEA and its overload videos. v0 (plan T9, exports/turret/fea/summary.json): every part meets SF 2 everyday and 1.2 in the 30 g falls, or has its peak at a modelled corner (yoke push 1.82, cartridge recoil 1.57, pan base at a servo stall 1.13; p99 SF 4-11), except the cradle, whose cheeks bend across the layers (0.78 / 0.62 in the falls, 0.77 under a 50 N push on the muzzle): named for v1 (plan T16); on the bench no pushing on the barrel. Open until the cradle is fixed.
- Servo torque margins (STS3215 pan through 63:21, tilt direct) for the turret's inertia at the wanted slew rates. Evidence: exports/turret/motion.json (cad/turret_motion.py, with the slip ring and the boards): tilt move 5.9, hold 47.6, world-frame 21.9; pan 90 deg in 1.2 s with a margin of 10.0 at the 50 % torque cap, yaw 104; the pinion (16 mm face, full-round roots) SF 2.52 at the cap and 1.26 at stall by FEA of one tooth.
- Clearances and stops over the whole pan/tilt range, including the lidar in front and the DT12 harness. The turret itself is clear: no overlap and nothing moving within 1 mm but the designed tilt stops, at pans 0-350 every 10 deg x tilts 0 and -25 (exports/turret/clash-sweep.json), and every degree where the cradle passes over the pan pinion at full depression (exports/turret/t1-fine-checks.json). Over the lidar (cad/turret_lidar.py, the 30 mm riser): limited only within pan +-26 deg, to -16 deg at worst (exports/turret/lidar/lidar.json, a firmware table every 5 deg). On the Orin box: no limit, firing kept out within 60 deg of straight back (exports/turret/lidar/box.json). The DT plug's 30 mm withdrawal path is clear of the fixed parts.
- Mass: 1.639 kg with a full hopper in v0 (the X2216s, the cartridge board and the taller cage: exports/turret/turret.json; it was 1.594 before D42) against the head payload's 1.5 kg (docs/head-payload.md): the slip ring, its carrier and driver, and the two board bays added ~250 g. Cut at least 100 g (infill where the FEA allows, a lighter carrier and bay) or agree a new budget with the leg's sizing.
Build
- Every printed part: orientation, supports, bed fit, print time; tolerances for bearings, inserts and the gear. Evidence: exports/turret/print/*.3mf (each part in its print orientation) and print.json, sliced by Bambu Studio for a P1S (1,291 g, 50 h); docs/turret-print.md (settings, schedule); the fits are bench test 1.1.
- Every bought part with a real part number and price; nothing left as "estimate" that can be sourced. Evidence
(v0): exports/turret/bench/bom.json (
not_in_a_catalogue,no_listed_priceandno_second_sourceempty) and the order sheets exports/turret/bench/orders/ (118 lines, 18 sellers, a backup seller on every line, $1,579 in whole packs with the stand and the tools, the servos and motors first; the boards' resistors, capacitors and diodes counted from exports/turret/wiring/parts.md). Each line carries a catalogue key, part number and source URL (thumbnails and alternates through cad/bom_media.py). Every part follows the parts rule (D40): the pan bearing is VXB's chrome-steel 6810-2RS, the ring the SparkFun ROB-13065, the laser Quarton's red VLM-650-03 LPT (D42), the pan clamp a SendCutSend 6061-T6 part (exports/turret/pan_clamp.dxf, instant price on upload). The DJI Snail, M2006 and C610 are out of v0 (D42: the X2216 880KV and a third STS3215); the longest lead is the servos from Waveshare (1-3 weeks). - Wiring (v0): docs/turret-wiring.md (every connector, conductor and crimp; the +24 V traced to each ESC phase; the arm chain drawn with a designator on every part and exports/turret/wiring/parts.md listing each part's value, catalogue key and pins; the boards laid out and checked by cad/turret_boards.py). The plan's 5 V logic, 5 V 555, floating brake star and 3.3 V laser gate were corrected before anything was soldered (D47).
- Assembly order with pictures; a bench test plan (chronograph, accuracy, jam rate, the pendant). Evidence: docs/turret-assembly.md (twelve steps, a picture each from the CAD, the v0 cartridge), docs/turret-bench-tests.md (the v0 procedure with its numbers).
Awesome
- Sounds, look and feedback (the laser, lights) on the 3D page match the real thing.
- Reload at the dock: the hopper's fill port and flap.
Log
Newest last. One entry per agent run: what it did, what it proved, what is next.
- 2026-10-26: charter written. The turret exists in CAD (cad/turret.py), on a bench stand (cad/turret_stand.py, cad/build_turret.py), in the physics (sim/turret.py: balls at 70 fps from a 30-ball hopper). Nothing above is validated yet.
- 2026-10-27 (in progress): the coordinator passed on the user's request for a high-quality electrical slip ring in the pan joint. A separate planner is researching it. The pan joint's cable path, the wiring and the DT12 pinout wait for its plan; the pan axis and centre hole stay free, and no new hard pan stops go in.
- 2026-10-27 (in progress): physics handed to a dedicated physics agent (coordinator), which now owns sim/turret_*.py
and builds sim/turret_e2e.py; its "Findings for the CAD" in docs/turret-physics.md feed the CAD. Committed as they
stood, for it to build on:
- sim/turret_launch.py: a one-shot flywheel model (Hertz foam contact with Hunt-Crossley damping, Coulomb tyre friction, the Snail's DJI motor constants behind an ESC current limit). It found that 12 % crush makes the speed swing with the foam (efficiency 0.36-0.89), while 25 % gives 0.66-0.92 with no rejections. Ø36 TPU tyres on a regulated 12 V flywheel rail: 55 fps at 90 % of no-load, 54.7 +- 1.0 fps over 400 balls (sourced size and mass spread, foam and grip swept); at 100 % throttle on a +5 % motor and a +4 % rail the worst ball reaches 66.4 fps (a 3.6 fps ceiling margin); 8-ball bursts at 10/s hold speed; recoil 0.031 N s, 0.045 at the cap. The model's efficiency (~0.95) is above what hobby Rival blasters measure (0.40-0.75 at higher wheel speeds), so the bench picks the tyre from a printed set: the fastest ball at 100 % stays at or under 66 fps.
- sim/turret_feed.py: MuJoCo, the dial, housing, baffle and chute from the CAD's numbers with 30 balls. Found: the hopper held only 15-16 balls under the old lid (raised to 100 mm, which fits 34); the parked dial never lets a ball out (tilt 0 and -25 deg, shaken at 5 m/s^2, powered or not); feeding is 30/30 at 0 deg and 27-30/30 down to -25 deg in 40 steps. Below about -12 deg the last balls of a magazine collect on the baffle (the front is the hopper's low side), so the firmware lifts to 0 deg for 0.4 s after an empty step. The one "double" traced so far was a ball that stalled at the top of the chute for a step and then reached the wheels with the next, not two out of the dial.
- 2026-10-27 (in progress): the user asked for thumbnail pictures of every BOM part, to be sure each can be ordered and to keep alternates in mind. Every turret bought part gets a catalogue key with a real part number and source URL; the turret BOM calls cad/bom_media.py enrich() once it is on main.
- 2026-10-27: the slip ring is in the turret (plan task T1), the physics' findings are in the CAD, every bought part
has a catalogue key, and the wiring and firmware plans are written (T2, T4).
- Pan joint (cad/turret.py):
RING_MODELS(SparkFun ROB-13065 for the bench, Senring M220-0210-12S for the robot, Moflon MC330, Servotecnica A03),ring_carrier(drops into the foot's Ø65 opening from below on three tabs, 3 x M3 x 10 flat heads into inserted bosses; the ring's flange on its top),ring_driver(in the turntable's bore; a 5/16 in tube couples the ring's rotor), the rotor leads as a Ø7.5 bundle beside the coupling and through the yoke.CONTINUOUS_PAN = True(the user's D1 stays open: False brings back the stop post and finger). The index: a 1/4 in magnet on the housing's top through the clamp ring, an A1120 hall switch in the turntable 2.9 mm over it. - Two deviations from the plan, by design: (1) the carrier's screws sit at r 35.75 under the foot, outside the TM's Ø45 hole and under the head ring, so the ring is changed with the turret off its mount (4 x M5 and the DT12; exports/turret/turret-pan-joint.png). The plan's Ø63 ledge would leave a 1 mm lip for M3 screws, and its screws at r 32 are outside the Ø45 hole as well. (2) The lead bundle is Ø7.5, not Ø12: 12 x 26 AWG + 2 x 17 AWG PTFE is Ø5.1 by area, and a Ø12 bundle cannot pass the coupling in the Ø40 bore with 3 mm on both sides. It keeps 3.25 mm from the bore and 3.7 from the coupling, 3.0 at the yoke's slot; the driver's crescent (0.75 mm) is where it is tied.
- The bays: the base board on four bosses on a wall at +x in the base; the turret's electronics on a three-board stack in a PETG tray on the -y yoke arm (the module footprints from Pololu and Adafruit set its 52 x 50 boards); it reaches r 118.6 at z 71..128, where nothing fixed stands (the tilt servo already sweeps r 112).
- What the clash sweep found and how it was fixed: the 16 mm pinion (for its strength) rose to the turntable's top
plane, so the yoke's underside is relieved 1.5 mm outside r 37, and the cradle's front at full depression met it
behind the turret, so the ball path rose 4 mm (
path_t125). Result: no overlap and nothing moving within 1 mm except the tilt stops, pans 0-350 every 10 deg x tilts 0/-25 (exports/turret/clash-sweep.json), 2.5 mm where the cradle passes over the pinion at every degree (exports/turret/t1-fine-checks.json). - The physics' findings in the CAD (docs/turret-physics.md section 12): the channel after the nip and the barrel bore ball + 2.0 (14); the gates 60 mm apart with 1 mm slits (11); the wheel cavities cut 0.5 mm wider for the tyre's growth (5); the tyre printed with its bore 4 % under the rim (5); the Ø34/Ø36 tyre set, Ø38 dropped after the ceiling map on this cage's fixed wheel spacing gave 69.7 fps (4); the dial's 30 deg cone with four fins (1). The physics agent reruns the feed and the flight on them.
- Motion (exports/turret/motion.json): tilt move 5.9, hold 47.6; pan 10.0 at the 50 % cap, yaw 104. Against the turret before this change (the same method): tilt move -5 %, hold -10 % (the cone and fins at the rear), pan -14 % (the turret boards, 110 g at r 90). All still large margins; the plan's "within 5 %" is not met for pan and hold.
- Mass: 1.594 kg with 30 balls, over the head payload's 1.5 kg. Open item on the checklist.
- Hardware and parts: inserts sized to their parts (short M3 inserts in 6 mm bumper tabs, bosses for the base board, insert pilots past every screw tip), the CT thumbscrew (DIN 464 M4 x 12, McMaster 92581A250, kept by an O-ring), the C610 held by two cable ties through slots, the yoke's M4 x 10. 35 McMaster product pages read one at a time (tools/mcmaster_cad.py --pages-only, now committed as a generic option), every one with cad.pending for its CAD. The pan bearing's catalogue entry (bearing_61810) now names the 6810-2Z 6656K229 that the CAD draws; the $194.88 stainless 4668K273 is its alternate. 40 electronics entries with maker part numbers (catalog/turret.json).
- Parts list (cad/build_turret.py, exports/turret/bench/bom.json): every line names a catalogue key and goes through
cad/bom_media.py enrich();
not_in_a_catalogueis empty. Pictures fetched with tools/part_images.py for the new keys (catalog/images.json; the photos themselves stay on this machine); 23 electronic parts have none because their buy link is a distributor that blocks automated reads (Mouser, Digi-Key) or a page without a product photo (TI's store): catalog/sourcing.jsonimage_pagescan name another page for them. - Bench stand (cad/turret_stand.py; the same cutouts in cad/turret_box.py): the rocker's 19.4 x 13.0 snap-in hole in a 2.5 mm thinned panel, the fuse holder's Ø12.75 D-hole, the jack's Ø6.35 in a 2 mm panel, the XT60E-M inlet (the supply's lead carries the female) with its screws slotted 18-28 mm, a DC-rated fuse (Littelfuse 0477005.MXP), the 3M SJ5003 feet, the terminal board's bosses moved onto the bottom plate at 66 x 46 (they were floating in the box). The box scene rebuilt: with the ball path 4 mm higher, its antennas no longer limit the depression.
- Wiring (docs/turret-wiring.md): the DT12 and ring pin maps, the ring's two Micro-Fit 2 x 4 connectors, the CT as a 2 x 3 power and a 2 x 6 signal Micro-Fit (they cannot cross-mate; 24 V reaches only the C610), every run's gauge and length, the arm chain with no MCU in it, the trip (2.98 ms for 60 mm gates), the brake, the fuses, the budget.
- Firmware (docs/turret-firmware.md, firmware/turret/): the pin map, the states, the spin-up with the pre-feed RPM check (below 11,500 rpm), the feed's dither and jam rules, the aiming rules from the physics, the CAN message set. Pan homing passes 1,000 random power-ups (firmware/turret/test_homing.py): all homed, median 3.5 s, at most 6.7 s and 368 deg; repeatability 0.07 deg; settled tracking 0.09 deg over random moves to +-720 deg; 980 index re-checks within 0.05 deg; no false re-home (exports/turret/firmware/homing.json).
- Supply (the thumbnails agent's finding, catalog/sourcing.json risk): the Snail 2305 is delisted, the M2006 and C610 are out of stock. The speed-ceiling rule is now a hard rule above: no drone motor goes on the 12 V rail.
- Next: the FEA of every printed part on this geometry; the lidar table and the box's keep-outs with the new obstacle check; cutting 100 g; the assembly order with pictures and the bench test plan (T5 needs the parts).
- Pan joint (cad/turret.py):
- 2026-10-27 (later): the parts rule (D40) and the decided choices (D41) applied, every printed load path analysed and
strengthened where the FEA asked, the clearance study generalised, the assembly order drawn.
- D40/D41: the SparkFun ROB-13065 ring on the bench and the robot (RING_MODELS keeps it and the Adafruit 1196 as its alternate; the quote-only rings are gone), mapped 2+2 power, CAN, safety, servo TTL, signal ground, two kept for a camera (docs/turret-wiring.md 1.2), on 2 x 6 Micro-Fit connectors; the CT signal connector is now 2 x 5, so the three Micro-Fit sizes cannot cross-mate. The pan bearing is VXB's chrome-steel 6810-2RS ($24.99); the laser a Quarton VLM-520-73 LPT from Digi-Key ($41.82, Class 2, Ø7 x 26, on 5 V); the pan clamp is a SendCutSend part in 0.125 in 6061-T6 (exports/turret/pan_clamp.dxf and .step, instant price on upload). The lidar study puts the TM face on the 30 mm riser. No quote text is left in the turret's files.
- FEA (cad/turret_studies.py; the last full run was stopped at the wrap-up, so its pictures and summary.json are not committed): on the final geometry the yoke is SF 2.53 / 2.35 / 1.99 in the 30 g falls and 1.72 in the everyday push at the muzzle (target 2; the peak sits at the relief's step, p99 12 MPa), the turntable 3.59 down; from the targeted runs, the pan base 7.2 / 1.61 (falls) and 2.52 / 1.26 (servo cap / stall), the aluminium clamp 3.1, the pinion 2.7 / 1.35, the turntable's teeth 3.2 / 1.6; the cradle 1.0 / 0.83 / 0.81 against the across-layer strength (1.7-2.1 along the layers, p99 SF 1.7-2.0). Not run yet: the cartridge (fall and recoil) and the dial (jam).
- What the FEA changed: the clamp ring (printed, 3 ears) failed the lift case at SF 0.18 and became aluminium with five ears (SF 3.1); the yoke's bridge, once its underside was relieved for the pinion, carried the arms as thin windowed beams (SF 0.6): it is now 10 mm, solid, and the relief steps up in three 0.5 mm steps from r 35; the cradle's rails and cheeks bent (SF 0.27-0.5 at first): 2 mm deeper rails behind the front dowel, filleted gussets at the cheeks, 6 mm cheeks, a Ø18 boss round the pivot insert and a deeper-rooted stop lug; the pan base's servo pocket wall now runs into the housing tube (its notch was the side-fall peak).
- Mass: 1.556 kg with 30 balls, 1.502 kg empty (exports/turret/turret.json), down from 1.594: still 56 g over the head payload's 1.5 kg with a full hopper. Saved: the bumper 4 mm with screw lugs, the carrier and the turntable pocketed, the bay's walls 1.2 mm, the lid 2 mm, the hopper's wall 1.6 mm, the pan base at 40 % gyroid, a 3.8 g laser. Added by the FEA: the 10 mm solid bridge, 6 mm cheeks, deeper rails, the aluminium clamp.
- Motion (exports/turret/motion.json): tilt move 5.8, hold 42.9, world-frame 21.8; pan 9.2 at the 50 % cap, yaw 96.7; the tilting group's centre 8-11 mm behind the axis empty to full (nose-up preload kept).
- Clearance (cad/turret_lidar.py, now a general obstacle check with point pruning, ~2 min a scene): on the robot, the VLP-16 148 mm ahead with its top 41.7 mm above the TM face, the depression is limited only within pan +-26 deg, to -16 deg at worst (the ball path binds, 11.8 mm clear; the firmware table every 5 deg is in exports/turret/lidar/lidar.json); on the Orin box nothing limits it (the deck and the antennas clear at full depression), and firing is kept out within 60 deg of straight back, where the operator stands (exports/turret/lidar/box.json).
- Assembly (cad/turret_assembly.py, docs/turret-assembly.md): twelve steps with an exploded picture each from the CAD and the hardware table from the parts list. Bench tests: docs/turret-bench-tests.md (fits, strength, the electrics without motors, the speed ceiling first, the feed, accuracy).
- Not done at the wrap-up (2026-10-27): the full FEA rerun (cartridge, dial, summary.json); the electronics' listed
prices (a research agent was gathering them;
no_listed_pricein exports/turret/bench/bom.json lists what is missing, with the bench stand's rocker and feet); the last 56 g. - Next: the prototype plan the planner is writing (main's docs/turret-prototype-plan.md) becomes the task list.
- 2026-10-27 (evening): v0 ready to order, print and build (docs/turret-prototype-plan.md T0-T5, T8 and the build
package; decision D42).
- T0: main as merged builds clean (1.556 kg with 30 balls, 1.502 empty; 0 clashes; tilt 5.8, pan 9.2).
- T1: D42 recorded (the X2216 880KV motors, the STS3215 feed, the red Class 2 laser, the bench items not fitted).
The catalogue has every v0 part with a listed price, a buy link and a second seller (three research passes:
Digi-Key and Mouser for the electronics, a second seller for the McMaster hardware, the bench kit and the tools);
not_in_a_catalogue,no_listed_priceandno_second_sourceare empty in exports/turret/bench/bom.json. Found on the way: the MF-R200 does not exist (MF-R250), the SN74HC123N is broker-only (CD74HC123E), the 3557-2 takes ATO not MINI blades, no 18 AWG silicone wire fits the Micro-Fit crimp (1.85 mm insulation at most: 1.75 mm PTFE), the ring's 28 AWG leads need the 26-30 AWG crimp (43030-0010), the plan's backup laser is a line laser, there is no Worker 200-pack, the Micro-Fit crimpers the plan named stop at 20 AWG. Order sheets: exports/turret/bench/orders/ (cad/turret_orders.py: 100 lines, 18 sellers, $1,559 in whole packs; the servos and motors first). - T2: the X2216 drawn from SunnySky's own drawing (cad/turret.py
MOTOR): bell-down under the cage plate (radial slots take its 16/19 cross in any quarter turn), the hub a PA6-CF disc on the motor's bolt-on prop adapter (Ø5 pilot, M5 stud, washer and nylon-insert nut); the cage plate rises 13.7 mm (plate_z0 42). The heavier motors moved the tilting group's centre ahead of the axis when empty, so the tilt axis is now 48 mm ahead of the dial axis (tilt_x): 8.5 mm behind with 30 balls, 5.2 empty; motion margins tilt 5.9, pan 8.9 (exports/turret/motion.json). - T3 (the launch physics rerun here, the physics agent having finished): with the X2216's constants the ceiling on Ø34
is 67.6 fps at the worst corner (2.3 under the cap), Ø36 70.7 (over):
TYRE_SETis Ø34 only; the bench keeps every ball under 66 fps by checking the no-load speed first (<= 11,180 rpm). The set speed is 8,855 rpm for 52 fps; the brake's tau 6.9 ms; the soft start 300 ms (2.5 A at the ring). docs/turret-physics.md sections 0, 4, 5, 9. - T4: the feed servo under the dial floor (its face 0.75 mm up in a pocket so the dial sits flat on its horn, 4 x M2 x 6 countersunk), the dial on the horn with 4 x M3 x 8 from the top; the M2006 and C610 features gone. T5: the feed physics on the servo: a step settles in 232-246 ms; 3 balls/s level and 2.5 at -25 deg on a fixed period, so the firmware paces each step on gate 1 (a dither, never a step, if no ball arrives); parked-safe passes.
- Build package: the print files (exports/turret/print/*.3mf in their print orientations, sliced by Bambu Studio for a P1S: 1,291 g, 50 h; docs/turret-print.md with the settings and the schedule); the SendCutSend clamp (exports/turret/pan_clamp.dxf, 6061-T6 0.125 in, in the order sheets); the wiring (docs/turret-wiring.md, rewritten for v0, with the boards laid out and checked by cad/turret_boards.py); the assembly order regenerated; the bench test procedure (docs/turret-bench-tests.md) with the v0 numbers.
- T8 changed three things in the plan's wiring, each with its reason in docs/turret-wiring.md: the flywheel regulator is always on and the 2815 switches its 12 V output (the tilt servo on the always-on 12 V; one regulator fewer: the bay's boards could not hold the plan's parts); one 5 A MINI fuse in line in the base protects the ring's 2 A circuits (no PCB blade holder fits the heights); and the 2815's slide switch must be OFF: with it ON the MOSFET stays on whatever ARM does (Pololu's page), so the plan's "slide switch left ON" would have defeated the safety line.
- Also: the turntable's lightening pockets moved off the shoulder's root (the side-fall peak, SF 1.12); the stand's rocker cutout is the Ø20.2 round of the DC-rated Bulgin / Switch Components rockers.
- Firmware v0 (T7) is with a separate agent in firmware/turret/ (the servo bus with the feed, the gate-paced feed, CONFIG from the physics, the AM32 settings, the bench host script).
- Next: the FEA rerun on this geometry (running: cad/turret_studies.py, summary.json); measure the X2216, a servo and the ring in hand (T6); order; print the stand and the pan stack; then T10-T15.
- 2026-10-27 (late): the electrics made buildable part by part and reviewed, the brake modelled as built, and the FEA
on the v0 geometry (plan T8, T9; decision D47).
Four faults in the plan's wiring, fixed first (docs/turret-wiring.md section 3, every part with a designator; exports/turret/wiring/parts.md lists them and cad/turret_boards.py counts the parts list's passives from them): the arm chain ran on 5 V and fed the Feather's pins, which are not 5 V tolerant (it runs on the Feather's 3.3 V now); a 5 V 555 drove the opto's LED at about 0.5 mA (the 555 runs on the pendant loop's 12 V: 2 mA); the brake FETs to a floating star could stay off (each phase is shorted to GND, as AM32's own brake does); the laser's 2N7000 had 3.3 V on its gate against a threshold of up to 3 V (the bus buffer's spare channel drives it at 5 V, from A5).
Then an independent review of the drawn circuit against the datasheets (a separate agent) found two blockers and nine more, all fixed: the trip latch could be cleared by the next slower ball (now set-only); the Feather could raise ARM through its own pins (it reads ARM and the pulse train through 100 k); a '123 one-shot fires on a rising reset (the CD74HC423E does not; an MCP120 makes the power-up reset); the ESC's phase edges would lift the brake FETs' gates (100 nF gate to source); complementary PWM would push the wheels' energy into the buck-fed 12 V rail (off, plus TVS diodes, Pololu's Schottky at the 2815 and ramps between set speeds); the RX divider had no margin (a Schottky); A4 shares an interrupt line with D10 (PULSE_MON on A3); the Feather's USB pin is its USB-C VBUS (fed through a 1N5817); signals into the unpowered ESCs and feed servo (1 k series, DShot low while disarmed); a reversed supply (a 1N5822 after the fuse); and any headphone plug would have closed the pendant's 3.5 mm loop (a keyed Switchcraft mini-XLR now, and a 0.1 A polyfuse). Also from it: K = 0.475 at 3.3 V (R11 starts at 63.4 k), R9 39 k for the 4N35's margin, 1 k gate pull-ups, a bench check of the gates' light (3.10), the hall's bypass capacitor.
The firmware and its bench host follow the circuit: 19 firmware tests pass (a new set speed ramps; no DShot frame while the ESCs are unpowered) and 33 of 33 simulated bench tests (3.1 reads the 555's frequency, the opto's current and every Feather input; 3.3 a slower pair after a trip stays latched; 3.10 the gates' light; 4.10 no FET gate kick and no rail over 13 V through a step down).
The brake as built: two FETs and the taps make the short 173 mOhm (tau 10.2 ms at low speed, 6.9 for the motor alone), and the winding's inductance (not published; 35 uH est, swept 0-50) weakens it at speed: 5 % speed 40-57 ms after release (48 at 35 uH; it read 30 before), nothing left at the 96 ms when a ball in the chute could arrive.
FEA (T9: cad/turret_studies.py on the final geometry, exports/turret/fea/summary.json). Targets: 2 everyday, 1.2 rare.
Part Falls (30 g) Everyday Stall / jam tilt yoke 2.78 / 2.05 / 1.78 1.82 (50 N push up at the muzzle; p99 SF 4.4) turntable, its teeth 8.38 / 2.19 3.31 (servo cap) 1.66 pinion 2.7 1.35 pan base 6.12 / 1.38 2.26 (servo cap) 1.13 (p99 SF 9) aluminium clamp 2.89 (lift) cradle (across the layers) 0.78 / 0.62 0.77 (the push) cartridge 1.57 1.57 (recoil at 66 fps; p99 SF 11) dial (PETG) 5.75 (jam at the 30 % cap) 1.72 Changes from it: the yoke's stepped relief is a cone (its 30 g down case 2.25 -> 2.78; the sweep still clean); the cartridge's recoil takes the X2216's own loads at 66 fps. Named for v1 (T16): the cradle's cheeks bend across the layers (0.77 under a 50 N push with humid PA6-CF, about 1.6 dry): on the bench no pushing on the barrel, keep it dry, print a spare.
The bench print plan holds v0's parts only with the slicer's numbers (19 parts, 1,292 g, 50 h; the build page reads it). The order sheets: 125 lines from 17 sellers, $1,609.89 with the stand and the tools; the boards' passives 94 pieces for $15.74; every line priced with a second seller.
cad/turret_box.py now tolerates the J501's Rev 0.9 parameters (its fan duct became a flush grille); the adapter's intake shroud was drawn round the Rev 0.8 duct and needs a look when Rev 0.9 lands.
Next: order (the servos and the X2216s first: Waveshare ships from China); print the stand and the pan stack; test- fit the TB3M's panel hole; T6's measurements on the parts in hand; then T10-T15.