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

Micro Servo Motor with Raspberry Pi / Visits:5

Micro servo motors are tiny, inexpensive, and surprisingly capable. They show up in camera gimbals, robotic grippers, pan-tilt brackets, animatronic eyes, and countless DIY automation projects. But if you have ever tried to drive one directly from a Raspberry Pi with a naive Python script, you already know the pain: jitter, buzzing, twitching at startup, or a servo that slams to one side the moment your program exits.

The problem is rarely the motor. It is almost always the pulse generation. The Raspberry Pi runs a full Linux operating system, which means your Python code is not running on bare metal. It is sharing CPU time with dozens of other processes, and any timing loop written in pure Python will drift by milliseconds. A micro servo expects a control pulse every 20 milliseconds with accuracy measured in microseconds. That gap between "good enough" and "actually precise" is exactly where the pigpio library earns its place.

This article walks through why micro servos are so sensitive, how pigpio solves the timing problem at the hardware level, and how to build a reusable, high-precision servo controller on a Raspberry Pi.

Why Micro Servo Motors Are a Different Beast

The 50 Hz Pulse Train Nobody Warns You About

Almost every hobby servo, including the ubiquitous SG90 and MG90S micro servos, follows the same control convention. You send a pulse roughly every 20 milliseconds. The width of that pulse tells the servo where to move:

  • About 500 microseconds (0.5 ms) for one extreme
  • About 1500 microseconds (1.5 ms) for center
  • About 2500 microseconds (2.5 ms) for the other extreme

That is a 2 millisecond window covering the entire range of motion. On a typical micro servo with 180 degrees of travel, one microsecond of pulse width translates to roughly 0.09 degrees of movement. If your pulse timing wobbles by 50 microseconds, your servo visibly twitches. If it wobbles by 200 microseconds, the servo buzzes constantly as it hunts for a position it can never quite hold.

Why Software Timing Fails

Consider the classic approach: a while loop in Python that toggles a GPIO pin high, sleeps for the pulse duration, then pulls it low and sleeps for the remainder of the 20 ms period.

python import RPi.GPIO as GPIO import time

GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.OUT)

while True: GPIO.output(17, True) time.sleep(0.0015) GPIO.output(17, False) time.sleep(0.0185)

This looks reasonable. It is also unreliable. Python's time.sleep() guarantees a minimum delay, not a precise one. Under load, the Linux scheduler may delay your thread by several milliseconds. Garbage collection, network activity, or even writing to stdout can introduce jitter. The result is a servo that jitters, overheats, or refuses to hold position.

The Micro Servo's Mechanical Reality

There is another reason micro servos punish sloppy signals. Their gear trains are plastic or lightweight metal, their motors are small, and their internal potentiometers are cheap. Unlike a large standard servo with a beefy motor and metal gears, a micro servo has very little mechanical damping. It responds instantly to every pulse variation. Precision in the signal is not a luxury here; it is the difference between a smooth panning camera and a twitching mess.

Enter pigpio: Hardware-Timed Pulses on the Raspberry Pi

What pigpio Actually Does

The pigpio library, written by Joan, uses the Raspberry Pi's DMA (Direct Memory Access) controller to generate GPIO waveforms with microsecond accuracy, entirely independent of the CPU. Once you schedule a pulse train, the hardware keeps producing it even if your Python process is descheduled, blocked on I/O, or busy doing something else.

This is the key insight. You are not asking Linux to be real-time. You are handing the timing job to dedicated hardware and letting Linux do what it does best.

Installing and Starting the Daemon

pigpio runs as a background daemon (pigpiod) that your Python code talks to over a local socket. On Raspberry Pi OS:

bash sudo apt update sudo apt install pigpio python3-pigpio sudo systemctl enable pigpiod sudo systemctl start pigpiod

Verify it is running:

bash sudo systemctl status pigpiod

You should see an active service. If you prefer to launch it manually for debugging, sudo pigpiod works fine, and you can stop it with sudo killall pigpiod.

Connecting the Micro Servo

Wiring is straightforward but the power question matters. A micro servo like the SG90 draws around 100–250 mA when idle and can spike well over 500 mA when moving quickly or stalled. Do not power it from the Raspberry Pi's 5V pin if you value your Pi's stability.

| Servo Wire | Connection | |---|---| | Brown / Black (GND) | Common ground with Pi and external supply | | Red (V+) | External 5V supply (2A or better) | | Orange / Yellow (Signal) | GPIO pin, e.g., GPIO 17 (physical pin 11) |

The grounds must be tied together. The signal wire carries only logic-level pulses, so no level shifting is needed; the Pi's 3.3V logic is comfortably above the servo's threshold.

Writing a High-Precision Servo Controller

The Minimal Working Example

Here is the smallest useful script using pigpio's servo API:

python import pigpio import time

pi = pigpio.pi() if not pi.connected: raise RuntimeError("pigpiod is not running")

SERVO_PIN = 17

Start the servo pulse train at 1500 us (center)

pi.setservopulsewidth(SERVO_PIN, 1500) time.sleep(1)

Sweep to one extreme

pi.setservopulsewidth(SERVO_PIN, 500) time.sleep(1)

Sweep to the other extreme

pi.setservopulsewidth(SERVO_PIN, 2500) time.sleep(1)

Always stop the pulses when done

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

That single call, set_servo_pulsewidth, does an enormous amount of work. It configures the DMA engine to emit a 50 Hz pulse train with the specified width, and it keeps doing so until you change it or set it to zero.

Why You Must Set Pulse Width to Zero

Setting the pulse width to 0 stops the pulse train. This matters for two reasons. First, a servo that keeps receiving pulses actively holds its position, which means its motor is constantly drawing current and heating up. Second, if your script crashes without cleanup, the daemon may keep the last pulse train running indefinitely. Build the habit of stopping the servo explicitly.

Calibrating Your Specific Micro Servo

The 500–2500 microsecond range is a convention, not a law. Cheap micro servos vary. Some will not reach their full mechanical range until 600–2400. Others will grind against their internal end stops at 400 or 2600, which damages the gears.

Calibrate carefully. Start with a conservative range like 700–2300, find the true endpoints by nudging the pulse width in 10 microsecond increments, and then back off by 50 microseconds on each side for safety. Write those numbers down. Every servo in your project may differ.

python MIN_PW = 700 # calibrated minimum MAX_PW = 2300 # calibrated maximum CENTER = (MIN_PW + MAX_PW) // 2

Mapping Angles to Pulse Widths

For readable code, map angles to pulse widths:

python def angletopulse(angle, minpw=MINPW, maxpw=MAXPW): angle = max(0, min(180, angle)) return int(minpw + (angle / 180.0) * (maxpw - min_pw))

def setangle(pi, pin, angle): pi.setservopulsewidth(pin, angleto_pulse(angle))

Now set_angle(pi, 17, 90) parks the servo at center, and set_angle(pi, 17, 0) sends it to one end.

Advanced Techniques for Genuinely High Precision

Smooth Motion with Interpolation

Commanding a servo to jump from 10 degrees to 170 degrees instantly causes a current spike, a mechanical jerk, and often a visible overshoot. For camera work or animatronics, interpolate:

python def smooth_move(pi, pin, start_angle, end_angle, duration=1.0, steps=100): delay = duration / steps for i in range(steps + 1): angle = start_angle + (end_angle - start_angle) * i / steps pi.set_servo_pulsewidth(pin, angle_to_pulse(angle)) time.sleep(delay)

This produces a linear ramp. For even smoother motion, apply an easing function such as a cosine or cubic curve to the interpolation parameter. The servo's own dynamics will smooth out the rest.

Driving Multiple Servos Simultaneously

pigpio handles many servos at once without breaking a sweat because each one is just another DMA-managed pulse train. A pan-tilt rig is trivial:

python PANPIN = 17 TILTPIN = 18

pi.setservopulsewidth(PANPIN, angletopulse(90)) pi.setservopulsewidth(TILTPIN, angletopulse(90))

Both servos receive their pulses independently and simultaneously. There is no software loop juggling them, so there is no cross-talk and no timing interference.

Reading Feedback with an ADC

Standard micro servos do not report their position. If you need closed-loop control, you have options. You can add a potentiometer coupled to the output shaft and read it with an MCP3008 ADC over SPI. Or you can upgrade to a serial bus servo like the Feetech or Dynamixel line, which report position, load, and temperature over a shared bus. For most hobby projects, open-loop control with pigpio is accurate enough that feedback is unnecessary.

Reducing Jitter at the Mechanical Level

Even a perfect signal can produce visible jitter if the mechanics are sloppy. A few practical fixes:

  • Mount the servo firmly. A servo held in place by tape will vibrate.
  • Keep signal wires short, or twist them with a ground return.
  • Use a separate, well-regulated 5V supply. Brownouts from a shared supply cause erratic behavior.
  • Add a 470 µF capacitor across the servo's power leads to absorb current spikes.

Common Pitfalls and How to Avoid Them

Forgetting That pigpiod Must Be Running

The single most common error is pi.connected returning False. Either the daemon is not running, or your user lacks permission to access its socket. Starting the daemon via systemd and adding your user to the appropriate group solves both.

Assuming Every Servo Is Linear

Micro servos are not perfectly linear across their range. The relationship between pulse width and angle drifts slightly near the endpoints. If your application demands sub-degree accuracy across the full sweep, build a lookup table by measuring actual positions with a protractor or a camera, then interpolate between measured points.

Over-Driving the Servo Past Its Stops

If you hear grinding, you have gone too far. Back off immediately. Continuous grinding strips gears in seconds on a micro servo. This is why calibration matters more than any clever code.

Leaving Pulses Running When Idle

A servo holding position against a load draws current continuously. For battery-powered projects, always set the pulse width to zero when the servo does not need to hold. If the load would move the servo when unpowered, either accept the movement or use a servo with a mechanical brake.

Putting It All Together: A Pan-Tilt Tracker Skeleton

Here is a compact example that ties the concepts together. It sweeps a pan-tilt pair smoothly and cleans up properly:

python import pigpio import time import math

pi = pigpio.pi() if not pi.connected: raise RuntimeError("Start pigpiod first")

PAN, TILT = 17, 18 MINPW, MAXPW = 700, 2300

def pulse(angle): return int(MINPW + (max(0, min(180, angle)) / 180.0) * (MAXPW - MIN_PW))

try: for t in range(0, 360, 2): panangle = 90 + 60 * math.sin(math.radians(t)) tiltangle = 90 + 30 * math.cos(math.radians(t)) pi.setservopulsewidth(PAN, pulse(panangle)) pi.setservopulsewidth(TILT, pulse(tiltangle)) time.sleep(0.02) finally: pi.setservopulsewidth(PAN, 0) pi.setservopulsewidth(TILT, 0) pi.stop()

The try/finally block guarantees that the servos stop even if the script is interrupted. That single habit will save you from more burned-out servos than any other practice.

Micro servo motors reward patience and precision. With pigpio handling the timing in hardware, the Raspberry Pi becomes a genuinely capable motion controller, and the limiting factor shifts from your software to the mechanics of a five-dollar motor. That is exactly where you want the bottleneck to be.

Copyright Statement:

Author: Micro Servo Motor

Link: https://microservomotor.com/micro-servo-motor-with-raspberry-pi/high-precision-pigpio-servo-raspberry-pi.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