Skip to content

Raspberry pi

Control webcam with servo motor and raspberry pi – Opencv with Python

Access community, courses and source codes
Logo

AI Vision Academy

Access the code of this tutorial, computer vision courses and an exclusive community on AI Vision Academy

  • Access to over 50+ source codes from Pysource.com/blog
  • Dedicated video courses about computer vision
  • Access to an exclusive community of professionals
  • Real-World AI Projects – Get hands-on experience building practical AI Computer Vision solutions with a structured path.
  • Monthly Coaching Calls – get support and any of your questions answered

Subscribe to our newsletter to learn more

We will learn in this tutorial how to control the webcam using a servo motor and the raspberry pi.

Our goal is to follow an object with the webcam which is moved by the servo motor like in the image below.

In this tutorial I will cover only the Opencv and Python part, but not the technical side about configuring and using the servo motor.
Keep in mind that to run the servo motor with the raspberry pi you need an extra board (like the one on the image below).

The important thing to know is that the characteristic of the servo motor I’m using is that moves with an angle of 180 degrees and we simply need to give the angle to the servo to move.

On the lines 1 and 2 we import the libraries Opencv and Numpy.
On line 3 we import the library to run the servo motor.

import cv2
import numpy as np
from PCA9685 import PCA9685

Then we create an object to run and configure the settings of the servo motor.
First two lines are technical details regaring my specific configuration while in the thirt line we set the position of the servo with index 0 to 90° which is the center in my case.

pwm = PCA9685(0x40, debug=False)
pwm.setPWMFreq(50)
pwm.setServoPosition(0, 90)

We then load the cap with opencv to take the frames from the screen and also we set a small camera resolution so that the raspberry pi can process the image much faster and the movement of the servo will be smoother.

cap = cv2.VideoCapture(0)

# Set camera resolution
cap.set(3, 480)
cap.set(4, 320)

_, frame = cap.read()
rows, cols, _ = frame.shape

At this point we define the default values that are the center position of the object to track, the center of the screen and the starting position of the servo motor.

x_medium = int(cols / 2)
center = int(cols / 2)
position = 90 # degrees

Now it’s everything ready to run the while loop and start detecting the red object.
So we detect the red object with the HSV Color space.

while True:
    _, frame = cap.read()
    hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    
    # red color
    low_red = np.array([161, 155, 84])
    high_red = np.array([179, 255, 255])
    red_mask = cv2.inRange(hsv_frame, low_red, high_red)
    _, contours, _ = cv2.findContours(red_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=lambda x:cv2.contourArea(x), reverse=True)

Once the object is detected by its color we need to find its center position. An easy way will be to bound the contours with a rectangle and then find the center of that rectangle.

We will find the center only of the x as we are interested to move the camera only horizontally and not vertically.

    for cnt in contours:
        (x, y, w, h) = cv2.boundingRect(cnt)
        
        x_medium = int((x + x + w) / 2)
        break
    
    cv2.line(frame, (x_medium, 0), (x_medium, 480), (0, 255, 0), 2)

Now using these values we can finally move the servo motor.

How do we now where and when to move it?
Simple. We know the center of the screen, we know that the center of the servo is 90° and we know the position of the object.
If the object is on the left side of the center we change the position by adding a degree and if it’s on the right of the center we remove a degree.

# Move servo motor
if x_medium < center -30:
    position += 1
elif x_medium > center + 30:
    position -= 1
pwm.setServoPosition(0, position)

We then show everything on the screen.

    cv2.line(frame, (x_medium, 0), (x_medium, 480), (0, 255, 0), 2)
    
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1)
    
    if key == 27:
        break
    
cap.release()
cv2.destroyAllWindows()

16 comments

  1. Excellent tutorial, thanks for doing these really appreciate your time and effort going into these videos.
    Don’t forget to put
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    at the start, as ive had issues in the past running scripts on the pi

  2. Hi Sergio,
    What steps do we have to follow in order to install CV2 and NUMPY in our Raspberry Pi 3?
    Also, how can I get the coordinates of the center of the object to the center of the scree? Lets say that the center of the screen is the origin (point 0,0) and the software provides the the pixels coordinates of the center of the object.
    Thanks bro!!!
    Max

  3. hi sir
    thank you for your tutorial it was very good.
    I have problem during writing your codes.

    _, contours, _ = cv2.findContours(red_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    ValueError: not enough values to unpack (expected 3, got 2)
    [ WARN:0] terminating async callback

    I don’t know what is wrong. can you help me out ?
    thanks

    1. IF YOU CAN’T USE THIS:
      _, contours, _ = cv2.findContours(red_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

      TRY TO USE THIS:
      contours, _ = cv2.findContours(red_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

  4. Hello sir. I’m Riski from Indonesia. I’m a student. I need a tutorial how to install PCA9685 library. Can you show me for that sir? Or can you give a link address? Thankyou… Sorry for this.

  5. hi sir
    I tried to build it. I used the 5-megapixel raspberry pi camera module.
    I just copied your code, but it’s not working well.
    can you tell what is wrong with running code with camera module

  6. For those wondering about “from PCA9685 import PCA9685”, if you watch the video you will note that at minute marker 17:00 Sergio specifically calls that library out as proprietary. That said, the problem is easily remedied via the Adafruit PCA9685 Circuit Python libraries. Look at the examples in that library and you should be able to figure it out as it supports a full range of servo types and setting angles is as easy as pwm.angle = 90. Also, if you are using 2 servos in a pan/tilt setup its super easy to extend Sergio’s code to support the y axis for a greater range of motion. Thanks Sergio.

    1. hey im stuck on this importing library part i installed the adafruit library
      but im not able to figure out the from and import part
      i get a error cannot import name pca9685 from pca9685
      i tried to change it with same command in example but it sill shows the same error

  7. What if we want to do this in two directions at the same time with two servo motors?
    I now have the line in the vertical direction. Do I want to have the horizontal direction at the same time?
    Can you help me?

Join the discussion