Back to projects

Robotics · CAD · Electronics

Quadruped Robot Dog

A 3D-printed quadruped I designed around eight servos, custom control electronics, wireless commands, and a diagonal-pair gait.

Open build PDF ↗
Blue and black 3D-printed quadruped robot dog

Legs

4 two-joint modules

Actuation

8 servos

Controller

ESP32 + PCA9685

Fabrication

Custom CAD + 3D printing

Mechanical design

Mechanical design and leg prototyping

I tested the two-joint mechanism by itself before printing and wiring all four legs. That made servo fit, linkage clearance, and the range of motion much easier to debug.

CAD model of the full quadruped robot dog assembly
Full four-leg CAD assembly.
Detailed CAD model of one robot dog leg and its servos
One two-servo leg in CAD.
Gold 3D-printed prototype of one two-joint robot dog leg
Printed single-leg test.
01

Single-leg prototype

A single printed module exposed linkage collisions and servo mounting problems before they were repeated four times.

02

Identical leg modules

Matching leg assemblies simplified printing, replacement parts, and the relationship between software channels and physical joints.

03

Hardware access

Openings in the chassis make it possible to trace wires, replace a servo, and reach mounting screws without dismantling the whole dog.

Electronics

Electronics prototyping and control board

The first version spread the controller, IMU, servo driver, and power modules across a breadboard. Once the behavior worked, I moved the control stack onto a soldered prototyping board sized for the robot's center frame.

Breadboard prototype and hand-drawn robot dog circuit sketch
Breadboard test and circuit sketch.
Soldered ESP32 robot dog control board with servo driver and power modules
Soldered ESP32 and servo control board.

Control path

Input

ESP-NOW remote

Speed, turn bias, and active state

Controller

ESP32

Gait timing, balance, and shutdown

PWM

PCA9685

Eight calibrated servo channels

Motion

Four legs

Two controlled joints per leg

Current gait

Diagonal pairs, half a cycle apart

Front-right moves with back-left. The opposite pair uses the same waveform shifted by π, keeping the timing relationship simple enough to tune on the physical dog.

Leg pairing

Two pairs alternate

Legs in the same pair move together. Pair B starts halfway through Pair A's cycle.

Pair A

Front right + Back left

Move at the same time

Pair B

180°

Front left + Back right

Move half a cycle later

Motion code

Pairing the legs in software

walkCycle()
// Pair A: front-right + back-left
writeServo(0, neutral[0] + cos(t) * amplitude);
writeServo(1, neutral[1] + sin(t) * amplitude);
writeServo(6, neutral[6] + cos(t) * amplitude);
writeServo(7, neutral[7] + sin(t) * amplitude);

// Pair B: same cycle, shifted by PI
writeServo(2, neutral[2] + cos(t + PI) * amplitude);
writeServo(3, neutral[3] + sin(t + PI) * amplitude);
writeServo(4, neutral[4] + cos(t + PI) * amplitude);
writeServo(5, neutral[5] + sin(t + PI) * amplitude);

t = fmod(t + gaitSpeed, TWO_PI);

Next control model

Inverse kinematics for foot positioning

The current gait directly changes servo angles. Inverse kinematics flips that around: choose a target foot position, use the two link lengths to solve the joints, then apply each servo's real calibration offset.

01

Choose the target foot coordinates relative to the hip joint.

02

Use the measured upper and lower link lengths to solve both joint angles.

03

Convert those angles into servo commands using the neutral offset measured for that physical leg.

Two-joint inverse kinematics diagram showing a target foot position and joint angles
A two-link leg model like the mechanism used on the dog.

Where it got difficult

Calibration and mechanical constraints

01

Eight servos do not share one real zero

Each installed horn landed differently, so I measured and stored a separate neutral angle for every channel before tuning movement.

02

Sensor noise can become visible twitching

The MPU6050 correction uses a dead zone, stepped changes, and a hard cap instead of reacting to every small tilt reading.

03

Power loss should not collapse the pose

The shutdown routine first eases every joint toward neutral, waits for the motion to finish, and only then switches the servo power off.

Feedback

Ignoring small tilt changes

applyBalance()
const float DEAD_ZONE = 10.0;
const float STEP_SIZE = 3.0;

float tilt = -getTilt();
if (abs(tilt) < DEAD_ZONE) return;

float beyond = abs(tilt) - DEAD_ZONE;
float snapped = floor(beyond / STEP_SIZE) * STEP_SIZE;
float effectiveTilt = copysign(snapped, tilt);
float adjust = constrain(effectiveTilt * 0.5, -20, 20);

Power control

Returning to neutral before shutdown

shutdownPi()
void shutdownPi() {
  slowMoveToNeutral();
  delay(1500);

  digitalWrite(MOSFET_PIN, LOW);
  delay(100);

  while (true);
}

What I would change next

01

Measure the real link lengths and servo offsets, then replace the fixed circular gait with inverse kinematics for deliberate foot placement.

02

Build foot trajectories with separate lift, forward, and landing phases instead of moving every joint through one continuous circle.

03

Tune the body geometry and center of mass around the finished battery and electronics rather than treating those as separate additions.

04

Move the prototype wiring onto a cleaner permanent board once the control behavior and power requirements stop changing.