Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,4 @@ Pure, batched, array-API functions with no dependency on `Sim`.
`attach_splats` performs no calibration. Both `.ply` files must already sit in the simulator's
frames, the scene in the MuJoCo world frame at metric scale and the drone in its body frame. An
uncalibrated splat looks correct alone but does not match the simulated geometry. See
[`splats.md`](docs/user-guide/splats.md).
[`splat-rendering.md`](docs/user-guide/splat-rendering.md).
24 changes: 24 additions & 0 deletions docs/examples/contacts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Contact examples

Querying contacts between drones and the scene, and choosing the collision geometry of the drone. See [MuJoCo Integration](../user-guide/mujoco.md) for how the scene is built.

---

## Contact queries

<div class="example-media-grid example-media-grid--contacts">
<figure>
<img src="../../img/examples/contact_sphere.png" alt="Contact query visualization using the default sphere collision geometry">
</figure>
<figure>
<img src="../../img/examples/contact_box.png" alt="Contact query visualization using the oriented box collision geometry">
</figure>
</div>

--8<-- example "examples/contacts/contacts.py"

---

## Crash detection

--8<-- example "examples/contacts/crash.py"
63 changes: 63 additions & 0 deletions docs/examples/control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Control examples

Flying drones with the different control modes, from state commands down to force and torque, comparing the dynamics models, and model-based control. See [Control Modes](../user-guide/control/index.md) for the controller behind each mode.

---

## Hover

--8<-- example "examples/control/hover.py"

---

## Changing the state

--8<-- example "examples/control/change_pos.py"

---

## Spirals

--8<-- example "examples/control/spiral.py"

---

## Attitude control

--8<-- example "examples/control/attitude.py"

---

## Body rate control

--8<-- example "examples/control/body_rate.py"

---

## Force and torque control

--8<-- example "examples/control/force_torque.py"

---

## Comparing integrators

--8<-- example "examples/control/integrators.py"

---

## Comparing dynamics models

--8<-- example "examples/control/dynamics.py"

---

## Symbolic dynamics

--8<-- example "examples/control/symbolic.py"

---

## Sampling-based MPC

--8<-- example "examples/control/sampling.py"
15 changes: 15 additions & 0 deletions docs/examples/environments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Environment examples

Running the Gymnasium environments that wrap `Sim` for reinforcement learning. See [Gymnasium Environments](../user-guide/gymnasium-envs.md) for the available environments.

---

## Figure-eight environment

--8<-- example "examples/environments/figure8.py"

---

## Reach-position environment

--8<-- example "examples/environments/gymnasium_env.py"
265 changes: 8 additions & 257 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
@@ -1,259 +1,10 @@
# Examples

These runnable examples cover control, JAX transformations, pipeline extensions, rendering, contacts, and Gymnasium environments. Start with hover if you're new, or jump to the section that matches your use case.

---

## Hover

A single drone commanded to hold a fixed height using state control. This is the minimal end-to-end loop: create a `Sim`, reset it, apply a state command, and step forward.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/control/hover.py"
```

```bash
python examples/control/hover.py
```

---

## Attitude control

Commanding roll, pitch, yaw, and collective thrust directly. This level bypasses the Mellinger position loop and is typical for RL agents that output attitude targets.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/control/attitude.py"
```

---

## Body rate control

Commanding body-frame angular rates and collective thrust. The firmware controller has no dedicated body rate mode and levels the drone with its attitude terms, so the example sets the `kR` and `ki_m` gains of the body rate controller to zero.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/control/body_rate.py"
```

---

## Sampling-based MPC

A sampling-based model predictive controller tracks a Lissajous curve while avoiding a grid of obstacles. It rolls out thousands of candidate control sequences in parallel using identified dynamics, then applies the first action from a cost-weighted update of the best samples. The controller automatically uses a GPU when one is available and lowers the sample count on CPU.

```bash
python examples/control/sampling.py
```

---

## Gradient descent through dynamics

Because the simulator is built entirely from JAX operations, `jax.grad` can differentiate through it. Starting the drone above the target height keeps it away from the floor, so the floor-clipping stage never fires and gradients flow freely through the entire trajectory.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/jax/gradient.py"
```

---

## Gradients and command clipping

Hard clips such as the `clip_rotor_vel_cmd` stage zero the gradients while the command is saturated. This example ramps a motor command beyond the rotor limits and compares the rotor state and the gradient of the vertical acceleration w.r.t. the command for three options: the default clip, a straight-through clip (clipped forward pass, unclipped gradients), and no clip. Replacing the default clip with the straight-through variant can help gradient-based methods such as trajectory optimization or policy learning, which would otherwise receive zero gradients whenever the motors saturate.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/jax/gradient_clipping.py"
```

---

## Sharding across devices

We can also distribute the simulation across devices. The host backend is asked for four logical devices. We then compare this against running on a single device. See [Sharding](../user-guide/sharding.md) for the details.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/jax/sharding.py"
```

---

## Domain randomization

Randomizing mass, inertia and drag per drone, and thrust and torque curves, rotor dynamics, arm lengths and propeller inertias per motor through the reset pipeline, each by a uniform factor around its default value. An optional mask limits randomization to selected worlds.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/plugins/randomize.py"
```

```bash
python examples/plugins/randomize.py
```

---

## Disturbance injection

Inserting a random external force and torque into the step pipeline. The disturbance fires on every dynamics tick, so the drone fights wind-like perturbations.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/plugins/disturbance.py"
```

---

## Downwash

Modeling downwash between identical Crazyflies with a far-field jet model. The plugin samples the flow at each target rotor and center of mass, computes rotor thrust loss and drag, and writes the resulting world-frame external force and torque.

The demo holds one `cf21B_500` drone in hover while a second makes three passes underneath at different speeds and heights.

<div class="example-media-grid example-media-grid--contacts">
<figure class="example-media">
<img src="../img/examples/downwash_example.gif" alt="Drone flying through downwash cone">
</figure>
<figure>
<img src="../img/examples/downwash_field.png" alt="Downwash field generated by hovering drone">
</figure>
</div>

```bash
python examples/plugins/downwash.py
```

---

## Ground effect

Modeling the increase in thrust near the floor for a `cf21B_500` drone. The plugin computes nominal thrust from the rotor speeds, applies a height-dependent gain, and adds the extra thrust along the drone's body z-axis as a world-frame external force.

<!-- notest: imported script -->
```{ .python notest }
--8<-- "examples/plugins/ground_effect.py"
```

```bash
python examples/plugins/ground_effect.py
```

---

## State derivatives

Adding the state derivatives, which are not stored by default, to the step pipeline with plugins. One plugin calls the dynamics at the end of each step, which gives the exact derivative at the current state. The other uses finite differences of the states, which give the exact average derivative from the last to the current step. Both store a `SimStateDeriv` in the plugins dict. The plot compares them while the drone flies a figure-eight.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/plugins/derivatives.py"
```

```bash
python examples/plugins/derivatives.py
```

---

## IMU

Simulating an IMU as a plugin. The accelerometer measures the specific force and the gyroscope the body rates, both with noise and drifting biases, while the drone flies loops of a figure-eight. The plot compares the readings with the true state, skipping the first loop.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/plugins/imu.py"
```

```bash
python examples/plugins/imu.py
```

---

## Cameras and RGBD

Offscreen rendering returns RGB-D images on every frame. The FPV camera (`fpv_cam`) is attached to the drone and moves with it.

<figure class="example-media">
<img src="../img/examples/cameras.gif" alt="RGB-D camera outputs from a Crazyflow drone simulation">
</figure>

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/rendering/cameras.py"
```

```bash
python examples/rendering/cameras.py
```

---

## LED deck and materials

`change_material` updates the RGBA colour and emission of any named material on any subset of drones at runtime.

<figure class="example-media example-media--compact">
<img src="../img/examples/led_decks.png" alt="Crazyflow drones with runtime-controlled LED deck materials">
</figure>

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/rendering/led_deck.py"
```

```bash
python examples/rendering/led_deck.py
```

---

## Contact queries

The default collision geometry is a sphere around the drone frame. `use_box_collision` replaces it with a tighter oriented box, useful for narrow-gap flight and accurate contact debugging.

<div class="example-media-grid example-media-grid--contacts">
<figure>
<img src="../img/examples/contact_sphere.png" alt="Contact query visualization using the default sphere collision geometry">
</figure>
<figure>
<img src="../img/examples/contact_box.png" alt="Contact query visualization using the oriented box collision geometry">
</figure>
</div>

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/contacts/contacts.py"
```

---

## Raycasting and depth sensing

`render_depth` fires rays from a camera and returns per-pixel distances. This is faster than full RGB rendering and useful for obstacle sensing or depth-based controllers.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/rendering/raycasting.py"
```

```bash
python examples/rendering/raycasting.py
```

---

## Gymnasium environment

Evaluating a random policy in the figure-8 environment. The env wraps `Sim` behind the standard Gymnasium `VectorEnv` interface.

<!-- notest: imported script, covered by tests/integration/test_examples.py -->
```{ .python notest }
--8<-- "examples/environments/figure8.py"
```
The runnable examples are grouped by topic, following the folders in `examples/`. Start with [hover](control.md#hover) if you're new, or jump to the group that matches your use case. Run an example from the repository root with `python examples/<group>/<script>.py`.

- [Control](control.md) — how to command the drones at different levels, such as positions, attitudes, or body rates, and how to build your own controllers on top
- [JAX](jax.md) — how to use JAX transformations on the simulation, such as gradients through the dynamics or sharding across devices
- [Plugins](plugins.md) — how to easily add features to the simulation, such as domain randomization, disturbances, or aerodynamic effects
- [Rendering](rendering.md) — how to render the simulation with MuJoCo or gaussian splats and extract sensor data, such as camera images or depth maps
- [Contacts](contacts.md) — how to detect collisions between drones and their environment
- [Environments](environments.md) — how to train and evaluate reinforcement learning policies with Gymnasium environments
Loading
Loading