You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -31,23 +32,40 @@ The outputs are the time derivatives of each state variable — the right-hand s
31
32
In the previous example `rotor_vel_dot` is `None` because we didn't tell the model what speed the motors are currently at. The model just assumed they were already at the commanded RPM. That's a reasonable approximation for slow maneuvers, but real motors take time to spin up and down. Passing `rotor_vel` enables the rotor dynamics, which computes the acceleration of each motor toward its target.
32
33
33
34
```python
35
+
import numpy as np
36
+
from drone_models import parametrize
37
+
from drone_models.first_principles import dynamics
38
+
39
+
model = parametrize(dynamics, drone_model="cf2x_L250")
40
+
pos = np.zeros(3)
41
+
quat = np.array([0., 0., 0., 1.])
42
+
vel = np.zeros(3)
43
+
ang_vel = np.zeros(3)
44
+
cmd = np.full(4, 15_000.)
34
45
# The motors are at 12 000 RPM but commanded to 15 000 — they're spinning up.
The first-principles model requires individual motor RPMs as input, which means you need rotor-level commands. The fitted models — `so_rpy`, `so_rpy_rotor`, `so_rpy_rotor_drag` — take a higher-level command instead: roll, pitch, yaw setpoints in radians plus collective thrust in Newtons. This matches the command interface of typical flight controllers and makes them convenient for control design and system identification.
46
57
47
58
```python
48
-
from drone_models.so_rpy_rotor_drag import dynamics as srrd
59
+
import numpy as np
60
+
from drone_models import parametrize
61
+
from drone_models.so_rpy import dynamics
49
62
50
-
model = parametrize(srrd, drone_model="cf2x_L250")
63
+
pos = np.zeros(3)
64
+
quat = np.array([0., 0., 0., 1.])
65
+
vel = np.zeros(3)
66
+
ang_vel = np.zeros(3)
67
+
68
+
model = parametrize(dynamics, drone_model="cf2x_L250")
51
69
52
70
# Collective thrust near hover: mass * g ≈ 0.0319 * 9.81 ≈ 0.31 N
The same model handles arbitrary leading batch dimensions — no special API, no loops. Add a leading dimension to all state and command arrays and the model evaluates all instances in a single call. This works identically across all backends.
86
105
87
106
```python
107
+
import torch
108
+
109
+
from drone_models import parametrize
110
+
from drone_models.first_principles import dynamics
111
+
112
+
# Parameters are stored as torch tensors — no per-call conversion needed.
113
+
model = parametrize(dynamics, drone_model="cf2x_L250", xp=torch)
@@ -64,13 +65,23 @@ These are the right-hand side of the continuous-time ODE $\dot{x} = f(x, u)$. To
64
65
Real motors don't respond instantaneously to commands. Passing the current motor state as `rotor_vel` enables the rotor dynamics model, which computes how the motors accelerate or decelerate toward the commanded RPM.
65
66
66
67
```python
68
+
import numpy as np
69
+
from drone_models import parametrize
70
+
from drone_models.first_principles import dynamics
71
+
72
+
model = parametrize(dynamics, drone_model="cf2x_L250")
73
+
pos = np.zeros(3)
74
+
quat = np.array([0., 0., 0., 1.])
75
+
vel = np.zeros(3)
76
+
ang_vel = np.zeros(3)
77
+
cmd = np.full(4, 15_000.)
67
78
# Current RPMs lag behind the 15 000 RPM command — motors are still spinning up.
**Option 2 — mutate `model.keywords` directly.** Simpler when you don't need JIT or are happy to retrace. Replace a scalar parameter with a `(N,)` array and each element in the batch uses its own value.
Copy file name to clipboardExpand all lines: docs/user-guide/models.md
+17-3Lines changed: 17 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,11 +27,17 @@ The full rigid-body physics model. The command is four individual motor RPMs. Th
27
27
Working at the rotor-velocity level means you need a controller that converts higher-level commands — position setpoints, attitude + collective thrust — down to individual motor RPMs. [drone-controllers](https://utiasdsl.github.io/drone-controllers/) provides a matching set of controllers designed for exactly this interface.
28
28
29
29
```python
30
+
import numpy as np
30
31
from drone_models import parametrize
31
32
from drone_models.first_principles import dynamics
32
33
33
34
model = parametrize(dynamics, drone_model="cf2x_L250")
@@ -45,7 +51,7 @@ See the [`first_principles` API reference](../reference/drone_models/first_princ
45
51
46
52
A fitted second-order model where the command is `[roll_rad, pitch_rad, yaw_rad, thrust_N]` — the same interface used by most flight controller firmware. First-order thrust dynamics model motor spin-up delay, and a linear body-frame drag term accounts for aerodynamic resistance. All coefficients are identified from flight data rather than derived from physics, which makes the model easy to calibrate and well-suited to real-time control.
47
53
48
-
```python
54
+
```{ .python notest }
49
55
from drone_models.so_rpy_rotor_drag import dynamics
50
56
51
57
model = parametrize(dynamics, drone_model="cf2x_L250")
@@ -78,6 +84,14 @@ from drone_models.so_rpy import dynamics
78
84
All four models accept optional `dist_f` (external force, world frame, N) and `dist_t` (external torque, body frame, N·m) arguments. These are useful for modelling wind, contact forces, or other perturbations without modifying the model itself.
79
85
80
86
```python
87
+
import numpy as np
88
+
from drone_models import parametrize
89
+
from drone_models.so_rpy import dynamics
90
+
91
+
model = parametrize(dynamics, drone_model="cf2x_L250")
0 commit comments