drones-sim 0.2.0__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.
- drones_sim-0.2.0/LICENSE +21 -0
- drones_sim-0.2.0/PKG-INFO +323 -0
- drones_sim-0.2.0/README.md +288 -0
- drones_sim-0.2.0/pyproject.toml +52 -0
- drones_sim-0.2.0/setup.cfg +4 -0
- drones_sim-0.2.0/src/drones_sim/__init__.py +15 -0
- drones_sim-0.2.0/src/drones_sim/control/__init__.py +15 -0
- drones_sim-0.2.0/src/drones_sim/control/allocation.py +76 -0
- drones_sim-0.2.0/src/drones_sim/control/cascaded.py +204 -0
- drones_sim-0.2.0/src/drones_sim/control/geometric.py +170 -0
- drones_sim-0.2.0/src/drones_sim/control/lqr.py +174 -0
- drones_sim-0.2.0/src/drones_sim/control/pid.py +50 -0
- drones_sim-0.2.0/src/drones_sim/dynamics/__init__.py +13 -0
- drones_sim-0.2.0/src/drones_sim/dynamics/config.py +117 -0
- drones_sim-0.2.0/src/drones_sim/dynamics/disturbances.py +262 -0
- drones_sim-0.2.0/src/drones_sim/dynamics/quadcopter.py +317 -0
- drones_sim-0.2.0/src/drones_sim/estimation/__init__.py +2 -0
- drones_sim-0.2.0/src/drones_sim/estimation/ahrs.py +79 -0
- drones_sim-0.2.0/src/drones_sim/estimation/ekf.py +594 -0
- drones_sim-0.2.0/src/drones_sim/logging/__init__.py +13 -0
- drones_sim-0.2.0/src/drones_sim/logging/csv_logger.py +76 -0
- drones_sim-0.2.0/src/drones_sim/logging/json_logger.py +53 -0
- drones_sim-0.2.0/src/drones_sim/math_utils.py +119 -0
- drones_sim-0.2.0/src/drones_sim/models/__init__.py +21 -0
- drones_sim-0.2.0/src/drones_sim/models/quadcopter.urdf +296 -0
- drones_sim-0.2.0/src/drones_sim/models/urdf_loader.py +444 -0
- drones_sim-0.2.0/src/drones_sim/rl/__init__.py +16 -0
- drones_sim-0.2.0/src/drones_sim/rl/actions.py +219 -0
- drones_sim-0.2.0/src/drones_sim/rl/env.py +195 -0
- drones_sim-0.2.0/src/drones_sim/rl/observations.py +40 -0
- drones_sim-0.2.0/src/drones_sim/rl/reward.py +69 -0
- drones_sim-0.2.0/src/drones_sim/rl/tasks.py +73 -0
- drones_sim-0.2.0/src/drones_sim/sensors/__init__.py +3 -0
- drones_sim-0.2.0/src/drones_sim/sensors/gps.py +158 -0
- drones_sim-0.2.0/src/drones_sim/sensors/imu.py +196 -0
- drones_sim-0.2.0/src/drones_sim/sensors/models.py +113 -0
- drones_sim-0.2.0/src/drones_sim/simulation.py +283 -0
- drones_sim-0.2.0/src/drones_sim/state.py +133 -0
- drones_sim-0.2.0/src/drones_sim/trajectory.py +391 -0
- drones_sim-0.2.0/src/drones_sim/visualization/__init__.py +23 -0
- drones_sim-0.2.0/src/drones_sim/visualization/api.py +50 -0
- drones_sim-0.2.0/src/drones_sim/visualization/dashboard.py +74 -0
- drones_sim-0.2.0/src/drones_sim/visualization/plots.py +183 -0
- drones_sim-0.2.0/src/drones_sim/visualization/rerun_viewer.py +411 -0
- drones_sim-0.2.0/src/drones_sim/visualization/viewer.py +452 -0
- drones_sim-0.2.0/src/drones_sim.egg-info/PKG-INFO +323 -0
- drones_sim-0.2.0/src/drones_sim.egg-info/SOURCES.txt +64 -0
- drones_sim-0.2.0/src/drones_sim.egg-info/dependency_links.txt +1 -0
- drones_sim-0.2.0/src/drones_sim.egg-info/requires.txt +24 -0
- drones_sim-0.2.0/src/drones_sim.egg-info/top_level.txt +1 -0
- drones_sim-0.2.0/tests/test_cascaded.py +130 -0
- drones_sim-0.2.0/tests/test_disturbances.py +132 -0
- drones_sim-0.2.0/tests/test_dynamics.py +65 -0
- drones_sim-0.2.0/tests/test_dynamics_quaternion.py +169 -0
- drones_sim-0.2.0/tests/test_ekf.py +169 -0
- drones_sim-0.2.0/tests/test_gps.py +118 -0
- drones_sim-0.2.0/tests/test_logging.py +138 -0
- drones_sim-0.2.0/tests/test_lqr.py +61 -0
- drones_sim-0.2.0/tests/test_math_utils.py +58 -0
- drones_sim-0.2.0/tests/test_pid.py +34 -0
- drones_sim-0.2.0/tests/test_rerun_visualization.py +114 -0
- drones_sim-0.2.0/tests/test_reworked_architecture.py +102 -0
- drones_sim-0.2.0/tests/test_rl_env.py +135 -0
- drones_sim-0.2.0/tests/test_rl_training.py +102 -0
- drones_sim-0.2.0/tests/test_sensor_models.py +143 -0
- drones_sim-0.2.0/tests/test_trajectory.py +75 -0
drones_sim-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 thanhndv212
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: drones-sim
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Composable quadcopter modeling, control, estimation, and visualization
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/thanhndv212/drones-sim
|
|
7
|
+
Project-URL: Documentation, https://github.com/thanhndv212/drones-sim#readme
|
|
8
|
+
Project-URL: Changelog, https://github.com/thanhndv212/drones-sim/blob/master/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/thanhndv212/drones-sim/issues
|
|
10
|
+
Project-URL: Source, https://github.com/thanhndv212/drones-sim
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: numpy>=1.24
|
|
15
|
+
Requires-Dist: scipy>=1.10
|
|
16
|
+
Requires-Dist: matplotlib>=3.7
|
|
17
|
+
Requires-Dist: rerun-sdk<1,>=0.27
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
20
|
+
Requires-Dist: ruff; extra == "dev"
|
|
21
|
+
Provides-Extra: viser
|
|
22
|
+
Requires-Dist: viser>=0.2; extra == "viser"
|
|
23
|
+
Provides-Extra: rl
|
|
24
|
+
Requires-Dist: torch>=2.2; extra == "rl"
|
|
25
|
+
Requires-Dist: stable-baselines3>=2.3; extra == "rl"
|
|
26
|
+
Requires-Dist: gymnasium>=0.29; extra == "rl"
|
|
27
|
+
Requires-Dist: tensorboard>=2.15; extra == "rl"
|
|
28
|
+
Requires-Dist: pyyaml>=6.0; extra == "rl"
|
|
29
|
+
Provides-Extra: rl-dev
|
|
30
|
+
Requires-Dist: drones-sim[rl]; extra == "rl-dev"
|
|
31
|
+
Requires-Dist: wandb; extra == "rl-dev"
|
|
32
|
+
Requires-Dist: optuna>=3.5; extra == "rl-dev"
|
|
33
|
+
Requires-Dist: moviepy; extra == "rl-dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# drones_sim
|
|
37
|
+
|
|
38
|
+
Composable quadcopter simulation for control and state-estimation experiments. It combines a nonlinear quaternion rigid-body model, bounded actuators, SE(3) geometric control, innovation-gated sensor fusion, reproducible sensor models, and result-native 2D/3D visualization.
|
|
39
|
+
|
|
40
|
+
[](https://github.com/thanhndv212/drones-sim/actions/workflows/ci.yml)
|
|
41
|
+
[](https://deepwiki.com/thanhndv212/drones-sim)
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd drones_sim
|
|
47
|
+
pip install -e ".[dev]"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
RL extras (Gymnasium env, SB3 PPO, TensorBoard):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install -e ".[rl]"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Full RL dev stack (adds Weights & Biases, Optuna, moviepy):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install -e ".[rl-dev]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Legacy Viser mission-control examples:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install -e ".[viser]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick start
|
|
69
|
+
|
|
70
|
+
The high-level runner owns timing, sensing, estimation, control, logging, and aligned outputs:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from drones_sim import ClosedLoopSimulator, SimulationConfig, visualize
|
|
74
|
+
from drones_sim.trajectory import generate_circular
|
|
75
|
+
|
|
76
|
+
reference = generate_circular(duration=10.0, sample_rate=100, radius=2.0)
|
|
77
|
+
simulator = ClosedLoopSimulator(
|
|
78
|
+
config=SimulationConfig(dt=0.01, use_estimator=True, seed=7)
|
|
79
|
+
)
|
|
80
|
+
result = simulator.run(reference)
|
|
81
|
+
|
|
82
|
+
print(result.summary())
|
|
83
|
+
visualize(result) # Rerun is the default backend
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For custom experiments, compose `QuadcopterConfig`, `QuadcopterDynamics`,
|
|
87
|
+
`GeometricController`, `IMUSimulator`, `GPSSimulator`, and
|
|
88
|
+
`ExtendedKalmanFilter`, then inject them into `ClosedLoopSimulator`.
|
|
89
|
+
|
|
90
|
+
### Working examples
|
|
91
|
+
|
|
92
|
+
| # | Script | Install | Description |
|
|
93
|
+
|---|--------|---------|-------------|
|
|
94
|
+
| 01 | `01_imu_ekf_basic.py` | Base | IMU simulation and 16-state EKF sensor fusion |
|
|
95
|
+
| 02 | `02_ekf_adaptive.py` | Base | Adaptive EKF with temperature-dependent sensor models |
|
|
96
|
+
| 03 | `03_waypoint_nav.py` | Base | Minimum-snap waypoint navigation with cascaded PID |
|
|
97
|
+
| 04 | `04_viser_viewer.py` | `[viser]` | Interactive Viser 3D playback |
|
|
98
|
+
| 05 | `05_full_pipeline.py` | `[viser]` | Dynamics, sensors, EKF, PID control, and interactive viewer |
|
|
99
|
+
| 06 | `06_trajectory_following.py` | `[viser]` | EKF-fused trajectory tracking with mission controls |
|
|
100
|
+
| 07 | `07_rl_hover.py` | `[rl]` | Train and evaluate a PPO hover policy |
|
|
101
|
+
| 08 | `08_rl_vs_pid.py` | `[rl]` | Compare a trained RL policy with cascaded PID |
|
|
102
|
+
| 09 | `09_unified_simulation.py` | Base | Typed end-to-end simulation and diagnostic dashboard |
|
|
103
|
+
| 10 | `10_rerun_viewer.py` | Base | Rerun 3D scene, telemetry, streaming, and `.rrd` export |
|
|
104
|
+
|
|
105
|
+
Run a base example directly:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python examples/09_unified_simulation.py
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The default `visualize(result)` call opens Rerun. It can also stream to an
|
|
112
|
+
existing gRPC server or save a portable recording:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python examples/10_rerun_viewer.py
|
|
116
|
+
python examples/10_rerun_viewer.py --save flight.rrd
|
|
117
|
+
rerun flight.rrd
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Matplotlib and Viser remain available explicitly:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
visualize(result, backend="matplotlib", title="Flight report")
|
|
124
|
+
visualize(result, backend="viser", port=8080) # requires drones-sim[viser]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Package structure
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
src/drones_sim/
|
|
131
|
+
├── state.py # Typed state, setpoint, and control contracts
|
|
132
|
+
├── simulation.py # Reproducible closed-loop orchestration and results
|
|
133
|
+
├── math_utils.py # Quaternion ops, rotation matrices, Euler helpers
|
|
134
|
+
├── trajectory.py # Trajectory generators (hover-cruise, circular, waypoints, min-snap)
|
|
135
|
+
├── models/
|
|
136
|
+
│ ├── urdf_loader.py # Pure-stdlib URDF parser (no external deps)
|
|
137
|
+
│ └── quadcopter.urdf # Bundled quadcopter model
|
|
138
|
+
├── sensors/
|
|
139
|
+
│ ├── imu.py # 9-axis IMU simulator (accel, gyro, mag)
|
|
140
|
+
│ ├── gps.py # GNSS receiver simulator (position + velocity)
|
|
141
|
+
│ └── models.py # SensorNoiseModel (Gauss-Markov bias), TemperatureModel
|
|
142
|
+
├── estimation/
|
|
143
|
+
│ ├── ekf.py # 16-state EKF + 9-state adaptive EKF + AHRS
|
|
144
|
+
│ └── ahrs.py # Complementary-filter AHRS
|
|
145
|
+
├── dynamics/
|
|
146
|
+
│ ├── quadcopter.py # 13-state quaternion Newton-Euler rigid body
|
|
147
|
+
│ └── disturbances.py # Wind, gust, ground effect, motor failure, payload drop
|
|
148
|
+
├── control/
|
|
149
|
+
│ ├── pid.py # Scalar PID with anti-windup
|
|
150
|
+
│ ├── cascaded.py # Position → Velocity → Attitude cascaded PID
|
|
151
|
+
│ ├── geometric.py # Nonlinear SE(3) trajectory controller
|
|
152
|
+
│ ├── allocation.py # Bounded weighted least-squares motor allocation
|
|
153
|
+
│ └── lqr.py # Full-state feedback LQR (CARE solution)
|
|
154
|
+
├── rl/
|
|
155
|
+
│ ├── env.py # QuadcopterEnv (gymnasium.Env wrapper)
|
|
156
|
+
│ ├── actions.py # MotorSpeedAction, ThrustBodyRatesAction, VelocityLevelAction, LQRResidualAction
|
|
157
|
+
│ ├── observations.py # RelativeStateObs (17-D observation)
|
|
158
|
+
│ ├── tasks.py # HoverTask, WaypointTask, TrackingTask
|
|
159
|
+
│ └── reward.py # Weighted multi-term reward function
|
|
160
|
+
├── logging/
|
|
161
|
+
│ ├── csv_logger.py # CSV telemetry logger
|
|
162
|
+
│ └── json_logger.py # JSON Lines telemetry logger
|
|
163
|
+
└── visualization/
|
|
164
|
+
├── api.py # visualize(); Rerun is the default backend
|
|
165
|
+
├── plots.py # Matplotlib multi-panel comparison plots
|
|
166
|
+
├── dashboard.py # SimulationResult engineering dashboard
|
|
167
|
+
├── rerun_viewer.py # Default synchronized 3D, telemetry, events, and replay
|
|
168
|
+
└── viewer.py # Optional Viser mission-control UI (`[viser]`)
|
|
169
|
+
|
|
170
|
+
training/
|
|
171
|
+
├── train_ppo.py # PPO training entry point (YAML config, CPU default, W&B)
|
|
172
|
+
├── eval_policy.py # Policy evaluation with success/crash metrics
|
|
173
|
+
├── configs/
|
|
174
|
+
│ ├── ppo_hover.yaml # Config for thrust_rates / lqr_residual actions
|
|
175
|
+
│ └── ppo_hover_vel.yaml # Config for velocity-level action
|
|
176
|
+
└── checkpoints/ # Saved models and VecNormalize stats
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Features
|
|
180
|
+
|
|
181
|
+
### Project status
|
|
182
|
+
|
|
183
|
+
| Area | Status |
|
|
184
|
+
|------|--------|
|
|
185
|
+
| Quaternion dynamics, bounded allocation, geometric/PID/LQR control | ✅ Implemented and tested |
|
|
186
|
+
| Sensor simulation, EKF/AHRS estimation, closed-loop orchestration | ✅ Implemented and tested |
|
|
187
|
+
| CI, disturbances, CSV/JSON telemetry, Rerun replay | ✅ Implemented and tested |
|
|
188
|
+
| Gymnasium environment, PPO training/evaluation, RL examples | ✅ Baseline implemented |
|
|
189
|
+
| ULog export and RL domain randomization/curriculum | 🟡 Partially implemented / planned |
|
|
190
|
+
| MPC, Monte Carlo evaluation, VIO, obstacle planning, swarms, SITL | ⬜ Planned |
|
|
191
|
+
|
|
192
|
+
See the [development roadmap](docs/development-roadmap.md) for acceptance criteria and remaining work.
|
|
193
|
+
|
|
194
|
+
### State estimation
|
|
195
|
+
|
|
196
|
+
| Filter | States | Description |
|
|
197
|
+
|--------|--------|-------------|
|
|
198
|
+
| Extended Kalman Filter | 16-state | Position(3), velocity(3), quaternion(4), accel bias(3), gyro bias(3). Analytical Jacobians, Joseph form covariance, GPS/baro/velocity corrections |
|
|
199
|
+
| Adaptive EKF | 9-state | Position(3), velocity(3), accel bias(3). Innovation-window adaptive noise, Gauss-Markov bias |
|
|
200
|
+
| AHRS | Complementary filter | Fuses accel, gyro, mag with gyro bias learning |
|
|
201
|
+
|
|
202
|
+
### Control
|
|
203
|
+
|
|
204
|
+
| Controller | Type | Description |
|
|
205
|
+
|------------|------|-------------|
|
|
206
|
+
| Geometric | Nonlinear SE(3) | Quaternion-safe trajectory tracking with velocity/acceleration feedforward and bounded allocation |
|
|
207
|
+
| Cascaded PID | 3-loop cascade | Position → Velocity → Attitude. 9 PID instances, motor-speed output |
|
|
208
|
+
| LQR | Full-state feedback | Linearized around hover, CARE solution, wrench → motor allocation |
|
|
209
|
+
|
|
210
|
+
### Disturbances (6 types)
|
|
211
|
+
|
|
212
|
+
| Disturbance | Category | Description |
|
|
213
|
+
|-------------|----------|-------------|
|
|
214
|
+
| `ConstantWind` | Wind | Steady world-frame drag force |
|
|
215
|
+
| `StepWind` | Wind | Wind that switches on at a given time |
|
|
216
|
+
| `DrydenGust` | Wind | Continuous turbulence — Gauss-Markov process (MIL-F-8785C) |
|
|
217
|
+
| `MotorFailure` | Failure | Degraded rotor thrust coefficient |
|
|
218
|
+
| `PayloadDrop` | Failure | Instantaneous mass change |
|
|
219
|
+
| `GroundEffect` | Environment | Thrust augmentation near ground (Cheeseman & Bennett) |
|
|
220
|
+
|
|
221
|
+
### Reinforcement learning
|
|
222
|
+
|
|
223
|
+
- **QuadcopterEnv** — Gymnasium `Env` compatible with SB3, CleanRL, Tianshou, RLlib
|
|
224
|
+
- **Four action parameterizations** — three levels of abstraction plus a residual:
|
|
225
|
+
|
|
226
|
+
| Action | Policy outputs | Stabilization |
|
|
227
|
+
|--------|---------------|---------------|
|
|
228
|
+
| `MotorSpeedAction` | Raw motor speeds (4× rad/s) | None (hardest) |
|
|
229
|
+
| `ThrustBodyRatesAction` | Thrust delta + body rates (ωx,ωy,ωz) | Rate → torque P-controller |
|
|
230
|
+
| `VelocityLevelAction` | World-frame velocity (vx,vy,vz) + yaw rate | Built-in cascaded P-controller (velocity → attitude → torque) |
|
|
231
|
+
| `LQRResidualAction` | Delta on LQR motor speeds (in [-1,1]) | Full-state LQR feedback (CARE solution) |
|
|
232
|
+
|
|
233
|
+
- **Three tasks**: hover, waypoint sequence, trajectory tracking
|
|
234
|
+
- **Weighted multi-term reward** (position, velocity, attitude, action smoothness, alive/crash)
|
|
235
|
+
- **PPO training** in `training/train_ppo.py` with YAML configs, TensorBoard logging, and optional W&B tracking
|
|
236
|
+
- **Policy evaluation** in `training/eval_policy.py` (RMSE, success rate, crash rate)
|
|
237
|
+
|
|
238
|
+
#### Training
|
|
239
|
+
|
|
240
|
+
The training script defaults to CPU for small MLP policies (GPU transfer overhead dominates):
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# LQR residual (recommended — 75%+ success rate at 500k steps)
|
|
244
|
+
python -m training.train_ppo \
|
|
245
|
+
--config training/configs/ppo_hover.yaml \
|
|
246
|
+
--timesteps 500000 \
|
|
247
|
+
--action-type lqr_residual
|
|
248
|
+
|
|
249
|
+
# Velocity-level (0% crash, 1.5m RMSE)
|
|
250
|
+
python -m training.train_ppo \
|
|
251
|
+
--config training/configs/ppo_hover_vel.yaml \
|
|
252
|
+
--timesteps 200000 \
|
|
253
|
+
--action-type velocity
|
|
254
|
+
|
|
255
|
+
# Thrust + body rates (legacy)
|
|
256
|
+
python -m training.train_ppo \
|
|
257
|
+
--config training/configs/ppo_hover.yaml \
|
|
258
|
+
--timesteps 200000 \
|
|
259
|
+
--action-type thrust_rates
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Track training with Weights & Biases:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
python -m training.train_ppo \
|
|
266
|
+
--config training/configs/ppo_hover.yaml \
|
|
267
|
+
--action-type lqr_residual \
|
|
268
|
+
--track --wandb-project drones-sim-ppo
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Open TensorBoard (logs are saved to `./tb/`):
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
tensorboard --logdir tb/
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
#### Evaluation
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# Evaluate a trained checkpoint
|
|
281
|
+
python -m training.eval_policy \
|
|
282
|
+
--path training/checkpoints/final.zip \
|
|
283
|
+
--episodes 20 \
|
|
284
|
+
--action-type lqr_residual
|
|
285
|
+
|
|
286
|
+
# Expected output:
|
|
287
|
+
# pos_rmse: 0.1370
|
|
288
|
+
# success_rate: 0.7500
|
|
289
|
+
# crash_rate: 0.0000
|
|
290
|
+
# mean_reward: 6390.6716
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
### Logging
|
|
296
|
+
|
|
297
|
+
| Logger | Format | Description |
|
|
298
|
+
|--------|--------|-------------|
|
|
299
|
+
| `CsvLogger` | CSV | Full state + motor speeds + estimate per row |
|
|
300
|
+
| `JsonLogger` | JSON Lines | Per-line JSON objects; machine-readable |
|
|
301
|
+
|
|
302
|
+
## Tests
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
pytest tests/ -v
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Release history is maintained in [CHANGELOG.md](CHANGELOG.md).
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
MIT — see [LICENSE](LICENSE).
|
|
313
|
+
|
|
314
|
+
## Dependencies
|
|
315
|
+
|
|
316
|
+
| Dependency | Purpose |
|
|
317
|
+
|------------|---------|
|
|
318
|
+
| numpy, scipy | Numerical computation |
|
|
319
|
+
| matplotlib | 2D plotting |
|
|
320
|
+
| [Rerun](https://rerun.io/) | Default synchronized 3D telemetry, replay, and `.rrd` export |
|
|
321
|
+
| [viser](https://github.com/nerfstudio-project/viser) | Optional interactive mission controls (`[viser]`) |
|
|
322
|
+
| torch, stable-baselines3, gymnasium | RL training (`[rl]` extra) |
|
|
323
|
+
| tensorboard, pyyaml | RL logging & config (`[rl]` extra) |
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# drones_sim
|
|
2
|
+
|
|
3
|
+
Composable quadcopter simulation for control and state-estimation experiments. It combines a nonlinear quaternion rigid-body model, bounded actuators, SE(3) geometric control, innovation-gated sensor fusion, reproducible sensor models, and result-native 2D/3D visualization.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/thanhndv212/drones-sim/actions/workflows/ci.yml)
|
|
6
|
+
[](https://deepwiki.com/thanhndv212/drones-sim)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
cd drones_sim
|
|
12
|
+
pip install -e ".[dev]"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
RL extras (Gymnasium env, SB3 PPO, TensorBoard):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install -e ".[rl]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Full RL dev stack (adds Weights & Biases, Optuna, moviepy):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -e ".[rl-dev]"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Legacy Viser mission-control examples:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install -e ".[viser]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
The high-level runner owns timing, sensing, estimation, control, logging, and aligned outputs:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from drones_sim import ClosedLoopSimulator, SimulationConfig, visualize
|
|
39
|
+
from drones_sim.trajectory import generate_circular
|
|
40
|
+
|
|
41
|
+
reference = generate_circular(duration=10.0, sample_rate=100, radius=2.0)
|
|
42
|
+
simulator = ClosedLoopSimulator(
|
|
43
|
+
config=SimulationConfig(dt=0.01, use_estimator=True, seed=7)
|
|
44
|
+
)
|
|
45
|
+
result = simulator.run(reference)
|
|
46
|
+
|
|
47
|
+
print(result.summary())
|
|
48
|
+
visualize(result) # Rerun is the default backend
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For custom experiments, compose `QuadcopterConfig`, `QuadcopterDynamics`,
|
|
52
|
+
`GeometricController`, `IMUSimulator`, `GPSSimulator`, and
|
|
53
|
+
`ExtendedKalmanFilter`, then inject them into `ClosedLoopSimulator`.
|
|
54
|
+
|
|
55
|
+
### Working examples
|
|
56
|
+
|
|
57
|
+
| # | Script | Install | Description |
|
|
58
|
+
|---|--------|---------|-------------|
|
|
59
|
+
| 01 | `01_imu_ekf_basic.py` | Base | IMU simulation and 16-state EKF sensor fusion |
|
|
60
|
+
| 02 | `02_ekf_adaptive.py` | Base | Adaptive EKF with temperature-dependent sensor models |
|
|
61
|
+
| 03 | `03_waypoint_nav.py` | Base | Minimum-snap waypoint navigation with cascaded PID |
|
|
62
|
+
| 04 | `04_viser_viewer.py` | `[viser]` | Interactive Viser 3D playback |
|
|
63
|
+
| 05 | `05_full_pipeline.py` | `[viser]` | Dynamics, sensors, EKF, PID control, and interactive viewer |
|
|
64
|
+
| 06 | `06_trajectory_following.py` | `[viser]` | EKF-fused trajectory tracking with mission controls |
|
|
65
|
+
| 07 | `07_rl_hover.py` | `[rl]` | Train and evaluate a PPO hover policy |
|
|
66
|
+
| 08 | `08_rl_vs_pid.py` | `[rl]` | Compare a trained RL policy with cascaded PID |
|
|
67
|
+
| 09 | `09_unified_simulation.py` | Base | Typed end-to-end simulation and diagnostic dashboard |
|
|
68
|
+
| 10 | `10_rerun_viewer.py` | Base | Rerun 3D scene, telemetry, streaming, and `.rrd` export |
|
|
69
|
+
|
|
70
|
+
Run a base example directly:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python examples/09_unified_simulation.py
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The default `visualize(result)` call opens Rerun. It can also stream to an
|
|
77
|
+
existing gRPC server or save a portable recording:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python examples/10_rerun_viewer.py
|
|
81
|
+
python examples/10_rerun_viewer.py --save flight.rrd
|
|
82
|
+
rerun flight.rrd
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Matplotlib and Viser remain available explicitly:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
visualize(result, backend="matplotlib", title="Flight report")
|
|
89
|
+
visualize(result, backend="viser", port=8080) # requires drones-sim[viser]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Package structure
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
src/drones_sim/
|
|
96
|
+
├── state.py # Typed state, setpoint, and control contracts
|
|
97
|
+
├── simulation.py # Reproducible closed-loop orchestration and results
|
|
98
|
+
├── math_utils.py # Quaternion ops, rotation matrices, Euler helpers
|
|
99
|
+
├── trajectory.py # Trajectory generators (hover-cruise, circular, waypoints, min-snap)
|
|
100
|
+
├── models/
|
|
101
|
+
│ ├── urdf_loader.py # Pure-stdlib URDF parser (no external deps)
|
|
102
|
+
│ └── quadcopter.urdf # Bundled quadcopter model
|
|
103
|
+
├── sensors/
|
|
104
|
+
│ ├── imu.py # 9-axis IMU simulator (accel, gyro, mag)
|
|
105
|
+
│ ├── gps.py # GNSS receiver simulator (position + velocity)
|
|
106
|
+
│ └── models.py # SensorNoiseModel (Gauss-Markov bias), TemperatureModel
|
|
107
|
+
├── estimation/
|
|
108
|
+
│ ├── ekf.py # 16-state EKF + 9-state adaptive EKF + AHRS
|
|
109
|
+
│ └── ahrs.py # Complementary-filter AHRS
|
|
110
|
+
├── dynamics/
|
|
111
|
+
│ ├── quadcopter.py # 13-state quaternion Newton-Euler rigid body
|
|
112
|
+
│ └── disturbances.py # Wind, gust, ground effect, motor failure, payload drop
|
|
113
|
+
├── control/
|
|
114
|
+
│ ├── pid.py # Scalar PID with anti-windup
|
|
115
|
+
│ ├── cascaded.py # Position → Velocity → Attitude cascaded PID
|
|
116
|
+
│ ├── geometric.py # Nonlinear SE(3) trajectory controller
|
|
117
|
+
│ ├── allocation.py # Bounded weighted least-squares motor allocation
|
|
118
|
+
│ └── lqr.py # Full-state feedback LQR (CARE solution)
|
|
119
|
+
├── rl/
|
|
120
|
+
│ ├── env.py # QuadcopterEnv (gymnasium.Env wrapper)
|
|
121
|
+
│ ├── actions.py # MotorSpeedAction, ThrustBodyRatesAction, VelocityLevelAction, LQRResidualAction
|
|
122
|
+
│ ├── observations.py # RelativeStateObs (17-D observation)
|
|
123
|
+
│ ├── tasks.py # HoverTask, WaypointTask, TrackingTask
|
|
124
|
+
│ └── reward.py # Weighted multi-term reward function
|
|
125
|
+
├── logging/
|
|
126
|
+
│ ├── csv_logger.py # CSV telemetry logger
|
|
127
|
+
│ └── json_logger.py # JSON Lines telemetry logger
|
|
128
|
+
└── visualization/
|
|
129
|
+
├── api.py # visualize(); Rerun is the default backend
|
|
130
|
+
├── plots.py # Matplotlib multi-panel comparison plots
|
|
131
|
+
├── dashboard.py # SimulationResult engineering dashboard
|
|
132
|
+
├── rerun_viewer.py # Default synchronized 3D, telemetry, events, and replay
|
|
133
|
+
└── viewer.py # Optional Viser mission-control UI (`[viser]`)
|
|
134
|
+
|
|
135
|
+
training/
|
|
136
|
+
├── train_ppo.py # PPO training entry point (YAML config, CPU default, W&B)
|
|
137
|
+
├── eval_policy.py # Policy evaluation with success/crash metrics
|
|
138
|
+
├── configs/
|
|
139
|
+
│ ├── ppo_hover.yaml # Config for thrust_rates / lqr_residual actions
|
|
140
|
+
│ └── ppo_hover_vel.yaml # Config for velocity-level action
|
|
141
|
+
└── checkpoints/ # Saved models and VecNormalize stats
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Features
|
|
145
|
+
|
|
146
|
+
### Project status
|
|
147
|
+
|
|
148
|
+
| Area | Status |
|
|
149
|
+
|------|--------|
|
|
150
|
+
| Quaternion dynamics, bounded allocation, geometric/PID/LQR control | ✅ Implemented and tested |
|
|
151
|
+
| Sensor simulation, EKF/AHRS estimation, closed-loop orchestration | ✅ Implemented and tested |
|
|
152
|
+
| CI, disturbances, CSV/JSON telemetry, Rerun replay | ✅ Implemented and tested |
|
|
153
|
+
| Gymnasium environment, PPO training/evaluation, RL examples | ✅ Baseline implemented |
|
|
154
|
+
| ULog export and RL domain randomization/curriculum | 🟡 Partially implemented / planned |
|
|
155
|
+
| MPC, Monte Carlo evaluation, VIO, obstacle planning, swarms, SITL | ⬜ Planned |
|
|
156
|
+
|
|
157
|
+
See the [development roadmap](docs/development-roadmap.md) for acceptance criteria and remaining work.
|
|
158
|
+
|
|
159
|
+
### State estimation
|
|
160
|
+
|
|
161
|
+
| Filter | States | Description |
|
|
162
|
+
|--------|--------|-------------|
|
|
163
|
+
| Extended Kalman Filter | 16-state | Position(3), velocity(3), quaternion(4), accel bias(3), gyro bias(3). Analytical Jacobians, Joseph form covariance, GPS/baro/velocity corrections |
|
|
164
|
+
| Adaptive EKF | 9-state | Position(3), velocity(3), accel bias(3). Innovation-window adaptive noise, Gauss-Markov bias |
|
|
165
|
+
| AHRS | Complementary filter | Fuses accel, gyro, mag with gyro bias learning |
|
|
166
|
+
|
|
167
|
+
### Control
|
|
168
|
+
|
|
169
|
+
| Controller | Type | Description |
|
|
170
|
+
|------------|------|-------------|
|
|
171
|
+
| Geometric | Nonlinear SE(3) | Quaternion-safe trajectory tracking with velocity/acceleration feedforward and bounded allocation |
|
|
172
|
+
| Cascaded PID | 3-loop cascade | Position → Velocity → Attitude. 9 PID instances, motor-speed output |
|
|
173
|
+
| LQR | Full-state feedback | Linearized around hover, CARE solution, wrench → motor allocation |
|
|
174
|
+
|
|
175
|
+
### Disturbances (6 types)
|
|
176
|
+
|
|
177
|
+
| Disturbance | Category | Description |
|
|
178
|
+
|-------------|----------|-------------|
|
|
179
|
+
| `ConstantWind` | Wind | Steady world-frame drag force |
|
|
180
|
+
| `StepWind` | Wind | Wind that switches on at a given time |
|
|
181
|
+
| `DrydenGust` | Wind | Continuous turbulence — Gauss-Markov process (MIL-F-8785C) |
|
|
182
|
+
| `MotorFailure` | Failure | Degraded rotor thrust coefficient |
|
|
183
|
+
| `PayloadDrop` | Failure | Instantaneous mass change |
|
|
184
|
+
| `GroundEffect` | Environment | Thrust augmentation near ground (Cheeseman & Bennett) |
|
|
185
|
+
|
|
186
|
+
### Reinforcement learning
|
|
187
|
+
|
|
188
|
+
- **QuadcopterEnv** — Gymnasium `Env` compatible with SB3, CleanRL, Tianshou, RLlib
|
|
189
|
+
- **Four action parameterizations** — three levels of abstraction plus a residual:
|
|
190
|
+
|
|
191
|
+
| Action | Policy outputs | Stabilization |
|
|
192
|
+
|--------|---------------|---------------|
|
|
193
|
+
| `MotorSpeedAction` | Raw motor speeds (4× rad/s) | None (hardest) |
|
|
194
|
+
| `ThrustBodyRatesAction` | Thrust delta + body rates (ωx,ωy,ωz) | Rate → torque P-controller |
|
|
195
|
+
| `VelocityLevelAction` | World-frame velocity (vx,vy,vz) + yaw rate | Built-in cascaded P-controller (velocity → attitude → torque) |
|
|
196
|
+
| `LQRResidualAction` | Delta on LQR motor speeds (in [-1,1]) | Full-state LQR feedback (CARE solution) |
|
|
197
|
+
|
|
198
|
+
- **Three tasks**: hover, waypoint sequence, trajectory tracking
|
|
199
|
+
- **Weighted multi-term reward** (position, velocity, attitude, action smoothness, alive/crash)
|
|
200
|
+
- **PPO training** in `training/train_ppo.py` with YAML configs, TensorBoard logging, and optional W&B tracking
|
|
201
|
+
- **Policy evaluation** in `training/eval_policy.py` (RMSE, success rate, crash rate)
|
|
202
|
+
|
|
203
|
+
#### Training
|
|
204
|
+
|
|
205
|
+
The training script defaults to CPU for small MLP policies (GPU transfer overhead dominates):
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# LQR residual (recommended — 75%+ success rate at 500k steps)
|
|
209
|
+
python -m training.train_ppo \
|
|
210
|
+
--config training/configs/ppo_hover.yaml \
|
|
211
|
+
--timesteps 500000 \
|
|
212
|
+
--action-type lqr_residual
|
|
213
|
+
|
|
214
|
+
# Velocity-level (0% crash, 1.5m RMSE)
|
|
215
|
+
python -m training.train_ppo \
|
|
216
|
+
--config training/configs/ppo_hover_vel.yaml \
|
|
217
|
+
--timesteps 200000 \
|
|
218
|
+
--action-type velocity
|
|
219
|
+
|
|
220
|
+
# Thrust + body rates (legacy)
|
|
221
|
+
python -m training.train_ppo \
|
|
222
|
+
--config training/configs/ppo_hover.yaml \
|
|
223
|
+
--timesteps 200000 \
|
|
224
|
+
--action-type thrust_rates
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Track training with Weights & Biases:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
python -m training.train_ppo \
|
|
231
|
+
--config training/configs/ppo_hover.yaml \
|
|
232
|
+
--action-type lqr_residual \
|
|
233
|
+
--track --wandb-project drones-sim-ppo
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Open TensorBoard (logs are saved to `./tb/`):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
tensorboard --logdir tb/
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
#### Evaluation
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Evaluate a trained checkpoint
|
|
246
|
+
python -m training.eval_policy \
|
|
247
|
+
--path training/checkpoints/final.zip \
|
|
248
|
+
--episodes 20 \
|
|
249
|
+
--action-type lqr_residual
|
|
250
|
+
|
|
251
|
+
# Expected output:
|
|
252
|
+
# pos_rmse: 0.1370
|
|
253
|
+
# success_rate: 0.7500
|
|
254
|
+
# crash_rate: 0.0000
|
|
255
|
+
# mean_reward: 6390.6716
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
### Logging
|
|
261
|
+
|
|
262
|
+
| Logger | Format | Description |
|
|
263
|
+
|--------|--------|-------------|
|
|
264
|
+
| `CsvLogger` | CSV | Full state + motor speeds + estimate per row |
|
|
265
|
+
| `JsonLogger` | JSON Lines | Per-line JSON objects; machine-readable |
|
|
266
|
+
|
|
267
|
+
## Tests
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
pytest tests/ -v
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Release history is maintained in [CHANGELOG.md](CHANGELOG.md).
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT — see [LICENSE](LICENSE).
|
|
278
|
+
|
|
279
|
+
## Dependencies
|
|
280
|
+
|
|
281
|
+
| Dependency | Purpose |
|
|
282
|
+
|------------|---------|
|
|
283
|
+
| numpy, scipy | Numerical computation |
|
|
284
|
+
| matplotlib | 2D plotting |
|
|
285
|
+
| [Rerun](https://rerun.io/) | Default synchronized 3D telemetry, replay, and `.rrd` export |
|
|
286
|
+
| [viser](https://github.com/nerfstudio-project/viser) | Optional interactive mission controls (`[viser]`) |
|
|
287
|
+
| torch, stable-baselines3, gymnasium | RL training (`[rl]` extra) |
|
|
288
|
+
| tensorboard, pyyaml | RL logging & config (`[rl]` extra) |
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "drones-sim"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Composable quadcopter modeling, control, estimation, and visualization"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"numpy>=1.24",
|
|
15
|
+
"scipy>=1.10",
|
|
16
|
+
"matplotlib>=3.7",
|
|
17
|
+
"rerun-sdk>=0.27,<1",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = ["pytest>=7.0", "ruff"]
|
|
22
|
+
viser = ["viser>=0.2"]
|
|
23
|
+
rl = ["torch>=2.2", "stable-baselines3>=2.3", "gymnasium>=0.29", "tensorboard>=2.15", "pyyaml>=6.0"]
|
|
24
|
+
rl-dev = ["drones-sim[rl]", "wandb", "optuna>=3.5", "moviepy"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/thanhndv212/drones-sim"
|
|
28
|
+
Documentation = "https://github.com/thanhndv212/drones-sim#readme"
|
|
29
|
+
Changelog = "https://github.com/thanhndv212/drones-sim/blob/master/CHANGELOG.md"
|
|
30
|
+
Issues = "https://github.com/thanhndv212/drones-sim/issues"
|
|
31
|
+
Source = "https://github.com/thanhndv212/drones-sim"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
line-length = 100
|
|
38
|
+
target-version = "py310"
|
|
39
|
+
src = ["src", "tests", "examples"]
|
|
40
|
+
extend-exclude = [".venv"]
|
|
41
|
+
|
|
42
|
+
[tool.ruff.lint]
|
|
43
|
+
select = ["E", "F", "W", "I", "UP"]
|
|
44
|
+
ignore = ["E501"] # long lines — not worth a mass reformat
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
addopts = "-q"
|
|
49
|
+
markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.package-data]
|
|
52
|
+
"drones_sim.models" = ["*.urdf"]
|