Warning
This repository is no longer maintained. All models have been integrated into crazyflow, its successor, and can be imported from there as before. Please use crazyflow for new projects.
Physics-based and data-driven quadrotor dynamics models for estimation, control, and simulation.
drone-models provides quadrotor dynamics as pure Python functions, from full physics-based implementations to lightweight data-driven approximations. All models support NumPy, JAX, and any Array API backend, plus CasADi symbolic variants for optimization-based control. Pre-fitted parameters are included for several Crazyflie platforms.
Available models — ranging from high-fidelity to lightweight:
| Model | Description |
|---|---|
first_principles |
Full rigid-body physics with optional rotor dynamics and aerodynamic drag |
so_rpy_rotor_drag |
Data-driven, attitude as roll/pitch/yaw, with rotor dynamics and drag |
so_rpy_rotor |
Data-driven, attitude as roll/pitch/yaw, with rotor dynamics |
so_rpy |
Lightest data-driven model, attitude as roll/pitch/yaw, no rotor dynamics |
Pre-fitted configurations for Crazyflie 2.x (brushed) and Crazyflie 2.1 Brushless:
cf2x_L250, cf2x_P250, cf2x_T350, cf21B_500
pip install drone-modelsFor system identification (fitting parameters from your own flight data):
pip install "drone-models[sysid]"Note:
drone_modelsmust be imported before SciPy to enable Array API support. If you encounter aRuntimeError, either importdrone_modelsfirst or setexport SCIPY_ARRAY_API=1in your shell.
Bind parameters to a drone configuration with parametrize, then call the model with state and command arrays:
import numpy as np
from drone_models import parametrize
from drone_models.first_principles import dynamics
model = parametrize(dynamics, drone_model="cf2x_L250")
pos = np.zeros(3)
quat = np.array([0., 0., 0., 1.]) # xyzw, identity
vel = np.zeros(3)
ang_vel = np.zeros(3)
rotor_vel = np.ones(4) * 12_000. # current motor RPMs
cmd = np.full(4, 15_000.) # commanded motor RPMs
pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot = model(
pos, quat, vel, ang_vel, cmd, rotor_vel
)The model returns continuous-time state derivatives scipy.integrate.solve_ivp) to simulate forward in time.
Pass any Array API-compatible array and the output type follows automatically — no code changes needed:
import jax.numpy as jnp
from drone_models import parametrize
from drone_models.first_principles import dynamics
model = parametrize(dynamics, drone_model="cf2x_L250", xp=jnp)
# Pass jax arrays — get jax arrays backArbitrary leading batch dimensions work out of the box: stack states for a thousand drones and evaluate them in one call.
Every model exposes a symbolic_dynamics function returning CasADi MX expressions, for use in MPC, trajectory optimization, or estimation:
from drone_models import parametrize
from drone_models.first_principles import symbolic_dynamics
sym_model = parametrize(symbolic_dynamics, drone_model="cf2x_L250")Clone and install with pixi:
git clone https://github.com/learnsyslab/drone-models.git
cd drone-models
pixi installRun tests:
pixi run -e tests tests@misc{schuck2026crazyflow,
title={Crazyflow: An Accurate, GPU-Accelerated, Differentiable Drone Simulator in JAX},
author={Martin Schuck and Marcel P. Rath and Yufei Hua and AbhisheK Goudar and SiQi Zhou and Angela P. Schoellig},
year={2026},
eprint={2606.01478},
archivePrefix={arXiv},
primaryClass={cs.RO},
url={https://arxiv.org/abs/2606.01478},
}