PROJECTTINKER

← all projects

The problem in one paragraph

A cube standing on one edge is an inverted pendulum: gravity pulls it further over the harder it leans, and there is nothing outside the cube to push against. The only available lever is internal momentum — spin a flywheel one way and the body must rotate the other. The control problem is to measure the lean at a few hundred hertz and command a motor torque that both corrects the lean and stops the wheel winding up to a speed where it has no authority left. Corner balancing is three of these, coupled.

Signal flow

   +----------+   tilt, rate    +---------------+  torque   +----------+
   |   IMU    |---------------->|  COORDINATOR  |---------->| FOC NODE |
   | SPI 500Hz|                 | fusion + K*x  |  setpoint |  G431    |
   +----------+                 +-------+-------+           +----+-----+
                                        | wheel speed            | 3-phase
                                        +------------------------+
                                                            +----v-----+
                                                            |  BLDC +  |
                                                            | FLYWHEEL |
                                                            +----------+

Controller state machine

  BOOT --> CALIBRATE --> IDLE --> BALANCING --> FAULT
             | gyro bias    | wheel  | |th|<20deg    | |th|>20deg
             | 10 s still   | free   | 500 Hz        | or w > 0.9*w_nl
             v              v        v               v
          store bias     motor off  tau = Kth*th      motor disabled,
                                    + Ktd*thd + Kw*w  dump ring buffer

Part numbers and costs were gathered from vendor and datasheet pages on 17 Sep 2026. Verify at the cart — two independent checks returned different DigiKey prices for the ESC board on the same day.

PartMPNQtyUnitSourceNote
Gimbal BLDC motorCubeMars GL35 KV1001–3$54.99cubemars.com0.15 N·m cont, 0.46 peak, 90 g — the only part verified to clear torque and speed
FOC driverB-G431B-ESC11–3$29–54DigiKey / STSTM32G431 + gate driver + current sense on one board
Budget driverSimpleFOCMini (DRV8313)1$6.59BanggoodVoltage mode only — no current sensing, no true torque command
EncoderAS5047P-TS-EK-AB1–3$4.93DigiKey14-bit SPI. AS5600 at ~$3 is the "get it running" part
IMUICM-42688-P1~$15TDK / breakout2.8 mdps/√Hz, 32 kHz ODR. Avoid BNO055 — fusion capped at 100 Hz
Coordinator MCUESP32-S3-DevKitC-11~$15EspressifHardware FPU. ESP32-C3 has none, and no MCPWM
Battery4S 1500 mAh LiPo1$19–28hobbyCapacity, not C-rating, is the constraint
Rim massM5×16 hex bolt + nut, A27–10/wheel~$0.15any~5.7 g each; count comes from the blueprint generator
PivotHardened steel ball ∅7 mm1–2~$1bearing supplierPrinted plastic creeps under point load
Bearing608-2RS1–3~$1any8×22×7, only if the motor needs support
Nozzle0.6 mm hardened steel1~$8printer vendorRequired for CF-filled filament; brass wears out in hours

Totals. One-axis bench rig ≈ $58. Three-axis corner cube ≈ $419, of which ~$60 is an unsourced mechanical estimate — the least reliable line on the page.

Control tick — from a hardware timer, never from loop()

float tau = K_TH * (tilt - biasInt) + K_TD * tiltRate + K_W * w;
tau = constrain(tau, -TAU_MAX, TAU_MAX);
if (fabsf(tilt) > TILT_LIMIT || fabsf(w) > W_LIMIT) { motor.disable(); return; }
motor.move(tau);                       // TORQUE mode, not voltage

Gains for the 120 mm baseline at 500 Hz, straight from the simulator: Kθ = 49.37, Kθ̇ = 5.35, Kω = 0.0268. The third is the one people omit; without it the wheel saturates and the cube drops after about four seconds.

Host environment

# requirements.txt - simulation + mesh side
numpy>=2.0
pillow>=10.0

# firmware toolchain
platformio>=6.1      # espressif32 platform
# NOTE: two espressif32 platforms resolve to core 2.0.17 vs 3.3.11;
# the LEDC and camera APIs differ between them. Pin it.

# Windows: export PYTHONIOENCODING=utf-8 before an upload, or esptool
# can hang mid-flash on a unicode progress glyph.

The browser simulator and blueprint generator are dependency-free ES modules that also run under node, which is how they are unit-tested against a numpy reference — 44/44 on gains, poles, trajectories and the bisection solver.

Drawings are generated from your cube size rather than fixed — the flywheel is sized backwards from the momentum budget, so the bolt count is a physics result. Change the target lean and it moves.

Baseline output, 120 mm edge

ItemValueDerived from
Flywheel OD69.6 mm0.58 × edge, clipped by rail clearance
Bolt circle53.6 mmOD − 2·bolt∅ − 6
Rim bolts7 × M5×16required inertia ÷ inertia per bolt
Wheel inertia3.18 × 10⁻⁵ kg·m²rim mass at bolt radius + disc
Pivot ball∅7 mm0.06 × edge

Honest scope. These are dimensioned concept drawings — correct in proportion and in the derived numbers, with no tolerances or fits. .STEP, .STL and Gerber archives are not hosted yet; once storage is connected they can be attached as files on a post in this section.

Driver wiring

B-G431B-ESC1                         MOTOR / SENSOR
  PHASE U  --------------------------  motor phase A
  PHASE V  --------------------------  motor phase B
  PHASE W  --------------------------  motor phase C
  3V3      --------------------------  AS5047P VDD
  GND      --------------------------  AS5047P GND   (star point at driver)
  SPI SCK  --------------------------  AS5047P CLK
  SPI MISO --------------------------  AS5047P MISO
  CS       --------------------------  AS5047P CSn
  UART TX  --------------------------  coordinator RX   (torque setpoints)
  UART RX  --------------------------  coordinator TX

If the motor locks or stutters at startup, swap any two phase wires.

Slicer settings, structural parts

MaterialNozzleHotendBedCoolingWalls
PA6-CF0.6 hardened290–300 °C80 °C0–20%5+ — dry the filament or it foams
PETG-CF0.6 hardened250–260 °C80 °C30–40%4
PLA (jigs only)0.4–0.6205–215 °C60 °C100%3

Walls, not infill. Frame stiffness comes almost entirely from perimeter count; raising infill instead adds mass, and mass costs recovery angle directly.

Everything below is simulated, against a model cross-checked in two languages. No hardware has been built, so there are no measured antenna, thermal or latency traces yet — those belong in posts in this section, with the raw data attached.

MeasurementValueMethod
Open-loop unstable pole9.3106 rad/seigenvalue of the linearised plant
Fall time constant107 ms1/pole — sets the loop-rate floor
Peak torque, 8° catch0.209 N·mnonlinear sim at 4 kHz
Peak wheel speed, 8° catch2740 rpmsame run
Max recoverable lean @ 0.18 N·m10.09°bisection over full sim runs
Momentum dump excursion6.76°wheel pre-spun to 2865 rpm, shed to 0
Kω deleted → failuret = 5.74 sidentical kicks; wheel winds to 4892 rpm

Torque budget vs cube size

EdgeMassFall τPeak torqueCatches @ 0.18 N·m
80 mm0.45 kg88 ms0.043 N·m18.1°
100 mm0.65 kg98 ms0.078 N·m16.3°
120 mm0.90 kg107 ms0.130 N·m14.5°
150 mm1.45 kg120 ms0.262 N·m12.4°
200 mm2.20 kg139 ms0.477 N·m8.7°

2.25× the size costs 11× the torque. That is the strongest argument for building small.