Rocket Lander Thruster Stabilization in Matter.js

This article explains how to build a 2D autonomous rocket lander simulation using the Matter.js 2D physics engine. You will learn how to initialize the physical environment, construct a rigid rocket body, calculate corrective thrust using a Proportional-Derivative (PD) control loop, and apply directional thruster impulses directly to the body to stabilize both its orientation and descent rate for a soft landing.

1. Setting Up the Matter.js Environment

Begin by importing the necessary Matter.js modules to establish the physics world, rendering canvas, and update runner.

const { Engine, Render, Runner, Bodies, Body, Composite, Vector, Events } = Matter;

const engine = Engine.create();
const world = engine.world;

// Set gravity appropriate for a planetary descent
world.gravity.y = 0.5;

const render = Render.create({
  element: document.body,
  engine: engine,
  options: {
    width: 800,
    height: 600,
    wireframes: false
  }
});

Render.run(render);
const runner = Runner.create();
Runner.run(runner, engine);

2. Creating the Lander and Landing Pad

The rocket lander requires a physical body with defined dimensions, mass, and rotational inertia. Add a ground body to act as the landing platform.

// Rocket dimensions
const rocketWidth = 20;
const rocketHeight = 60;

// Create the rocket body
const rocket = Bodies.rectangle(400, 100, rocketWidth, rocketHeight, {
  mass: 10,
  frictionAir: 0.01,
  angle: (Math.random() - 0.5) * 0.5 // Initial random tilt
});

// Create the ground
const ground = Bodies.rectangle(400, 580, 800, 40, { isStatic: true });

Composite.add(world, [rocket, ground]);

3. Thruster Physics Concepts

Stabilizing a descent requires two independent control operations:

  1. Attitude Stabilization (Rotation): Regulating the angle to ensure the rocket points upright (angle = 0). This is achieved using side Reaction Control System (RCS) thrusters that exert force offset from the center of mass, producing rotational torque.
  2. Descent Velocity Control (Altitude): Applying upward force along the longitudinal axis of the rocket using the main thruster to prevent crash impact.

In Matter.js, forces are applied using Body.applyForce(body, position, force). Applying a force at an offset from body.position introduces angular acceleration.

4. Implementing Attitude Control via a PD Controller

To prevent overshooting and perpetual oscillation, use a Proportional-Derivative (PD) controller for the attitude thrusters. The controller computes corrective force based on the current angle error (Proportional) and the angular speed (Derivative).

function calculateAttitudeThrust(rocket) {
  const targetAngle = 0;
  const currentAngle = rocket.angle;
  const currentAngularVelocity = rocket.angularVelocity;

  // Controller gains
  const kP = 0.05; // Proportional gain (corrects position error)
  const kD = 0.3;  // Derivative gain (dampens oscillation)

  // Compute torque output
  const angleError = targetAngle - currentAngle;
  return (kP * angleError) - (kD * currentAngularVelocity);
}

5. Implementing Descent Speed Regulation

The main thruster slows the vehicle down as it approaches the surface. Compute the upward thrust required by evaluating vertical velocity and altitude.

function calculateMainThrust(rocket, groundY) {
  const targetVelocity = 1.0; // Safe touchdown speed (pixels per frame)
  const altitude = groundY - rocket.position.y;
  
  // Apply thrust if descending faster than desired, scaling with proximity
  if (rocket.velocity.y > targetVelocity) {
    const kP_descent = 0.0008;
    return -Math.min(0.02, (rocket.velocity.y - targetVelocity) * kP_descent);
  }
  return 0;
}

6. Executing Thrusters in the Simulation Loop

Hook into the Matter.js beforeUpdate event. This step runs before every physics calculation, allowing you to compute errors and apply vector forces dynamically.

Events.on(engine, 'beforeUpdate', () => {
  // 1. Attitude stabilization (RCS impulses)
  const torque = calculateAttitudeThrust(rocket);
  
  // Calculate the position of the top of the rocket
  const topOffset = Vector.rotate({ x: 0, y: -rocketHeight / 2 }, rocket.angle);
  const topPos = Vector.add(rocket.position, topOffset);
  
  // Apply lateral impulse perpendicular to rocket orientation at the top tip
  const rcsForce = Vector.rotate({ x: torque, y: 0 }, rocket.angle);
  Body.applyForce(rocket, topPos, rcsForce);

  // 2. Main thruster impulses (counteracting gravity)
  const mainThrustMagnitude = calculateMainThrust(rocket, 560);
  
  if (mainThrustMagnitude < 0) {
    // Force direction aligned with the rocket's longitudinal axis
    const thrusterForce = Vector.rotate({ x: 0, y: mainThrustMagnitude }, rocket.angle);
    
    // Bottom position of the rocket
    const bottomOffset = Vector.rotate({ x: 0, y: rocketHeight / 2 }, rocket.angle);
    const bottomPos = Vector.add(rocket.position, bottomOffset);
    
    Body.applyForce(rocket, bottomPos, thrusterForce);
  }
});

7. Tuning for Stability