L2F-faithful quadrotor dynamics and RAPTOR-contract environments for NVIDIA Isaac Lab 3.0 on Newton (MJWarp).
The RAPTOR foundation policy flying a 6 s figure-eight in Isaac-L2F-Hover-Direct
on Newton/MJWarp, replayed in Isaac Sim. Yellow: reference, cyan: flown path.
L2F ("Learning to Fly in Seconds", rl-tools) is the fastest quadrotor dynamics simulator and the training ground of the RAPTOR foundation policy. It has no rendering and no collisions. Isaac Lab has GPU rendering, collisions and obstacle scenes, but its own multirotor support is PhysX-only and uses a different motor model. Isaac-L2F brings L2F's exact motor and wrench model and the RAPTOR observation/action contract into Isaac Lab, so that policies trained in L2F fly in Isaac Lab zero-shot, and vision policies can be trained on top of the same dynamics.
Everything in this package is written to one contract, the one RAPTOR and L2F use:
| Frames | body FLU (x forward, y left, z up), world z-up |
| Observation (22) | position − p_ref (3, world) · rotation matrix row-major (9) · linear velocity − v_ref (3, world) · angular velocity (3, body) · previous action (4, raw) |
| Action (4) | per-motor, normalised to [-1, 1], order front-right, back-right, back-left, front-left |
| Rate | 100 Hz (dt = 0.01 s), one RK4 step per control step |
| Quaternion | (w, x, y, z), body-to-world |
The reference (p_ref, v_ref) is an input with neutral defaults (origin, zero velocity); a
static setpoint has v_ref = 0. The previous action is the raw, unclamped action passed to
the last step, as the l2f oracle keeps it. Trajectory references, position-error clamps,
observation delays and noise schedules are downstream wrappers, not variants of the contract.
isaac_l2f.dynamics is a pure-PyTorch, batched, device-agnostic port of the L2F model:
- parameter JSON schema (same keys as L2F / RAPTOR), load, save, stack, select
- first-order motor lag with separate rising and falling time constants
- thrust polynomial
c0 + c1 u + c2 u², rotor drag torque, arm moments - RK4 step, post-integration, observation, reward, termination, initial-state sampling
- the RAPTOR domain-randomisation sampler (thrust-to-weight, torque-to-inertia, mass, size, time constants, torque constant, disturbance magnitude)
rotor_wrench(params, u, effectiveness)returning the body-frame force and torque, which is what the Isaac Lab environment injects through the wrench composer; the optional per-rotoreffectiveness[N, 4]scales a rotor's thrust and with it its reaction torque and arm moment (the hook fault schedules drive from downstream)- world-frame disturbance force and body-frame disturbance torque on the state
isaac_l2f.policy is the policy protocol every env runs: reset(mask) / step(obs) -> action.
The policy owns its recurrent state and re-seeds the masked envs from its own h0; the hidden
size comes from the artefact. Loaders exist for TorchScript (a scripted module with
forward(obs, hidden) -> (action, hidden), an h0 buffer and a hidden_size attribute;
feed-forward policies use size 0) and ONNX (inputs obs, hidden; outputs action,
hidden_out; needs the onnx extra).
Upstream quirks are reproduced on purpose, not fixed: J and J_inv are stored
independently and used as loaded, the orientation cost is 2·acos(1 − |q_z|), and the
initial attitude angle ignores max_angle. Golden tests check every step against the
l2f pip package.
import torch
from isaac_l2f.dynamics import load_registry, sample_initial_state, step, student_observation
params = load_registry("crazyflie").expand(1024)
gen = torch.Generator().manual_seed(0)
state = sample_initial_state(params, 1024, gen)
action = torch.zeros(1024, 4)
state = step(params, state, action)
obs = student_observation(params, state) # [1024, 22], reference = origin / zero velocity
from isaac_l2f.policy import load_torchscript
policy = load_torchscript("student.pt") # hidden size read from the artefact
action = policy.step(obs) # raw action; the env clamps
policy.reset(mask=torch.tensor([True] + [False] * 1023)) # re-seed env 0 onlyDevelopment (any platform, no GPU needed for the dynamics core and its tests):
uv sync
uv run pytest -qWith Isaac Lab 3.0 (Linux, NVIDIA GPU, Python 3.12). Isaac Lab ships as one distribution on NVIDIA's package index with Newton included. The primary path is kit-less: Newton physics and the Newton Warp renderer run without Isaac Sim:
uv sync --extra isaacThe optional Kit path installs full Isaac Sim 6.2 as the RTX render path for
scripts/render_flight.py and for USD/GUI work; physics is still Newton. The two extras are
mutually exclusive (Isaac Sim pins numpy 2.3.1 and packaging 26.0, the kit-less split takes the
latest), so switching is one sync either way:
UV_HTTP_TIMEOUT=1800 uv sync --extra isaacsim # extension-cache wheels are multi-GB; uv's 30 s default times outTraining uses physics=newton_mjwarp; kit-less rendering is Newton's (renderer=newton_renderer,
--viz newton_gl). NVIDIA's index is a
regular extra index here (unsafe-best-match), as in NVIDIA's own pip instructions, because
Isaac Sim pins NVIDIA-built transitive packages that exist only there. Isaac Sim 6.2 also pins
urllib3==2.7.0 against Isaac Lab 3.0.0's urllib3>=2.8.0, so pyproject.toml overrides it to
urllib3>=2.8.0 until NVIDIA aligns the two.
Two Linux-only rules that follow from the split lock:
- Pass the same extra to every
uv runon that machine (uv run --extra isaacsim python ...,uv run --extra isaac isaaclab train ...) or useuv run --no-sync. A bareuv runre-syncs to the default extra set, which is a different resolver split (both Linux splits pintorch==2.11.0through Isaac Lab, the Kit split alsonumpy==2.3.1; macOS takes the latest), so it silently moves the venv off Isaac Lab's and Isaac Sim's pins. - Anything that imports
isaacsimneeds NVIDIA's EULA accepted non-interactively:OMNI_KIT_ACCEPT_EULA=YES. Export it in your shell profile on the box if you accept it.
Dynamics model and parameter format from rl-tools/l2f (MIT) and the RAPTOR pre-training code in rl-tools.
Please cite L2F (RA-L 2024), whose dynamics this package ports, and RAPTOR (Science Robotics 2026) if you fly the foundation policy; "Cite this repository" in the sidebar gives this package.
MIT, see LICENSE.