Skip to the content
PCBKitNew board
2 layers56.5 × 42 mmBall Tracking Robot Controller, the printed circuit board in 3D

Ball Tracking Robot Controller

linusnc183/ball-tracking-robot

A two-wheeled robot that visually tracks and follows a red ball. A Raspberry Pi handles vision (Picamera2 + OpenCV) and sends drive commands over UART to a custom STM32-based motor controller PCB, which closes the loop…

Size
56.5 × 42 mm
Copper layers
2
Thickness
1.6 mm
Parts
56
Nets
60
Pads
230
Tracks, vias
795, 76
Pours
1
Routed
Yes
KiCad
KiCad 9.0 (file 20241229)
Created
26 Jul 2026
Updated
30 Jul 2026

Overview

Ball Tracking Robot Controller is an open-source printed circuit board by linusnc183, from the linusnc183/ball-tracking-robot repository on GitHub. It is a 2-layer board of 56.5 × 42 mm with 56 parts on 60 nets, routed with 795 tracks and 76 vias.

It is built around the AP2112K-3.3. Its parts include 20 capacitors, 14 resistors, 9 connectors, 4 diodes and 3 ICs. It belongs with the robots and motor control boards and the STM32 boards of this library.

Main components

1

README

README.md

Autonomous Ball Tracking Robot

A two-wheeled robot that visually tracks and follows a red ball. A Raspberry Pi handles vision (Picamera2 + OpenCV) and sends drive commands over UART to a custom STM32-based motor controller PCB, which closes the loop with encoder feedback and PID.

I designed the controller PCB from scratch in KiCad and wrote the embedded firmware in C (STM32CubeIDE, HAL).

Skills demonstrated: PCB design (KiCad) · embedded C / STM32 HAL · real-time control loops (PID) · analog/digital circuit design (power regulation, signal protection) · computer vision (OpenCV) · serial communication protocol design

https://github.com/user-attachments/assets/ed4e9871-f768-4ec5-b8f6-aebbd3194bc8

Table of Contents

Features

  • Real-time red-ball tracking with Picamera2 + OpenCV
  • Custom 2-layer STM32G431 motor controller PCB with protection circuitry
  • Closed-loop wheel velocity control via quadrature encoders + independent left/right PID
  • UART link between Raspberry Pi and STM32, with a motor-safety timeout if the link drops
  • Ultrasonic obstacle sensing (HC-SR04)

System Architecture

System architecture diagram

Why split the work this way: the Raspberry Pi handles the computationally heavy, non-deterministic side — image processing, ball detection, high-level navigation decisions. The STM32 handles the deterministic, real-time side — PWM generation, encoder feedback, PID control, and motor safety. Keeping them separate means a slow camera frame can never disturb the timing of the motor control loop.

Hardware

Custom 2-layer controller PCB designed from scratch in KiCad 9 (schematic + layout), organized into hierarchical sheets by subsystem.

Block diagram

Power (Power.kicad_sch)

Power section

  • Input: USB-C receptacle, with 5.1 kΩ pull-down resistors on the CC1/CC2 lines so the port correctly advertises itself to a USB-C source as a 5V/default-current sink.
  • Protection: A resettable polyfuse (2A) guards the input against overcurrent/short circuits, and SMF5V0A TVS diodes clamp transient voltage spikes on the input.
  • Regulation: An AP2112K-3.3 LDO steps the 5V USB rail down to 3.3V for the STM32 and logic-level signals, with bulk/decoupling caps (10 µF, 100 µF) on both rails.
  • Status indication: Power-good LEDs on the 5V and 3.3V rails; test points (TP1–TP3) broken out for probing key rails during bring-up/debug.
  • Both 5V and 3.3V are distributed to the Raspberry Pi header, so the Pi is powered directly from the board rather than needing its own separate supply.

Motor Driver (MotorDriver.kicad_sch)

Motor driver section

  • A single TB6612FNG dual H-bridge IC drives both DC motors (one chip, two independent channels — no need for two separate driver ICs).
  • The STM32 drives AIN1/AIN2/BIN1/BIN2 for direction and PWMA/PWMB (from TIM1 CH1/CH2) for speed, with STBY used to enable/disable the driver in firmware.
  • Bulk capacitance (470 µF + 100 µF) sits close to the motor supply input to absorb the current spikes motors draw on startup/direction changes.
  • TVS diodes across the motor connector outputs suppress the inductive kickback/flyback transients DC motors generate when switching direction.

Sensors (Sensors.kicad_sch)

Sensors section

  • HC-SR04 ultrasonic: TRIG/ECHO connect directly to the STM32 (PA0/PA1). Since the HC-SR04's echo pin outputs a 5V logic level and the STM32 is 3.3V-tolerant, a resistive divider (20 kΩ/10 kΩ) steps the echo signal down to a safe ~3.3V before it reaches the MCU pin.
  • Quadrature encoders: Separate 4-pin headers for the left and right motor encoders, each feeding an A/B channel pair into the STM32's hardware timer encoder inputs (TIM3 for one wheel, TIM4 for the other).
  • Small filtering capacitors (10 nF) on the encoder/sensor lines help reject electrical noise from the nearby motors.

MCU (STM32.kicad_sch)

STM32 section

  • STM32G431CBTx (Arm Cortex-M4, 170 MHz), with standard decoupling capacitors on all supply pins plus a bulk 4.7 µF cap.
  • BOOT0 and NRST have pull resistors for reliable reset/boot behavior; two push buttons (SW1/SW2) are broken out for reset and boot-mode selection during flashing/debug.

Raspberry Pi Interface (RaspberryPi.kicad_sch)

Raspberry Pi section

  • A 4-pin header carries 5V, 3.3V, and UART TX/RX between the board and the Raspberry Pi's GPIO header.
  • Series resistors (220 Ω) on the UART lines add a layer of protection against miswiring/contention; no level shifting is needed since both the STM32 and Pi run 3.3V logic.

Debug (Debug.kicad_sch)

Debug section

  • 6-pin SWD header (SWDIO, SWCLK, NRST, 3V3, GND) for programming and debugging the STM32 with an ST-Link, independent of the USART2 link to the Pi.

Key parts: STM32G431CBTx · TB6612FNG motor driver · HC-SR04 ultrasonic sensor · 2x quadrature encoders · AP2112K-3.3 LDO · USB-C power input · SMF5V0A TVS protection

PCB layout PCB 3D render

Firmware (STM32, C / HAL, STM32CubeIDE)

ModuleResponsibility
main.cSystem init, 50 Hz control loop scheduling, 10 Hz ultrasonic scheduling
motor.c/hPWM + direction output to the TB6612FNG (TIM1 CH1/CH2, AIN/BIN GPIO)
encoder.c/hReads TIM3/TIM4 in quadrature (TI12) encoder mode, converts counts to counts/sec
pid.c/hPer-wheel velocity PID with conditional-integration anti-windup
uart_control.c/hParses single-character drive commands from the Pi over USART2 (interrupt-driven)
ultrasonic.c/hSoftware-timed HC-SR04 ranging using the DWT cycle counter for microsecond resolution

Control loop

  • Runs at 50 Hz (CONTROL_LOOP_PERIOD_MS = 20): reads both encoders, runs independent PID controllers for the left/right wheel, and writes the resulting PWM + direction to the motor driver.
  • Ultrasonic sensing runs on its own 10 Hz cadence, separate from the control loop, since an HC-SR04 read is a blocking, up-to-30 ms operation and would otherwise disturb the PID's timing.

PID with anti-windup

Each wheel has an independent PID controller operating on measured wheel speed (encoder counts/sec) with output clamped to a ±1000 PWM range. Integral accumulation uses conditional integration: once the output saturates, the integral term stops accumulating unless the error is already pulling it back out of saturation. This prevents the classic windup overshoot where a saturated integrator keeps growing while the motor is already maxed out.

Quadrature encoding

Wheel encoders are read using the STM32's hardware timer encoder mode (TIM_ENCODERMODE_TI12) on TIM3/TIM4, decoding both edges of both channels for full 4x quadrature resolution and reliable direction sensing, rather than software-polling the encoder pins.

UART command protocol + failsafe

The Pi sends single-byte commands over USART2 (interrupt-driven RX):

CommandAction
FForward
BBackward
LTurn left
RTurn right
SStop

If no byte arrives for 500 ms (e.g. the Pi crashes or the link drops), the firmware automatically zeroes both wheel targets and stops the robot — the robot doesn't require a "stop" command to fail safe.

Ultrasonic ranging without a timer channel

The HC-SR04's echo line isn't wired to a timer input-capture pin, so pulse width is timed in software using the Cortex-M4's DWT cycle counter, giving microsecond-level resolution independent of the main loop's timing.

Vision (Raspberry Pi, Python)

ball_tracking.py captures frames with Picamera2, finds the ball with OpenCV, and streams drive commands to the STM32 over the same UART protocol the firmware implements.

Detection: Frames are converted to HSV and thresholded for red across two hue ranges (red wraps around hue 0/180 in OpenCV's HSV space), combined into one mask, then cleaned up with erosion/dilation to remove noise. The largest contour's minimum enclosing circle gives the ball's center and apparent radius.

Decision logic: The ball's horizontal offset from frame-center picks a turn command first (turning takes priority so the robot centers on the ball before closing distance); once centered, the ball's apparent radius acts as a distance proxy — small radius drives forward, large radius backs up, and an in-between radius holds position.

Serial keepalive thread: The STM32 firmware stops the motors if it doesn't receive a UART byte within 500 ms. Rather than tying that timing to the camera's frame rate, SerialCommander runs a dedicated background thread that resends the current command every 150 ms on its own schedule — so a slow or dropped camera frame can never cause an unexpected stop or leave a stale command running longer than intended.

Debounced ball loss: Losing the ball for a single frame doesn't stop the robot; it keeps issuing the last command until the ball has been missing for a full second, avoiding jerky stops from momentary detection blips.

Run it with:

bash
pip install picamera2 opencv-python pyserial numpy
python3 ball_tracking.py

Requires UART enabled via raspi-config (Interface Options → Serial Port) and, on Pi models where UART is routed through Bluetooth, dtoverlay=disable-bt in /boot/firmware/config.txt. Wiring: Pi GPIO14 (TX) → STM32 PA3 (RX), Pi GPIO15 (RX) → STM32 PA2 (TX), common ground — both sides are 3.3V logic, no level shifter needed.

Repository Structure

text
Ball_Tracking_Robot/
├── README.md
├── .gitignore
│
├── hardware/                          # Custom PCB design files
│   └── PCB/
│       ├── KiCad/
│       │   ├── Ball_Tracking_Robot_Controller.kicad_pro
│       │   ├── Ball_Tracking_Robot_Controller.kicad_sch
│       │   ├── Ball_Tracking_Robot_Controller.kicad_pcb
│       │   ├── STM32.kicad_sch
│       │   ├── Power.kicad_sch
│       │   ├── MotorDriver.kicad_sch
│       │   ├── Sensors.kicad_sch
│       │   ├── RaspberryPi.kicad_sch
│       │   └── Debug.kicad_sch
│       ├── Gerbers/                   # Manufacturing files
│       └── BOM/
│
├── firmware/                          # STM32 embedded firmware (STM32CubeIDE project)
│   └── STM32CubeIDE/
│       ├── Core/
│       │   ├── Inc/                   # main.h, motor.h, encoder.h, pid.h,
│       │   │                          # uart_control.h, ultrasonic.h, ...

Getting Started

Hardware

  1. Open hardware/PCB/KiCad/Ball_Tracking_Robot_Controller.kicad_pro in KiCad 9.
  2. Review the schematics/PCB layout, then generate Gerbers/BOM for fabrication and assembly.

Firmware

  1. Open firmware/STM32CubeIDE directly as a project in STM32CubeIDE (targets the STM32G431CBTx).
  2. Flash over the onboard SWD header with an ST-Link.
  3. Connect USART2 (PA2/PA3) to the Raspberry Pi's UART pins.

Vision

  1. On the Raspberry Pi, install dependencies and run software/RaspberryPi/ball_tracking.py (see the Vision section above for setup details).

Testing

Validation performed during hardware/firmware bring-up:

  • Hardware: 3.3V/5V rail verification, UART loopback test, motor driver output check, encoder direction/count check, ultrasonic ranging accuracy check
  • Firmware: PID step-response tuning, encoder speed measurement validation, UART-disconnect failsafe test (confirms motors stop within 500 ms of link loss)
  • Vision: red-ball detection under varying lighting conditions, ball-loss/recovery behavior, end-to-end command latency

Future Improvements

  • Replace single-character UART commands with a packet-based protocol (with CRC error checking)
  • Add an IMU for orientation estimation
  • Add wheel odometry and localization
  • Move from manual PID tuning toward autonomous/self-tuning gains
  • Full autonomous navigation (beyond ball-following)

Parts

56
QtyValueFootprintReferences
9100nFCapacitor_SMD:C_0805_2012MetricC2, C5, C6, C7, C8, C11, C13, C14, C15
810kResistor_SMD:R_0805_2012MetricR3, R4, R5, R8, R11, R12, R13, R14
410nFCapacitor_SMD:C_0805_2012MetricC17, C18, C19, C20
2100μFCapacitor_SMD:C_1206_3216MetricC1, C16
210μFCapacitor_SMD:C_0805_2012MetricC3, C4
2220RResistor_SMD:R_0805_2012MetricR6, R7
25.1kResistor_SMD:R_0805_2012MetricR1, R10
2D_TVSDiode_SMD:D_SMAD2, D3
2SW_PushButton_Switch_SMD:SW_SPST_TL3305ASW1, SW2
1100μFCapacitor_SMD:C_1210_3225MetricC12
11kResistor_SMD:R_0805_2012MetricR2
120kResistor_SMD:R_0805_2012MetricR9
12AFuse:Fuse_1206_3216MetricF1
13V3TestPoint:TestPoint_Pad_D2.0mmTP2
14.7μFCapacitor_SMD:C_0805_2012MetricC9
1470μFCapacitor_SMD:C_Elec_6.3x5.8C10
15VTestPoint:TestPoint_Pad_D2.0mmTP1
1AP2112K-3.3Package_TO_SOT_SMD:SOT-23-5U1
1GNDTestPoint:TestPoint_Pad_D2.0mmTP3
1HC_SR04Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ6
1J_LEFT_ENCODERConnector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ2
1J_RIGHT_ENCODERConnector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ9
1LEDLED_SMD:LED_0805_2012MetricD1
1LEFT_MOTORConnector_JST:JST_VH_B2P-VH_1x02_P3.96mm_VerticalJ3
1RASPBERRY_PIConnector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ5
1RASPBERRY_PI_INConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ7
1RIGHT_MOTORConnector_JST:JST_VH_B2P-VH_1x02_P3.96mm_VerticalJ4
1SMF5V0ADiode_SMD:D_SMFD4
1STM32G431CBTxPackage_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.6x4.6mmU2
1SWD HEADERConnector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_VerticalJ8
1TB6612FNGPackage_SO:HTSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.4x7.8mm_Mask2.4x4.68mmU3
1USB_C_Receptacle_USB2.0_16PConnector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_HorizontalJ1

Questions

How big is the Ball Tracking Robot Controller PCB?

It measures 56.5 × 42 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Ball Tracking Robot Controller use?

56 parts, built around the AP2112K-3.3. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Ball Tracking Robot Controller KiCad files?

From the linusnc183/ball-tracking-robot repository on GitHub, at the commit this page was made from: the board is hardware/PCB/KiCad/Ball_Tracking_Robot_Controller.kicad_pcb and its schematic hardware/PCB/KiCad/Ball_Tracking_Robot_Controller.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Ball Tracking Robot Controller design in my own project?

Its repository states no licence, so its author keeps all rights: ask linusnc183 before reusing the design.

Similar boards

  • 2 layers26 × 30 mmMicrocontroller, the printed circuit board in 3D

    Microcontroller

    FrameworkComputer/ExpansionCards

    Reference designs and documentation to create Expansion Cards for the Framework Laptop

    34 parts1.1kCC-BY-4.0
  • 2 layers53.2 × 24.8 mmPicoLink, the printed circuit board in 3D

    PicoLink

    ErichStyger/mcuoneclipse

    McuOnEclipse projects and examples

    46 parts765
  • 2 layers36 × 20 mmCombined, the printed circuit board in 3D

    Combined

    adamb314/ServoProject

    Getting the most out of your hobby servo

    24 parts545
  • 2 layers25 × 25 mmSmallEncoder, the printed circuit board in 3D

    SmallEncoder

    DDeGonge/OS-ARM

    This arm is not industrial and it will not be fast, but that is not my goal. But it will hopefully be a very nice looking arm fully capable of 6dof motion, cheap enough to keep it accessable but capable enough to…

    17 parts498MIT
  • 4 layers70 × 72 mmSPINC AA Charger, the printed circuit board in 3D

    SPINC AA Charger

    CoretechR/SPINC

    DIY Automatic Battery Charger Using RP2040

    101 parts467GPL-3.0
  • 4 layers60.8 × 61.3 mmOS3M Mouse, the printed circuit board in 3D

    OS3M Mouse

    spoter368/os3m-hardware

    All the hardware needed to build your own OS3M Mouse!

    51 parts399
  • 4 layers74 × 104 mmBeepy, the printed circuit board in 3D

    Beepy

    sqfmi/beepy-hardware

    Beepy Hardware

    160 parts348MIT