System designHow-to

Drive servos from a vision board without PWM pins

Drive servos from image results on any vision board: 1-2 ms pulses every 20 ms from a timer library or an I2C driver chip, with the math, wiring and checks.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01What a servo actually expects
  2. 02Why a PWM pin is the wrong tool
  3. 03Two ways to generate the pulse train
  4. 04Worked example: pixel offset to pulse counts on a pan axis
  5. 05Wiring: the bus and the power
  6. 06From image result to servo command
  7. 07Prove it before you mount it
  8. 08Common mistakes, and parts that avoid them
  9. 09Questions

Your rig needs six servos that move when the image says so, and the pinout lists GPIO, I2C and SPI without a single pin labelled PWM. That is a timing job, not a pin shortage. An RC servo takes a 1 to 2 ms pulse about every 20 ms, and something has to generate that train: a timer-based servo library on a plain GPIO, or a 16-channel I2C PWM driver on the bus you already need. This guide gives the pulse math, a worked pixel-to-pulse example for a pan axis, the wiring rules that keep servos from resetting the camera, and the scope checks to run before you mount the rig.

What a servo actually expects

A servo positions on pulse width, not on duty cycle. The RC convention puts 1,000 µs of pulse at one end of travel, 2,000 µs at the other and 1,500 µs at centre, with a fresh pulse about every 20 ms - a 50 Hz frame. The spacing only has to stay regular; the width carries the command. The RC servo control convention is described in exactly those terms: width commands position, the frame just repeats it.

Across a typical 180° of travel, the 1,000 µs of width span fixes the slope:

pulse width (µs) = 1500 + offset from centre (°) × 5.56

The 5.56 µs/° is 1,000 µs divided by 180°. Many servos also accept a wider 500 to 2,500 µs window for proportionally more travel, so calibrate both endpoints on your actual unit before trusting the extremes.

Why a PWM pin is the wrong tool

The PWM peripheral a chip vendor advertises modulates duty cycle at a fixed carrier, typically hundreds of hertz or more for dimming and motor drive. That frame rate is wrong for a servo by an order of magnitude, and a duty-cycle API never exposes width in microseconds. Servo support therefore lives in a library that owns a hardware timer and toggles a pin inside a timed interrupt, once per 20 ms frame, holding the line high for exactly the width requested. That works on any capable pin, which is why a pinout never needs to say PWM.

The order of attack: if your board's SDK ships a servo library and lists your board, use it - the timer and interrupt plumbing is already debugged. Writing your own interrupt is possible, but timer peripherals differ from processor to processor, so start from a vendor example rather than from zero. For a feel for how far the timer route scales, the write-up on driving 12 servos from timers alone on one board shows the ceiling of that approach. When no library supports your board, or you would rather keep the timers for your own timing, the driver chip in the next section takes over.

Two ways to generate the pulse train

Timer-based servo library I2C PWM driver chip
Host pins one GPIO per servo two for the bus, SDA and SCL
Channels one pin per servo 16 per chip
CPU cost a timed interrupt every frame one bus write per change
Width resolution timer tick, often sub-µs 12-bit count: 4.88 µs at 50 Hz
Extra hardware none a driver board on the bus
Fits when your board is in the SDK's supported list no library, many servos, or a CPU busy with vision

The standard chip for the second column is the PCA9685: 16 independent 12-bit channels, an I2C slave with its address set by six pins, and up to 62 of them on one bus. It regenerates every pulse from its own oscillator between writes, so the processor sets a width and walks away. A host without an I2C header bridges one through USB. Where the host speaks only UART, a dedicated servo stage over a serial link is the same idea with different wires - the servo IO board wiring and serial setup guide walks that path. Six servos sit on one chip with ten channels spare.

Worked example: pixel offset to pulse counts on a pan axis

The case: one pan servo points a board camera, the vision loop finds a target 60 px right of the image centre, and the servo must command from that offset.

Inputs, with their sources:

  • Servo travel: 1,000 to 2,000 µs over 180° (RC convention, above).
  • Driver: 12-bit, 4,096 counts per channel, frame set to 50 Hz, so 20,000 µs per frame (chip facts, above).
  • Camera head: LS-BCM-0003 - 3.1 MP, 120 fps over USB3 (catalog).
  • Sensor width: 2,048 px (3.1 MP at the sensor's 4:3 ratio).
  • Assumption: the fitted lens covers 60° horizontally - substitute your lens's number.
  • Processing budget: 5 ms (your measurement on the target platform).

The camera head in the example is the LS-BCM-0003: a 3.1 MP USB3 board camera on a 1/1.8" global-shutter sensor with a C-mount.

Board-level cameras3.1 MP USB3 board-level camera, IMX252, 1/1.8" global shutter, 120 fps (LS-BCM-0003)LS-BCM-0003
Sensor format
1/1.8"
Lens mount
C-mount
Interface
USB3
Resolution
5 MP
$690.00View partBuild a system with it

Three rates turn a pixel offset into a register value - degrees per pixel, microseconds per degree, counts per microsecond:

degrees per pixel = 60° / 2,048 px = 0.0293°/px angle offset = 60 px × 0.0293°/px = 1.76° pulse width = 1500 µs + 1.76° × 5.56 µs/° = 1510 µs counts per µs = 20,000 µs / 4,096 = 4.88 µs register value = 1510 µs / 4.88 µs = 309

Write 309 to that channel and the chip holds a 1,510 µs pulse every 20 ms until you write again.

Now the lag. At 120 fps a fresh frame lands every 8.3 ms, so the newest image is 0 to 8.3 ms old when processing starts. Add the 5 ms processing budget and up to 20 ms waiting for the next frame edge, in the worst case where the new width takes effect on the frame boundary: typical lag lands near 19 ms (4.2 + 5 + 10), worst case 33 ms.

The decision falls out of two numbers. The driver quantizes aim at 4.88 µs per count, which is 4.88 / 5.56 ≈ 0.9° per count - about 30 px of camera scale, so the driver, not the camera, sets pointing resolution. If the task tolerates roughly 1° of aim error and a few tens of milliseconds of lag, this 50 Hz loop is the right tool. If it needs 0.1° of hold or single-digit-millisecond response, move to a serial bus servo that takes a position command directly instead of a pulse width.

Wiring: the bus and the power

Three lines reach the driver: SDA and SCL from the board's I2C header plus a shared ground, and then the servo supply on its own. Set the driver's address pins away from anything already on the bus and scan the bus once before the first write - two devices on one address answer together and neither works. Most driver boards carry their own pull-ups; on a bare bus, 4.7 kΩ to the logic rail is the usual starting point.

Join the servo supply ground and the logic ground at one point so the pulse has a return path. Keep servo leads a few centimetres clear of camera and bus lines, and put the supply's bulk capacitance beside the drivers. The camera GPIO wiring guide applies the same triage to the camera side of the harness.

From image result to servo command

  1. Acquire each frame through the board's normal path. A continuous stream keeps frame age predictable (continuous acquisition for live loops), and the minimal acquisition walkthrough shows the shortest pipeline.
  2. Find the target and return its pixel offset from the image centre. An edge-based matcher holds position when lighting drifts (edge-based pattern matching under lighting changes).
  3. Convert with the three rates from the worked example: pixels × 0.0293°/px, then × 5.56 µs/°, then ÷ 4.88 µs per count.
  4. Write the count once per processed frame, and only when it changed - fewer bus transactions and less servo chatter.
  5. Budget the whole loop before promising cycle times: frame age, processing, plus up to one 20 ms frame. The full vision-to-motion latency budget does that accounting step by step.
  6. Pick compute that keeps step 2 short. On an embedded board the ARM board selection for USB3 acquisition applies; on a fanless box, the vision controllers range does.

Prove it before you mount it

Put a scope or logic analyzer on one servo channel. You should read a 20 ms period, a width equal to the commanded value within a count or two, and no dropped pulses while the vision loop runs at full rate.

Write centre - 1,500 µs, count 307 - with the horn detached. The servo should sit silent at mid-travel; buzzing at a fixed command usually means a soft supply or a missing common ground.

Sweep 1,000 to 2,000 µs and confirm the horn covers its travel and stops cleanly at both ends.

Time the end-to-end lag by moving a marker in the scene and measuring from the move to the servo's motion. For the example rig the estimate is 19 to 33 ms; a measured value far above that points at the processing step, not at the pulse train.

Common mistakes, and parts that avoid them

  • Hunting the pinout for PWM labels. The pulses come from a library or a driver chip, not from the label.
  • Driving from duty-cycle PWM at its default carrier. The frame rate is wrong by an order of magnitude.
  • Sharing the logic rail with the servo motors. See the warning above.
  • Floating grounds: servo supply and logic ground must meet at one point.
  • Blocking on the bus write. Keep it short and asynchronous so it never delays the next frame.

For the camera head, match the part to the space and the cable run from the board-level cameras range. Where the rig is tight, the MV-CB016-10GM-M-W pairs a 1.6 MP mono sensor on a 1/2.9" format with an M12 mount and a GigE port; its global shutter tolerates motion while the frame is captured. The board camera selection guide walks that matching decision in full.

Board-level camerasHikrobot MV-CB016-10GM-M-W 1.6 MP GigE board-level camera, IMX296, Mono, M12 lens mount, WTB GigE portMV-CB016-10GM-M-W
Sensor format
1/2.9"
Lens mount
M12
Interface
GigE
Resolution
Up to 3 MP
$559.00View partBuild a system with it

When the loop runs on a fanless controller rather than a bare board, the MV-VC2140-128G40-1T covers compute: an Intel N-series processor, 8 GB of memory with a 128 GB SSD plus a 1 TB HDD, and four built-in light channels for the strobes. The pulse train stays off its CPU entirely - the driver regenerates pulses between writes, so the controller sends a width only when the target moves.

Vision controllersHikrobot MV-VC2140-128G40-1T VC2100 Vision Controller, N97, 8GB+128G SSD+ 1THDDMV-VC2140-128G40-1T
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$1,679.00View partBuild a system with it

The decision, one line per case: board supported by the SDK's servo library - timer library on a plain GPIO. No library, six or more servos, or a CPU busy with vision - 16-channel I2C driver at 50 Hz, as worked above. Tolerance near 1° of aim error and tens of milliseconds of lag - that loop is done. Tighter than that - serial bus servos, not pulse widths.

To size the rest of the station, start at the cameras finder and browse the vision controllers range, or configure the camera side directly. The systems topic page collects the guides on buses, latency and GPIO that surround this one.

Questions

How many servos can one I2C PWM driver chip handle?

Sixteen independent channels per chip, each with its own 12-bit width register, so a six-servo rig leaves ten channels spare and the chip keeps regenerating every pulse between writes. Address pins let up to 62 of the chips share one bus, far past anything an image-guided rig needs. Add drivers only when channel count, not bus load, runs out: each write is a few bytes regardless of chip count.

Why does my servo buzz when the image is still?

Usually the supply, not the signal. A soft or shared rail lets the servo hunt around the commanded width, and a missing common ground does the same. Send the width only when it changes, scope the rail while the servo holds a load, and confirm one ground point between servo supply and logic. Pulse jitter from a timer-based library is rare; if the width is stable on a scope, look at power first.

What supply should the servos get?

A dedicated 5-6 V rail, sized for stall current rather than running current - stall draws several times more when the servo pushes against a limit - with its ground joined to the logic ground at one point. Standard hobby servos run in the 4.8-6.0 V band; take the stall figure from the servo sheet and size the supply and wiring for every axis moving at once.

Does a faster camera improve how well the servo follows?

Only the first term of the lag. Frame age falls from 33 ms at 30 fps to 8.3 ms at 120 fps, but processing time and the up-to-20 ms wait for the next servo frame edge remain. Past about 120 fps the servo frame dominates, so spend the effort on processing speed and on writing the width immediately after each frame rather than on more camera.

Can I write the pulse generator myself with a timer interrupt?

Yes - that is all a servo library is: an interrupt that sets a pin high for the commanded width once per 20 ms frame. It is worth writing only when no library supports your board, because timer peripherals and their clock trees differ between processors. Measure the output on a scope against your requested value; a stable 20 ms period with the right width means the generator is done.