Sensing & control

Balance is
a moving target.

Estimate the tilt. Move the wheels. Correct again. We built the loop between sensing and physical motion.

01 · Sense

Start with
a better signal.

An Arduino Nano 33 BLE Sense reads the IMU. Calibration and complementary filtering help estimate the robot’s tilt.

02 · Correct

Turn an angle
into action.

The proportional controller drives two DC motors through an L298N. Deadband, saturation and motor friction all affect the response.

03 · Test

The work is
in the tuning.

Power delivery, wiring, batteries and chassis mechanics shaped the behavior we saw during testing.

04 · Outcome

About 30 seconds.
More to learn.

Our best runs balanced for about 30 seconds, against a one-minute target.

Blender concept of a two-wheeled balancing robot with a layered chassis, controller board and motor driver.
TWO-WHEELED BALANCING ROBOTBlender model of our balancing robot
Read the case study
Scroll or use arrow keys on the slider

Four-person team · ROBO 205 · April 2026

Two-Wheeled Balancing Robot

Finding balance through sensing, control, and a lot of tuning.

ArduinoMicroPythonIMUMotor control

At a glance

Role
Led the build and wrote the MicroPython control code
Team
Four people, with Mohammed Muqeet, Obaid Alaleeli and Ibrahim Alhammadi
Course
ROBO 205 Electronic Circuits
Built with
Arduino Nano 33 BLE Sense, L298N motor driver, two DC motors
Result
Best runs balanced about 30 seconds, against a one-minute target

01 / Purpose

What we set out to do.

Build a two-wheeled robot that can estimate its own tilt and correct it through motor control. Our course objective was to balance for one minute. Reaching that objective meant working across sensing, software, assembly, and power delivery.

Balancing robot · prototype testSilent clip. Press play for the full video with sound

Two-wheeled balancing robot prototype test. ROBO 205 team project, April 2026.

Husain Altelly, Mohammed Muqeet, Obaid Alaleeli, Ibrahim AlhammadiOpen on YouTube (Balancing robot · prototype test)

02 / Approach

How it came together.

An Arduino Nano 33 BLE Sense reads its LSM9DS1 inertial measurement unit. A complementary filter combines the accelerometer’s estimate of gravity with short-term angular changes measured by the gyroscope. The resulting tilt estimate feeds the motor controller.

The MicroPython code runs a proportional controller with a deadband, output saturation and a minimum PWM level to overcome motor friction, driving two DC motors through an L298N driver. Moving to a fully tuned PID controller is the next step.

I used a complementary-filter weight of 0.7, a 300-sample gyro calibration and a control loop targeting about 100 Hz.

Try it · the control rule

What the wheels do at each tilt

Drag across the graph or use the slider. The curve is the exact rule in my final MicroPython code, so it shows how the robot responds, not a recorded run.

dead-band ±8°−80%−65%0%+65%+80%−30°−20°−10°0°10°20°30°Measured tilt (pitch)Motor command (PWM)balance point 1.8°
Tilt error+12.2°
Right wheel67.9%
Left wheel (×1.18 trim)80.1%

Wheels drive toward the lean at 67.9% PWM.

Inside 8° of the balance point the wheels stop, so sensor noise doesn’t make them chatter. Past that they jump to 65% PWM, the level where our motors overcame static friction, and ramp to an 80% cap at 30°. We tried a full PID controller first, but on this hardware the integral term wound up and the derivative amplified IMU noise, so we kept this saturated proportional rule. The left wheel runs 1.18 times harder because the two motors didn’t match.

Parts I used

As an Amazon Associate I earn from qualifying purchases.

03 / Contribution

What I built.

I led the build in a four-person team with Mohammed Muqeet, Obaid Alaleeli and Ibrahim Alhammadi. I wrote the MicroPython control code, calibrated the IMU and tuned the controller, and worked on the wiring, assembly and testing alongside the team.

04 / Demonstration

See it working.

The clip under Purpose is a real prototype test: the robot is steadied by hand while its wheels respond to the tilt. The graph under Approach shows exactly how the code turns that tilt into motor commands, and the Blender opening explains the sensing, hardware and correction loop.

05 / Results

What we achieved.

Our best runs balanced for about 30 seconds on a hard, flat surface. We didn’t reach the one-minute target, and the limits below explain why.

06 / Limitations

Limits and next steps.

The robot stayed sensitive to controller tuning, battery level and chassis mechanics, and we fought shakiness, a faulty screw terminal and power drops along the way.

Next time I’d log tilt and motor commands, start every trial from the same position and repeat runs, so the tuning could be done systematically.

07 / Lessons learned

What I’m taking forward.

This project made the relationship between software and hardware tangible. Filtering a signal is only useful when calibration, power, wiring, and the motor response are also dependable. The next useful step would be to improve those foundations, then tune and evaluate the controller with repeatable measurements.

Next project

Robot Motion Planning

Loading image
Visual sources & attribution

The Blender scenes and animations are illustrations made for this portfolio, not recorded results. The YouTube videos show the real robots and lab demos, and the AURORA film includes footage from the interactive learning demo.

The T42 scene uses the Yale OpenHand Model T42 CAD from the Yale GRAB Lab’s OpenHand repository, licensed CC BY-NC 3.0, with the assembly, materials, tendon path and motion adapted for illustration.

R. R. Ma, L. U. Odhner, A. M. Dollar, “A Modular, Open-Source 3D Printed Underactuated Hand,” ICRA 2013.

The Yale OpenHand Project is an initiative to advance the design and use of robotic hands designed and built through rapid-prototyping techniques in order to encourage more variation and innovation in mechanical hardware. Please visit the Yale OpenHand site for more details.