How to Control Servo Motors Using Raspberry Pi and the pigpio Library for Precision Robotics

Micro Servo Motor with Raspberry Pi / Visits:6

When it comes to building a precision robotics project on a budget, few combinations are as powerful and accessible as the Raspberry Pi paired with a micro servo motor. Whether you’re constructing a robotic arm, a pan-tilt camera system, or a tiny animatronic creature, the ability to control a servo with sub-degree accuracy can make or break your project. And while there are many ways to drive a servo from a Pi—using the built-in PWM hardware, software PWM, or even dedicated servo drivers—the pigpio library stands out as the gold standard for achieving smooth, jitter-free, and high-resolution control.

In this guide, I’ll walk you through everything you need to know to control micro servo motors using a Raspberry Pi and the pigpio library. We’ll cover the hardware setup, the software installation, the theory behind servo PWM signals, and, most importantly, the code that will let you command your servo with precision down to a single microsecond. By the end, you’ll have a solid foundation for integrating micro servos into any precision robotics application.

Why Micro Servo Motors Are the Heart of Small-Scale Robotics

Before we dive into the technical details, let’s take a moment to appreciate why micro servo motors have become the go-to actuator for hobbyist and educational robotics. A micro servo, such as the popular SG90 or MG90S, is a small, lightweight motor with an integrated gearbox, a feedback potentiometer, and a control circuit. All you need to provide is a 5V power supply, a ground connection, and a PWM (Pulse Width Modulation) signal to tell the servo where to move.

The key advantage of a micro servo is its closed-loop control. Unlike a simple DC motor where you have to guess the position based on runtime, a servo constantly reads its own position and adjusts the motor to reach and hold the commanded angle. This makes them ideal for tasks that require repeatable, precise motion—like pointing a camera, opening a gripper, or actuating a joint in a robotic arm.

But here’s the catch: the quality of that control depends almost entirely on the PWM signal you feed it. A poorly generated signal—one with jitter, timing errors, or inconsistent pulse widths—will result in a servo that buzzes, overshoots, or drifts. That’s where the Raspberry Pi and pigpio come in.

The Raspberry Pi’s PWM Problem

The Raspberry Pi’s built-in hardware PWM is actually quite good for many applications, but it has a significant limitation when driving multiple servos: the hardware PWM channels are shared with the audio output and are limited to just two independent channels. If you want to control four, eight, or sixteen servos (as many robotics projects do), you quickly run out of hardware options.

Software PWM, on the other hand, can drive any GPIO pin, but it suffers from timing inconsistencies because the Linux kernel is not a real-time operating system. If the CPU gets busy with other tasks, the PWM signal can become distorted, causing the servo to twitch or lose position.

The pigpio library solves this problem by offloading the PWM generation to the Pi’s DMA (Direct Memory Access) controller. This means the PWM signal is generated in hardware, independent of the CPU load, and can be assigned to any GPIO pin. The result is a rock-solid, high-resolution signal that can drive dozens of servos simultaneously with microsecond precision.

Hardware Setup: What You’ll Need

Let’s get physical. Here’s a list of components for this tutorial:

  • Raspberry Pi (any model from Pi 3 onwards works well; Pi Zero W is also fine for simple projects)
  • Micro servo motor (SG90 or MG90S recommended for testing)
  • 5V power supply (a dedicated power source for the servo; do not rely on the Pi’s 5V rail for more than one or two small servos)
  • Breadboard and jumper wires
  • 100 µF capacitor (optional but recommended to filter power supply noise)

Wiring Diagram

The wiring is straightforward. A micro servo typically has three wires:

  • Red (middle wire) – 5V power
  • Brown or Black – Ground
  • Orange, Yellow, or White – Signal (PWM)

Connect them as follows:

  1. Servo power (red) to the 5V pin of your external power supply.
  2. Servo ground (brown) to the ground of the external power supply and to a ground pin on the Raspberry Pi. This common ground is essential for the signal to work.
  3. Servo signal (orange) to a GPIO pin on the Pi, e.g., GPIO18 (physical pin 12).

Important: If you’re powering the servo from the Pi’s 5V pin, do not connect more than one or two micro servos. The Pi’s voltage regulator can only supply about 1.5A total, and a single servo can draw 500mA or more under load. For anything beyond basic testing, use a separate 5V supply.

Setting Up the pigpio Library

The pigpio library is not installed by default on Raspberry Pi OS. You’ll need to install it and start the daemon before you can use it.

Step 1: Install pigpio

Open a terminal on your Pi and run:

bash sudo apt update sudo apt install pigpio python3-pigpio

This installs the pigpio C library and the Python wrapper.

Step 2: Start the pigpio Daemon

The pigpio library works through a background daemon called pigpiod. You can start it manually:

bash sudo pigpiod

To make it start automatically on boot, enable the service:

bash sudo systemctl enable pigpiod sudo systemctl start pigpiod

Step 3: Verify the Installation

Run a quick test to ensure the daemon is running:

bash pigs h

If you see a list of commands, you’re good to go.

Understanding Servo PWM: The Microsecond Matters

To control a servo with precision, you need to understand the PWM signal it expects. Most analog micro servos, like the SG90, expect a 50 Hz signal (a period of 20 milliseconds). The position is determined by the width of the positive pulse within that period.

  • 1.0 ms pulse → 0 degrees (full left)
  • 1.5 ms pulse → 90 degrees (center)
  • 2.0 ms pulse → 180 degrees (full right)

These values are typical, but they vary between brands and even between individual servos. The MG90S, for example, might use a range of 0.5 ms to 2.5 ms for a full 180-degree sweep. The key is to calibrate your servo by finding its actual minimum and maximum pulse widths.

Pulse Width Resolution

The pigpio library allows you to set the pulse width in microseconds. This means you can theoretically command a servo to 1,500 µs (center) with a resolution of 1 µs. Since a typical servo has a range of 1,000 µs (from 1.0 ms to 2.0 ms) covering 180 degrees, that gives you a resolution of 0.18 degrees per microsecond. In practice, the servo’s internal potentiometer and gearbox limit the actual precision to about 1–2 degrees, but the signal itself is capable of much finer control.

Writing the Control Code with pigpio

Now let’s write some Python code to control the servo. We’ll start with a simple sweep, then move on to precise angle control.

Basic Servo Sweep

Create a file called servo_sweep.py:

python import pigpio import time

Connect to the pigpio daemon

pi = pigpio.pi() if not pi.connected: print("Failed to connect to pigpio daemon") exit()

Set GPIO 18 as servo output

SERVOPIN = 18 pi.setmode(SERVO_PIN, pigpio.OUTPUT)

Set PWM frequency to 50 Hz (period = 20 ms)

pi.setPWMfrequency(SERVO_PIN, 50)

Enable the servo by setting a pulse width

pi.setservopulsewidth(SERVO_PIN, 1500) # Center position time.sleep(1)

Sweep from 0 to 180 degrees

for pulse in range(1000, 2001, 10): # 1 ms to 2 ms in 10 µs steps pi.setservopulsewidth(SERVO_PIN, pulse) time.sleep(0.02)

Sweep back

for pulse in range(2000, 999, -10): pi.setservopulsewidth(SERVO_PIN, pulse) time.sleep(0.02)

Stop the servo (remove the signal)

pi.setservopulsewidth(SERVO_PIN, 0)

Cleanup

pi.stop()

Run the script:

bash python3 servo_sweep.py

You should see the servo sweep smoothly from one end to the other and back. Notice the use of set_servo_pulsewidth()—this is the pigpio function that sets the pulse width in microseconds. The library handles all the timing internally.

Calibrating Your Servo’s Range

Not all servos respond identically to the standard 1.0–2.0 ms range. Some may reach their mechanical limits before 2.0 ms, while others may need a wider range. To avoid damaging your servo (by forcing it past its stop), you should calibrate it.

Here’s a calibration script that lets you manually find the minimum and maximum pulse widths:

python import pigpio import time

pi = pigpio.pi() SERVOPIN = 18 pi.setmode(SERVOPIN, pigpio.OUTPUT) pi.setPWMfrequency(SERVOPIN, 50)

print("Calibrating servo range.") print("Enter a pulse width in microseconds (e.g., 1500 for center).") print("Type 'min' to find the minimum, 'max' to find the maximum.") print("Type 'q' to quit.")

while True: cmd = input("Pulse width: ") if cmd == 'q': break elif cmd == 'min': for pw in range(1000, 500, -10): # Sweep down from 1.0 ms pi.setservopulsewidth(SERVOPIN, pw) time.sleep(0.05) print(f"Trying {pw} µs") print("Minimum test complete. Set a new pulse to stop.") elif cmd == 'max': for pw in range(2000, 2500, 10): # Sweep up from 2.0 ms pi.setservopulsewidth(SERVOPIN, pw) time.sleep(0.05) print(f"Trying {pw} µs") print("Maximum test complete. Set a new pulse to stop.") else: try: pw = int(cmd) if 500 <= pw <= 2500: pi.setservopulsewidth(SERVO_PIN, pw) print(f"Set to {pw} µs") else: print("Out of range (500–2500)") except ValueError: print("Invalid input")

pi.setservopulsewidth(SERVO_PIN, 0) pi.stop()

Run this script and manually find the lowest pulse width that moves the servo to one mechanical stop and the highest pulse width that moves it to the other. Write these values down—you’ll use them for precise angle mapping.

Mapping Pulse Width to Angle

Once you know your servo’s min and max pulse widths, you can create a function that converts an angle (0–180) to the corresponding pulse width. For example, if your servo has a min of 600 µs and a max of 2400 µs:

python def angle_to_pulse(angle, min_pw=600, max_pw=2400): # Map angle (0–180) to pulse width (min_pw–max_pw) return int(min_pw + (angle / 180.0) * (max_pw - min_pw))

Now you can command the servo to any angle with confidence:

python target_angle = 45 pulse = angle_to_pulse(target_angle) pi.set_servo_pulsewidth(SERVO_PIN, pulse)

Advanced Techniques for Precision Robotics

Now that you have basic control, let’s explore some advanced techniques that leverage pigpio’s capabilities for precision robotics.

Smooth Motion with Ramping

Abruptly commanding a servo to jump from 0 to 180 degrees can cause mechanical stress and overshoot. For smooth motion, implement a ramping function that moves the servo in small increments:

python def moveservosmooth(pi, pin, targetpulse, step=10, delay=0.02): currentpulse = pi.getservopulsewidth(pin) if currentpulse == 0: currentpulse = 1500 # Assume center if no signal

step = step if target_pulse > current_pulse else -step  for pulse in range(current_pulse, target_pulse, step):     pi.set_servo_pulsewidth(pin, pulse)     time.sleep(delay)  # Set final position precisely pi.set_servo_pulsewidth(pin, target_pulse) 

This function reads the current pulse width (if available) and moves toward the target in small steps. The step and delay parameters control the speed and smoothness.

Controlling Multiple Servos Simultaneously

One of pigpio’s biggest strengths is its ability to control many servos at once without jitter. Here’s an example that drives four servos:

python import pigpio

pi = pigpio.pi()

servo_pins = [18, 19, 20, 21] positions = [1000, 1500, 2000, 1500] # Different positions

for pin in servopins: pi.setmode(pin, pigpio.OUTPUT) pi.setPWMfrequency(pin, 50)

Set all servos simultaneously

for pin, pos in zip(servopins, positions): pi.setservo_pulsewidth(pin, pos)

Wait and then stop all

time.sleep(2) for pin in servopins: pi.setservo_pulsewidth(pin, 0)

pi.stop()

Because pigpio uses DMA, all four servos receive their signals with consistent timing. This is impossible to achieve with software PWM on a standard Pi.

Real-Time Position Feedback

Some micro servos (like the MG996R or digital servos) offer a feedback signal that reports the actual position. However, most hobbyist micro servos do not. If you need position feedback, you can add a potentiometer or encoder to the output shaft and read it with an ADC (like the MCP3008) connected to the Pi’s SPI bus.

Alternatively, you can use a continuous rotation servo modified for position feedback, but that’s a topic for another article.

Using Waveforms for Complex Sequences

pigpio allows you to create waveforms—precise sequences of GPIO events that can be played back with hardware timing. This is useful for complex robotic choreography where you need multiple servos to move in a coordinated pattern.

Here’s a simple example that creates a waveform to move a servo from 1000 µs to 2000 µs over 1 second:

python import pigpio

pi = pigpio.pi() SERVO_PIN = 18

Create a waveform

wf = [] for i in range(1000, 2001, 10): # Each step: set pulse width, then wait 10 ms wf.append(pigpio.pulse(0, 1 << SERVO_PIN, 10)) # Not exactly correct—see pigpio docs for proper waveform creation

The above is simplified. For actual waveform creation, you need to use

pi.waveaddnew() and pi.wave_create(). Refer to the pigpio documentation

for detailed waveform API usage.

Waveforms are powerful but complex. For most robotics projects, the set_servo_pulsewidth() method is sufficient.

Troubleshooting Common Issues

Even with pigpio, you may encounter problems. Here are the most common ones and their solutions.

Servo Jitters or Buzzing

  • Cause: Power supply noise or insufficient current.
  • Fix: Add a 100–470 µF capacitor between the servo’s power and ground lines. Use a dedicated 5V supply rated for at least 2A.

Servo Doesn’t Move

  • Cause: The pigpio daemon isn’t running, or the GPIO pin is wrong.
  • Fix: Run sudo pigpiod and verify with pigs h. Check your wiring and pin number.

Servo Moves Erratically

  • Cause: Ground loop or interference from other electronics.
  • Fix: Ensure the servo ground is connected to the Pi’s ground. Keep servo wires away from high-current motor wires.

Pulse Width Range Is Off

  • Cause: The servo’s internal potentiometer may have a different range than expected.
  • Fix: Use the calibration script to find the actual min and max pulse widths.

Putting It All Together: A Precision Pan-Tilt System

Let’s build a practical example: a two-axis pan-tilt system using two micro servos. This is a common building block for camera tracking, laser aiming, or sensor scanning.

Hardware

  • 2x micro servos (e.g., SG90 for tilt, MG90S for pan)
  • Pan-tilt bracket (available cheaply online)
  • Raspberry Pi with pigpio installed

Code

python import pigpio import time

pi = pigpio.pi()

PANPIN = 18 TILTPIN = 19

Calibrated ranges (replace with your values)

PANMIN = 600 PANMAX = 2400 TILTMIN = 700 TILTMAX = 2300

for pin in [PANPIN, TILTPIN]: pi.setmode(pin, pigpio.OUTPUT) pi.setPWM_frequency(pin, 50)

def setpan(angle): pulse = int(PANMIN + (angle / 180.0) * (PANMAX - PANMIN)) pi.setservopulsewidth(PAN_PIN, pulse)

def settilt(angle): pulse = int(TILTMIN + (angle / 180.0) * (TILTMAX - TILTMIN)) pi.setservopulsewidth(TILT_PIN, pulse)

def lookat(panangle, tiltangle): setpan(panangle) settilt(tilt_angle) time.sleep(0.5) # Allow time to reach position

Example: scan a grid

for pan in range(0, 181, 30): for tilt in range(0, 181, 30): look_at(pan, tilt) print(f"Pan: {pan}, Tilt: {tilt}")

Center and stop

lookat(90, 90) pi.setservopulsewidth(PANPIN, 0) pi.setservopulsewidth(TILT_PIN, 0) pi.stop()

This system gives you independent control of both axes with calibrated pulse widths, ensuring repeatable positioning.

Scaling Up: Beyond Two Servos

If you’re building a hexapod or a robotic arm with six or more servos, the same principles apply. Just connect each servo’s signal wire to a separate GPIO pin and call set_servo_pulsewidth() for each one. The pigpio library can handle up to 54 servos (one per GPIO pin) with no performance degradation.

For very large numbers of servos, consider using a servo driver board like the PCA9685, which uses I2C to control 16 servos with a single chip. However, for projects that demand the highest precision and lowest jitter, the direct GPIO approach with pigpio is still superior because it avoids the I2C bus latency.

Final Thoughts on Micro Servo Control with pigpio

The combination of a Raspberry Pi, a micro servo motor, and the pigpio library is a match made in robotics heaven. The Pi provides the computational power and connectivity, the micro servo offers compact and precise actuation, and pigpio delivers the timing accuracy that makes it all work smoothly.

Whether you’re building a robotic arm that can pick up a coin, a camera stabilizer that tracks a moving target, or an animatronic face that expresses emotions, the techniques covered here will serve as your foundation. The key takeaways are:

  • Always calibrate your servos to find their true min and max pulse widths.
  • Use a separate power supply for anything beyond one or two servos.
  • Leverage pigpio’s DMA-based PWM for jitter-free multi-servo control.
  • Implement smooth ramping for professional-grade motion.

Now it’s your turn. Grab a Raspberry Pi, a micro servo, and start experimenting. The precision robotics projects you’ve dreamed of are only a few lines of code away.

Copyright Statement:

Author: Micro Servo Motor

Link: https://microservomotor.com/micro-servo-motor-with-raspberry-pi/control-servo-pigpio-raspberry-pi-precision.htm

Source: Micro Servo Motor

The copyright of this article belongs to the author. Reproduction is not allowed without permission.

About Us

Lucas Bennett avatar
Lucas Bennett
Welcome to my blog!

Tags