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.
- aerial_kit-0.1.0/LICENSE +21 -0
- aerial_kit-0.1.0/PKG-INFO +82 -0
- aerial_kit-0.1.0/README.md +391 -0
- aerial_kit-0.1.0/aerial_kit/README.md +58 -0
- aerial_kit-0.1.0/aerial_kit/__init__.py +5 -0
- aerial_kit-0.1.0/aerial_kit/airframes/__init__.py +9 -0
- aerial_kit-0.1.0/aerial_kit/airframes/base.py +25 -0
- aerial_kit-0.1.0/aerial_kit/airframes/fixed_wing.py +104 -0
- aerial_kit-0.1.0/aerial_kit/airframes/multirotor.py +81 -0
- aerial_kit-0.1.0/aerial_kit/controllers/__init__.py +18 -0
- aerial_kit-0.1.0/aerial_kit/controllers/basic.py +79 -0
- aerial_kit-0.1.0/aerial_kit/controllers/fixed_wing.py +147 -0
- aerial_kit-0.1.0/aerial_kit/controllers/position.py +79 -0
- aerial_kit-0.1.0/aerial_kit/dynamics/__init__.py +17 -0
- aerial_kit-0.1.0/aerial_kit/dynamics/fixed_wing.py +248 -0
- aerial_kit-0.1.0/aerial_kit/dynamics/multirotor.py +157 -0
- aerial_kit-0.1.0/aerial_kit/dynamics/pointmass.py +38 -0
- aerial_kit-0.1.0/aerial_kit/guidance/__init__.py +1 -0
- aerial_kit-0.1.0/aerial_kit/guidance/l1.py +56 -0
- aerial_kit-0.1.0/aerial_kit/guidance/tecs.py +53 -0
- aerial_kit-0.1.0/aerial_kit/interfaces.py +73 -0
- aerial_kit-0.1.0/aerial_kit/py.typed +0 -0
- aerial_kit-0.1.0/aerial_kit/registry.py +112 -0
- aerial_kit-0.1.0/aerial_kit/types.py +89 -0
- aerial_kit-0.1.0/aerial_kit.egg-info/PKG-INFO +82 -0
- aerial_kit-0.1.0/aerial_kit.egg-info/SOURCES.txt +29 -0
- aerial_kit-0.1.0/aerial_kit.egg-info/dependency_links.txt +1 -0
- aerial_kit-0.1.0/aerial_kit.egg-info/requires.txt +2 -0
- aerial_kit-0.1.0/aerial_kit.egg-info/top_level.txt +1 -0
- aerial_kit-0.1.0/pyproject.toml +35 -0
- aerial_kit-0.1.0/setup.cfg +4 -0
aerial_kit-0.1.0/LICENSE
ADDED
|
@@ -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,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)."""
|