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()