Two-Wheeled Self-Balancing Robot
TODO: 1-2 sentence summary — what it is and the headline result (e.g. recovery angle, balance time).
My role
Designed, built, and programmed solo.
Problem
TODO: why build a self-balancing robot — what were you trying to learn or prove?
Constraints
TODO: budget, parts availability, what you deliberately kept simple in Rev 1.
Design & Decisions
TODO: overall concept and the big design decisions (sensing, actuation, control approach) — the revisions below cover how this evolved.
Build
TODO: mechanical build, electronics, firmware/software stack. List the tools and skills used, e.g. CAD, embedded C/C++, PID control, PCB design.
Testing & Results
TODO: how you tested it, and the quantified results (e.g. recovery angle, time to settle, disturbance rejection).
What I’d Do Differently
TODO: honest retrospective, or what’s planned for the next revision.
Revisions
Spec comparison
| Spec | Rev 1 — First balance, bang-bang control | Rev 2 — Switch to PID control | Rev 3 — Faster controller, sensor fusion | Rev 4 — BLDC motors, cascaded PID | Rev 5 — LQR control on ESP32 |
|---|---|---|---|---|---|
| Motors | 28BYJ-48 stepper (x2) | NEMA17 stepper (x2) | NEMA17 stepper (x2) | BLDC gimbal motor (x2) | BLDC gimbal motor (x2) |
| Controller | Arduino Uno | Arduino Uno | STM32F4 Nucleo | STM32F4 Nucleo | ESP32 |
| Imu | MPU6050 | MPU6050 | MPU9250 | MPU9250 | BNO055 |
| Battery | 4x AA | 2S LiPo, 7.4 V | 2S LiPo, 7.4 V | 3S LiPo, 11.1 V | 3S LiPo, 11.1 V |
| Control Method | Bang-bang | PID | PID + complementary filter | Cascaded PID | LQR |
| Mass | 650 g | 720 g | 740 g | 810 g | 790 g |
| Balance Performance | ~10 s before falling | ~45 s before falling | Stable indefinitely on flat ground | Recovers from small pushes | Recovers from pushes up to ~15°, holds station-keeping |
Timeline
Rev 5 — LQR control on ESP32
Jun 2026
Why: Wanted better disturbance rejection and to remove manual PID retuning between tests.
Changes
- Moved to an ESP32 for onboard wireless telemetry/tuning
- Switched from cascaded PID to an LQR controller
- Upgraded IMU to a BNO055 with onboard sensor fusion
Outcome: Recovers from pushes up to roughly 15 degrees and holds station-keeping on a flat floor.
TODO: optional longer notes for this revision.
Rev 4 — BLDC motors, cascaded PID
Feb 2026
Why: Steppers couldn't react fast enough to reject larger disturbances like pushes.
Changes
- Replaced steppers with BLDC gimbal motors for smoother, higher-bandwidth control
- Restructured control as a cascaded PID (velocity loop inside the angle loop)
- Upgraded to a 3S LiPo pack for the higher-current motors
Outcome: Robot now recovers from small pushes instead of just holding still on flat ground.
TODO: optional longer notes for this revision.
Rev 3 — Faster controller, sensor fusion
Oct 2025
Why: The Arduino's control loop rate was limiting how aggressive the PID gains could be.
Changes
- Moved from Arduino Uno to an STM32F4 Nucleo for a faster control loop
- Upgraded IMU and added a complementary filter for sensor fusion
Outcome: Achieved indefinite stable balance on flat ground, with noticeably less drift.
TODO: optional longer notes for this revision.
Rev 2 — Switch to PID control
Jun 2025
Why: Bang-bang control was too jerky to hold a stable balance point; needed proportional control.
Changes
- Upgraded to NEMA17 steppers for more torque
- Replaced bang-bang control with a tuned PID loop
- Moved from AA batteries to a 2S LiPo pack
Outcome: Balance time improved to roughly 45 seconds before falling, with much smoother motion.
TODO: optional longer notes for this revision.
Rev 1 — First balance, bang-bang control
Feb 2025
Why: Prove the chassis and sensing concept would work before investing in a proper controller.
Changes
- First working chassis and two-wheel drivetrain
- Basic bang-bang balance control to prove the concept
Outcome: Balanced for roughly 10 seconds before falling; confirmed the IMU placement and wheel base were workable.
TODO: optional longer notes for this revision.