orbital-landing-tgo

A learned powered-descent guidance net — 1,281 parameters — that lands a vehicle on the Moon, Mars, or Earth from one forward pass, with no flight plan and no clock. It matches the analytic optimal guidance it was distilled from (91.7% vs 90.8% on a held-out benchmark) while touching down at 0.03 m/s, 0.00 m from the pad.

Institute for Physical AI @ BMI · The Charlot Lab · Technical Report TR-2026-17

What it is, and why it is shaped this way

ZEM/ZEV is the classic closed-form powered-descent law — but it needs a time-to-go. Classically you search for a flight time before ignition and then fly that clock down, which makes the guidance a function of the schedule rather than of the vehicle's state.

It does not have to be. For a fixed tgo, the minimum-energy cost of the ZEM/ZEV solution is itself closed-form:

J(tgo) = 12|ZEM|²/tgo³ − 12(ZEM·ZEV)/tgo² + 4|ZEV|²/tgo

so the optimal time-to-go can be solved from the state at every step — no clock, no plan. That scalar minimisation is then the only expensive part of the loop, and it is exactly what this net replaces.

The net predicts the time-to-go; the closed form does the rest.

  • Input (5): [dx, dy, vx, vy, g] — position relative to the pad (m), velocity (m/s), the body's gravity (m/s²). One net spans Moon / Mars / Earth.
  • Output (1): tgo — the optimal time-to-go (s). Feed it to ZEM/ZEV, clamp to the engine limit, fly.
  • Arch: 5 → 32 → 32 → 1, ReLU, input/output normalization. 1,281 parameters.

The finding: factorization beat the training algorithm

Cloning the thrust directly is the obvious approach and it fails. The ZEM/ZEV gains go as 6/tgo², so the target is stiff exactly at touchdown, where being wrong matters most — and a smooth net cannot track it. DAgger (rolling out the learner, relabelling with the expert) helps a lot but plateaus well short:

Approach soft landings (held-out, 120 starts) miss touchdown
Clone thrust — plain behavior cloning 9/120
Clone thrust — + DAgger, 6 rounds 72/120 0.12 m 1.05 m/s
Clone the time-to-go (this model) 110/120 0.00 m 0.03 m/s

The time-to-go model reached 109/120 at round 0 — plain behavior cloning, before any DAgger. Choosing the right seam to cut the controller at mattered far more than the training algorithm: learn the part that is expensive and smooth, keep the part that is cheap and exact.

Benchmark (Landing-Bench, 120 fixed starts)

Guidance success mean miss touchdown mean Δv
Scheduled ZEM/ZEV (search a flight time, fly the clock) 95.0% 0.03 m 0.45 m/s 139 m/s
Clock-free ZEM/ZEV (solve tgo every step) 90.8% 0.00 m 0.02 m/s 159 m/s
Learned · thrust (1,346p) 60.0% 0.12 m 1.05 m/s 142 m/s
Learned · tgo (this model) 91.7% 0.00 m 0.03 m/s 160 m/s

Reproduce with node bench/landing_bench.mjs. Note the honest trade the benchmark exposes: the scheduled search still wins on success rate and fuel, because on thrust-marginal starts it can find some feasible flight time where the energy-optimal tgo demands more thrust than the engine has. The clock-free family buys an order of magnitude in precision, and needs no plan, for about 15% more Δv.

Use

Everything the runtime needs is in land_policy.json (W1,b1,W2,b2,W3,b3 and the xm/xsd/ym/ysd normalization; mode: "tgo"). The net is one forward pass; ZEM/ZEV is four lines:

const P = await (await fetch('land_policy.json')).json();
const mv = (W,a)=>W[0].map((_,j)=>a.reduce((s,ai,i)=>s+ai*W[i][j],0)), relu=z=>z.map(v=>v>0?v:0);
function tgo(obs){ // obs = [dx, dy, vx, vy, g]
  const x = obs.map((v,j)=>(v-P.xm[j])/P.xsd[j]);
  const a1 = relu(mv(P.W1,x).map((v,j)=>v+P.b1[j]));
  const a2 = relu(mv(P.W2,a1).map((v,j)=>v+P.b2[j]));
  return Math.max(0.5, mv(P.W3,a2).map((v,j)=>v+P.b3[j])[0]*P.ysd[0]+P.ym[0]);
}
function thrust(r, v, rT, g, aMax){            // r,v,rT,g are 2-vectors; g = [0,-grav]
  const t = tgo([r[0]-rT[0], r[1]-rT[1], v[0], v[1], -g[1]]);
  const zx = rT[0]-(r[0]+v[0]*t+0.5*g[0]*t*t), zy = rT[1]-(r[1]+v[1]*t+0.5*g[1]*t*t);
  const ex = -(v[0]+g[0]*t), ey = -(v[1]+g[1]*t);
  let a = [6/(t*t)*zx - 2/t*ex, 6/(t*t)*zy - 2/t*ey];
  const am = Math.hypot(...a);
  return am > aMax ? a.map(x => x*aMax/am) : a;
}

Honest scope

This is a research demonstrator, not flight software. The dynamics are a planar double integrator under constant gravity with a hard thrust limit; there is no attitude loop, no mass depletion, no navigation error, no atmosphere. The pad is assumed known and the guidance is unconstrained apart from the thrust clamp (no glide-slope or thrust-pointing constraints). It reports no flight result. It exists to show that the expensive part of an optimal guidance law can be compressed into a net small enough to ship as JSON and run in a browser tab, on the device.

MIT licensed. Institute for Physical AI @ BMI · The Charlot Lab.

Downloads last month
-
Video Preview
loading