Build pathIntermediateA weekend

Build a 4-DOF Robot Arm: PCA9685, Servos, and Kinematics

A desktop robot arm that moves to the poses you command—driven by a PCA9685 and tuned with inverse kinematics you understood in simulation before wiring a single servo.

Build a 4-DOF Robot Arm: PCA9685, Servos, and Kinematics technical schematicREACHABLE WORKSPACE

A robot arm is the first project where a robot stops moving around and starts doing something to the world. A line follower follows; an obstacle-avoider dodges; an arm reaches, positions, and grips. That shift—from driving to manipulation—brings in the one idea every manipulator is built on: kinematics, the geometry that connects the angles you can command to the point in space you actually want.

This build path treats the arm as exactly that: a lesson in kinematics you can hold. You start by understanding how a two-link arm turns joint angles into a tool position and back again, gather the parts a servo arm needs and learn what each one does, then—before spending anything or stripping a gear—explore the reach, the two elbow solutions, and the unreachable poses in the browser simulator. Only then do you wire the real arm, calibrate its servos by hand, and run the inverse kinematics on hardware.

Follow the tech tree below top to bottom. Each node opens once its prerequisites are done, and your progress is saved on this device, so you can build the arm over a weekend without losing your place.

Bill of materials

Part Qty Approx. cost Notes
Arduino Uno 1 $5–8 Any board works — it only speaks I²C here
PCA9685 16-channel driver 1 $3 Not strictly required for four servos, but it is the right tool
SG90 servo 4 $8 Buy an MG90S for the shoulder — metal gears, and it is the joint that fails
4-DOF arm kit 1 $12–25 Acrylic is fine to learn on; aluminium flexes far less
5–6 V supply, ≥3 A 1 $8 Four servos stalling together is ~2.6 A
1000 µF capacitor 1 $1 Across V+, close to the terminal
Spare servo horns 1 set $2 You will fit one wrong. Everybody does

Total: roughly $40–60.

Two purchases are worth thinking about rather than defaulting on. A metal-gear MG90S at the shoulder costs a dollar more and is the joint carrying every other joint’s weight — nylon gears there strip under sustained load, not under impact. And a supply sized for stall, because an arm’s distinctive failure is that when it collides with its own frame or grips something solid, every servo stalls at once.

Measure your arm before you write any code

Inverse kinematics is arithmetic on link lengths. These four numbers are the entire input, and getting them from a product listing rather than a ruler is the most common reason an arm reaches confidently to the wrong place.

Symbol Measure from To Typical
L1 Shoulder joint axis Elbow joint axis 90–105 mm
L2 Elbow joint axis The gripper’s actual grip point 80–95 mm

That last row is doing more work than it looks. “The gripper’s actual grip point” moves as the jaws close, and sizing the gripper itself is a separate problem from positioning it — with its own walls, its own arithmetic and its own failure modes. If your arm reaches the part and then drops it or crushes it, the answer is not in this page: start at the gripper simulator. | h | Table surface | Shoulder joint axis | 50–70 mm | | d | Base rotation axis | Shoulder axis, horizontally | 0–15 mm |

Measure between joint axes, not bracket edges. The axis is the centre of the servo’s output spline, often 5–10 mm inside the visible bracket. On a 95 mm link a 10 mm error is over 10%, and it produces an arm that is systematically short in a way no gain corrects.

Those two lengths give you the workspace immediately:

maximum reach = L1 + L2          e.g. 100 + 90 = 190 mm
minimum reach = |L1 - L2|        e.g. |100 - 90| = 10 mm

The workspace is an annulus, not a disc. There is a hole in the middle the arm physically cannot reach, and a target near the base coming back “unreachable” is correct behaviour rather than a bug.

Build it in milestones

The assembly order here contains one step that is nearly impossible to undo, and it is step 2.

# Milestone The test A pass looks like
1 Driver responds Scan the I²C bus 0x40 appears; V+ has its own supply
2 Servos homed before assembly Command every servo to 90° before pressing on a horn Each horn is fitted at your intended zero pose
3 Per-servo end stops Step outward from centre until the servo buzzes You have min/max counts per servo, not library defaults
4 Joint map recorded Command each joint through its range Offset, sign and mechanical limits written down for all four
5 Collision check Sweep every joint through its full range slowly Nothing hits the frame; limits reflect what you observed
6 Forward kinematics Set known angles, compute the tool position, measure it Computed and measured agree within a few millimetres
7 Round trip Feed the computed position back through IK The angles out match the angles in
8 All four quadrants Request targets in front, behind, left, right All four work — if two mirror, you used atan not atan2
9 Reachability Request a target well beyond L1 + L2 A clean “unreachable” — not a NaN, and not a lunge
10 Smooth motion Command a large move Interpolated over steps, all joints together, no lurch
11 Repeatability Return to the same pose ten times from different directions Landing spread measured — that number is your backlash

Milestone 2 cannot be recovered from later. A servo horn is splined — around 20 teeth — so it fits only in discrete positions roughly 18° apart. Assemble first and calibrate second, and you may find a joint whose usable range is offset by 40° with no mechanical way to fix it. Power each servo, command 90°, then press the horn on.

Move it gently, always

A servo commanded to a new angle goes there at full speed. On an arm that means a violent movement, a current spike across all four servos, and a mechanical shock through nylon gears with the arm’s own leverage multiplying it.

// Interpolate every joint together, so the tool takes a sensible path and
// no single servo carries the whole transition.
void moveTo(float base, float shoulder, float elbow, int steps, int stepMs) {
  static float cur[3] = {0, 0, 0};
  const float target[3] = {base, shoulder, elbow};

  for (int s = 1; s <= steps; s++) {
    const float t = (float)s / steps;
    for (int j = 0; j < 3; j++) {
      const float a = cur[j] + (target[j] - cur[j]) * t;
      pwm.setPWM(JOINT[j].channel, 0, countsFor(JOINT[j], a));
    }
    delay(stepMs);
  }
  memcpy(cur, target, sizeof(cur));
}

Interpolating all joints together matters as much as the smoothing itself. Moving joints one at a time sends the tool on a strange arc through the workspace — slower, and far more likely to collide with something on the way.

What good looks like

Measurement Acrylic kit, SG90s Aluminium, MG90S
Repeatability, same approach direction ±3–5 mm ±1–2 mm
Repeatability, mixed approach directions ±8–15 mm ±3–5 mm
Usable payload at full extension 20–30 g 40–60 g
Sag at full extension, unloaded 3–8 mm 1–3 mm

The gap between the two repeatability rows is the backlash, and it is the single largest error source on a hobby arm. Four degrees of play at the shoulder over a 190 mm reach is more than 13 mm at the gripper. The practical answer is not better maths — it is to always approach a target from the same direction, which removes the sign change entirely and turns a ±13 mm error into a repeatable offset you can calibrate out.

Troubleshooting

Symptom Likely cause Fix
Arm reaches the wrong point entirely Horn fitted at an arbitrary angle Home every servo to 90° before fitting horns
One joint’s range is offset ~18° Horn one spline tooth out Remove and refit — software cannot fix this
Reaches correctly in front, mirrors behind atan instead of atan2 atan2 resolves all four quadrants
IK returns NaN and the arm lunges acos argument outside −1..1 Test reachability before the trigonometry
Targets near the base fail Inside the minimum-reach hole Correct behaviour — the workspace is an annulus
Board resets when the arm moves Servo current through the Arduino Separate V+ supply, common ground, 1000 µF cap
Arm sags at full extension Shoulder past its comfortable torque MG90S at the shoulder; counterbalance with a rubber band
Buzzing while holding a pose Servo fighting a load at its limit Reduce the load, or detach when holding is unnecessary
Joint velocity spikes near full extension Driving through a singularity Keep planned paths inside a margin of the boundary
Lands differently from each direction Backlash Always approach from the same direction
All angles consistently short PCA9685 oscillator not exactly 25 MHz Measure the real output frequency and call setOscillatorFrequency

Where to take it next

Add a straight-line move. Interpolating in joint space, as the code above does, makes the tool follow a curve. Interpolating in Cartesian space — computing IK at every step along a straight line — makes it travel straight, which is what any real pick-and-place needs. It is a small change and it is where singularities stop being theoretical: a straight path through near-full extension demands enormous joint velocities.

Add a camera and close the loop. Everything here is open loop: the arm goes where it is told and never checks. A camera reading an ArUco marker on the object lets the arm correct for the error backlash and sag leave behind — which is how a cheap arm becomes accurate.

Step up the hardware once you hit the ceiling. Aluminium links remove most of the flex, MG90S servos remove most of the backlash, and a 6-DOF arm adds a wrist so the tool’s approach angle becomes yours to choose. None of it changes the maths: the two-link solver you wrote here is the same one a better arm uses, which is exactly why the cheap frame was worth building on first.

Project roadmap

The build path

Follow the tech tree from parts to a robot that follows a taped line. Each node unlocks when its prerequisites are done, and your progress saves on this device.

0 / 13 done

100%
Learning path

Robot arm kinematics

Learning path

Open path
Build

Wire the arm

40 min

Build

Calibrate servo home and limits

30 min

Build

Run inverse kinematics on hardware

45 min

Goal

4-DOF robot arm complete

You built it

Components

Tutorials in this path

Practise before you wire

Tune it in the live simulator

The build path routes through a browser lab. Find gains that follow the track cleanly here, then transfer them to the real robot.

Frequently asked questions

How do you make a robot arm with Arduino?

Mount four servos in a 4-DOF frame—base, shoulder, elbow, and gripper—and drive them with a PCA9685 over I²C so the Arduino doesn't run out of PWM pins. Calibrate each servo's angle-to-pulse range, then feed target points to a two-link inverse-kinematics solver that returns the shoulder and elbow angles. This build path walks that whole chain, and you tune the geometry in a simulator before wiring anything.

How many servos does a 4-DOF robot arm need?

Four—one per degree of freedom: a rotating base, a shoulder, an elbow, and a gripper. Each servo needs its own PWM channel, which is exactly why a multi-joint arm uses a PCA9685 driver instead of the Arduino's handful of hardware PWM pins.

Do I need a PCA9685 for a robot arm?

It is the right tool, though not strictly required. Four servos will run from an Arduino if you power them separately, but the PCA9685 frees the microcontroller's timers, drives every joint from two I²C pins, and routes the servo current through its own V+ supply—which keeps the high current away from your logic and the board from browning out.

Do I have to buy parts before I start this project?

No. The kinematics and the pose-solving run in the browser simulator, so you can understand and explore the arm's reach, its two solutions, and its unreachable poses before buying anything. Only the final steps—wiring, calibration, and running the IK—need the physical Arduino, PCA9685, servos, arm frame, and power supply.

How is inverse kinematics used in a robot arm?

Forward kinematics tells you where the tool ends up given the joint angles; inverse kinematics does the useful inverse—given a target point, it returns the joint angles that reach it. For this arm the shoulder and elbow form a two-link planar solver, the base aims that plane, and the gripper does the picking. The simulator lets you see the solver work, including where a target is out of reach.