Feature/downwash - #128
Feature/downwash#128rducrist wants to merge 5 commits into
Conversation
Screencast.from.24.09.2026.11.17.34.webm |
ratheron
left a comment
There was a problem hiding this comment.
Thank for for working on this!
In general, the approach is sound. I've added some minor comments. Would be nice to have the specific equations such that it's easier to understand whats going on.
| offsets = data.params.L * jnp.stack( | ||
| [-mixing_matrix[1], mixing_matrix[0], jnp.zeros_like(mixing_matrix[0])], | ||
| axis=0 | ||
| ) |
There was a problem hiding this comment.
The offsets are set freely by you, right? So we could just use the mixing matrix without sign changes and order flips, no?
|
|
||
| # Broadcast each source rotation across all target drones and rotors. | ||
| source_to_target_body = ( | ||
| R_body_to_world.as_matrix().mT[:, :, None, None, :, :] @ source_to_target[..., None] |
There was a problem hiding this comment.
Why don't we use rotation.apply() here?
| offsets = data.params.L * jnp.stack( | ||
| [-mixing_matrix[1], mixing_matrix[0], jnp.zeros_like(mixing_matrix[0])], axis=0 | ||
| ) | ||
| rotor_offsets_world = (R_body_to_world.as_matrix() @ offsets).mT |
There was a problem hiding this comment.
| rotor_offsets_world = (R_body_to_world.as_matrix() @ offsets).mT | |
| rotor_offsets_world = R_body_to_world.apply(offsets)).as_matrix() |
If we need the inverse, use .inverse()
There was a problem hiding this comment.
We can use apply here, it's just not as simple as written here. Since offsets is shape (3, 4) and R_body_to_world (1,2,4), we'll encounter some broadcasting issues.
This implements downwash as an external wrench. It uses the far-field velocity model from https://arxiv.org/pdf/2403.13321 and the thrust loss computation from https://arxiv.org/pdf/2207.09645.
It works like this:
For each source-target rotor pair, the target rotor’s world position is
The relative displacement from source to target rotor gives axial separation$$s$$ and lateral distance $$r$$ . The far-field velocity model is
All downwash source velocities are summed to obtain$U_{D,j}$ at each target rotor.
Using the thrust-decay model,
Note that$-b_v$ is the thrust loss coefficient. It has to be fitted normally. However for proof of concept I went with a value suggested by AI.
The four individual thrust losses are mapped into a body-frame wrench:
Roll/pitch torque comes from the motor arms and mixing matrix; yaw torque comes from the corresponding change in propeller reaction torque. Finally, both are rotated into the world frame and written to
states.forceandstates.torque.The experiment consists of a drone hovering and the other one flying forth and back underneath the downwash cone at different heights.

The resulting downwash cone of the hovering drone looks like this
