### Install FlyGym with Examples Optional Dependency Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Include the 'examples' optional dependency to follow along with tutorials. ```sh pip install flygym[examples] ``` -------------------------------- ### Install FlyGym Gymnasium (v1) with Optional Dependencies Source: https://github.com/nely-epfl/flygym/blob/main/docs/migration.md Install the Gymnasium-compliant version of FlyGym along with optional dependencies for examples and development. ```sh pip install "flygym-gymnasium[examples,dev]" ``` -------------------------------- ### Install Basic FlyGym with Pip Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Use this command for a standard installation of FlyGym with basic functionalities. ```sh pip install flygym ``` -------------------------------- ### Install FlyGym with uv for Development Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Install FlyGym and all optional dependencies using uv for development purposes. Remove '--extra' flags if specific optional dependencies are not needed. ```sh uv sync --extra warp --extra dev --extra examples ``` -------------------------------- ### Install FlyGym with Multiple Optional Dependencies Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Combine multiple optional dependencies like 'warp' and 'examples' in a single pip command. ```sh pip install flygym[warp,examples] ``` -------------------------------- ### Launch MuJoCo Interactive Viewer Source: https://github.com/nely-epfl/flygym/blob/main/docs/interactive.md Execute this command from the root of the cloned flygym directory to start the interactive viewer. ```sh uv run python scripts/launch_interactive_viewer.py ``` -------------------------------- ### Install FlyGym Gymnasium (v1) Source: https://github.com/nely-epfl/flygym/blob/main/docs/migration.md Install the Gymnasium-compliant version of FlyGym using pip. This command installs the core package. ```sh pip install flygym-gymnasium ``` -------------------------------- ### Setup Fly and Simulation Environment Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4a_cpg_controller.ipynb Initializes the simulation environment, creates a fly model with adhesion, attaches a tracking camera, and sets up a flat ground world. This code is essential for preparing the simulation for locomotion experiments. ```python import numpy as np import matplotlib.pyplot as plt from tqdm import trange from flygym import Simulation from flygym.anatomy import BodySegment from flygym.compose import FlatGroundWorld from flygym_demo.complex_terrain import ( CPGController, LocomotionAction, PreprogrammedSteps, apply_locomotion_action, make_locomotion_fly, make_tripod_cpg_network, ) from flygym.utils.math import Rotation3D from pathlib import Path output_dir = Path("outputs/cpg_controller") output_dir.mkdir(parents=True, exist_ok=True) fly = make_locomotion_fly( name="cpg_demo", add_adhesion=True, colorize=True, ) body_cam = fly.add_tracking_camera( name="body_cam", pos_offset=(0.0, -8.0, 1.0), rotation=Rotation3D("euler", (1.57, 0.0, 0.0)), fovy=30.0, ) world = FlatGroundWorld() spawn_pos = [0, 0, 0.5] # FlyGym v1 default spawn z spawn_rot = Rotation3D("quat", [1, 0, 0, 0]) world.add_fly(fly, spawn_pos, spawn_rot) sim = Simulation(world) renderer = sim.set_renderer([body_cam]) ``` -------------------------------- ### Setup FlyGym Simulation Environment Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4b_rule_based_controller.ipynb Initializes the FlyGym simulation with a legs-only fly, adhesion, a tracking camera, and a flat ground world. This code sets up the basic environment for testing the rule-based controller. ```python from pathlib import Path import matplotlib.pyplot as plt import numpy as np from tqdm import trange from flygym import Simulation from flygym.anatomy import BodySegment from flygym.compose import FlatGroundWorld from flygym_demo.complex_terrain import ( LocomotionAction, PreprogrammedSteps, RuleBasedController, apply_locomotion_action, construct_rules_graph, make_locomotion_fly, ) from flygym.utils.math import Rotation3D output_dir = Path("outputs/rule_based_controller") output_dir.mkdir(parents=True, exist_ok=True) fly = make_locomotion_fly( name="rule_demo", add_adhesion=True, colorize=True, ) body_cam = fly.add_tracking_camera( name="body_cam", pos_offset=(0.0, -8.0, 1.0), rotation=Rotation3D("euler", (1.57, 0.0, 0.0)), fovy=30.0, ) world = FlatGroundWorld() spawn_pos = [0, 0, 0.5] # FlyGym v1 default spawn z spawn_rot = Rotation3D("quat", [1, 0, 0, 0]) world.add_fly(fly, spawn_pos, spawn_rot) sim = Simulation(world) renderer = sim.set_renderer([body_cam]) ``` -------------------------------- ### Install FlyGym with Warp Optional Dependency Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Add the 'warp' optional dependency to enable GPU acceleration for fly.warp. ```sh pip install flygym[warp] ``` -------------------------------- ### Install EGL Dependencies on Ubuntu/Debian Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Install necessary EGL-related dependencies on Ubuntu/Debian systems for headless rendering. ```sh apt-get install libegl1-mesa-dev ``` -------------------------------- ### Install nbstripout Filter Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Install the nbstripout filter to automatically remove Jupyter Notebook outputs when adding files to git. This is recommended for developers. ```sh source .venv/bin/activate nbstripout --install --attributes .gitattributes ``` -------------------------------- ### Import necessary libraries for hybrid controller simulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4c_hybrid_controller.ipynb Imports required libraries for simulation, plotting, and the hybrid controller components. Ensure these are installed before running. ```python from pathlib import Path import matplotlib.pyplot as plt import numpy as np from tqdm import trange from flygym import Simulation from flygym.anatomy import BodySegment, ContactBodiesPreset from flygym.compose import MixedTerrainWorld from flygym_demo.complex_terrain import ( HybridController, LocomotionAction, PreprogrammedSteps, apply_locomotion_action, make_locomotion_fly, ) from flygym.utils.math import Rotation3D output_dir = Path("outputs/hybrid_controller") output_dir.mkdir(parents=True, exist_ok=True) ``` -------------------------------- ### Reset simulation and apply initial action Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4c_hybrid_controller.ipynb Resets the simulation and controller states, then applies an initial locomotion action to set up the fly's starting pose before the simulation begins. ```python sim.reset() controller.reset(seed=0) initial_action = LocomotionAction( joint_angles=preprogrammed_steps.default_pose_by_dof_order(dof_order), adhesion_onoff=np.ones(6, dtype=bool), ) apply_locomotion_action(sim, fly.name, initial_action) sim.warmup() ``` -------------------------------- ### Sync Dependencies Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Synchronizes project dependencies using uv. Ensure to include necessary extras like 'dev', 'examples', and 'warp'. ```bash uv sync --extra dev --extra examples --extra warp ``` -------------------------------- ### Execute Batch Rendering Simulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Calls the `run_simulation_with_batch_rendering` function to start a simulation with GPU batch rendering enabled for a specified number of worlds. ```python run_simulation_with_batch_rendering(n_worlds=100) ``` -------------------------------- ### Compile and Visualize World Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Compile the MJCF model into MuJoCo objects and visualize the world with the fly interacting with the ground. This step allows for a quick preview of the simulation setup. ```python mj_model, mj_data = world.compile() preview_model(mj_model, mj_data, camera, show_in_notebook=True) ``` -------------------------------- ### Check for Compatible GPU Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Verifies the availability of a compatible GPU for accelerated simulations. Ensure you have a suitable NVIDIA GPU and the necessary drivers installed. ```python from flygym.warp.utils import check_gpu check_gpu() ``` -------------------------------- ### Prepare and Get Joint Angles for Simulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb This snippet prepares the simulation timestep and retrieves joint angles from experimental data, upsampling them to match the simulation frequency. It also calculates the simulation duration and creates a time grid. ```python sim_timestep = 1e-4 joint_angles_nmf = snippet.get_joint_angles( output_timestep=sim_timestep, output_dof_order=fly.get_actuated_jointdofs_order(actuator_type), ) sim_duration_sec = snippet.joint_angles.shape[0] / snippet.data_fps nmfsim_timegrid = np.arange(0, sim_duration_sec, sim_timestep) print("Number of steps to simulate:", nmfsim_timegrid.size) print("Shape of target joint angles:", joint_angles_nmf.shape) ``` -------------------------------- ### Initialize fly and simulation environment Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4c_hybrid_controller.ipynb Sets up the simulated fly, adds a tracking camera, creates a mixed terrain world, and initializes the simulation with the fly and camera. ```python fly = make_locomotion_fly( name="hybrid_demo", add_adhesion=True, colorize=True, ) body_cam = fly.add_tracking_camera( name="body_cam", pos_offset=(0.0, -8.0, 1.2), rotation=Rotation3D("euler", (1.57, 0.0, 0.0)), fovy=35.0, ) world = MixedTerrainWorld() world.add_fly( fly, [0, 0, 1.2], Rotation3D("quat", [1, 0, 0, 0]), bodysegs_with_ground_contact=ContactBodiesPreset.TIBIA_TARSUS_ONLY, add_ground_contact_sensors=False, ) sim = Simulation(world) renderer = sim.set_renderer( [body_cam], camera_res=(240, 320), playback_speed=0.1, output_fps=25, ) ``` -------------------------------- ### Initialize Fly and Simulation Environment Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4d_turning_controller.ipynb Creates a fly instance with adhesion and color, adds a tracking camera, sets up a flat ground world, and initializes the simulation with the fly and camera. ```python fly = make_locomotion_fly( name="turning_demo", add_adhesion=True, colorize=True, ) body_cam = fly.add_tracking_camera( name="body_cam", pos_offset=(0.0, -8.0, 1.0), rotation=Rotation3D("euler", (1.57, 0.0, 0.0)), fovy=35.0, ) world = FlatGroundWorld() world.add_fly( fly, [0, 0, 0.8], Rotation3D("quat", [1, 0, 0, 0]), bodysegs_with_ground_contact=ContactBodiesPreset.TIBIA_TARSUS_ONLY, add_ground_contact_sensors=False, ) sim = Simulation(world) renderer = sim.set_renderer( [body_cam], camera_res=(240, 320), playback_speed=0.1, output_fps=25, ) ``` -------------------------------- ### Set up Simulation World and Environment Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb Creates a simulation environment with a flat ground, attaches the configured fly model to it, and initializes the simulation with a renderer. Requires importing Simulation and FlatGroundWorld. ```python from flygym.anatomy import JointDOF, RotationAxis, BodySegment from flygym.compose import FlatGroundWorld from flygym.utils.math import Rotation3D from flygym import Simulation spawn_pos = [0, 0, 0.7] # center of thorax is at 0.7 mm above the ground spawn_rot = Rotation3D(format="quat", values=[1, 0, 0, 0]) # no rotation world = FlatGroundWorld() world.add_fly(fly, spawn_pos, spawn_rot) sim = Simulation(world) sim.set_renderer(tracking_cam) ``` -------------------------------- ### Configure and instantiate the hybrid controller Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4c_hybrid_controller.ipynb Initializes the `PreprogrammedSteps` and `HybridController`, obtaining the actuated DoF order and simulation timestep. Prints key controller and simulation parameters. ```python preprogrammed_steps = PreprogrammedSteps() dof_order = fly.get_actuated_jointdofs_order("position") controller = HybridController( timestep=sim.timestep, preprogrammed_steps=preprogrammed_steps, output_dof_order=dof_order, ) print("Actuated DoFs:", len(dof_order)) print("Simulation timestep:", sim.timestep) print( "Controller CPG frequency (Hz):", float(controller.cpg_network.intrinsic_freqs[0]) ) ``` -------------------------------- ### Modify Simulation Timestep Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1b_advanced_model_composition.ipynb Change the simulation timestep to alter speed and stability. This example shows how to access and modify the timestep attribute. ```python print("Old timestep:", fly.mjcf_root.option.timestep) fly.mjcf_root.option.timestep = 0.001 print("New timestep:", fly.mjcf_root.option.timestep) ``` -------------------------------- ### Initialize Simulation and Apply Initial Action Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4a_cpg_controller.ipynb Resets the simulation, sets the fly to a default pose with adhesion enabled, and allows the fly to settle on the ground. This warmup phase is crucial for stable locomotion. ```python sim.reset() initial_action = LocomotionAction( joint_angles=preprogrammed_steps.default_pose_by_dof_order(dof_order), adhesion_onoff=np.ones(6, dtype=bool), ) apply_locomotion_action(sim, fly.name, initial_action) sim.warmup() ``` -------------------------------- ### Instantiate Rule-Based Controller and Parameters Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4b_rule_based_controller.ipynb Creates instances of `PreprogrammedSteps`, constructs the `rules_graph`, and defines `weights` for the Walknet rules. This code initializes the controller with specific parameters for locomotion. ```python preprogrammed_steps = PreprogrammedSteps() dof_order = fly.get_actuated_jointdofs_order("position") rules_graph = construct_rules_graph() weights = { "rule1": -10.0, "rule2_ipsi": 2.5, "rule2_contra": 1.0, "rule3_ipsi": 3.0, "rule3_contra": 2.0, } controller = RuleBasedController( timestep=sim.timestep, rules_graph=rules_graph, weights=weights, preprogrammed_steps=preprogrammed_steps, output_dof_order=dof_order, seed=0, ) print("Actuated DoFs:", len(dof_order)) print("Simulation timestep:", sim.timestep) print( "Kinematic spline cycle rate (Hz):", round(preprogrammed_steps.step_cycle_frequency_hz, 4), ) print("Rule weights:", weights) for rule_name, edges_by_source in rules_graph.items(): print(f"{rule_name}:", ``` -------------------------------- ### Instantiate CPG Controller and Print Info Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4a_cpg_controller.ipynb Creates a CPG controller by combining a CPG network with preprogrammed step trajectories. It also prints key simulation and controller parameters for verification. Ensure the timestep and CPG parameters are set appropriately for your simulation. ```python preprogrammed_steps = PreprogrammedSteps() dof_order = fly.get_actuated_jointdofs_order("position") cpg_network = make_tripod_cpg_network( timestep=sim.timestep, intrinsic_amplitude=1.0, coupling_strength=10.0, convergence_coef=20.0, seed=0, ) controller = CPGController( cpg_network=cpg_network, preprogrammed_steps=preprogrammed_steps, output_dof_order=dof_order, ) print("Actuated DoFs:", len(dof_order)) print("Simulation timestep:", sim.timestep) print( "CPG intrinsic frequency (network, Hz):", float(cpg_network.intrinsic_freqs[0]), ) print( "Kinematic spline cycle rate (Hz, informational):", round(preprogrammed_steps.step_cycle_frequency_hz, 4), ) print("CPG phases:", np.round(cpg_network.curr_phases, 3)) ``` -------------------------------- ### List Distribution Files Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Verifies that the wheels were successfully built and are present in the `dist/` directory. ```bash ls dist/ ``` -------------------------------- ### Modify Headlight Ambience Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1b_advanced_model_composition.ipynb Adjust the background lighting brightness by modifying the headlight ambience. This example demonstrates accessing and setting RGB values for ambient light. ```python print("Old headlight ambience:", fly.mjcf_root.visual.headlight.ambient) fly.mjcf_root.visual.headlight.ambient = [0.1, 0.1, 0.1] # in RGB print("New headlight ambience:", fly.mjcf_root.visual.headlight.ambient) ``` -------------------------------- ### Initialize FlatGroundWorld Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Instantiate a FlatGroundWorld for a standard simulation environment. This world provides an infinite ground plane for free fly movement. ```python from flygym.compose import FlatGroundWorld world = FlatGroundWorld() ``` -------------------------------- ### Select Actuated DoFs from Preset Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Get a list of actuated joint DoFs based on a chosen preset, such as 'LEGS_ACTIVE_ONLY'. This determines which joints will be actively controlled by actuators. ```python from flygym.anatomy import ActuatedDOFPreset actuation_preset = ActuatedDOFPreset.LEGS_ACTIVE_ONLY actuated_jointdofs = skeleton.get_actuated_dofs_from_preset(actuation_preset) print( f"{len(actuated_jointdofs)} actuated DoFs selected based on '{actuation_preset.name}'." ) ``` -------------------------------- ### Stage and Commit Changes for New Branch Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Stages all modified files and commits them with a message indicating the start of a new development branch. Verify `git status` before staging. ```bash git add . ``` ```bash git commit -m "start new branch for vy.y.y" ``` -------------------------------- ### Run Basic GPU Simulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb This snippet sets up and runs a GPU-accelerated simulation with adhesion enabled for all legs. It includes a simulation loop with control inputs and profiling for performance analysis. ```python from tqdm import trange # for progress bar fly_name = fly.name sim.reset() # Turn adhesion on for all 6 legs across all worlds sim.set_leg_adhesion_states(fly_name, np.ones((n_worlds, 6), dtype=np.float32)) # As before, warm up the simulation for a few steps before applying the control inputs sim.warmup() # Run simulation loop for step in trange(sim_steps): control_inputs = dof_angles_all_worlds[:, step, :] sim.set_actuator_inputs(fly_name, ActuatorType.POSITION, control_inputs) sim.step_with_profile() sim.render_as_needed_with_profile() ``` -------------------------------- ### Initialize GPUSimulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Instantiates GPUSimulation with a given world and the number of parallel worlds to simulate. Ensure all worlds share the same model. ```python from flygym.warp import GPUSimulation n_worlds = 100 sim = GPUSimulation(world, n_worlds) ``` -------------------------------- ### Export World Model Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Save the complete world model, including the attached fly, to an MJCF XML file and associated assets. This allows for inspection and further use of the simulation setup. ```python world.save_xml_with_assets("./demo_output/world_model/") ``` -------------------------------- ### Launch Interactive Viewer with uv Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Use this command to launch the interactive viewer when managing packages with `uv`. Ensure you are in the project directory. ```sh uv run mjpython scripts/launch_interactive_viewer.py ``` -------------------------------- ### Get Path to MJCF XML File Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1b_advanced_model_composition.ipynb Constructs and prints the file path to the generated MJCF XML file for the FlyGym model. This path can be used to access and inspect the model's XML specification. ```python xml_path = export_dir / f"{fly.name}.xml" # path to the actual XML print(f"View XML file at this path: {xml_path}") ``` -------------------------------- ### Configure Hybrid Turning Controller Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4d_turning_controller.ipynb Initializes the HybridTurningController with simulation timestep and preprogrammed steps. It also prints the number of actuated degrees of freedom and the simulation timestep. ```python preprogrammed_steps = PreprogrammedSteps() dof_order = fly.get_actuated_jointdofs_order("position") controller = HybridTurningController( timestep=sim.timestep, preprogrammed_steps=preprogrammed_steps, output_dof_order=dof_order, ) print("Actuated DoFs:", len(dof_order)) print("Simulation timestep:", sim.timestep) ``` -------------------------------- ### Set Environment Variables for Headless Rendering (Bash) Source: https://github.com/nely-epfl/flygym/blob/main/docs/installation.md Set MUJOCO_GL and PYOPENGL_PLATFORM environment variables to 'egl' for rendering on machines without a display. Add these to your .bashrc for persistence. ```sh export MUJOCO_GL=egl export PYOPENGL_PLATFORM=egl ``` -------------------------------- ### Get Joint Angles by Axis Order Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Retrieve joint angles from a KinematicPose object, specifying the desired axis order (e.g., ROLL_YAW_PITCH or PITCH_ROLL_YAW). This is useful for understanding how axis permutations affect angle representation. ```python from flygym.compose import KinematicPosePreset from flygym.util import AxisOrder neutral_pose = KinematicPosePreset.NEUTRAL pose_ryp = neutral_pose.get_pose_by_axis_order(AxisOrder.ROLL_YAW_PITCH) angles_ryp = pose_ryp.joint_angles_lookup_rad pose_pry = neutral_pose.get_pose_by_axis_order(AxisOrder.PITCH_ROLL_YAW) angles_pry = pose_pry.joint_angles_lookup_rad print("Roll-yaw-pitch ordering:") print(f" c_thorax-lf_coxa-roll: {angles_ryp.get('c_thorax-lf_coxa-roll', 0):.4f} rad") print(f" c_thorax-lf_coxa-yaw: {angles_ryp.get('c_thorax-lf_coxa-yaw', 0):.4f} rad") print(f" c_thorax-lf_coxa-pitch: {angles_ryp.get('c_thorax-lf_coxa-pitch', 0):.4f} rad") print("\nPitch-roll-yaw ordering:") print(f" c_thorax-lf_coxa-pitch: {angles_pry.get('c_thorax-lf_coxa-pitch', 0):.4f} rad") print(f" c_thorax-lf_coxa-roll: {angles_pry.get('c_thorax-lf_coxa-roll', 0):.4f} rad") print(f" c_thorax-lf_coxa-yaw: {angles_pry.get('c_thorax-lf_coxa-yaw', 0):.4f} rad") ``` -------------------------------- ### Display and Save Rule-Based Walking Rollout Video Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4b_rule_based_controller.ipynb Displays the rendered video of the rule-based walking simulation directly in the notebook and saves it to a specified output file. ```python sim.renderer.show_in_notebook() sim.renderer.save_video(output_dir / "rule_based_controller.mp4") ``` -------------------------------- ### Configure Renderer for Multiple Worlds Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Sets up a renderer for the simulation, specifying which worlds to render to manage memory usage. If 'worlds' is omitted, all worlds are rendered. ```python renderer = sim.set_renderer( cam, playback_speed=0.2, output_fps=25, worlds=list(range(9)), # render the first 9 worlds only ) ``` -------------------------------- ### Define GPU Simulation Function Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Encapsulates the setup and execution of a GPU simulation, including model creation, renderer configuration, and the simulation loop. This function is designed to ensure simulation isolation by recreating models and data for each run. ```python def run_simulation(n_worlds): dof_angles_all_worlds = make_target_angles_all_worlds(n_worlds) fly, world, cam = make_model() fly_name = fly.name sim = GPUSimulation(world, n_worlds) renderer = sim.set_renderer( cam, playback_speed=0.2, output_fps=25, worlds=list(range(9)), # render the first 9 worlds only ) sim.set_leg_adhesion_states(fly_name, np.ones((n_worlds, 6), dtype=np.float32)) sim.warmup() for step in trange(sim_steps): control_inputs = dof_angles_all_worlds[:, step, :] sim.set_actuator_inputs(fly_name, ActuatorType.POSITION, control_inputs) sim.step_with_profile() sim.render_as_needed_with_profile() sim.print_performance_report() renderer.show_in_notebook(world_id=list(range(9)), scale=0.5) ``` -------------------------------- ### List Exported XML File Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Verify the creation of the world model XML file by listing its contents. This confirms that the export process was successful. ```bash !ls ./demo_output/world_model/*.xml ``` -------------------------------- ### Instantiate and Configure Fly Model Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb Initializes a FlyGym fly model with specified skeletal structure, actuated degrees of freedom, and actuator properties. Requires importing necessary classes from flygym.compose and flygym.anatomy. ```python from flygym.compose import Fly, KinematicPosePreset, ActuatorType from flygym.anatomy import Skeleton, AxisOrder, JointPreset, ActuatedDOFPreset axis_order = AxisOrder.YAW_PITCH_ROLL articulated_joints = JointPreset.LEGS_ONLY actuated_dofs = ActuatedDOFPreset.LEGS_ACTIVE_ONLY neutral_pose = KinematicPosePreset.NEUTRAL actuator_type = ActuatorType.POSITION # This controlls how strongly the actuators try to track the target joint angles actuator_gain = 150.0 # in uN*mm/rad (torque applied per angular discrepancy) fly = Fly() skeleton = Skeleton(axis_order=axis_order, joint_preset=articulated_joints) fly.add_joints(skeleton, neutral_pose=neutral_pose) actuated_dofs_list = fly.skeleton.get_actuated_dofs_from_preset(actuated_dofs) fly.add_actuators( actuated_dofs_list, actuator_type=actuator_type, kp=actuator_gain, neutral_input=neutral_pose, ) sites = fly.add_joint_sites(JointPreset.LEGS_ONLY.to_joint_list()) fly.colorize() tracking_cam = fly.add_tracking_camera() ``` -------------------------------- ### Display Rendered Videos in Notebook Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Renders and displays a specified subset of simulation worlds in the notebook. Use the `scale` argument to manage memory usage by downscaling videos. ```python renderer.show_in_notebook(world_id=list(range(9)), scale=0.5) ``` -------------------------------- ### Execute Full GPU Simulation Benchmark with Rendering Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb This code snippet calls the `run_simulation_fullgpu` function to benchmark the simulation performance with rendering enabled for 1000 worlds. ```python run_simulation_fullgpu(n_worlds=1000, enable_rendering=True) ``` -------------------------------- ### Build Docker Image Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Builds the Docker image for the project. Tags are applied for the general image and a specific version. ```bash docker build -t nelyepfl/flygym -t nelyepfl/flygym:vx.x.x . ``` -------------------------------- ### Run Pytest Suite Source: https://github.com/nely-epfl/flygym/blob/main/CONTRIBUTING.md Execute the test suite for FlyGym using `uv` and `pytest`. Ensure you are on the correct development branch before running. ```bash uv run pytest tests/ ``` -------------------------------- ### Check Code Formatting Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Verifies that all files adhere to the project's code formatting standards using ruff. Use `uv run ruff format .` to automatically format files if needed. ```bash uv run ruff format --check . ``` -------------------------------- ### Compile and Preview Model Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1b_advanced_model_composition.ipynb Compile the MuJoCo model and data, then preview the model in a notebook. This snippet is useful for visualizing changes made to the simulation parameters. ```python mj_model, mj_data = fly.compile() preview_model(mj_model, mj_data, camera, show_in_notebook=True) ``` -------------------------------- ### Print Simulation Performance Report Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb This snippet prints a detailed breakdown of the compute time for simulation steps and rendering, useful for performance analysis. Use `sim.step()` and `sim.render_as_needed()` for slightly faster execution in practice. ```python sim.print_performance_report() ``` -------------------------------- ### Preview Compiled Model Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Simulates the compiled fly model for a very short period to preview its behavior and rendering. Requires the compiled model, data, and camera objects. ```python from flygym.rendering import preview_model preview_model(mj_model, mj_data, camera, show_in_notebook=True) ``` -------------------------------- ### Main Simulation Loop with Data Recording and Rendering Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb This snippet implements the core simulation loop. It sets target joint angles, steps the physics, records joint angles and actuator torques, and renders frames as needed. Leg adhesion is enabled before the loop and maintained throughout. ```python from tqdm import trange fly_name = fly.name nsteps_sim = nmfsim_timegrid.size n_dofs = len(fly.get_jointdofs_order()) n_actuated_dofs = len(fly.get_actuated_jointdofs_order(actuator_type)) simulated_joint_angles = np.full((nsteps_sim, n_dofs), np.nan, dtype=np.float32) actuator_torques = np.full((nsteps_sim, n_actuated_dofs), np.nan, dtype=np.float32) site_positions = np.full((nsteps_sim, len(sites), 3), np.nan, dtype=np.float32) sim.reset() # Turn adhesion on for all 6 legs sim.set_leg_adhesion_states(fly_name, np.ones(6, dtype=bool)) # Warm up the simulation: simulate a few steps so that the fly "lands" on the ground and # settles into a stable pose before we start replaying the experimental joint angles. sim.warmup() # use sim.warmup(duration_s=...) to use non-default warmup period for step_idx in trange(nsteps_sim, desc="Simulating"): # Set actuator inputs target_angles = joint_angles_nmf[step_idx, :] sim.set_actuator_inputs(fly_name, actuator_type, target_angles) # Step simulation sim.step_with_profile() # can be replaced with sim.step() # Record simulation data simulated_joint_angles[step_idx, :] = sim.get_joint_angles(fly_name) actuator_torques[step_idx, :] = sim.get_actuator_forces(fly_name, actuator_type) site_positions[step_idx, :, :] = sim.get_site_positions(fly_name) # Render as needed (flygym internally decides whether to actually render a frame # based on renderer configs) sim.render_as_needed_with_profile() # can be replaced with sim.render_as_needed() ``` -------------------------------- ### Visualizing Fly Pose Over Time in 3D Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb Plots snapshots of the fly's 3D pose at regular intervals throughout the simulation. Uses site positions to draw leg segments and annotates with time. ```python snapshot_interval = int(0.3 / sim.timestep) # plot a snapshot every 0.3 s n_snapshots = nsteps_sim // snapshot_interval n_anatomical_joints_per_leg = 8 fig = plt.figure(figsize=(8, 8)) ax = fig.add_subplot(projection="3d") for snapshot_idx in range(n_snapshots): step_idx = snapshot_idx * snapshot_interval for leg_idx in range(6): slice_ = slice( leg_idx * n_anatomical_joints_per_leg, (leg_idx + 1) * n_anatomical_joints_per_leg, ) x = site_positions[step_idx, slice_, 0] y = site_positions[step_idx, slice_, 1] z = site_positions[step_idx, slice_, 2] alpha = (snapshot_idx / n_snapshots) * 0.8 + 0.2 ax.plot(x, y, z, color="black", alpha=alpha) leadline_origin = (x[0], y[0], z[0] + 1) leadline_end = (x[0], y[0], z[0] + 3) text_center = (x[0], y[0], z[0] + 3.2) ax.plot(*zip(leadline_origin, leadline_end), color="black", alpha=0.5) t = step_idx * sim.timestep ax.text(*text_center, f"t={t:.1f}s", color="black", ha="center", va="bottom") ax.set_xlabel("x (mm)") ax.set_ylabel("y (mm)") ax.set_zlabel("z (mm)") ax.set_aspect("equal") ``` -------------------------------- ### Compose a Fly Object with Custom Settings Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1b_advanced_model_composition.ipynb Initializes a Fly object and adds joints, actuators, and a tracking camera with specified presets and parameters. This sets up the basic model structure before MJCF customization. ```python from flygym.compose import Fly, KinematicPosePreset from flygym.anatomy import AxisOrder, JointPreset, Skeleton, ActuatedDOFPreset fly = Fly(name="my_fly_model") axis_order = AxisOrder.ROLL_PITCH_YAW joint_preset = JointPreset.ALL_BIOLOGICAL skeleton = Skeleton(joint_preset=joint_preset, axis_order=axis_order) neutral_pose = KinematicPosePreset.NEUTRAL joints = fly.add_joints(skeleton, neutral_pose=neutral_pose) actuation_preset = ActuatedDOFPreset.LEGS_ACTIVE_ONLY actuator_gain = 50 actuated_jointdofs = skeleton.get_actuated_dofs_from_preset(actuation_preset) actuators = fly.add_actuators( actuated_jointdofs, actuator_type="position", neutral_input=neutral_pose, kp=actuator_gain, ) fly.colorize() camera = fly.add_tracking_camera() ``` -------------------------------- ### Import necessary libraries Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4d_turning_controller.ipynb Imports all required modules from pathlib, matplotlib, numpy, and flygym for simulation and visualization. ```python from pathlib import Path import matplotlib.pyplot as plt import numpy as np from tqdm import trange from flygym import Simulation from flygym.anatomy import BodySegment, ContactBodiesPreset from flygym.compose import FlatGroundWorld from flygym_demo.complex_terrain import ( HybridTurningController, LocomotionAction, PreprogrammedSteps, apply_locomotion_action, make_locomotion_fly, ) from flygym.utils.math import Rotation3D output_dir = Path("outputs/turning_controller") output_dir.mkdir(parents=True, exist_ok=True) ``` -------------------------------- ### Publish Package to PyPI Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Publishes the built package to the Python Package Index (PyPI). This can be done using a UV publish token or by providing the token directly. ```bash uv publish ``` ```bash uv publish --token ``` -------------------------------- ### Preview MJCF model with FlyGym Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1b_advanced_model_composition.ipynb Compile the MJCF model and visualize it using `flygym.rendering.preview_model`. This function requires the compiled MuJoCo model and data, along with camera parameters. ```python from flygym.rendering import preview_model mj_model, mj_data = fly.compile() preview_model(mj_model, mj_data, camera, show_in_notebook=True) ``` -------------------------------- ### Run Rule-Based Controller Rollout and Record Data Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4b_rule_based_controller.ipynb Executes the rule-based controller for a specified duration, applying actions to the simulation and recording thorax position, leg phases, scores, and adhesion states. Renders frames as needed. ```python run_time = 2.0 nsteps_sim = int(run_time / sim.timestep) time_grid = np.arange(nsteps_sim) * sim.timestep thorax_idx = fly.get_bodysegs_order().index(BodySegment("c_thorax")) thorax_positions = np.full((nsteps_sim, 3), np.nan, dtype=np.float32) rule_phases = np.full((nsteps_sim, 6), np.nan, dtype=np.float32) combined_scores = np.full((nsteps_sim, 6), np.nan, dtype=np.float32) adhesion_onoff = np.zeros((nsteps_sim, 6), dtype=bool) for step_idx in trange(nsteps_sim, desc="Running rule-based controller"): action = controller.step() apply_locomotion_action(sim, fly.name, action) sim.step_with_profile() thorax_positions[step_idx] = sim.get_body_positions(fly.name)[thorax_idx] rule_phases[step_idx] = controller.leg_phases % (2 * np.pi) combined_scores[step_idx] = controller.combined_scores adhesion_onoff[step_idx] = action.adhesion_onoff sim.render_as_needed_with_profile() print( "Rendered frames (body_cam):", len(renderer.frames[body_cam.full_identifier]), ) print( "Thorax forward displacement (first to last sample, sim):", f"{thorax_positions[-1, 0] - thorax_positions[0, 0]:.2f} mm", ) sim.print_performance_report() ``` -------------------------------- ### List Exported Mesh Files Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Lists the first 5 mesh files (.stl) exported to the specified directory. This command is executed in the shell. ```shell !ls ./demo_output/fly_model/*.stl | head -n 5 # list first 5 only ``` -------------------------------- ### Display Rendered Video in Notebook Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/2_replaying_experimental_recordings.ipynb This snippet displays the rendered video of the simulation directly within the notebook environment. ```python sim.renderer.show_in_notebook() ``` -------------------------------- ### Run CPG Controller and Render Simulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4a_cpg_controller.ipynb Executes the CPG controller for a specified duration, applying locomotion actions, stepping the physics, and rendering frames. This loop simulates the fly's locomotion and collects data on its movement. ```python run_time = 2.0 nsteps_sim = int(run_time / sim.timestep) time_grid = np.arange(nsteps_sim) * sim.timestep thorax_idx = fly.get_bodysegs_order().index(BodySegment("c_thorax")) thorax_positions = np.full((nsteps_sim, 3), np.nan, dtype=np.float32) cpg_phases = np.full((nsteps_sim, 6), np.nan, dtype=np.float32) adhesion_onoff = np.zeros((nsteps_sim, 6), dtype=bool) for step_idx in trange(nsteps_sim, desc="Running CPG"): action = controller.step() apply_locomotion_action(sim, fly.name, action) sim.step_with_profile() thorax_positions[step_idx] = sim.get_body_positions(fly.name)[thorax_idx] cpg_phases[step_idx] = controller.cpg_network.curr_phases % (2 * np.pi) adhesion_onoff[step_idx] = action.adhesion_onoff sim.render_as_needed_with_profile() print( "Rendered frames (body_cam):", len(renderer.frames[body_cam.full_identifier]), ) print( "Thorax forward displacement (first to last sample, sim):", f"{thorax_positions[-1, 0] - thorax_positions[0, 0]:.2f} mm", ) sim.print_performance_report() ``` -------------------------------- ### Execute Scaled GPU Simulation Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/3_gpu_accelerated_simulation.ipynb Initiates a GPU simulation with a large number of parallel worlds (e.g., 1000). Demonstrates the scalability of GPU simulations where adding more worlds has minimal impact on simulation time until hardware capacity is reached. ```python run_simulation(n_worlds=1000) ``` -------------------------------- ### Render and Save Simulation Video Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4d_turning_controller.ipynb Displays the simulation video in the notebook and saves it to a specified output file. ```python sim.renderer.show_in_notebook() sim.renderer.save_video(output_dir / "hybrid_turning_controller.mp4") ``` -------------------------------- ### Run Diagnostic Simulation with Rule-Based Controller Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4b_rule_based_controller.ipynb Initializes and runs a diagnostic simulation using the RuleBasedController. It records various metrics like scores and leg phases over time. ```python diagnostic_run_time = 1.0 diagnostic_steps = int(diagnostic_run_time / sim.timestep) diagnostic_controller = RuleBasedController( timestep=sim.timestep, rules_graph=rules_graph, weights=weights, preprogrammed_steps=preprogrammed_steps, output_dof_order=dof_order, seed=0, ) score_hist_overall = np.full((diagnostic_steps, 6), np.nan, dtype=np.float32) score_hist_rule1 = np.full((diagnostic_steps, 6), np.nan, dtype=np.float32) score_hist_rule2 = np.full((diagnostic_steps, 6), np.nan, dtype=np.float32) score_hist_rule3 = np.full((diagnostic_steps, 6), np.nan, dtype=np.float32) leg_phases_hist = np.full((diagnostic_steps, 6), np.nan, dtype=np.float32) for step_idx in range(diagnostic_steps): diagnostic_controller.step() score_hist_overall[step_idx] = diagnostic_controller.combined_scores score_hist_rule1[step_idx] = diagnostic_controller.rule1_scores score_hist_rule2[step_idx] = diagnostic_controller.rule2_scores score_hist_rule3[step_idx] = diagnostic_controller.rule3_scores leg_phases_hist[step_idx] = diagnostic_controller.leg_phases % (2 * np.pi) ``` -------------------------------- ### Display and save simulation video Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/4c_hybrid_controller.ipynb Renders the simulation video in the notebook and saves it to a specified output directory. This is useful for visualizing the locomotion behavior. ```python sim.renderer.show_in_notebook() sim.renderer.save_video(output_dir / "hybrid_controller_mixed_terrain.mp4") ``` -------------------------------- ### List Exported XML File Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb Lists the XML file exported to the specified directory. This command is executed in the shell. ```shell !ls ./demo_output/fly_model/*.xml ``` -------------------------------- ### List Available Actuated DoF Presets Source: https://github.com/nely-epfl/flygym/blob/main/tutorials/1a_basic_model_composition.ipynb View all available presets for selecting actively controlled joint degrees of freedom (DoFs). These presets simplify the process of choosing which joints to actuate. ```python from flygym.anatomy import ActuatedDOFPreset list(ActuatedDOFPreset) ``` -------------------------------- ### Create New Development Branch Source: https://github.com/nely-epfl/flygym/wiki/Internal-Note:-Release-procedure Creates a new local development branch for the next release cycle. ```bash git checkout -b dev-vy.y.y ```