Ball Balancing PID System




About: Hi, I'm Johan Link, a 18 years old student living in Switzerland. I love robotics, computers, 3D printing, photography and skate.

This system holds a ball in balance on a plate. A webcam films the system and a python program analyzes the images to find the position of the ball. The python program calculates the tilting of the tray to prevent the ball from falling.

A proportional-integral-derivative (PID) regulator is used to compensate the movements of the ball. The position and speed of the ball are measured by the camera and these measurements are used by the PID regulator in the python program.

Sorry about the English faults, it’s not my mother tongue.

Step 1: PCB

PCB manufacturing

I drew the pcb with eagle then manufactured it.

PCB assembly :

I soldered all smd components without paste flux. I don't recommend you to do like me. Instead, follow this tutorial to solder smd components:

The PCB is powered with a 5-6v 2A power supply.
PCB components :

Role of the PCB

The PCB communicates with the computer continuously. This PCB serves only to control the servomotors.

Step 2: 3D Printing

Warning : inferieur.stl, superieur.stl and plateau.stl files are not 3D printable. inferieur.stl and superieur.stl are made of acrylic. You can use a CNC to make these parts, but you can also cut them yourself in an acrylic sheet because these parts don't have a very complicated design. plateau.stl can be cut in cardboard.

Step 3: Parts Assembly

Step 4: PCB Programming

The PCB contains the same microcontroller as the arduino leonardo. Therefore it can be programmed with Arduino software. The code can be downloaded here.

Before programming the PCB, you have to burn the bootloader: Arduino as ISP and Arduino Bootloaders

Burning the Bootloader

  1. You need an Arduino
  2. Upload the ArduinoISP sketch (arduino software / Examples / ArduinoISP) onto your Arduino board
  3. Make the connections as in the picture
  4. Select "Arduino Leonardo" from the Tools > Board menu.
  5. Select "Arduino as ISP" from Tools > Programmer
  6. Run Tools > Burn Bootloader
  7. The process can take several minutes.
  8. Disconnect all cables.

After this step you have to connect the pcb to the computer with a USB cable and upload this code.

Step 5: Python Program

The most important code is in the computer that controls the system. You can find the code here.

How does it work. The first step is to find the position of the ball. The camera sends the computer live video. The python program receives the video and has to process it. I use the OpenCV library to do the image processing. The program detects the ball thanks to its color. Here the ball is orange, the program will then average the position of all the orange pixels of the image to find the position of the ball. With OpenCV all this is quite simple. I recommend this tutorial for color recognition with OpenCV:

Now that we have the ball position we can calculate the inclination of the board with PID (proportional, integral, and derivative) control. The regulation is done in three stages. The first step is the simplest. Imagine that we want to stabilize the ball in the center of the board. The further away the ball is from the center, the more it will be necessary to tilt the board. Then you have to measure the speed of the ball: the faster the ball moves away from the center, the more you have to tilt the board.

Step 6: Conclusion

Thanks for reading! If you have any questions, do not hesitate to write a comment.

You can also vote for me in the PCB contest.

PCB Contest

First Prize in the
PCB Contest



    • Arduino Contest 2019

      Arduino Contest 2019
    • Trash to Treasure

      Trash to Treasure
    • Tape Contest

      Tape Contest

    153 Discussions


    7 days ago

    Hi Johan,
    I constructed your Ball Balancer, but I am getting the following error code thrown up every time, can you help?
    cv2.error: OpenCV(4.0.0) /Users/travis/build/skvark/opencv-python/opencv/modules/imgproc/src/shapedescr.cpp:272: error: (-215:Assertion failed) npoints >= 0 && (depth == CV_32F || depth == CV_32S) in function 'contourArea'

    1 reply
    Johan Linkletitgo

    Reply 7 days ago

    I noticed that you have to replace line 421 ("cnts = cnts [0] if imutils.is_cv2 () else cnts [1]") by "cnts = imutils.grab_contours (cnts)" when using OpenCV 4. This solution works for me, I hope it will work for you too.


    22 days ago

    Hi Johan, I have a question regarding the camera. I've included a image so that you can tell me if the frame width is OK. Seems a bit wide to me but I may be wrong.
    I installed everything on a Raspberry pie thanks to Adrian @pyimagesearch.

    7 replies
    Johan LinkAreJayJay

    Reply 18 days ago

    Does the system work ?
    Is there latency with the Raspberry pie?
    It looks nice!

    AreJayJayJohan Link

    Reply 17 days ago

    Hi Johan,
    I haven't completed my tests yet, but it looks promising. I can see some latency, so that can be an issue I guess. FPS is 24.
    What do you think?

    AreJayJayJohan Link

    Reply 15 days ago

    Hi Johan,
    Is the default ball color supposed to be orange ?
    I find that the color is blue on my RPi, which is kind of the complementary color.
    Is this normal?

    Johan LinkAreJayJay

    Reply 15 days ago

    There is no default color. You have to right click on a color to select it.

    AreJayJayJohan Link

    Reply 14 days ago

    Hi Johan,

    OK for the right mouse click to store the color but what are the HSV sliders for on the interface program?


    Reply 20 days ago

    Hi Johan, please forget that question, I simply forgot to consider that the 'plateau' will raise to a position that centers it.


    20 days ago

    Great work!!
    Best example to demonstrate the effectiveness of the PID control algorithm.


    2 months ago

    John, again great work, very impressive.

    I had to make some modifications to get the
    python code to work my Windows 10 system.

    1) Line 527: Removed background=black so the button will show.
    BConnect = tk.Button(controllerWindow, text ="Connect", command = connectArduino)

    2) I’m using an Arduino Nano for servo control. My system does not return “Arduino” in the port description. I printed the port and it returns “CH340”. I modified line 273 in the connectArduino() module to account for it:
    if "CH340" in p.description:

    Hope this will help anyone else using Windows.

    3 replies
    Johan LinkJerryolsen

    Reply 2 months ago

    Thank you for those modifications that will help many people !

    Araujo04Johan Link

    Reply 23 days ago

    Hello! Beautiful design, congratulations! I'm reproducing.
    I'm using an Arduino Uno with Ubuntu Linux system. I can already do the mapping of the ball, but I can not establish the connection with the arduino. I made the impression of the port "ttyACM0" and replaced in line 273 of the code "Arduino", but it did not work. Any tips to help?

    Johan LinkAraujo04

    Reply 22 days ago


    Did you write "print(p.description)" to print the port ?


    8 weeks ago

    Bonjour John,
    Ton projet est vraiment fascinant. J'ai tout imprimé les pièces déjà. Je veut maintenant m'attaquer à la partie logiciel. Python c'est assez nouveau pour moi et j'aimerais savoir quel version de python tu utilise ainsi que les version des diférents modules comme Numpy, OpenCV, Tkinter, PIL(Pillow) etc. Merci d'avance, car cela m'aiderais beaucoup. Mon objectif est de le rendre tout a fait autonome en implantant le tout sur un RPI,

    2 replies
    Johan LinkAreJayJay

    Reply 8 weeks ago


    Merci pour le commentaire.
    Voila les versions des librairies que j'utilise:
    Python : 3.6.4
    Numpy : 1.14.5
    OpenCV : 3.3.0
    Tkinter : 8.5
    PIL : 5.1.0
    imutils : 0.4.6
    serial : 3.4

    N'hésite pas à m'envoyer une photo du projet, ça m'intéresse :)
    AreJayJayJohan Link

    Reply 4 weeks ago

    Effectivement, cela fait la difference, merci beaucoup. C'est le problème avec toutes ces versions. Maintenant je n'arrive pas a connecter mon Arduino l'interface donne toujours "L'Arduino n'est pas connecté" Est-ce qu'il faut un Arduino particulier? J'ai essayé des Uno, Mega et Duemilanova.