Self-Balancing Robot in Matter.js with PID
This article provides a practical guide to creating an active two-wheeled self-balancing robot in the Matter.js 2D physics engine. You will learn how to model the inverted pendulum chassis and wheel base, read the robot's physical orientation, implement a Proportional-Integral-Derivative (PID) control algorithm, and apply dynamic corrective torques during the simulation loop to maintain equilibrium.
Modeling the Robot Structure
In a 2D physics environment like Matter.js, a two-wheeled self-balancing robot is viewed from the side, effectively modeled as a single wheel supporting an inverted pendulum body.
To construct this assembly:
- Wheel: Create a circular dynamic body using
Matter.Bodies.circle. Ensure it has adequate friction so it can push off the ground without slipping uncontrollably. - Chassis: Create a tall, thin rectangular dynamic
body using
Matter.Bodies.rectangle. Place its center of mass above the wheel. - Axle Constraint: Bind the wheel and the chassis
together using
Matter.Constraint.create. Set the anchor point at the center of the wheel and the base of the chassis, with astiffnessof1andlengthof0to act as a revolute (pin) joint.
const wheel = Matter.Bodies.circle(400, 500, 30, {
friction: 0.9,
restitution: 0
});
const chassis = Matter.Bodies.rectangle(400, 420, 20, 140, {
friction: 0.1,
density: 0.002
});
const axle = Matter.Constraint.create({
bodyA: wheel,
bodyB: chassis,
pointA: { x: 0, y: 0 },
pointB: { x: 0, y: 60 },
stiffness: 1,
length: 0
});
Matter.Composite.add(engine.world, [wheel, chassis, axle]);Implementing the PID Controller
Active balancing relies on calculating how far the chassis is tilted away from its vertical balance point and applying an opposing torque to the wheel to drive the base under the falling center of mass.
The PID algorithm computes the control output \(u(t)\) based on three terms:
- Proportional (\(K_p\)): Corrects based on current tilt angle.
- Integral (\(K_i\)): Corrects accumulated small steady-state errors over time.
- Derivative (\(K_d\)): Dampens oscillation by opposing rapid changes in angular velocity.
let targetAngle = 0; // Upright vertical orientation
let integral = 0;
let lastError = 0;
const Kp = 0.8;
const Ki = 0.001;
const Kd = 12.0;
function computePID(currentAngle, deltaTime) {
const error = targetAngle - currentAngle;
integral += error * deltaTime;
const derivative = (error - lastError) / deltaTime;
lastError = error;
return (Kp * error) + (Ki * integral) + (Kd * derivative);
}Applying Corrective Torques in the Engine Loop
Hook into the physics engine update cycle using
Matter.Events.on(engine, 'beforeUpdate', callback). During
each tick, sample the chassis angle, calculate the required torque, and
apply equal and opposite rotational effects to the wheel and chassis to
adhere to Newton's third law.
Matter.Events.on(engine, 'beforeUpdate', (event) => {
const deltaTime = engine.timing.lastDelta || 16.67;
const currentAngle = chassis.angle;
// Calculate corrective torque
const controlSignal = computePID(currentAngle, deltaTime);
// Clamp maximum torque to prevent simulation instability
const maxTorque = 0.15;
const torque = Math.max(-maxTorque, Math.min(maxTorque, controlSignal));
// Apply torque: accelerating the wheel forward pushes the chassis backward
wheel.torque = torque;
chassis.torque = -torque;
});Tuning and Stability Considerations
- Center of Mass: A higher center of mass falls more slowly, making it easier for the controller to react. If the system is unstable, increase the height of the chassis body.
- Tuning Strategy: Start by setting \(K_i\) and \(K_d\) to zero. Increase \(K_p\) until the robot responds to tilts, then increase \(K_d\) to eliminate violent wobbling. Add a small \(K_i\) only if the robot drifts persistently in one direction.
- Ground Traction: Ensure the ground body has
sufficient friction (
friction: 1.0). If the wheel skids during rapid torque changes, active balancing will fail.