crazyflow 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. crazyflow-0.0.1/LICENSE +9 -0
  2. crazyflow-0.0.1/PKG-INFO +156 -0
  3. crazyflow-0.0.1/README.md +113 -0
  4. crazyflow-0.0.1/benchmark/compile.py +39 -0
  5. crazyflow-0.0.1/benchmark/main.py +156 -0
  6. crazyflow-0.0.1/benchmark/op_count.py +17 -0
  7. crazyflow-0.0.1/benchmark/performance.py +89 -0
  8. crazyflow-0.0.1/crazyflow/__init__.py +6 -0
  9. crazyflow-0.0.1/crazyflow/constants.py +13 -0
  10. crazyflow-0.0.1/crazyflow/control/__init__.py +3 -0
  11. crazyflow-0.0.1/crazyflow/control/control.py +169 -0
  12. crazyflow-0.0.1/crazyflow/exception.py +10 -0
  13. crazyflow-0.0.1/crazyflow/gymnasium_envs/__init__.py +44 -0
  14. crazyflow-0.0.1/crazyflow/gymnasium_envs/crazyflow.py +631 -0
  15. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_0.obj +9912 -0
  16. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_1.obj +196 -0
  17. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_2.obj +9480 -0
  18. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_3.obj +60 -0
  19. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_4.obj +1808 -0
  20. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_5.obj +3862 -0
  21. crazyflow-0.0.1/crazyflow/models/cf2/assets/cf2_6.obj +4798 -0
  22. crazyflow-0.0.1/crazyflow/models/cf2/assets.xml +19 -0
  23. crazyflow-0.0.1/crazyflow/models/cf2/cf2.xml +55 -0
  24. crazyflow-0.0.1/crazyflow/models/cf2/scene.xml +24 -0
  25. crazyflow-0.0.1/crazyflow/randomize/__init__.py +3 -0
  26. crazyflow-0.0.1/crazyflow/randomize/randomize.py +53 -0
  27. crazyflow-0.0.1/crazyflow/sim/__init__.py +5 -0
  28. crazyflow-0.0.1/crazyflow/sim/integration.py +106 -0
  29. crazyflow-0.0.1/crazyflow/sim/physics.py +165 -0
  30. crazyflow-0.0.1/crazyflow/sim/sim.py +634 -0
  31. crazyflow-0.0.1/crazyflow/sim/structs.py +232 -0
  32. crazyflow-0.0.1/crazyflow/sim/symbolic.py +336 -0
  33. crazyflow-0.0.1/crazyflow/utils.py +119 -0
  34. crazyflow-0.0.1/crazyflow.egg-info/PKG-INFO +156 -0
  35. crazyflow-0.0.1/crazyflow.egg-info/SOURCES.txt +64 -0
  36. crazyflow-0.0.1/crazyflow.egg-info/dependency_links.txt +1 -0
  37. crazyflow-0.0.1/crazyflow.egg-info/requires.txt +21 -0
  38. crazyflow-0.0.1/crazyflow.egg-info/top_level.txt +7 -0
  39. crazyflow-0.0.1/examples/cache.py +50 -0
  40. crazyflow-0.0.1/examples/change_pos.py +29 -0
  41. crazyflow-0.0.1/examples/contacts.py +23 -0
  42. crazyflow-0.0.1/examples/disturbance.py +93 -0
  43. crazyflow-0.0.1/examples/gradient.py +44 -0
  44. crazyflow-0.0.1/examples/gymnasium_env.py +33 -0
  45. crazyflow-0.0.1/examples/gymnasium_env_trajectory.py +46 -0
  46. crazyflow-0.0.1/examples/hover.py +36 -0
  47. crazyflow-0.0.1/examples/randomize.py +42 -0
  48. crazyflow-0.0.1/examples/render.py +76 -0
  49. crazyflow-0.0.1/examples/spiral.py +31 -0
  50. crazyflow-0.0.1/examples/symbolic.py +22 -0
  51. crazyflow-0.0.1/examples/thrust.py +25 -0
  52. crazyflow-0.0.1/pyproject.toml +104 -0
  53. crazyflow-0.0.1/setup.cfg +4 -0
  54. crazyflow-0.0.1/tests/conftest.py +7 -0
  55. crazyflow-0.0.1/tests/integration/__init__.py +0 -0
  56. crazyflow-0.0.1/tests/integration/test_disturbance.py +39 -0
  57. crazyflow-0.0.1/tests/integration/test_examples.py +36 -0
  58. crazyflow-0.0.1/tests/integration/test_interfaces.py +98 -0
  59. crazyflow-0.0.1/tests/integration/test_randomize.py +60 -0
  60. crazyflow-0.0.1/tests/integration/test_reset.py +64 -0
  61. crazyflow-0.0.1/tests/integration/test_symbolic.py +114 -0
  62. crazyflow-0.0.1/tests/unit/__init__.py +0 -0
  63. crazyflow-0.0.1/tests/unit/test_randomize.py +99 -0
  64. crazyflow-0.0.1/tests/unit/test_sim.py +341 -0
  65. crazyflow-0.0.1/tests/unit/test_symbolic.py +46 -0
  66. crazyflow-0.0.1/tests/unit/test_utils.py +49 -0
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+ Copyright © 2025 Martin Schuck
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: crazyflow
3
+ Version: 0.0.1
4
+ Summary: Fast, parallelizable simulations of Crazyflies with JAX and MuJoCo.
5
+ License: The MIT License (MIT)
6
+ Copyright © 2025 Martin Schuck
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13
+
14
+
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: Intended Audience :: Education
20
+ Classifier: Intended Audience :: Science/Research
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: jax!=0.5.3,>=0.5.0
25
+ Requires-Dist: mujoco>=3.3.0
26
+ Requires-Dist: mujoco-mjx>=3.3.0
27
+ Requires-Dist: gymnasium
28
+ Requires-Dist: imageio
29
+ Requires-Dist: einops
30
+ Requires-Dist: flax
31
+ Requires-Dist: ml_collections
32
+ Requires-Dist: casadi
33
+ Requires-Dist: numpy
34
+ Provides-Extra: test
35
+ Requires-Dist: pytest>=8.0.0; extra == "test"
36
+ Requires-Dist: pytest-cov; extra == "test"
37
+ Requires-Dist: pytest-timeout; extra == "test"
38
+ Provides-Extra: gpu
39
+ Requires-Dist: jax[cuda12]; extra == "gpu"
40
+ Provides-Extra: benchmark
41
+ Requires-Dist: fire; extra == "benchmark"
42
+ Dynamic: license-file
43
+
44
+ ![Crazyflow Logo](https://github.com/utiasDSL/crazyflow/raw/main/docs/img/logo.png)
45
+
46
+ --------------------------------------------------------------------------------
47
+
48
+ Fast, parallelizable simulations of Crazyflies with JAX and MuJoCo.
49
+
50
+ [![Python Version]][Python Version URL] [![Ruff Check]][Ruff Check URL] [![Documentation Status]][Documentation Status URL] [![Tests]][Tests URL]
51
+
52
+ [Python Version]: https://img.shields.io/badge/python-3.10+-blue.svg
53
+ [Python Version URL]: https://www.python.org
54
+
55
+ [Ruff Check]: https://github.com/utiasDSL/crazyflow/actions/workflows/ruff.yml/badge.svg?style=flat-square
56
+ [Ruff Check URL]: https://github.com/utiasDSL/crazyflow/actions/workflows/ruff.yml
57
+
58
+ [Documentation Status]: https://readthedocs.org/projects/crazyflow/badge/?version=latest
59
+ [Documentation Status URL]: https://crazyflow.readthedocs.io/en/latest/?badge=latest
60
+
61
+ [Tests]: https://github.com/utiasDSL/crazyflow/actions/workflows/testing.yml/badge.svg
62
+ [Tests URL]: https://github.com/utiasDSL/crazyflow/actions/workflows/testing.yml
63
+
64
+
65
+ ## Architecture
66
+
67
+ Crazyflow is a high-performance simulation framework for Crazyflie drones that leverages JAX for efficient parallelization and automatic differentiation. The architecture is designed around a flexible pipeline that can be configured at initialization time, enabling users to swap out physics backends, control methods, and integration schemes.
68
+
69
+ ### Core Components
70
+
71
+ #### Simulation Pipeline
72
+ The simulation is built as a pipeline of functions that are composed at initialization time based on the configuration. This approach avoids runtime branching and allows JAX to optimize the entire pipeline as a single computation. Users can insert their own pure functions into the pipeline to modify the simulation behavior while maintaining compatibility with JAX's optimizations.
73
+
74
+ #### Physics Backends
75
+ Multiple physics models are supported:
76
+ - analytical: A first-principles model based on physical equations
77
+ - sys_id: A system-identified model trained on real drone data
78
+ - mujoco: MuJoCo physics engine for more complex interactions
79
+
80
+ #### Control Modes
81
+ Different control interfaces are available:
82
+ - state: High-level control of position, velocity, and yaw
83
+ - attitude: Mid-level control of collective thrust and orientation
84
+ - thrust: Low-level control of individual motor thrusts
85
+
86
+ #### Integration Methods
87
+ For analytical and system-identified physics:
88
+ - euler: Simple first-order integration
89
+ - rk4: Fourth-order Runge-Kutta integration for higher accuracy
90
+
91
+ ### Parallelization
92
+ Crazyflow supports massive parallelization across:
93
+ - Worlds: Independent simulation environments that can run in parallel
94
+ - Drones: Multiple drones within each world
95
+ - Devices: Computations can be executed on CPU or GPU
96
+ This parallelization is achieved through JAX's vectorization capabilities, allowing thousands of simulations to run simultaneously with minimal overhead.
97
+
98
+ ### Domain Randomization
99
+ The framework supports domain randomization through the crazyflow/randomize module, allowing parameters like mass to be varied across simulations to improve sim-to-real transfer.
100
+
101
+ ### Functional Design
102
+ The simulation follows a functional programming paradigm: All state is contained in immutable data structures. Updates create new states rather than modifying existing ones. All functions are pure, enabling JAX's transformations (JIT, grad, vmap) and thus automatic differentiation through the entire simulation, making it suitable for gradient-based optimization and reinforcement learning.
103
+
104
+ ## Examples
105
+ The repository includes several example scripts demonstrating different capabilities:
106
+ | Example | Description |
107
+ | ----------------------------------------- | ----------------------------------------------------------- |
108
+ | [`hover.py`](examples/hover.py) | Basic hovering using state control |
109
+ | [`thrust.py`](examples/thrust.py) | Direct motor control using thrust commands |
110
+ | [`render.py`](examples/render.py) | Visualization of multiple drones with motion traces |
111
+ | [`contacts.py`](examples/contacts.py) | Collision detection between drones |
112
+ | [`gradient.py`](examples/gradient.py) | Computing gradients through the simulation for optimization |
113
+ | [`change_pos.py`](examples/change_pos.py) | Manipulating drone positions programmatically |
114
+
115
+
116
+
117
+ ## Known Issues
118
+ - `"RuntimeError: MUJOCO_PATH environment variable is not set"` upon installing this package. This error can be resolved by using `venv` instead of `conda`. Somtimes the `mujoco` install can [fail with `conda`](https://github.com/google-deepmind/mujoco/issues/1004).
119
+ - If using `zsh` don't forget to escape brackets when installing additional dependencies: `pip install .\[gpu\]`.
120
+
121
+ ### Using the project with VSCode devcontainers
122
+
123
+ **Running on CPU**: by default the containers run on CPU. You don't need to take any action.
124
+
125
+ **Running on GPU**: The devcontsainers can easily run using your computer's NVIDIA GPU on Linux and Windows. This makes sense if you want to accelerate simulation by running thousands of simulation in parallel. In order to work you need to install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=WSL-Ubuntu&target_version=2.0&target_type=deb_local), [NVIDIA Container runtime](https://developer.nvidia.com/container-runtime) for your computer. Finally, enable GPU access to the devcontainers by setting the commented out `"--gpus=all"` and `"--runtime=nvidia"` flags in `devcontainer.json`.
126
+
127
+
128
+ **Linux**
129
+ 1. Make sure to be in a X11 session ([link](https://askubuntu.com/questions/1410256/how-do-i-use-the-x-window-manager-instead-of-wayland-on-ubuntu-22-04)), otherwise rendering of the drone will fail.
130
+ 2. Install [Docker](https://docs.docker.com/engine/install/) (, and make sure Docker Daemon is running)
131
+ 3. Install [VSCode](https://code.visualstudio.com/), with [devcontainer extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), and [remote dev pack](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker).
132
+ 4. Clone this project's code. Rename `/.devcontainer/devcontainer.linux.json` to `/.devcontainer/devcontainer.json`.
133
+ 5. Open this project in VSCode. VSCode should automatically detect the devcontainer and prompt you to `Reopen in container`. If not, see [here](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container) to open manually. Note: Opening the container for the first time might take a while (up to 15 min), as the container is pulled from the web and build.
134
+
135
+ **Windows** (requires Windows 10 or later)
136
+
137
+ For windows, we require WSL2 to run the devcontainer. (So its actually Linux with extra steps.) Full instructions can be found [in the official docs](https://code.visualstudio.com/blogs/2020/07/01/containers-wsl#_getting-started). Here are the important steps:
138
+ 1. Install [Docker](https://docs.docker.com/desktop/setup/install/windows-install/), and WSL2, and Ubuntu 22.04 LTS (, and make sure Docker Daemon is running)
139
+ 2. Docker will recognize that you have WSL installed and prompt you via Windows Notifications to enable WSL integration -> confirm this with `Enable WSL integration`. If not, open `Docker Desktop`, navigate to the settings, and manually enable WSL integration. (There are TWO setting options for this. Make sure to enable BOTH!)
140
+ 3. Install [VSCode](https://code.visualstudio.com/), with the [WSL extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl), [devcontainer extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), and [remote dev pack](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker).
141
+ 4. Clone the source code for the exercises in the WSL2 file system to `/home` (`~`), or wherever you like. (Performance when working on the WSL file system is much better compared to Windows file system). You can access the WSL filesystem either by starting a WSL/Ubuntu console, or via the Windows File Explorer at `\\wsl.localhost\Ubuntu\home` (replace `Ubuntu` with your distro, if necessary).
142
+ 7. Rename `/.devcontainer/devcontainer.windows.json` to `/.devcontainer/devcontainer.json`.
143
+ 8. Open this project in VSCode. The easiest way to do so is by starting a WSL/Ubuntu shell, navigating via `cd` to the source code, then type `code .` to open VSCode. VSCode should automatically detect the devcontainer and prompt you to `Reopen in container`. If not, see [here](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container) to open manually. Note: Opening the container for the first time might take a while (up to 15 min), as the container is pulled from the web and build.
144
+
145
+
146
+ **MacOS**
147
+
148
+ Unfortunately, we did not get the devcontainer to work with MacOS yet, even after following [those](https://gist.github.com/sorny/969fe55d85c9b0035b0109a31cbcb088) steps. We expect that the issue is related to Mujoco rendering from inside the Docker container and display forwarding with X11. There is also an [unresolved Issue](https://github.com/google-deepmind/mujoco/issues/1047) on GitHub. If you manage to make it work, please let us know.
149
+
150
+ Until then, MacOS users are required to install this project using an python environment manager such as [conda](https://docs.anaconda.com/anaconda/install/) or [mamba](https://mamba.readthedocs.io/en/latest/). If you use conda, these are the required commands: ```conda create --name crazyflow -c conda-forge python=3.11```, ```conda activate crazyflow```, ```conda install pip```, ```pip install -e .```.
151
+
152
+ ____________
153
+
154
+ Known Issues:
155
+ - if building docker container fails at `RUN apt-get update`, make sure your host systems time is set correct: https://askubuntu.com/questions/1511514/docker-build-fails-at-run-apt-update-error-failed-to-solve-process-bin-sh
156
+
@@ -0,0 +1,113 @@
1
+ ![Crazyflow Logo](https://github.com/utiasDSL/crazyflow/raw/main/docs/img/logo.png)
2
+
3
+ --------------------------------------------------------------------------------
4
+
5
+ Fast, parallelizable simulations of Crazyflies with JAX and MuJoCo.
6
+
7
+ [![Python Version]][Python Version URL] [![Ruff Check]][Ruff Check URL] [![Documentation Status]][Documentation Status URL] [![Tests]][Tests URL]
8
+
9
+ [Python Version]: https://img.shields.io/badge/python-3.10+-blue.svg
10
+ [Python Version URL]: https://www.python.org
11
+
12
+ [Ruff Check]: https://github.com/utiasDSL/crazyflow/actions/workflows/ruff.yml/badge.svg?style=flat-square
13
+ [Ruff Check URL]: https://github.com/utiasDSL/crazyflow/actions/workflows/ruff.yml
14
+
15
+ [Documentation Status]: https://readthedocs.org/projects/crazyflow/badge/?version=latest
16
+ [Documentation Status URL]: https://crazyflow.readthedocs.io/en/latest/?badge=latest
17
+
18
+ [Tests]: https://github.com/utiasDSL/crazyflow/actions/workflows/testing.yml/badge.svg
19
+ [Tests URL]: https://github.com/utiasDSL/crazyflow/actions/workflows/testing.yml
20
+
21
+
22
+ ## Architecture
23
+
24
+ Crazyflow is a high-performance simulation framework for Crazyflie drones that leverages JAX for efficient parallelization and automatic differentiation. The architecture is designed around a flexible pipeline that can be configured at initialization time, enabling users to swap out physics backends, control methods, and integration schemes.
25
+
26
+ ### Core Components
27
+
28
+ #### Simulation Pipeline
29
+ The simulation is built as a pipeline of functions that are composed at initialization time based on the configuration. This approach avoids runtime branching and allows JAX to optimize the entire pipeline as a single computation. Users can insert their own pure functions into the pipeline to modify the simulation behavior while maintaining compatibility with JAX's optimizations.
30
+
31
+ #### Physics Backends
32
+ Multiple physics models are supported:
33
+ - analytical: A first-principles model based on physical equations
34
+ - sys_id: A system-identified model trained on real drone data
35
+ - mujoco: MuJoCo physics engine for more complex interactions
36
+
37
+ #### Control Modes
38
+ Different control interfaces are available:
39
+ - state: High-level control of position, velocity, and yaw
40
+ - attitude: Mid-level control of collective thrust and orientation
41
+ - thrust: Low-level control of individual motor thrusts
42
+
43
+ #### Integration Methods
44
+ For analytical and system-identified physics:
45
+ - euler: Simple first-order integration
46
+ - rk4: Fourth-order Runge-Kutta integration for higher accuracy
47
+
48
+ ### Parallelization
49
+ Crazyflow supports massive parallelization across:
50
+ - Worlds: Independent simulation environments that can run in parallel
51
+ - Drones: Multiple drones within each world
52
+ - Devices: Computations can be executed on CPU or GPU
53
+ This parallelization is achieved through JAX's vectorization capabilities, allowing thousands of simulations to run simultaneously with minimal overhead.
54
+
55
+ ### Domain Randomization
56
+ The framework supports domain randomization through the crazyflow/randomize module, allowing parameters like mass to be varied across simulations to improve sim-to-real transfer.
57
+
58
+ ### Functional Design
59
+ The simulation follows a functional programming paradigm: All state is contained in immutable data structures. Updates create new states rather than modifying existing ones. All functions are pure, enabling JAX's transformations (JIT, grad, vmap) and thus automatic differentiation through the entire simulation, making it suitable for gradient-based optimization and reinforcement learning.
60
+
61
+ ## Examples
62
+ The repository includes several example scripts demonstrating different capabilities:
63
+ | Example | Description |
64
+ | ----------------------------------------- | ----------------------------------------------------------- |
65
+ | [`hover.py`](examples/hover.py) | Basic hovering using state control |
66
+ | [`thrust.py`](examples/thrust.py) | Direct motor control using thrust commands |
67
+ | [`render.py`](examples/render.py) | Visualization of multiple drones with motion traces |
68
+ | [`contacts.py`](examples/contacts.py) | Collision detection between drones |
69
+ | [`gradient.py`](examples/gradient.py) | Computing gradients through the simulation for optimization |
70
+ | [`change_pos.py`](examples/change_pos.py) | Manipulating drone positions programmatically |
71
+
72
+
73
+
74
+ ## Known Issues
75
+ - `"RuntimeError: MUJOCO_PATH environment variable is not set"` upon installing this package. This error can be resolved by using `venv` instead of `conda`. Somtimes the `mujoco` install can [fail with `conda`](https://github.com/google-deepmind/mujoco/issues/1004).
76
+ - If using `zsh` don't forget to escape brackets when installing additional dependencies: `pip install .\[gpu\]`.
77
+
78
+ ### Using the project with VSCode devcontainers
79
+
80
+ **Running on CPU**: by default the containers run on CPU. You don't need to take any action.
81
+
82
+ **Running on GPU**: The devcontsainers can easily run using your computer's NVIDIA GPU on Linux and Windows. This makes sense if you want to accelerate simulation by running thousands of simulation in parallel. In order to work you need to install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=WSL-Ubuntu&target_version=2.0&target_type=deb_local), [NVIDIA Container runtime](https://developer.nvidia.com/container-runtime) for your computer. Finally, enable GPU access to the devcontainers by setting the commented out `"--gpus=all"` and `"--runtime=nvidia"` flags in `devcontainer.json`.
83
+
84
+
85
+ **Linux**
86
+ 1. Make sure to be in a X11 session ([link](https://askubuntu.com/questions/1410256/how-do-i-use-the-x-window-manager-instead-of-wayland-on-ubuntu-22-04)), otherwise rendering of the drone will fail.
87
+ 2. Install [Docker](https://docs.docker.com/engine/install/) (, and make sure Docker Daemon is running)
88
+ 3. Install [VSCode](https://code.visualstudio.com/), with [devcontainer extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), and [remote dev pack](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker).
89
+ 4. Clone this project's code. Rename `/.devcontainer/devcontainer.linux.json` to `/.devcontainer/devcontainer.json`.
90
+ 5. Open this project in VSCode. VSCode should automatically detect the devcontainer and prompt you to `Reopen in container`. If not, see [here](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container) to open manually. Note: Opening the container for the first time might take a while (up to 15 min), as the container is pulled from the web and build.
91
+
92
+ **Windows** (requires Windows 10 or later)
93
+
94
+ For windows, we require WSL2 to run the devcontainer. (So its actually Linux with extra steps.) Full instructions can be found [in the official docs](https://code.visualstudio.com/blogs/2020/07/01/containers-wsl#_getting-started). Here are the important steps:
95
+ 1. Install [Docker](https://docs.docker.com/desktop/setup/install/windows-install/), and WSL2, and Ubuntu 22.04 LTS (, and make sure Docker Daemon is running)
96
+ 2. Docker will recognize that you have WSL installed and prompt you via Windows Notifications to enable WSL integration -> confirm this with `Enable WSL integration`. If not, open `Docker Desktop`, navigate to the settings, and manually enable WSL integration. (There are TWO setting options for this. Make sure to enable BOTH!)
97
+ 3. Install [VSCode](https://code.visualstudio.com/), with the [WSL extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl), [devcontainer extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), and [remote dev pack](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker).
98
+ 4. Clone the source code for the exercises in the WSL2 file system to `/home` (`~`), or wherever you like. (Performance when working on the WSL file system is much better compared to Windows file system). You can access the WSL filesystem either by starting a WSL/Ubuntu console, or via the Windows File Explorer at `\\wsl.localhost\Ubuntu\home` (replace `Ubuntu` with your distro, if necessary).
99
+ 7. Rename `/.devcontainer/devcontainer.windows.json` to `/.devcontainer/devcontainer.json`.
100
+ 8. Open this project in VSCode. The easiest way to do so is by starting a WSL/Ubuntu shell, navigating via `cd` to the source code, then type `code .` to open VSCode. VSCode should automatically detect the devcontainer and prompt you to `Reopen in container`. If not, see [here](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container) to open manually. Note: Opening the container for the first time might take a while (up to 15 min), as the container is pulled from the web and build.
101
+
102
+
103
+ **MacOS**
104
+
105
+ Unfortunately, we did not get the devcontainer to work with MacOS yet, even after following [those](https://gist.github.com/sorny/969fe55d85c9b0035b0109a31cbcb088) steps. We expect that the issue is related to Mujoco rendering from inside the Docker container and display forwarding with X11. There is also an [unresolved Issue](https://github.com/google-deepmind/mujoco/issues/1047) on GitHub. If you manage to make it work, please let us know.
106
+
107
+ Until then, MacOS users are required to install this project using an python environment manager such as [conda](https://docs.anaconda.com/anaconda/install/) or [mamba](https://mamba.readthedocs.io/en/latest/). If you use conda, these are the required commands: ```conda create --name crazyflow -c conda-forge python=3.11```, ```conda activate crazyflow```, ```conda install pip```, ```pip install -e .```.
108
+
109
+ ____________
110
+
111
+ Known Issues:
112
+ - if building docker container fails at `RUN apt-get update`, make sure your host systems time is set correct: https://askubuntu.com/questions/1511514/docker-build-fails-at-run-apt-update-error-failed-to-solve-process-bin-sh
113
+
@@ -0,0 +1,39 @@
1
+ import time
2
+
3
+ import fire
4
+ import jax
5
+
6
+ from crazyflow import Sim
7
+
8
+
9
+ def main(cache: bool = False):
10
+ """Main entry point for profiling."""
11
+ if cache:
12
+ jax.config.update("jax_compilation_cache_dir", "/tmp/jax_cache")
13
+ jax.config.update("jax_persistent_cache_min_entry_size_bytes", -1)
14
+ jax.config.update("jax_persistent_cache_min_compile_time_secs", 0)
15
+ jax.config.update("jax_persistent_cache_enable_xla_caches", "all")
16
+
17
+ # Time initialization
18
+ start = time.perf_counter()
19
+ sim = Sim(n_worlds=1, n_drones=1, physics="sys_id", control="attitude")
20
+ init_time = time.perf_counter() - start
21
+
22
+ # Time reset compilation
23
+ start = time.perf_counter()
24
+ sim._reset.lower(sim.data, sim.default_data, None).compile()
25
+ reset_time = time.perf_counter() - start
26
+
27
+ # Time step compilation
28
+ start = time.perf_counter()
29
+ sim._step.lower(sim.data, 1).compile()
30
+ step_time = time.perf_counter() - start
31
+
32
+ print(f"Simulation startup times | {sim.physics} | {sim.control}")
33
+ print(f"Initialization: {init_time:.2f}s")
34
+ print(f"Reset: {reset_time:.2f}s")
35
+ print(f"Step: {step_time:.2f}s")
36
+
37
+
38
+ if __name__ == "__main__":
39
+ fire.Fire(main)
@@ -0,0 +1,156 @@
1
+ import time
2
+
3
+ import gymnasium
4
+ import jax
5
+ import jax.numpy as jnp
6
+ import numpy as np
7
+ from ml_collections import config_dict
8
+
9
+ import crazyflow # noqa: F401, ensure gymnasium envs are registered
10
+ from crazyflow.sim import Sim
11
+
12
+
13
+ def analyze_timings(times: list[float], n_steps: int, n_worlds: int, freq: float) -> None:
14
+ """Analyze timing results and print performance metrics."""
15
+ if not times:
16
+ raise ValueError("The list of timing results is empty.")
17
+
18
+ tmin, idx_tmin = np.min(times), np.argmin(times)
19
+ tmax, idx_tmax = np.max(times), np.argmax(times)
20
+
21
+ # Check for significant variance
22
+ if tmax / tmin > 10:
23
+ print("Warning: Fn time varies by more than 10x. Is JIT compiling during the benchmark?")
24
+ print(f"Times: max {tmax:.2e} @ {idx_tmax}, min {tmin:.2e} @ {idx_tmin}")
25
+
26
+ # Performance metrics
27
+ n_frames = n_steps * n_worlds # Number of frames simulated
28
+ total_time = np.sum(times)
29
+ avg_step_time = np.mean(times)
30
+ step_time_std = np.std(times)
31
+ fps = n_frames / total_time
32
+ real_time_factor = (n_steps / freq) * n_worlds / total_time
33
+
34
+ print(
35
+ f"Avg fn time: {avg_step_time:.2e}s, std: {step_time_std:.2e}"
36
+ f"\nFPS: {fps:.3e}, Real time factor: {real_time_factor:.2e}\n"
37
+ )
38
+
39
+
40
+ def profile_gym_env_step(sim_config: config_dict.ConfigDict, n_steps: int, device: str):
41
+ """Profile the Crazyflow gym environment step performance."""
42
+ times = []
43
+ device = jax.devices(device)[0]
44
+
45
+ envs = gymnasium.make_vec(
46
+ "DroneReachPos-v0",
47
+ time_horizon_in_seconds=3,
48
+ num_envs=sim_config.n_worlds,
49
+ device=sim_config.device,
50
+ freq=sim_config.attitude_freq,
51
+ physics=sim_config.physics,
52
+ )
53
+
54
+ # Action for going up (in attitude control)
55
+ action = np.zeros((sim_config.n_worlds, 4), dtype=np.float32)
56
+ action[..., 0] = 0.3
57
+ # Step through env once to ensure JIT compilation
58
+ envs.reset(seed=42)
59
+ envs.step(action)
60
+
61
+ jax.block_until_ready(envs.unwrapped.sim.data) # Ensure JIT compiled dynamics
62
+
63
+ # Step through the environment
64
+ for _ in range(n_steps):
65
+ tstart = time.perf_counter()
66
+ envs.step(action)
67
+ jax.block_until_ready(envs.unwrapped.sim.data)
68
+ times.append(time.perf_counter() - tstart)
69
+
70
+ envs.close()
71
+ print("Gym env step performance:")
72
+ analyze_timings(times, n_steps, envs.unwrapped.sim.n_worlds, envs.unwrapped.sim.freq)
73
+
74
+
75
+ def profile_step(sim_config: config_dict.ConfigDict, n_steps: int, device: str):
76
+ """Profile the Crazyflow simulator step performance."""
77
+ sim = Sim(**sim_config)
78
+ times = []
79
+ device = jax.devices(device)[0]
80
+
81
+ cmd = jnp.zeros((sim.n_worlds, sim.n_drones, 4), device=device)
82
+ cmd = cmd.at[0, 0, 0].set(1)
83
+
84
+ sim.reset()
85
+ sim.attitude_control(cmd)
86
+ sim.step(sim.freq // sim.control_freq)
87
+ jax.block_until_ready(sim.data) # Ensure JIT compiled dynamics
88
+
89
+ for _ in range(n_steps):
90
+ tstart = time.perf_counter()
91
+ sim.attitude_control(cmd)
92
+ sim.step(sim.freq // sim.control_freq)
93
+ jax.block_until_ready(sim.data)
94
+ times.append(time.perf_counter() - tstart)
95
+
96
+ print("Sim step performance:")
97
+ analyze_timings(times, n_steps, sim.n_worlds, sim.freq)
98
+
99
+
100
+ def profile_reset(sim_config: config_dict.ConfigDict, n_steps: int, device: str):
101
+ """Profile the Crazyflow simulator reset performance."""
102
+ sim = Sim(**sim_config)
103
+ times = []
104
+ times_masked = []
105
+ device = jax.devices(device)[0]
106
+
107
+ # Ensure JIT compiled reset
108
+ sim.reset()
109
+ jax.block_until_ready(sim.data)
110
+
111
+ # Test full reset
112
+ for _ in range(n_steps):
113
+ tstart = time.perf_counter()
114
+ sim.reset()
115
+ jax.block_until_ready(sim.data)
116
+ times.append(time.perf_counter() - tstart)
117
+
118
+ # Test masked reset (only reset first world)
119
+ mask = jnp.zeros(sim.n_worlds, dtype=bool, device=device)
120
+ mask = mask.at[0].set(True)
121
+ sim.reset(mask)
122
+ jax.block_until_ready(sim.data)
123
+
124
+ for _ in range(n_steps):
125
+ tstart = time.perf_counter()
126
+ sim.reset(mask)
127
+ jax.block_until_ready(sim.data)
128
+ times_masked.append(time.perf_counter() - tstart)
129
+
130
+ print("Sim reset performance:")
131
+ analyze_timings(times, n_steps, sim.n_worlds, sim.freq)
132
+ print("Sim masked reset performance:")
133
+ analyze_timings(times_masked, n_steps, sim.n_worlds, sim.freq)
134
+
135
+
136
+ def main():
137
+ """Main entry point for profiling."""
138
+ device = "cpu"
139
+ sim_config = config_dict.ConfigDict()
140
+ sim_config.n_worlds = 1
141
+ sim_config.n_drones = 1
142
+ sim_config.physics = "analytical"
143
+ sim_config.control = "attitude"
144
+ sim_config.attitude_freq = 500
145
+ sim_config.device = device
146
+
147
+ print("Simulator performance\n")
148
+ profile_step(sim_config, 1000, device)
149
+ profile_reset(sim_config, 1000, device)
150
+
151
+ print("Gymnasium environment performance\n")
152
+ profile_gym_env_step(sim_config, 1000, device)
153
+
154
+
155
+ if __name__ == "__main__":
156
+ main()
@@ -0,0 +1,17 @@
1
+ import crazyflow # noqa: F401, ensure gymnasium envs are registered
2
+ from crazyflow.sim import Sim
3
+
4
+
5
+ def main():
6
+ """Main entry point for profiling."""
7
+ sim = Sim(n_worlds=1, n_drones=1, physics="analytical", control="attitude")
8
+
9
+ compiled_reset = sim._reset.lower(sim.data, sim.default_data, None).compile()
10
+ compiled_step = sim._step.lower(sim.data, 1).compile()
11
+ op_count_reset = compiled_reset.cost_analysis()["flops"]
12
+ op_count_step = compiled_step.cost_analysis()["flops"]
13
+ print(f"Op counts:\n Reset: {op_count_reset}\n Step: {op_count_step}")
14
+
15
+
16
+ if __name__ == "__main__":
17
+ main()
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import gymnasium
6
+ import jax
7
+ import numpy as np
8
+ from ml_collections import config_dict
9
+ from pyinstrument import Profiler
10
+ from pyinstrument.renderers.html import HTMLRenderer
11
+
12
+ import crazyflow # noqa: F401, ensure gymnasium envs are registered
13
+ from crazyflow.sim import Sim
14
+
15
+ if TYPE_CHECKING:
16
+ from crazyflow.gymnasium_envs import CrazyflowEnvReachGoal
17
+
18
+
19
+ def profile_step(sim_config: config_dict.ConfigDict, n_steps: int, device: str):
20
+ sim = Sim(**sim_config)
21
+ device = jax.devices(device)[0]
22
+ ndim = 13 if sim.control == "state" else 4
23
+ control_fn = sim.state_control if sim.control == "state" else sim.attitude_control
24
+ cmd = np.zeros((sim.n_worlds, sim.n_drones, ndim))
25
+ # Ensure JIT compiled dynamics and control
26
+ sim.reset()
27
+ control_fn(cmd)
28
+ sim.step()
29
+ jax.block_until_ready(sim.data)
30
+
31
+ profiler = Profiler()
32
+ profiler.start()
33
+
34
+ for _ in range(n_steps):
35
+ control_fn(cmd)
36
+ # sim.reset()
37
+ sim.step()
38
+ jax.block_until_ready(sim.data)
39
+ profiler.stop()
40
+ renderer = HTMLRenderer()
41
+ renderer.open_in_browser(profiler.last_session)
42
+
43
+
44
+ def profile_gym_env_step(sim_config: config_dict.ConfigDict, n_steps: int, device: str):
45
+ device = jax.devices(device)[0]
46
+
47
+ envs: CrazyflowEnvReachGoal = gymnasium.make_vec(
48
+ "DroneReachPos-v0", time_horizon_in_seconds=2, num_envs=sim_config.n_worlds, **sim_config
49
+ )
50
+
51
+ # Action for going up (in attitude control)
52
+ action = np.zeros((sim_config.n_worlds, 4), dtype=np.float32)
53
+ action[..., 0] = 0.3
54
+
55
+ # Step through env once to ensure JIT compilation.
56
+ envs.reset(seed=42)
57
+ envs.step(action)
58
+ envs.step(action) # Ensure all paths have been taken at least once
59
+ envs.reset(seed=42)
60
+ jax.block_until_ready(envs.unwrapped.sim.data)
61
+
62
+ profiler = Profiler()
63
+ profiler.start()
64
+
65
+ for _ in range(n_steps):
66
+ envs.step(action)
67
+ jax.block_until_ready(envs.unwrapped.sim.data)
68
+
69
+ profiler.stop()
70
+ renderer = HTMLRenderer()
71
+ renderer.open_in_browser(profiler.last_session)
72
+ envs.close()
73
+
74
+
75
+ def main():
76
+ device = "cpu"
77
+ sim_config = config_dict.ConfigDict()
78
+ sim_config.n_worlds = 1
79
+ sim_config.n_drones = 1
80
+ sim_config.physics = "analytical"
81
+ sim_config.control = "attitude"
82
+ sim_config.device = device
83
+
84
+ profile_step(sim_config, 1000, device)
85
+ profile_gym_env_step(sim_config, 1000, device)
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -0,0 +1,6 @@
1
+ import crazyflow.gymnasium_envs # noqa: F401, ensure gymnasium envs are registered
2
+ from crazyflow.control import Control
3
+ from crazyflow.sim import Physics, Sim
4
+
5
+ __all__ = ["Sim", "Physics", "Control"]
6
+ __version__ = "0.0.1"
@@ -0,0 +1,13 @@
1
+ import numpy as np
2
+ from numpy.typing import NDArray
3
+
4
+ # Physical constants
5
+ GRAVITY: float = 9.81
6
+
7
+ # Drone constants
8
+ ARM_LEN: float = 0.0325 * np.sqrt(2)
9
+ MIX_MATRIX: NDArray = np.array([[-0.5, -0.5, -1], [-0.5, 0.5, 1], [0.5, 0.5, -1], [0.5, -0.5, 1]])
10
+ SIGN_MIX_MATRIX: NDArray = np.sign(MIX_MATRIX)
11
+ MASS: float = 0.027
12
+ J: NDArray = np.array([[2.3951e-5, 0, 0], [0, 2.3951e-5, 0], [0, 0, 3.2347e-5]])
13
+ J_INV: NDArray = np.linalg.inv(J)
@@ -0,0 +1,3 @@
1
+ from crazyflow.control.control import Control
2
+
3
+ __all__ = ["Control"]