Raspberry Pi Camera Panning with a Servo Motor

picamera_MG90S_constructed_II.JPG

“As an Amazon Associates Program member, clicking on links may result in Maker Portal receiving a small commission that helps support future projects.”

 

The Raspberry Pi is a powerful computer that enables users to harness the power of a microcomputer while also taking advantage of general-purpose inputs/outputs (GPIOs). The inputs and outputs make it easy for engineers and makers to measure, control, and monitor devices. In this tutorial, a Raspberry Pi's GPIO pins will be used to power and control an MG90S servo motor that requires pulse-width modulation (PWM) to control the angular rotation about its axis. The basics of PWM will be first explored, then an application with the MG90S. Python will be used along with some built-in libraries that make pulse-width modulation easy to understand. Lastly, an application to rotating a camera about a 2-D plane will be presented, which has applications in security, photography, or machine learning applications in image processing.


Pulse-Width Modulation

Raspberry Pi uses its general-purpose input/output (GPIO) pins to control various sensors, motors, and indicators. One way in which the RPi is able to control motors and indicators is through pulse-width modulation (PWM) - where a constant voltage modulates from on to off over a given period. The inverse of the period represents the frequency of the PWM cycle (this will be used in the Python code). The pulse width is associated with the amount of time that the voltage is high, sometimes called the duty cycle. The duty cycle represents the length of time given by manufacturers which alters the speed, angle, brightness, and other correlative variable of a given device. For example, the datasheet for the servo motor used in this tutorial (MG90S) states that a duty cycle of 1.5 ms turns the servo motor to 0 degrees (centered), a 1.0 ms duty cycle turns the motor to -90 degrees, and a duty cycle of 2.0 ms turns the motor to +90 degrees.

An animation of varying duty cycles is shown below, ranging from a 2ms pulse (10% duty cycle) to a 100% duty cycle (no modulation, always high voltage). Note that the PWM period given here is 20ms (which matches the MG90S period) and does not change; only the modulated pulse width changes (duty cycle), which relates to different functions of a servo motor or LED, etc. For example, the MG90S uses pulses ranging from roughly 1ms - 2ms that change the angle of the motor between 0 - 180 degrees (or -90 degrees to +90 degrees).

The pulse wave can be modeled using the Fourier series expansion of a rectangular wave (see more on this at Wikipedia):

pulse_wave_fourier_PWM_eqn.png

where τ is the pulse duration, T is the period of the PWM signal (20ms here), n is the fourier sum iterable, and t is the time step. Below is the implemented PWM signal using the Fourier representation:

PWM_duty_cycle_animation_compressed.gif

The pulse-width modulation shown in the animation above is the basis for how the Raspberry Pi will control the servo motor used in this tutorial.

For the MG90S servo motor used here, the datasheet states that the servo can be positioned to 0° with a 1ms pulse, 90° with a 1.5 ms, and 180° with a 2ms pulse. However, after some tests with the MG90S and the Raspberry Pi, it was found to be closer to 0.4ms, 1.4ms, and 2.4ms, for the same series of angular movements. Some minor adjustments may be necessary to characterize an individual motor, however, most of the MG90S motors rotate from 0°-180° with 0.4ms/0.5ms - 2.4/2.5ms pulse widths. This is shown below:

 
servo_pwm_duty_cycles_w_servos.png
 

These values will be used in the Python code on the Raspberry Pi to control the servo motor and direct the motor to each desired angle.


Parts List and Wiring with Raspberry Pi

Pulse-width modulation using a Raspberry Pi is simple because of its GPIO pins and integration with the Python programming language. To start, the Raspberry Pi will be wired to an MG90S servo motor for PWM control. The parts list for the entire tutorial is given below:

  1. Raspberry Pi 4 Computer - $65.00 [Amazon], $55.00 [2GB from Our Store]

  2. MG90S Micro Servo - $7.00 [Our Store]

  3. Jumper Wires - $0.45 (3 pcs) [Our Store]

  4. Raspberry Pi Camera (with Standard Long Cable) - $18.00 [Our Store]

MG90S Micro Servo
$8.00
Quantity:
Add To Cart

The GPIO pins are defined as follows on the Raspberry Pi 4 (most of which are consistent with other Raspberry Pi boards):

Image Courtesy of Raspberry Pi Foundation

Image Courtesy of Raspberry Pi Foundation

Thus, the MG90S can be wired to any of the available GPIO pins. It is common to select a GPIO pin which does not have a parentheses next to it, for example - it is safe to use pins: 17,27,22,5,6,26,,23,24,25,16. Here, GPIO 13 will be used - as it is the designated hardware PWM pin on the left-hand side of the RPi header. The reason we specifically want a hardware PWM pin is that the hardware-defined pins tend to have less jitter and are generally faster. The wiring diagram for controlling the MG90S servo via a Raspberry Pi is given below:

 
MG90S_wiring_RPI.png
 

Using the three-wire wiring method and the PWM protocols discussed in the previous section, we can fully control the MG90S servo using Python on the Raspberry Pi; the methods for which are discussed in the next section.


PWM on the Raspberry Pi - Controlling the Servo

The MG90S servo motor will be henceforth controlled using solely the orange wire (the red and brown pins are 5V power and ground, respectively). Thus, by modulating the voltage on GPIO 13 of the RPi, which is connected to the servo’s orange wire, the MG90S will turn as prescribed by the pulse widths ranging from 2% - 12%, depicted in the diagram above. In Python, this process can be coded as follows:

# Raspberry Pi + MG90S Servo PWM Control Python Code
#
#
import RPi.GPIO as GPIO
import time

# setup the GPIO pin for the servo
servo_pin = 13
GPIO.setmode(GPIO.BCM)
GPIO.setup(servo_pin,GPIO.OUT)

# setup PWM process
pwm = GPIO.PWM(servo_pin,50) # 50 Hz (20 ms PWM period)

pwm.start(7) # start PWM by rotating to 90 degrees

for ii in range(0,3):
    pwm.ChangeDutyCycle(2.0) # rotate to 0 degrees
    time.sleep(0.5)
    pwm.ChangeDutyCycle(12.0) # rotate to 180 degrees
    time.sleep(0.5)
    pwm.ChangeDutyCycle(7.0) # rotate to 90 degrees
    time.sleep(0.5)

pwm.ChangeDutyCycle(0) # this prevents jitter
pwm.stop() # stops the pwm on 13
GPIO.cleanup() # good practice when finished using a pin

One thing to note right away is that in Python, there is a library that is specifically designed for the Raspberry Pi GPIO pins (see the ‘import RPi.GPIO’ line). This makes handling the PWM much easier, as the library dedicates the pin directly to the function of modulating amplitudes to control devices such as servo motors, LEDs, and other lower power DC devices.

The code can be summarized as a loop that carries out the following:

  1. Setup GPIO 12 as an output

  2. Start PWM on GPIO 12 at 50 Hz (20ms period)

  3. Move the servo to 90°

  4. loop three times, doing the following:

    1. rotate to 0°, wait 0.5 seconds

    2. rotate to 180°, wait 0.5 seconds

    3. rotate to 90°, wait 0.5 seconds

  5. Stop PWM on GPIO 13, clear designation on GPIO 13

A GIF carrying out the process outlined above is given below:

 

MG90S_RPI_servo_demo.gif

Raspberry Pi + MG90S Servo Demonstration

 

Camera Panning Using a Servo

One of the most used applications of servo motor control is through rotating the field of view of a camera. Many security cameras use servos in both the horizontal and vertical directions. Here, the focus is on the horizontal, which can be thought of as a panning of the camera from left to right. The MG90S is perfect for this application, as it is fairly small, requires minimal power, and is limited to rotations in a 180° plane. The Raspberry Pi is particularly suitable for this project, because it has a dedicated camera port for the PiCamera, it has a hardware-defined PWM pin (demonstrated in the previous section), and it’s able to analyze images at a fairly efficient rate - meaning that the panning camera application could be useful for real-world situations such as motion tracking, security monitoring, or adaptive object recognition.

Below is a photo of the PiCamera + Servo Camera Pan bundle on our site, which allows users to control the panning of the PiCamera using a 3D printed frame for the MG90S servo motor.

There is minimal attention dedicated to the PiCamera portion of the code given below, however, there are copious tutorials available throughout the internet. The PiCamera API documentation is very useful, along with the introductory tutorials on the Raspberry Pi site. In short, the PiCamera is being used to demonstrate the movement of the servo controlled by the RPi. In reality, the movements of the servo are too jittery for a true application in camera panning, however, the panning is beneficial for spaced-out movements. This means that if motion is detected, the servo movement is useful for following the motion and then stopping to record or view the video. That being said, the code is presented and a video demonstration of the moving servo, along with the actual video recorded by the PiCamera affixed to the servo.

# Raspberry Pi + MG90S Servo PWM Control Python Code
#
#
from picamera import PiCamera
import RPi.GPIO as GPIO
import numpy as np
import time,os

#####################################
# setup picamera for monitoring
#####################################
#
camera = PiCamera() # start picamera

# camera presets
camera.resolution = (1920,1080)
camera.vflip = True
camera.hflip = True
camera.iso = 100
time.sleep(2)
camera.shutter_speed = camera.exposure_speed
camera.exposure_mode = 'off'
g = camera.awb_gains
camera.awb_mode = 'off'
camera.awb_gains = g
camera.framerate = 30

#####################################
# setup the GPIO pin for the servo
#####################################

servo_pin = 13
GPIO.setmode(GPIO.BCM)
GPIO.setup(servo_pin,GPIO.OUT)
pwm = GPIO.PWM(servo_pin,50) # 50 Hz (20 ms PWM period)

# mapping duty cycle to angle
pwm_range = np.linspace(2.0,12.0)
pwm_span = pwm_range[-1]-pwm_range[0]
ang_range = np.linspace(0.0,180.0)
ang_span = ang_range[-1]-ang_range[0]
def angle_to_duty(ang):
    # rounding to approx 0.01 - the max resolution
    # (based on 10-bits, 2%-12% PWM period)
    print('Duty Cycle: '+str(round((((ang - ang_range[0])/ang_span)*pwm_span)+pwm_range[0],1)))
    return round((((ang - ang_range[0])/ang_span)*pwm_span)+pwm_range[0],1)

# optimizing the delay to reduce jitter
def cust_delay(ang,prev_ang):
    # minimum delay using max speed 0.1s/60 deg
    return (10.0/6.0)*(abs(ang-prev_ang))/1000.0

def change_to_angle(prev_ang,curr_ang):
    pwm.ChangeDutyCycle(angle_to_duty(curr_ang))
    camera.wait_recording(cust_delay(curr_ang,prev_ang))
    pwm.ChangeDutyCycle(0) # reduces jitter
    return

prev_ang = 75 # angle to start
pwm.start(angle_to_duty(prev_ang)) # start servo at 0 degrees

cycles = np.linspace(0.0,180.0,20) # duty cycles vector
cycles = np.append(np.append(0.0,cycles),np.linspace(180.0,0.0,20)) # reverse duty cycles

#####################################
# continual scan 0 -> 180 -> 0 [degrees]
# + recording video to a file
#####################################
#
t0 = time.localtime()
vid_name = '{0}_{1}_{2}_{3}_{4}'.format(t0.tm_year,t0.tm_yday,t0.tm_hour,t0.tm_min,t0.tm_sec)
lib_folder = './picamera_videos/'
if os.path.isdir(lib_folder)==False:
    os.mkdir(lib_folder)

camera.start_preview()
camera.start_recording(lib_folder+vid_name+'.h264')
while True:
    try:
        for ii in range(0,len(cycles)):
            change_to_angle(cycles[ii-1],cycles[ii])
            camera.wait_recording(0.5)
           
    except KeyboardInterrupt:
        camera.stop_preview()
        camera.stop_recording()
        break # if CTRL+C is pressed

#####################################
# cleanup RPi, camera, and servo pin
#####################################
#
pwm.ChangeDutyCycle(0) # this prevents jitter
pwm.stop() # stops the pwm on 13
GPIO.cleanup() # good practice when finished using a pin

Video demonstration of the panning camera:

 

Seen from the perspective of the picamera:

 
RPi_angle_servo_movement_picamera.gif

PiCamera Recording Atop Servo Moving from 75

°

to 105

°

 

Note: The MG90S servo can be jittery when powered by the Raspberry Pi's 5V pin (and 3.3V, though less so). One recommendation is to either power via the 3.3V (less torque, which is okay for the PiCamera). Another option is to independently power the servo from another DC power source, which will produce fewer spikes.

 

The video above uses the PiCamera V1.3 that is sold on our site and is included in the MG90S servo PiCamera kit. The video mostly mimics the process coded above, except that the code uses rotations over the entire 0° - 180° range of the servo.


Conclusion

MG90S_RPI_blog_main_darker.JPG

Raspberry Pi computers have become a great resource in the engineering education community. The general-purpose inputs/outputs are beneficial for learning about sensors, motors, and indicators - all without the need for external data acquisition cards or control devices. This is the power of the RPi. It allows cross-platform learning, ranging from electronics, computer programming, digital signal processing, image processing, and many others. In this tutorial, the RPi was used to introduce pulse-width modulation (PWM) and apply it to controlling a servo motor. Then, the servo was used to control the panning of a camera - which was also controlled by the native camera port on the Raspberry Pi. This tutorial is a simple introduction that can be expanded into a full 360° controllable camera project, or a project involving a robotic arm, or any project involving servo motors or PWM-controlled devices.

 
Citation for This Page:
 

See More in Raspberry Pi and Motors: