A real-time ball-balancing system built on the STM32 Nucleo F446RE microcontroller. A VL53L1X Time-of-Flight laser sensor measures the ball's position on a beam, and a discrete PID controller drives a servo motor to keep the ball centered.
Demo video:
ball_beam_PID_vid.mp4
The ball and beam is a classic control theory problem: a ball rolls freely along a beam whose angle is controlled by a servo motor. Without feedback, the ball rolls off the end. This project implements a discrete PID (Proportional-Integral-Derivative) feedback loop that continuously adjusts the beam angle to stabilize the ball at a fixed setpoint.
flowchart LR
A["VL53L1X\nToF Sensor"] -->|"Ball distance (mm)"| B["PID Controller\n(STM32F446RE)"]
B -->|"PWM signal"| C["MG996R\nServo Motor"]
C -->|"Beam angle"| D["Ball Position"]
D -->|"Feedback"| A
| Component | Model | Role |
|---|---|---|
| Microcontroller | STM32 Nucleo F446RE (ARM Cortex-M4, 180 MHz) | Processing & PWM generation |
| Servo Motor | Miuzei MG996R | Beam actuation |
| Distance Sensor | VL53L1X 4 m ToF (CJMCU-531 breakout) | Ball position measurement |
| Mechanical Frame | 3D-printed — GrabCAD model | Structure |
| Peripheral | Function |
|---|---|
| I2C1 | VL53L1X sensor communication |
| TIM2 CH1 (PWM) | Servo motor control (700–1250 µs pulse width) |
| UART2 | Debug output at 115 200 baud |
| Parameter | Value | Description |
|---|---|---|
| Setpoint | 280 mm | Target ball distance from sensor |
| Kp | 0.3 | Proportional gain |
| Ki | 0.001 | Integral gain |
| Kd | 150.0 | Derivative gain |
Implementation notes:
- Error signal is filtered (53% current + 47% previous sample) to reduce ToF sensor noise
- Proportional output is clamped to ±25 to respect physical servo travel limits
- Control effort is reduced when the ball is within 10 mm of setpoint to prevent oscillation
Ball_Beam_PID_Control/
├── Core/
│ ├── Src/
│ │ ├── main.c # PID algorithm, servo driver, control loop
│ │ ├── VL53L1X_api.c # ST Time-of-Flight sensor driver
│ │ ├── VL53L1X_calibration.c # Sensor offset calibration
│ │ └── ... # STM32 HAL / system files
│ └── Inc/ # Corresponding header files
├── Drivers/
│ ├── CMSIS/ # ARM Cortex-M core support
│ └── STM32F4xx_HAL_Driver/ # ST Hardware Abstraction Layer
├── ball_beam_PID.ioc # STM32CubeMX project configuration
└── ball_beam_PID_vid.mp4 # Demo recording
- STM32CubeIDE v1.x or later
- STM32 Nucleo F446RE board with ST-Link USB cable
- VL53L1X sensor and MG996R servo wired per
ball_beam_PID.ioc
-
Clone the repository
git clone https://github.com/<your-username>/Ball_Beam_PID_Control.git
-
Import into STM32CubeIDE File → Open Projects from File System → select the cloned folder
-
Build Project → Build All (
Ctrl+B) -
Flash to board Connect the Nucleo via USB, then Run → Run (or
F11for debug mode)
Full pin assignments are defined in ball_beam_PID.ioc and can be viewed in STM32CubeMX. Open the .ioc file in STM32CubeIDE to see the pinout diagram.
| Library | Source |
|---|---|
| STM32F4xx HAL | Included — generated by STM32CubeMX |
| CMSIS | Included — ARM Cortex-M core support |
| VL53L1X ULD API | Included — ST STSW-IMG009 |
The VL53L1X ULD API and STM32 HAL drivers are distributed under ST's SLA0044 license. See Drivers/STM32F4xx_HAL_Driver/LICENSE.txt and Drivers/CMSIS/LICENSE.txt for details.