aerial-kit 0.1.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.
Files changed (31) hide show
  1. aerial_kit-0.1.0/LICENSE +21 -0
  2. aerial_kit-0.1.0/PKG-INFO +82 -0
  3. aerial_kit-0.1.0/README.md +391 -0
  4. aerial_kit-0.1.0/aerial_kit/README.md +58 -0
  5. aerial_kit-0.1.0/aerial_kit/__init__.py +5 -0
  6. aerial_kit-0.1.0/aerial_kit/airframes/__init__.py +9 -0
  7. aerial_kit-0.1.0/aerial_kit/airframes/base.py +25 -0
  8. aerial_kit-0.1.0/aerial_kit/airframes/fixed_wing.py +104 -0
  9. aerial_kit-0.1.0/aerial_kit/airframes/multirotor.py +81 -0
  10. aerial_kit-0.1.0/aerial_kit/controllers/__init__.py +18 -0
  11. aerial_kit-0.1.0/aerial_kit/controllers/basic.py +79 -0
  12. aerial_kit-0.1.0/aerial_kit/controllers/fixed_wing.py +147 -0
  13. aerial_kit-0.1.0/aerial_kit/controllers/position.py +79 -0
  14. aerial_kit-0.1.0/aerial_kit/dynamics/__init__.py +17 -0
  15. aerial_kit-0.1.0/aerial_kit/dynamics/fixed_wing.py +248 -0
  16. aerial_kit-0.1.0/aerial_kit/dynamics/multirotor.py +157 -0
  17. aerial_kit-0.1.0/aerial_kit/dynamics/pointmass.py +38 -0
  18. aerial_kit-0.1.0/aerial_kit/guidance/__init__.py +1 -0
  19. aerial_kit-0.1.0/aerial_kit/guidance/l1.py +56 -0
  20. aerial_kit-0.1.0/aerial_kit/guidance/tecs.py +53 -0
  21. aerial_kit-0.1.0/aerial_kit/interfaces.py +73 -0
  22. aerial_kit-0.1.0/aerial_kit/py.typed +0 -0
  23. aerial_kit-0.1.0/aerial_kit/registry.py +112 -0
  24. aerial_kit-0.1.0/aerial_kit/types.py +89 -0
  25. aerial_kit-0.1.0/aerial_kit.egg-info/PKG-INFO +82 -0
  26. aerial_kit-0.1.0/aerial_kit.egg-info/SOURCES.txt +29 -0
  27. aerial_kit-0.1.0/aerial_kit.egg-info/dependency_links.txt +1 -0
  28. aerial_kit-0.1.0/aerial_kit.egg-info/requires.txt +2 -0
  29. aerial_kit-0.1.0/aerial_kit.egg-info/top_level.txt +1 -0
  30. aerial_kit-0.1.0/pyproject.toml +35 -0
  31. aerial_kit-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 c-y-i
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,82 @@
1
+ Metadata-Version: 2.4
2
+ Name: aerial-kit
3
+ Version: 0.1.0
4
+ Summary: Shared control-stack package for aerial robots: airframe capabilities, allocation/trim, dynamics, controllers, and guidance -- no ROS, no matplotlib.
5
+ Author: c-y-i
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/RawFish69/UAV-Controller
8
+ Project-URL: Repository, https://github.com/RawFish69/UAV-Controller
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Classifier: Topic :: Scientific/Engineering :: Physics
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpy>=1.22
22
+ Requires-Dist: scipy>=1.10
23
+ Dynamic: license-file
24
+
25
+ # aerial_kit
26
+
27
+ A shared control-stack package for aerial robots: airframe capabilities, actuator
28
+ allocation/trim, 6-DOF and point-mass dynamics models, controllers (PID/LQR/MPC, and an
29
+ L1 + TECS fixed-wing autopilot), and lateral/longitudinal guidance laws -- all pure
30
+ numpy/scipy, no ROS and no matplotlib dependency.
31
+
32
+ It grew out of [UAV-Controller](https://github.com/RawFish69/UAV-Controller), a
33
+ quadcopter-to-general-aerial-robotics control stack, where it's the shared layer imported
34
+ by both a standalone Python simulator and ROS 2 flight nodes so they don't duplicate
35
+ control code.
36
+
37
+ ## What's in it
38
+
39
+ - `aerial_kit.types` -- `SimState`, `ControlTarget`, `Capabilities`, `Wrench`,
40
+ `CommandKind`, `Waypoint`
41
+ - `aerial_kit.interfaces` -- `Controller`, `DynamicsBackend`, `Planner` ABCs
42
+ - `aerial_kit.registry` -- a pluggable component registry
43
+ (`register_airframe`/`register_controller`/etc., `create_*` factories)
44
+ - `aerial_kit.airframes` -- `Airframe` ABC, `MultirotorAirframe` (mixer-driven quad/hex/
45
+ octo), `TwinWingAirframe` (elevon + differential-thrust allocation, trim)
46
+ - `aerial_kit.dynamics` -- 6-DOF multirotor dynamics, point-mass dynamics, and a
47
+ hand-rolled 6-DOF fixed-wing model with a flat-plate-blended lift curve, drag polar,
48
+ and moment derivatives
49
+ - `aerial_kit.controllers` -- PID/LQR/MPC position controllers, and
50
+ `FixedWingL1TECSController` (L1 lateral guidance + TECS-lite longitudinal control +
51
+ coordinated-turn attitude PID)
52
+ - `aerial_kit.guidance` -- `l1_bank_command`, `tecs_command` as standalone functions
53
+
54
+ ## Quick start
55
+
56
+ ```python
57
+ from aerial_kit.registry import register_builtin_components, create_airframe, create_controller
58
+
59
+ register_builtin_components()
60
+ airframe = create_airframe("quad")
61
+ controller = create_controller("pid")
62
+ print(airframe.capabilities)
63
+ ```
64
+
65
+ `register_builtin_components()` here registers only what lives inside `aerial_kit`
66
+ itself (airframes, controllers) -- it has no ROS or matplotlib dependency and does not
67
+ know about any host application's own dynamics backends or planners. A host application
68
+ (like `sim_py` in the parent repo) registers its own backends/planners into the same
69
+ registry alongside this.
70
+
71
+ ## Status
72
+
73
+ Early, actively developed alongside the parent repo's twin-motor-wing simulation work.
74
+ The multirotor path (quad/hex/octo airframes, PID/LQR/MPC) is stable and used by a flying
75
+ quadcopter's ground-station simulator. The fixed-wing path (`TwinWingAirframe`,
76
+ `FixedWingL1TECSController`, guidance) is simulation-only so far -- not flown, and its aero
77
+ model uses plausible placeholder coefficients rather than a fitted model of any specific
78
+ real airframe.
79
+
80
+ ## License
81
+
82
+ MIT
@@ -0,0 +1,391 @@
1
+ # UAV Controller + Path Planning
2
+
3
+ Control, planning, and radio-link stack for **aerial robots**. Alongside the ROS 2 /
4
+ Python control stack, this repo carries fully custom ESP32 flight-link firmware
5
+ (ESP-NOW, ELRS, LoRa, GPS) — custom multirotor firmware is being added here too,
6
+ alongside the existing links.
7
+
8
+ - **ROS 2 control + safety pipeline** (hardware + Gazebo Sim / fast sim)
9
+ - **Standalone Python simulator** (no ROS) for fast iteration on planners/controllers
10
+ - **Autopilot bridges** for PX4, ArduPilot, and Betaflight
11
+ - **ESP32 firmware** (ESP-NOW, ELRS, LoRa, GPS) for manual flight + autonomous command relay
12
+
13
+ ## What's in this repo
14
+
15
+ - **Controllers**
16
+ - **ROS 2**: PID / LQR / MPC (work-in-progress depending on package)
17
+ - **Python-only** (`sim_py`): PID / LQR / MPC position controllers + Matplotlib teleop
18
+ - **Path planning (Python-only)**: straight / A* / RRT planners
19
+ - **Terrain generation**: forest / mountains / plains (shared between ROS and Python sim)
20
+ - **Safety**: validation, limiting, watchdog (`safety_gate`)
21
+ - **Hardware link**: CRSF adapter + ESP-NOW / ELRS TX/RX + protocol bridging
22
+ - **Autopilot bridges**: MAVLink (PX4 / ArduPilot) and CRSF (Betaflight)
23
+
24
+ ## Demo
25
+
26
+ All demos below are **quadcopter** flights — see [Supported airframes](#supported-airframes)
27
+ for what else is in progress.
28
+
29
+ ### Python Sim & ROS2 Gazebo Sim
30
+
31
+ <p>
32
+ <img src="docs/forest_rotor.png" alt="Forest RotorPy demo with quad pose overlay" width="49%">
33
+ <img src="docs/mountain_rrt_star_1.png" alt="Mountain path planner with RRT*" width="49%">
34
+ </p>
35
+
36
+ <p>
37
+ <img src="docs/sim_demo_1.png" alt="ROS2 Gazebo simulation demo" width="98%">
38
+ </p>
39
+
40
+ ### ROS2 Gazebo
41
+
42
+ <img src="docs/sim_demo_2.png" alt="ROS2 Gazebo demo screenshot (additional)" width="900">
43
+
44
+ ### Hovering & Landing (IMU + Barometer + GPS)
45
+
46
+ https://github.com/user-attachments/assets/44837663-b281-45db-9803-5aaa9812833d
47
+
48
+ *Autonomous hover and landing, commanded over the CRSF/ESP-NOW link. The state estimate fuses IMU
49
+ attitude, barometric altitude, and GPS position.*
50
+
51
+ ## Supported airframes
52
+
53
+ | Airframe | Simulation | Control |
54
+ |----------|-----------|---------|
55
+ | Quadcopter | ✅ Working | ✅ PID / LQR / MPC |
56
+ | Hexacopter | ✅ Mixer verified | ✅ PID / LQR / MPC |
57
+ | Octacopter | ✅ Mixer verified | ✅ PID / LQR / MPC |
58
+ | Twin-motor wing | 🚧 6-DOF backend | 🚧 L1/TECS guidance |
59
+ | Single-motor wing | ⏳ To be added | ⏳ To be added |
60
+ | Monocopter | ⏳ To be added | ⏳ To be added |
61
+ | TVC (thrust-vectored) | ⏳ To be added | ⏳ To be added |
62
+
63
+ Quadcopter is the reference airframe and everything in the demos above is a quad. The
64
+ pieces that are still quad-shaped today are the `rotorpy` dynamics backend, the
65
+ `hover_throttle` / velocity-to-stick mapping in `hw_bridge`, and the Gazebo `x3` /
66
+ `lr_drone` models. Lifting those into a shared airframe layer is the active line of work;
67
+ see [Roadmap](#roadmap).
68
+
69
+ ## Autopilot & firmware support
70
+
71
+ | Platform | Link | Package | Status |
72
+ |----------|------|---------|--------|
73
+ | Betaflight | CRSF over ESP-NOW/UDP relay | `ros2_ws/src/hw_bridge` (`crsf_backend_adapter_node`) | ✅ Working |
74
+ | PX4 | MAVLink over USB / UART / telemetry radio | `ros2_ws/src/mavlink_bridge` | ✅ Working |
75
+ | ArduPilot | MAVLink, same node (`flight_stack: ardupilot`) | `ros2_ws/src/mavlink_bridge` | 🚧 Supported, in testing |
76
+ | Custom firmware (this repo) | ESP-NOW + CRSF, 2.4 GHz SX1280 ELRS, LoRa telemetry | `firmware/` | ✅ Working |
77
+
78
+ All autopilot backends implement the same `/uav/backend/*` contract as the simulation
79
+ adapters, so the mission and control stack above them is unchanged between sim, Betaflight,
80
+ and PX4/ArduPilot.
81
+
82
+ ## Architecture (high level)
83
+
84
+ **Manual Flight**
85
+
86
+ ```
87
+ TX (IMU+Joystick) -> ESP-NOW -> RX -> Protocol Bridge -> Flight Controller
88
+ (CRSF/SBUS/PPM/iBus/FrSky)
89
+ ```
90
+
91
+ **Autonomous (Hardware-in-the-loop)**
92
+
93
+ ```
94
+ /uav/backend/cmd_twist + /uav/backend/enable
95
+ -> hw_bridge (crsf_backend_adapter_node) -> Betaflight over CRSF/ESP-NOW
96
+ | mavlink_bridge (mavlink_bridge_node) -> PX4 / ArduPilot over MAVLink
97
+
98
+ FC sensors (/uav/hw/imu, /uav/hw/baro, /uav/hw/gps)
99
+ -> hw_state_estimator_node
100
+ -> /uav/backend/odom
101
+ -> telemetry_adapter_node -> /uav/backend/telemetry_raw
102
+ ```
103
+
104
+ **Simulation**
105
+
106
+ - **ROS 2 (Gazebo / fast sim)**:
107
+
108
+ ```
109
+ Ground Station / Air Unit -> sim_bridge -> Gazebo Sim (or sim_fast)
110
+ ```
111
+
112
+ - **Python-only (no ROS)**:
113
+
114
+ ```
115
+ Planner -> Controller -> Dynamics backend (pointmass/rotorpy) -> Matplotlib 3D
116
+ ```
117
+
118
+ ## Quick start (Python-only simulator)
119
+
120
+ ```bash
121
+ ./scripts/setup_sim_py_venv.sh
122
+ source sim_py/.venv/bin/activate
123
+ python -m sim_py.run_sim
124
+ ```
125
+
126
+ Optional RotorPy backend:
127
+
128
+ ```bash
129
+ ./scripts/setup_sim_py_venv.sh --with-rotorpy
130
+ source sim_py/.venv/bin/activate
131
+ python -m sim_py.run_sim --backend rotorpy
132
+ ```
133
+
134
+ Useful overrides:
135
+
136
+ ```bash
137
+ # Switch controller
138
+ python -m sim_py.run_sim --controller mpc
139
+
140
+ # Change terrain type (still uses the terrain config YAML unless overridden)
141
+ python -m sim_py.run_sim --terrain forest
142
+
143
+ # Override sim time / dt (if you pass these, they override sim_config.yaml)
144
+ python -m sim_py.run_sim --sim-time 240 --dt 0.01
145
+
146
+ # Use a different terrain config file
147
+ python -m sim_py.run_sim --terrain-config ros2_ws/src/terrain_generator/config/terrain_params.yaml
148
+
149
+ # Select dynamics backend (default: pointmass)
150
+ python -m sim_py.run_sim --backend rotorpy
151
+
152
+ # Interactive teleop (Matplotlib, best with RotorPy backend)
153
+ python -m sim_py.run_sim --controller teleop --backend rotorpy --terrain forest
154
+ ```
155
+
156
+ ### Python sim teleop (Matplotlib)
157
+
158
+ `sim_py` now includes an interactive teleop mode for quick manual flying in the Matplotlib 3D viewer.
159
+
160
+ - Launch: `python -m sim_py.run_sim --controller teleop --backend rotorpy`
161
+ - Controls (focus the plot window first):
162
+ - `W/S`: +/- X
163
+ - `A/D`: +/- Y
164
+ - `R/F`: +/- Z
165
+ - `P`: pause/resume
166
+ - `Esc`: close
167
+
168
+ Teleop is acceleration-command based and works best with the RotorPy backend.
169
+
170
+ ### Python sim configuration
171
+
172
+ - **Main config**: `sim_py/sim_config.yaml`
173
+ - **Start/goal**: `path.start_relative_*`, `path.end_relative_*`
174
+ - `end_relative_z: "auto"` picks a random goal altitude in \([0, \text{tallest tree}]\)
175
+ - **Planner**: `path.planner_type` = `straight` | `astar` | `rrt` | `rrt*`
176
+ - **Runtime**: `controller.sim_time`, `controller.dt`
177
+ - **Backend**: `simulation.backend` = `pointmass` | `rotorpy` (CLI `--backend` overrides)
178
+ - **Terrain appearance / scaling**:
179
+ - `visual.forest_density_scale`: scales forest density (clamped to 1.0)
180
+ - `visual.tree_height_scale`: scales sampled tree heights
181
+ - `visual.height_ratio`: sets map height as `height_ratio * tallest_tree`
182
+ - `visual.tree_radius_ref`: reference radius for drawing thicker/thinner trunks
183
+
184
+ - **Terrain config** (shared with ROS):
185
+ - `ros2_ws/src/terrain_generator/config/terrain_params.yaml`
186
+ - Forest obstacle count is mainly set by:
187
+ - `forest.grid_size` and `forest.density`
188
+ - expected trees ~= \(grid\_size^2 \cdot density\)
189
+
190
+ ## Quick start (ROS 2)
191
+
192
+ ### Gazebo / Ground-Air stack (current)
193
+
194
+ The rebuilt Gazebo + ground-station / air-unit stack now lives in `ros2_ws`.
195
+
196
+ - Workspace docs / runbook: `ros2_ws/README.md`
197
+ - Primary sim bringup: `ros2 launch sim_gazebo bringup.launch.py`
198
+ - Ground station bringup: `ros2 launch ground_station ground.launch.py`
199
+
200
+ ### Hardware autonomous flight
201
+
202
+ CRSF/Betaflight backend launch:
203
+
204
+ ```bash
205
+ cd ros2_ws
206
+ source install/setup.bash
207
+ ros2 launch hw_bridge hw_crsf.launch.py udp_host:=192.168.4.1
208
+ ```
209
+
210
+ PX4 / ArduPilot MAVLink backend launch:
211
+
212
+ ```bash
213
+ # Direct MAVLink to the flight controller (USB / UART / telemetry radio).
214
+ # Defaults come from mavlink_bridge/config/mavlink_bridge_default.yaml.
215
+ ros2 launch mavlink_bridge real_hardware.launch.py
216
+
217
+ # To change connection_url / baud / flight_stack ("px4" or "ardupilot"),
218
+ # copy that YAML and point the launch at it:
219
+ ros2 launch mavlink_bridge real_hardware.launch.py \
220
+ mavlink_bridge_params_file:=/path/to/my_vehicle.yaml
221
+
222
+ # SITL / UDP via the older hw_bridge adapter
223
+ ros2 launch hw_bridge hw_px4.launch.py mavlink_url:=udpin:0.0.0.0:14540
224
+ ```
225
+
226
+ Wiring and parameter details: `ros2_ws/src/mavlink_bridge/README.md`.
227
+
228
+ Sensor topic contract for hardware estimation:
229
+ - `/uav/hw/imu` (`sensor_msgs/msg/Imu`)
230
+ - `/uav/hw/baro` (`std_msgs/msg/Float64`, meters)
231
+ - `/uav/hw/gps` (`sensor_msgs/msg/NavSatFix`)
232
+
233
+ Bring-up and TX integration details are in `docs/HARDWARE.md`.
234
+
235
+ **Before real flight:** tune `hover_throttle` and mapping gains per airframe, and keep Betaflight in **Angle mode** for the velocity-to-stick mapping used by `hw_bridge`.
236
+
237
+ ### Build
238
+
239
+ ```bash
240
+ cd ros2_ws
241
+ colcon build --symlink-install
242
+ source install/setup.bash
243
+ ```
244
+
245
+ ### Run Gazebo / Fast simulation
246
+
247
+ ```bash
248
+ ros2 launch sim_gazebo bringup.launch.py
249
+ ros2 launch ground_station ground.launch.py
250
+
251
+ # Fast headless backend
252
+ ros2 launch sim_fast bringup.launch.py
253
+ ```
254
+
255
+ For the current tested Gazebo + terrain + planner demo commands (including dense forest and path visualization), use:
256
+ - `ros2_ws/README.md` -> `Recommended Test Flows (Current)`
257
+
258
+ ### Legacy ROS2 prototype scripts
259
+
260
+ The old RViz/controller prototype workspace was replaced during consolidation.
261
+ If you still need the legacy PID/LQR/MPC ROS2 stack, recover it from git history.
262
+ Current ROS2 workflows are documented in `ros2_ws/README.md`.
263
+
264
+ ## Docker quick start
265
+ Dockerfiles are split by workflow:
266
+
267
+ - ROS 2 Humble: `docker/Dockerfile.humble`
268
+ - Python simulator/tools: `docker/Dockerfile.sim`
269
+ - Firmware tooling (PlatformIO): `docker/Dockerfile.firmware`
270
+
271
+ Build and run:
272
+
273
+ ```bash
274
+ # ROS 2 image
275
+ docker build -f docker/Dockerfile.humble --target ros-dev -t uav-controller:ros-humble .
276
+ docker run --rm -it --network=host --privileged -v "$PWD":/workspace uav-controller:ros-humble
277
+
278
+ # sim_py + Python tools image
279
+ docker build -f docker/Dockerfile.sim -t uav-controller:sim .
280
+ docker run --rm -it -v "$PWD":/workspace uav-controller:sim
281
+
282
+ # firmware / PlatformIO image
283
+ docker build -f docker/Dockerfile.firmware -t uav-controller:firmware .
284
+ docker run --rm -it -v "$PWD":/workspace uav-controller:firmware
285
+ ```
286
+
287
+ More details: `docker/README.md`.
288
+
289
+ ## GPS module (ESP32)
290
+
291
+ The `firmware/gps/` project is an ESP32 GPS bring-up/telemetry module using `Adafruit_GPS`.
292
+
293
+ - Supports PMTK/NMEA modules (Adafruit Ultimate GPS / MTK33xx style)
294
+ - Supports u-blox modules with UBX configuration (while parsing NMEA output)
295
+ - Auto-probes common UART baud rates (9600/38400/115200), parses fix/satellite/SNR metrics, and prints diagnostics over serial
296
+ - Build/flash protocol options:
297
+ - `pio run -d firmware/gps -e gps_auto -t upload` (AUTO detect PMTK vs UBLOX)
298
+ - `pio run -d firmware/gps -e gps_pmtk -t upload` (force PMTK mode)
299
+ - `pio run -d firmware/gps -e gps_ublox -t upload` (force UBLOX mode)
300
+
301
+ The host-side live dashboard is `tools/gps_dashboard.py` (see `tools/GPS_DASHBOARD.md`).
302
+
303
+ <img src="docs/gps_demo_1.png" alt="GPS telemetry demo output" width="700">
304
+
305
+ ## Packages / folders
306
+
307
+ | Path | Type | Purpose |
308
+ |------|------|---------|
309
+ | `ros2_ws/src/air_unit` | Python | Air-side command manager / mission executor / telemetry adapter |
310
+ | `ros2_ws/src/ground_station` | Python | CLI, monitor, and demo mission tools |
311
+ | `ros2_ws/src/planner` | Python | ROS2 planner service wrapper for `sim_py` planners |
312
+ | `ros2_ws/src/sim_bridge` | Python | Backend adapters (Gazebo / fast sim) |
313
+ | `ros2_ws/src/hw_bridge` | Python | Hardware backend adapters + estimator (CRSF/Betaflight) |
314
+ | `ros2_ws/src/mavlink_bridge` | Python | MAVLink backend adapter for PX4 / ArduPilot |
315
+ | `ros2_ws/src/sim_fast` | Python | Headless simulation bringup |
316
+ | `ros2_ws/src/sim_gazebo` | Python | Gazebo Sim bringup and assets |
317
+ | `ros2_ws/src/uav_algorithms` | Python | Shared algorithms / planning API helpers |
318
+ | `ros2_ws/src/drone_msgs` | ROS msgs/srvs | Command, telemetry, mission, planner interfaces |
319
+ | `ros2_ws/src/terrain_generator` | Python | Terrain + obstacles (forest/mountains/plains) |
320
+ | `sim_py` | Python | Standalone planner/controller/dynamics/visualization |
321
+ | `firmware/espnow` | ESP32 | ESP-NOW TX/RX firmware + protocol bridging |
322
+ | `firmware/elrs` | ESP32 | ExpressLRS-compatible SX1280 TX/RX (CRSF over the air) |
323
+ | `firmware/lora` | ESP32 | LoRa point-to-point template (long-range telemetry) |
324
+ | `firmware/gps` | ESP32 | GPS telemetry module (Adafruit_GPS / NMEA + PMTK + UBX) |
325
+ | `tools` | Python | RC protocol decoders/monitors, 3D visualizer, GPS dashboard |
326
+ | `assets/urdf` | URDF/SDF | Airframe descriptions, one folder per airframe family |
327
+
328
+ ## ROS 2 topics (current stack)
329
+
330
+ The current ROS2 Gazebo/fast-sim stack uses the `/uav/...` namespace by default.
331
+
332
+ - `/uav/command` (`drone_msgs/msg/Command`)
333
+ - `/uav/mission` (`drone_msgs/msg/Trajectory`)
334
+ - `/uav/telemetry` (`drone_msgs/msg/Telemetry`)
335
+ - `/uav/mission_status` (`drone_msgs/msg/MissionStatus`)
336
+ - `/uav/backend/cmd_twist` (`geometry_msgs/msg/Twist`)
337
+ - `/uav/backend/enable` (`std_msgs/msg/Bool`)
338
+ - `/uav/backend/odom` (`nav_msgs/msg/Odometry`)
339
+ - `/uav/backend/telemetry_raw` (`drone_msgs/msg/Telemetry`)
340
+ - `/uav/hw/imu` (`sensor_msgs/msg/Imu`)
341
+ - `/uav/hw/baro` (`std_msgs/msg/Float64`)
342
+ - `/uav/hw/gps` (`sensor_msgs/msg/NavSatFix`)
343
+
344
+ Gazebo bridged topics:
345
+
346
+ - `/model/x3/odometry`
347
+ - `/X3/gazebo/command/twist`
348
+ - `/X3/enable`
349
+
350
+ See `ros2_ws/README.md` for the full topic/node diagram and troubleshooting notes.
351
+
352
+ ## Docs
353
+
354
+ - **[docs/SOFTWARE_GUIDE.md](docs/SOFTWARE_GUIDE.md)**: software guide (start here)
355
+ - **[docs/EXAMPLE_USAGE.md](docs/EXAMPLE_USAGE.md)**: terrain + controller examples
356
+ - **`sim_py/INFO.md`**: standalone simulator architecture + usage
357
+ - **`docker/README.md`**: Docker build/run commands by workflow
358
+ - **`docs/HARDWARE.md`**: TX integration for autonomous mode
359
+ - **`firmware/README.md`**: firmware project index (ESP-NOW / ELRS / LoRa / GPS)
360
+ - **`firmware/espnow/README.md`**: TX/RX firmware details
361
+ - **`firmware/elrs/README.md`**: ExpressLRS-compatible link details
362
+ - **`ros2_ws/src/mavlink_bridge/README.md`**: PX4 / ArduPilot wiring and setup
363
+ - **`tools/README.md`**: protocol monitor / decoder tooling
364
+ - **`tools/GPS_DASHBOARD.md`**: GPS serial dashboard usage
365
+ - **`assets/urdf/README.md`**: airframe description conventions
366
+
367
+ ## Roadmap
368
+
369
+ The repo is moving from a quadcopter stack to a general aerial-robotics stack. In order:
370
+
371
+ 1. **Airframe abstraction** — done: a shared `aerial_kit` package provides
372
+ `Airframe`/`Capabilities`/`Wrench`/`trim()`, with `sim_py.core` re-exporting so existing
373
+ imports are unaffected. Hex/octo mixers are config-only, unit-tested, and fly the same
374
+ mission as quad. A controller/airframe `CommandKind` mismatch fails at startup with a
375
+ readable error.
376
+ 2. **Twin-motor wing** — control stack done: `TwinWingAirframe`, `FixedWingBackend`
377
+ (6-DOF + aero model), L1/TECS guidance with coordinated turns, and Dubins-planner path
378
+ generation all pass their acceptance tests. Still needed: URDF and a sim demo
379
+ (user-supplied assets).
380
+ 3. **Multirotor variants** — done: hexacopter and octacopter reuse the multirotor control
381
+ law with a different mixer (config-only).
382
+ 4. **Remaining airframes** — single-motor wing, monocopter, TVC.
383
+ 5. **Autopilot breadth** — flight-test the ArduPilot path, and keep the Betaflight, PX4, and
384
+ custom-firmware backends behind one backend contract.
385
+
386
+ ## Notes
387
+
388
+ - **ROS 2 Humble** is required for ROS-based control + RViz simulation
389
+ - The **Python-only sim** (`sim_py`) is designed for fast iteration (no ROS needed)
390
+ - Airframe-specific behavior belongs in the control stack, not the firmware — the ESP32
391
+ projects in `firmware/` carry RC channels and telemetry and are airframe-agnostic
@@ -0,0 +1,58 @@
1
+ # aerial_kit
2
+
3
+ A shared control-stack package for aerial robots: airframe capabilities, actuator
4
+ allocation/trim, 6-DOF and point-mass dynamics models, controllers (PID/LQR/MPC, and an
5
+ L1 + TECS fixed-wing autopilot), and lateral/longitudinal guidance laws -- all pure
6
+ numpy/scipy, no ROS and no matplotlib dependency.
7
+
8
+ It grew out of [UAV-Controller](https://github.com/RawFish69/UAV-Controller), a
9
+ quadcopter-to-general-aerial-robotics control stack, where it's the shared layer imported
10
+ by both a standalone Python simulator and ROS 2 flight nodes so they don't duplicate
11
+ control code.
12
+
13
+ ## What's in it
14
+
15
+ - `aerial_kit.types` -- `SimState`, `ControlTarget`, `Capabilities`, `Wrench`,
16
+ `CommandKind`, `Waypoint`
17
+ - `aerial_kit.interfaces` -- `Controller`, `DynamicsBackend`, `Planner` ABCs
18
+ - `aerial_kit.registry` -- a pluggable component registry
19
+ (`register_airframe`/`register_controller`/etc., `create_*` factories)
20
+ - `aerial_kit.airframes` -- `Airframe` ABC, `MultirotorAirframe` (mixer-driven quad/hex/
21
+ octo), `TwinWingAirframe` (elevon + differential-thrust allocation, trim)
22
+ - `aerial_kit.dynamics` -- 6-DOF multirotor dynamics, point-mass dynamics, and a
23
+ hand-rolled 6-DOF fixed-wing model with a flat-plate-blended lift curve, drag polar,
24
+ and moment derivatives
25
+ - `aerial_kit.controllers` -- PID/LQR/MPC position controllers, and
26
+ `FixedWingL1TECSController` (L1 lateral guidance + TECS-lite longitudinal control +
27
+ coordinated-turn attitude PID)
28
+ - `aerial_kit.guidance` -- `l1_bank_command`, `tecs_command` as standalone functions
29
+
30
+ ## Quick start
31
+
32
+ ```python
33
+ from aerial_kit.registry import register_builtin_components, create_airframe, create_controller
34
+
35
+ register_builtin_components()
36
+ airframe = create_airframe("quad")
37
+ controller = create_controller("pid")
38
+ print(airframe.capabilities)
39
+ ```
40
+
41
+ `register_builtin_components()` here registers only what lives inside `aerial_kit`
42
+ itself (airframes, controllers) -- it has no ROS or matplotlib dependency and does not
43
+ know about any host application's own dynamics backends or planners. A host application
44
+ (like `sim_py` in the parent repo) registers its own backends/planners into the same
45
+ registry alongside this.
46
+
47
+ ## Status
48
+
49
+ Early, actively developed alongside the parent repo's twin-motor-wing simulation work.
50
+ The multirotor path (quad/hex/octo airframes, PID/LQR/MPC) is stable and used by a flying
51
+ quadcopter's ground-station simulator. The fixed-wing path (`TwinWingAirframe`,
52
+ `FixedWingL1TECSController`, guidance) is simulation-only so far -- not flown, and its aero
53
+ model uses plausible placeholder coefficients rather than a fitted model of any specific
54
+ real airframe.
55
+
56
+ ## License
57
+
58
+ MIT
@@ -0,0 +1,5 @@
1
+ """aerial_kit: shared control-stack package for aerial robots.
2
+
3
+ No ROS and no matplotlib dependency, so it can be imported by both the
4
+ standalone ``sim_py`` simulator and the ROS 2 nodes in ``ros2_ws/src/``.
5
+ """
@@ -0,0 +1,9 @@
1
+ """Airframe profiles: capabilities, allocation, and trim per vehicle family."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .base import Airframe
6
+ from .fixed_wing import TwinWingAirframe
7
+ from .multirotor import MultirotorAirframe
8
+
9
+ __all__ = ["Airframe", "MultirotorAirframe", "TwinWingAirframe"]
@@ -0,0 +1,25 @@
1
+ """Abstract airframe interface: capabilities, allocation, trim."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+
7
+ import numpy as np
8
+
9
+ from ..types import Capabilities, SimState, Wrench
10
+
11
+
12
+ class Airframe(ABC):
13
+ """A vehicle profile: what it can do, and how a wrench becomes actuator commands."""
14
+
15
+ name: str
16
+ capabilities: Capabilities
17
+
18
+ @abstractmethod
19
+ def allocate(self, wrench: Wrench, state: SimState) -> np.ndarray:
20
+ """Body thrust+moment -> per-actuator commands."""
21
+
22
+ @abstractmethod
23
+ def trim(self, state: SimState) -> np.ndarray:
24
+ """Actuator vector holding steady flight (hover for multirotor, level
25
+ cruise for a wing)."""