Skip to content

Gymnasium Environments

Crazyflow ships a set of Gymnasium vectorized environments built on top of Sim. They follow the standard VectorEnv interface and are suitable for training RL agents with frameworks such as Stable Baselines3, CleanRL, or custom JAX trainers.

Available environments

Class Task Observation Action
DroneEnv Base class (no reward) pos, quat, vel, ang_vel attitude or force/torque
ReachPosEnv Reach a target position pos, quat, vel, ang_vel, target attitude
ReachVelEnv Match a target velocity vel, ang_vel, target_vel attitude
LandingEnv Land safely pos, quat, vel, ang_vel attitude
FigureEightEnv Follow a figure-8 trajectory pos, quat, vel, ang_vel, phase attitude

All environments run num_envs parallel instances backed by a single Sim with n_worlds=num_envs.

Basic usage

import gymnasium
import crazyflow.envs  # noqa: F401 - registers the environments

env = gymnasium.make_vec("DroneFigureEightTrajectory-v0", num_envs=16)
obs, info = env.reset()

for _ in range(500):
    action = env.action_space.sample()  # random policy for illustration
    obs, reward, terminated, truncated, info = env.step(action)

env.close()

Constructor arguments

All environments accept these common arguments:

Argument Default Description
num_envs 1 Number of parallel environments
max_episode_time 10.0 Episode length before truncation, seconds
dynamics Dynamics.so_rpy Dynamics
drone "cf2x_L250" Drone configuration
freq 500 Dynamics frequency, Hz
device "cpu" "cpu" or "gpu"
reset_randomization None Optional (SimData, SimData, mask) → SimData function applied at reset (base DroneEnv only)

Action normalization

NormalizeActionsWrapper rescales the action space to [-1, 1], which simplifies policy learning:

import gymnasium
import crazyflow.envs  # noqa: F401 - registers the environments
from crazyflow.envs.norm_actions_wrapper import NormalizeActions

env = NormalizeActions(gymnasium.make_vec("DroneFigureEightTrajectory-v0", num_envs=32))
obs, info = env.reset()
action = env.action_space.sample()  # in [-1, 1]^4
obs, reward, terminated, truncated, info = env.step(action)
env.close()

Reset randomization

Pass a reset_randomization callable to vary initial conditions between episodes. The function receives SimData, the default SimData, and a boolean mask selecting the environments being reset, and must return updated SimData:

import jax
from crazyflow.envs.drone_env import DroneEnv
from crazyflow.sim.data import SimData
from crazyflow.utils import leaf_replace

def randomize(data: SimData, default_data: SimData, mask: jax.Array | None) -> SimData:
    key, subkey = jax.random.split(data.core.rng_key)
    data = data.replace(core=data.core.replace(rng_key=key))  # Make sure to update the rng_key
    noise = jax.random.normal(subkey, data.states.pos.shape) * 0.05
    states = leaf_replace(data.states, mask, pos=data.states.pos + noise)
    return data.replace(states=states)

env = DroneEnv(num_envs=64, reset_randomization=randomize)
env.close()

Next steps

  • Examples — figure-8 and RL training examples
  • Functional API — building fully jittable training loops with jax.lax.scan