PWM Control in Robotics: A Practical Guide
If you’ve ever built a robot arm, a walking hexapod, or a simple pan-tilt camera mount, you’ve almost certainly used a micro servo motor. These tiny, inexpensive actuators are the workhorses of hobby robotics, and their operation is almost universally governed by one thing: Pulse Width Modulation (PWM). But while the basic concept of sending a 50 Hz signal with a variable pulse width sounds simple, the practical reality is filled with nuance—timing constraints, power supply considerations, jitter, and the subtle differences between analog and digital servos.
This guide is written for the roboticist who has already wired up a servo and watched it twitch, but wants to truly understand how to control it reliably, precisely, and efficiently. We’ll go beyond the textbook 1-2 ms pulse range and dive into the real-world behavior of micro servos like the SG90, MG90S, and TowerPro series. By the end, you’ll know exactly how to generate clean PWM signals, how to handle multiple servos without timing conflicts, and how to avoid the common pitfalls that turn a smooth motion into a shuddering mess.
The Anatomy of a Micro Servo: What PWM Actually Controls
Before we write a single line of code, it’s worth understanding what happens inside that little plastic case. A micro servo is a closed-loop system, but a surprisingly simple one. It contains a DC motor, a set of reduction gears, a potentiometer (or sometimes a magnetic encoder) for position feedback, and a control board.
The control board’s job is straightforward: it receives a PWM signal, measures the width of the pulse (typically in microseconds), and compares that to the current position reported by the potentiometer. If the desired position (from the pulse) is different from the actual position, it drives the motor in the appropriate direction until they match. This is a proportional control loop—no fancy PID, just a simple error-driven motor drive.
The 50 Hz Standard and Why It Matters
The standard for hobby servos is a 50 Hz update rate, meaning a new pulse arrives every 20 milliseconds. The pulse width itself usually ranges from 1.0 ms (full counterclockwise) to 2.0 ms (full clockwise), with 1.5 ms being the center position. But here’s the first practical nuance: not all servos interpret these values identically.
I’ve tested dozens of SG90 clones from different manufacturers, and the actual usable range often varies. Some will accept a pulse as narrow as 0.5 ms and as wide as 2.5 ms, giving you nearly 180 degrees of rotation. Others will hit their mechanical stops at 0.7 ms and 2.3 ms. If you blindly send a 1.0 ms pulse assuming it’s the leftmost position, you might only get 90 degrees of travel, or worse, you’ll hear that horrible buzzing sound of a servo straining against its internal stop.
Practical tip: Always calibrate your servos. Write a test sketch that slowly sweeps from 0.5 ms to 2.5 ms in 10 microsecond steps. Listen for the point where the servo stops moving but isn’t buzzing. That’s your real range. Record those values and use them in your code.
Generating Clean PWM: Hardware vs. Software
The most common mistake beginners make is trying to generate servo PWM with software delay loops. You can do it—a simple digitalWrite(HIGH); delayMicroseconds(1500); digitalWrite(LOW); delay(18.5); will spin a servo—but it’s a terrible idea. The CPU is fully occupied, any interrupt will cause jitter, and if you have more than one servo, you’re in for a world of timing headaches.
Dedicated PWM Hardware: The Right Way
Microcontrollers like the Arduino Uno (ATmega328P) have built-in timers that can generate PWM signals in hardware. This is the gold standard. The timer runs independently of your main code, producing a clean, consistent pulse train with zero CPU overhead.
For an Arduino Uno, you have three timers: - Timer0: Used by delay() and millis(). Avoid using it for servo control unless you want to break timekeeping. - Timer1: A 16-bit timer, perfect for servo PWM. This is what the Servo.h library uses. - Timer2: An 8-bit timer, also usable but with less resolution.
The Servo.h library is excellent for most applications. It handles the timer configuration, allows you to attach up to 12 servos (on Uno), and lets you specify pulse widths in microseconds directly. But it has limitations: it uses Timer1 exclusively, and if you need that timer for something else (like generating a different frequency PWM for a motor driver), you’re stuck.
When to Write Your Own PWM Driver
There are two scenarios where you’ll want to bypass Servo.h and write your own PWM routine:
You need more than 12 servos. The library has a hard limit because it stores pulse widths in an array of fixed size. You can modify the library, but it’s often easier to use a PWM driver IC like the PCA9685, which handles 16 channels over I2C.
You need a non-standard frequency. Some high-performance servos (especially the digital ones) respond better to 333 Hz or even 500 Hz update rates. The
Servo.hlibrary is locked at 50 Hz. If you want to push a servo faster, you need direct timer control.
Here’s a minimal example of direct Timer1 control for a single servo:
cpp // Direct Timer1 PWM for servo control // Pin 9 (OC1A) on Arduino Uno
void setup() { pinMode(9, OUTPUT); // Set Timer1 to Fast PWM mode, 16-bit, prescaler 8 // This gives a 50 Hz update rate (16 MHz / 8 / 40000 = 50 Hz) TCCR1A = (1 << WGM11) | (1 << COM1A1); TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); ICR1 = 40000; // TOP value for 50 Hz OCR1A = 3000; // 1.5 ms pulse (1500 us * 2 = 3000 counts) }
void loop() { // Nothing to do here - hardware handles it }
This code generates a continuous 50 Hz signal on pin 9. The pulse width is set by OCR1A, and you can change it anytime by writing a new value. The key advantage: zero CPU load, zero jitter, and you can still use delay() and millis() because Timer0 is untouched.
Power Supply: The Silent Killer of Servo Performance
I’ve seen more robot projects fail due to poor power supply design than any other single issue. Micro servos draw surprisingly high current, especially when under load or during acceleration. A typical SG90 can draw 200-300 mA while moving, and up to 700 mA when stalled. If you have six servos on a hexapod leg, that’s potentially 4+ amps of peak current.
The USB Power Trap
Many beginners power their servos directly from the Arduino’s 5V pin, which is fed from the USB port. USB 2.0 provides only 500 mA. Try to move two servos simultaneously, and the voltage will sag. The servo will twitch, reset the Arduino, or behave erratically. The solution is simple: never power servos from the Arduino’s onboard regulator.
Use a separate 5V power supply rated for at least 2 amps for a small robot, or 5-10 amps for a larger one. Connect the servo power lines directly to this supply, and only connect the servo signal wires to the Arduino. Make sure the ground of the servo supply is connected to the Arduino ground—this is critical for the PWM signal to have a reference.
Decoupling Capacitors: Your Best Friend
Even with a good supply, the instantaneous current draw of a servo can cause voltage dips on the order of microseconds. A 1000 µF electrolytic capacitor placed near the servo power terminals will act as a local reservoir, smoothing out these transients. For high-torque applications, add a 100 nF ceramic capacitor in parallel to handle high-frequency noise.
I once spent three days debugging a robot arm that would randomly twitch every few seconds. The culprit? A single servo was back-driving the power rail with 200 mV ripple, which coupled into the PWM signal line. A 1000 µF capacitor on each servo’s power input fixed it instantly.
Analog vs. Digital Micro Servos: What’s the Real Difference?
You’ll see micro servos marketed as “analog” or “digital.” The difference is in the control electronics, not the PWM signal itself. Both types accept the same 50 Hz PWM input. The difference is what happens inside between pulses.
Analog Servos
An analog servo’s control board uses a simple comparator and a transistor H-bridge. It compares the incoming pulse to the potentiometer voltage and drives the motor continuously until they match. The motor is always either on or off—there’s no intermediate state. This means the servo is constantly “hunting” for the exact position, which causes a slight jitter, especially at rest.
The advantage of analog servos is low cost and simplicity. The disadvantage is higher power consumption at rest (because the motor is always trying to correct) and less precise holding torque.
Digital Servos
A digital servo uses a microcontroller to interpret the PWM signal. It can drive the motor with a higher frequency internal signal (often 300 Hz or more), which allows for smoother motion and stronger holding torque. The microcontroller can also implement deadband—a small range of error where the motor is turned off to prevent hunting.
The practical result is that digital servos hold their position more accurately and consume less power at rest. However, they cost more and can be more sensitive to noise on the PWM line because they sample the pulse width more precisely.
Which should you use? For most hobby robots, analog servos are perfectly adequate. Use digital servos only when you need precise positioning (like a camera gimbal) or when you’re running on battery power and need to minimize idle current.
Advanced PWM Techniques: Smooth Motion and Trajectory Planning
Sending a raw pulse width to a servo will make it jump instantly to that position. That’s fine for on/off actions, but for fluid robotic motion, you need to gradually transition from one angle to another. This is called trajectory planning.
Linear Interpolation: The Bare Minimum
The simplest approach is to break a movement into small steps and update the pulse width incrementally. For example, to move from 0 to 90 degrees over 1 second:
cpp int startPulse = 1000; // microseconds int endPulse = 2000; int steps = 50; // update 50 times per second int delayMs = 1000 / steps; // 20 ms per step
for (int i = 0; i <= steps; i++) { int pulse = startPulse + (endPulse - startPulse) * i / steps; servo.writeMicroseconds(pulse); delay(delayMs); }
This works, but the motion is linear in time, not in velocity. The servo will start and stop abruptly, which can cause mechanical stress and overshoot.
S-Curve Profiling for Smooth Starts and Stops
A better approach is to use an S-curve velocity profile. The idea is to accelerate smoothly at the beginning, maintain constant velocity in the middle, and decelerate at the end. This is standard in industrial robotics but rarely used in hobby projects—which is a shame, because it makes a huge difference in perceived quality.
Here’s a simple implementation using a cosine acceleration profile:
cpp void smoothMove(int startPulse, int endPulse, int durationMs) { int steps = durationMs / 20; // 20 ms per step for (int i = 0; i <= steps; i++) { float t = (float)i / steps; // 0.0 to 1.0 // Cosine S-curve: sin^2 gives smooth acceleration/deceleration float s = sin(t * PI / 2.0); float position = sin(s * PI / 2.0); int pulse = startPulse + (endPulse - startPulse) * position; servo.writeMicroseconds(pulse); delay(20); } }
This produces a motion that starts gently, speeds up, and then slows down to a soft stop. The difference is dramatic when watching a robot arm—the motion looks deliberate and controlled rather than jerky.
Handling Multiple Servos: The Timing Challenge
When you have multiple servos, you have two options: sequential updates or simultaneous updates.
Sequential Updates (Easy but Slow)
In this method, you update each servo one after another in a loop. The total time to update all servos is the sum of the individual update times. If you have 8 servos and each update takes 20 ms (due to the delay()), you’re looking at 160 ms per full cycle. That’s only 6.25 Hz update rate, which is too slow for smooth motion.
Simultaneous Updates (The Right Way)
To update all servos at the same time, you need to send all the pulses within a single 20 ms window. This is what the Servo.h library does internally—it uses Timer1 to generate interrupts that turn each pin on and off at the correct times. All pulses start at the same time (the beginning of the 20 ms cycle), and each one ends after its specified width.
If you’re using a PCA9685 or other I2C PWM driver, this is handled automatically. The driver chip has its own oscillator and can produce 16 simultaneous channels with no CPU involvement after initialization.
The 12-Servo Limit on Arduino
The Servo.h library’s 12-servo limit comes from the size of an internal array and the fact that Timer1 has only one output compare register that can be used for precise timing. The library uses a clever trick: it stores all the pulse widths and uses a timer interrupt to turn pins on and off sequentially. But it can only handle 12 because the interrupt service routine takes too long for more.
If you need more than 12 servos, use a PCA9685 board. It’s cheap, well-supported, and can drive up to 16 servos per chip (and you can daisy-chain multiple chips). The I2C communication takes a few milliseconds, but that’s fine for 50 Hz updates.
Real-World Case Study: Building a Six-Axis Robot Arm
Let’s put all this together with a practical example. I recently built a small six-axis robot arm using MG90S metal-gear servos. Here’s what I learned:
Power Supply
Each MG90S draws about 250 mA while moving and 500 mA when stalled. With six servos, I used a 5V, 5A switching power supply. I placed a 1000 µF capacitor on the power input of each servo, and a 4700 µF capacitor on the main power rail. This eliminated all the twitching I had seen in earlier prototypes.
PWM Generation
I used an Arduino Mega (which has four hardware timers) and the Servo.h library for the first five servos. For the sixth servo (the gripper), I used a separate timer (Timer3) to generate a PWM signal on a different pin, because I wanted to experiment with higher update rates for the gripper’s fast open/close action.
Calibration
I wrote a calibration routine that moved each servo from 500 to 2500 microseconds in 10 µs steps, and I recorded the actual mechanical limits by listening for the stall sound. The results varied by up to 30 µs between servos of the same model. I stored these calibrated ranges in EEPROM and used them in all subsequent movements.
Motion Smoothing
For the arm’s trajectory, I implemented S-curve profiling for all joints. The difference was night and day. Without smoothing, the arm looked like a broken toy. With smoothing, it moved with a fluid, almost organic quality. I also added a velocity limit: no joint could move faster than 60 degrees per second, which prevented the arm from jerking when executing rapid movements.
The One Mistake I Made
I initially used long (30 cm) jumper wires for the servo signal lines. The robot arm would occasionally glitch, especially when the gripper was holding a heavy object. The problem was signal integrity: the long wires acted as antennas, picking up noise from the motor drivers. I switched to twisted-pair ribbon cable (signal and ground twisted together) and the glitches disappeared.
Debugging Common PWM Problems
Even with careful design, things can go wrong. Here are the most common PWM issues I’ve encountered and how to fix them:
Servo Jitters or Oscillates at Rest
This is almost always a power supply issue. Measure the voltage at the servo connector while it’s jittering. If it’s below 4.8V, add capacitance or upgrade your power supply. If the voltage is stable, the servo itself may be worn out—the potentiometer inside can develop dead spots over time.
Servo Moves to the Wrong Position
Double-check your pulse width mapping. If you’re using servo.write(angle) instead of servo.writeMicroseconds(pulse), remember that the library maps 0-180 degrees to 544-2400 microseconds by default. This range might not match your servo. Always use writeMicroseconds() with calibrated values for precise control.
Multiple Servos Move Erratically When Updated Together
This is a timing conflict. If you’re updating servos in a loop with delay() between each one, you’re not updating them simultaneously. Use the Servo.h library’s built-in simultaneous update (it happens automatically) or use a dedicated PWM driver chip.
Servo Gets Hot
A hot servo is a stalled servo. Either the mechanical load is too high (the arm is trying to move past a physical stop) or the PWM signal is commanding a position outside the servo’s range. Check your calibration and ensure the servo isn’t binding.
Beyond 50 Hz: When to Use Higher Update Rates
Most micro servos are designed for 50 Hz, but some digital servos can handle 200 Hz or even 400 Hz. The advantage is lower latency—the servo responds faster to changes in the command signal. This is critical for applications like quadcopter control surfaces or fast pan-tilt systems.
To use a higher rate, you need to change the timer period. On an Arduino, this means modifying the ICR1 value. For 200 Hz, set ICR1 = 10000 (16 MHz / 8 / 10000 = 200 Hz). But be careful: not all servos can handle this. If the servo’s control board expects a 20 ms period, sending a 5 ms period will confuse it. Always check the datasheet or test empirically.
I tested a high-end digital micro servo (the BlueBird BMS-127) at 250 Hz and got noticeably smoother motion with less overshoot. The same test with a cheap SG90 caused it to buzz and overheat within 30 seconds. Know your hardware.
Final Practical Checklist for Your Next Robot Project
Before you write a single line of code for your next servo-based robot, run through this checklist:
- Power: Is your servo power supply rated for at least 2x the expected peak current? Do you have decoupling capacitors on each servo?
- Ground: Are all grounds (Arduino, servo supply, any other modules) connected together?
- Signal wires: Are they short (under 20 cm) or shielded? Are they routed away from high-current wires?
- Calibration: Have you measured the actual pulse width range for each servo?
- Update rate: Are you using hardware PWM (either built-in timers or a dedicated IC) rather than software delays?
- Motion profiling: Are you using at least linear interpolation, and ideally S-curve profiling, for smooth motion?
- Testing: Have you tested each servo individually under load before assembling the full robot?
If you can answer yes to all of these, your robot’s servos will move smoothly, reliably, and without those mysterious glitches that plague so many projects. PWM control is not complicated, but it demands attention to detail. The difference between a robot that twitches and one that moves with precision is often just a 1000 µF capacitor and a few lines of trajectory code.
Copyright Statement:
Author: Micro Servo Motor
Link: https://microservomotor.com/pulse-width-modulation-pwm-control/pwm-robotics-guide.htm
Source: Micro Servo Motor
The copyright of this article belongs to the author. Reproduction is not allowed without permission.
Recommended Blog
- PWM Control in Power Systems: Applications and Design Considerations
- How to Implement PWM in Arduino Projects
- PWM Control in Power Distribution Systems
- PWM Control in Lighting Systems: Design Considerations
- PWM Control in Lighting Systems: Applications and Benefits
- PWM Control in Lighting Systems: Techniques and Tips
- PWM in Communication Systems: Encoding Information
- Understanding the PWM Duty Cycle Formula
- The Role of PWM in Signal Filtering
- The Use of PWM in Signal Filtering: Applications and Techniques
About Us
- Lucas Bennett
- Welcome to my blog!
Hot Blog
- Vector's Micro Servo Motors: Compact and Lightweight for Pan-Tilt Systems
- How to Build a Remote-Controlled Car with a 3D-Printed Chassis
- How Gear Teeth Design Influences Servo Motor Operation
- The Impact of Gear Materials on Servo Motor Heat Generation
- Operating Voltage Ranges for Micro Servos Explained
- Best Micro Servo Motors for DIY Electronics Projects
- The Best Micro Servo Motors for Arduino Projects: Brand Recommendations
- Advances in Acoustic Management for Micro Servo Motors
- Using Raspberry Pi to Control Servo Motors in Automated Packaging and Labeling Systems
- Micro Servo Motor Explained: A Simple Guide for Students
Latest Blog
- PWM Control in Robotics: A Practical Guide
- Maintenance Schedules for Micro Servos on Working Drones
- How to Implement Active Cooling Systems in Motors
- Micro Servo vs Standard Servo: Start-Up Torque Performance
- The Impact of Quantum Computing on Micro Servo Motor Design
- How to Build a Remote-Controlled Car with a Smartphone App
- How to Design Motors for Optimal Heat Dissipation
- Using Arduino to Control the Position, Speed, and Direction of a Micro Servo Motor
- Comparing Micro Servo Brands for Robotics Projects
- Using a Joystick to Control Your Micro Servo Robotic Arm
- Best Practices for Testing Micro Servos Before Drone Integration
- Building Your First Remote-Controlled Car: A Beginner's Guide
- The Role of Voltage and Current in Motor Torque and Speed
- Micro Servo vs Standard Servo: Signal Noise Sensitivity
- The Role of Micro Servo Motors in Smart Grids
- How to Connect a Micro Servo Motor to Arduino MKR IoT Bundle
- Brushless vs Brushed Micro Servos for Long-Lasting RC Boat Use
- How to Clean and Maintain Your RC Car's Motor
- Understanding the Basics of Control Circuit Design
- Best Micro Servo Motors for DIY Electronics Projects