Skip to content

Parametrize

Every controller function takes physical parameters (gains, mass, mixing matrix, PWM bounds) as keyword-only arguments, and the exact values differ per drone. Rather than passing them at every call site, parametrize loads them for a named drone and binds them upfront, so call sites only need to provide state and command.

The parameters stay individually accessible after binding. Because they are plain keyword-argument defaults on a functools.partial, any of them can be overridden at call time, or batched across a set of environments, without re-parametrizing the function. This makes it straightforward to randomize physical properties across a simulated batch.

import numpy as np
from drone_controllers import parametrize
from drone_controllers.mellinger import state2attitude

ctrl = parametrize(state2attitude, "cf2x_L250")

# Inspect what was bound
list(ctrl.keywords.keys())
# ['mass', 'kp', 'kd', 'ki', 'gravity_vec', 'mass_thrust',
#  'int_err_max', 'thrust_max', 'pwm_max']

Overriding parameters at call time

Because parametrize returns a functools.partial, the bound parameters are just keyword-argument defaults. Pass a different value at call time to override for that call only; ctrl.keywords is not modified:

import numpy as np
from drone_controllers import parametrize
from drone_controllers.mellinger import state2attitude

ctrl = parametrize(state2attitude, "cf2x_L250")
pos  = np.zeros(3)
quat = np.array([0., 0., 0., 1.])
vel  = np.zeros(3)
cmd  = np.zeros(13)

# Simulate with a heavier drone for this call only.
rpyt, _ = ctrl(pos, quat, vel, cmd, mass=0.035)

To make a change persist across all future calls, mutate ctrl.keywords directly:

import numpy as np
from drone_controllers import parametrize
from drone_controllers.mellinger import state2attitude

ctrl = parametrize(state2attitude, "cf2x_L250")
ctrl.keywords["mass"] = np.float64(0.035)

Warning

ctrl.keywords is a mutable dict shared across all references to the same partial. Call parametrize again for an independent copy.

Available drone configurations

The following configurations ship with pre-fitted parameters:

drone_model Platform
"cf2x_L250" Crazyflie 2.x, L250 props
"cf2x_P250" Crazyflie 2.x, P250 props
"cf2x_T350" Crazyflie 2.x, T350 props
"cf21B_500" Crazyflie 2.1 Brushless, 500 props

Pass the model name as a plain string:

import numpy as np
from drone_controllers import parametrize
from drone_controllers.mellinger import state2attitude

ctrl = parametrize(state2attitude, "cf2x_L250")
pos  = np.zeros(3)
quat = np.array([0., 0., 0., 1.])
vel  = np.zeros(3)
cmd  = np.zeros(13)
rpyt, _ = ctrl(pos, quat, vel, cmd)

Loading raw parameters

Use load_params to inspect or override the values that parametrize would bind:

from drone_controllers.core import load_params
from drone_controllers.mellinger import state2attitude

params = load_params(state2attitude, "cf2x_L250")
float(params["mass"])  # 0.029

load_core_params returns the shared [core] section for an entire controller module without filtering to a specific function's signature. This is useful when you need parameters like rpm2thrust that are not accepted by a particular stage:

from drone_controllers import mellinger
from drone_controllers.core import load_core_params

core = load_core_params(mellinger, "cf2x_L250")
core["L"]  # arm length [m]

Switching array backends

By default parameters are stored as NumPy arrays. Pass xp to convert them upfront, which avoids per-call conversion overhead in frameworks like PyTorch or JAX:

import torch
from drone_controllers import parametrize
from drone_controllers.mellinger import state2attitude

ctrl = parametrize(state2attitude, "cf2x_L250", xp=torch)

You can also specify a compute device:

import jax
import jax.numpy as jnp
from drone_controllers import parametrize
from drone_controllers.mellinger import state2attitude

ctrl = parametrize(
    state2attitude, "cf2x_L250",
    xp=jnp, device=jax.devices("cpu")[0],
)

The output backend is always inferred from the arrays you pass at call time, regardless of where the parameters live.