feat: preliminary multicopter plugin - #17
Conversation
c87c3d2 to
81a73ca
Compare
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
minisky | 25c1a8a | Aug 07 2026, 04:27 PM |
Comprehensive proposal for simulating small electric multirotors: hover-yaw at zero speed, track/heading decoupling, and a battery/power model driven by vendored PyThrust data (no runtime dependency). One behaviour-preserving core refactor (extract Kinematics from Traffic as a replaceable entity); everything else lands as a plugin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Not a guidance rewrite - LNAV's track output already drives the decoupled kinematics, and fly-over waypoints exist. The subclass adds mission primitives (HOVER, DELIVER), a fixed capture radius replacing bank-based turn distance at low speed, and fly-over route defaults. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Checkbox task lists per phase so progress can be marked off as implementation lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move update_airspeed / update_groundspeed / update_pos out of Traffic into a new Kinematics(TrafficArrays) entity, along with the ax, az, swhdgsel and swaltsel per-aircraft arrays. Traffic.update() now calls self.kinematics.update(), and perfoap.py reads traf.kinematics.ax. Because Kinematics is a first-level TrafficArrays entity, flight-state integration becomes hot-swappable: plugins can subclass it and activate it with SELECTIMPL KINEMATICS <IMPL>, which is what the multicopter plugin needs to fly rotorcraft differently from fixed wing. This is Phase 1 of docs/multicopter-plan.md; behaviour is unchanged.
Scaffold example_plugins/multicopter/ with one module per class, as Phase 2 of docs/multicopter-plan.md describes: Multicopter (membership plus selhdg/yawrate arrays), MulticopterKinematics, MulticopterAPorASAS and MulticopterAutopilot. Classes, per-aircraft arrays, create() and update() signatures and the stack commands are in place; the bodies are TODOs, so behaviour is still that of the core implementations. plugin.py selects the three implementations through select_implementation rather than the select() classmethod, so the live instances on traf are swapped and not just the generator for future ones, and re-selects them from its reset hook because a reset reverts every replaceable to its default. Commands are registered as bound methods so they get rebound when an instance is replaced.
Scaffold example_plugins/multicopter/ with one module per class, as docs/multicopter-plan.md describes: Multicopter (membership plus the selhdg/yawrate arrays), MulticopterKinematics, MulticopterAPorASAS, MulticopterAutopilot and MulticopterPerf. Classes, per-aircraft arrays, create()/update() signatures and the stack commands MCOPT, YAW, YAWRATE, HOVER, DELIVER and BATT are in place; the bodies are TODOs, so behaviour is still that of the core implementations. perf.py belongs to Phase 3 and is scaffolded early so the whole plugin shape is visible in one place; its generated data/ maps do not exist yet. plugin.py selects the four implementations through select_implementation rather than the select() classmethod, so the live instances on traf are swapped and not just the generator for future ones, and re-selects them from its reset hook because a reset reverts every replaceable to its default. Commands are registered as bound methods so they get rebound when an instance is replaced.
e0ae5e7 to
13a17b7
Compare
Move example_plugins/multicopter to the minisky-multicopter workspace package with a minisky.plugins entry point. Commands, hooks, and replacements now use the plugin declaration API, and the entity keeps the multicopter implementations selected on load and after reset. Register Kinematics, APorASAS, and ActiveWaypoint as replaceable bases in the runtime, and register test replacements runtime-locally.
…tecture notes Completes phase 4 of the multicopter plan: new guides/multicopters.md (with nav entry), Kinematics entity and replaceable-components section in architecture.md, and the multicopter_delivery.scn example scenario in the plugin package. Marks the plan complete.
| simulation step: | ||
|
|
||
| - **Required thrust** supports the weight and overcomes flat-plate parasite | ||
| drag: `T = hypot(m * sqrt(g^2 + az^2), 0.5 * rho * v^2 * CdS)`. |
There was a problem hiding this comment.
We should use KaTeX (I will enable the arithmatex plugin soon)
| Three places, all keyed on the ICAO-style typecode: | ||
|
|
||
| 1. **Performance data** — add a rotor entry to | ||
| `packages/minisky/minisky/data/performance/openap/rotor/aircraft.json` |
There was a problem hiding this comment.
To make plugins truly self contained we should allow users to configure their own multicopter parameters in their cache directory (the core already has platformdirs.user_cache_dir())
| 2. **Membership** — add the typecode to `MULTICOPTER_TYPES` in | ||
| `minisky_multicopter/entity.py`. | ||
| 3. **Battery capacity** — add a `CONSTANTS` entry in | ||
| `minisky_multicopter/perf.py` with the usable pack energy `battery_wh` |
There was a problem hiding this comment.
Code should be closed for modification, so we should not encourage users to modify the source code just to add a new multicopter entry. This is especially true when we decide to publish things on PyPI.
Can you make the plugin read a TOML configuration file (which stores all of the multicopter performance data) from the user cache dir, defining Pydantic shapes, and load/validate that on plugin startup?
| from minisky_multicopter.entity import get_multicopter | ||
|
|
||
| #: Waypoint capture radius for multicopters [m]. | ||
| CAPTURE_RADIUS = 10.0 |
There was a problem hiding this comment.
We should make this configurable, the core supports plugin-scoped settings (see minisky_tangram)
|
|
||
|
|
||
| @plugin_api.replacement | ||
| class MulticopterPerf(OpenAP): |
There was a problem hiding this comment.
Not too familiar with minisky or openap internals but I think we should decouple OpenAP from the core and don't inherit from OpenAP. I will open a new issue
There was a problem hiding this comment.
Yeah this is mostly because in order to simulate both regular aircraft and multicopters at the same time I inherit from openap to keep aircraft calcs the same. And then just modify the multicopter types.
There was a problem hiding this comment.
I see, I think if we haven't already we should define a u8[] in the core that stores the type of the entity (say, 0 = openap aircraft, 1 = pythrust multicopters, 2 = some other future plugin, maybe birds?). The core should then be responsible for dispatching the corresponding subset of entities to individual plugins.
The reason being, I want to eventually add a taxiing plugin for ground vehicles to assess congestion. The current model of SELECTIMPL assumes there is only one performance model controlling all entities and I hope to move towards a system that doesn't use implementation inheritance at all.
There are still some details to narrow down (e.g. whether we want to continue using a single table for all entities, or whether we want multiple archetypes of tables per entity type) but I will take some time to see what we can learn from popular ECS systems like flecs
|
Hello @amorfinv I was busy working on #37 so I didn't have the time to properly review/test this, but I have just left some comments after a quick skim. Maybe we can open a new PR to address them. I should be done with #37 tonight. Regarding releasing 0.1.0, I'm not opposed to it but we should stay on v0.0.x for the time being since there will be many breaking changes. I will set up the github workflow and the PyPI trusted publishers thing maybe next week |
- migrates all legacy `bool, str` with `Result` #17 (comment)
Summary
Adds
docs/multicopter-plan.md— a comprehensive proposal for simulating small electric multirotors (MAVIC/M600-class, delivery drones) in MiniSky. The plan is the design document; implementation will land on this branch phase by phase, checking off the TODOs below as it goes.Target behaviours
Approach (4 phases)
update_airspeed/update_groundspeed/update_posfromTrafficinto a first-levelKinematics(TrafficArrays)entity so it auto-registers as replaceable (SELECTIMPL KINEMATICS ...). Behaviour-preserving.plugins/multicopter.py: typecode-based membership (OpenAP rotor list minus helicopters — deliberately named multicopter, EC35 excluded),MulticopterKinematics,MulticopterAPorASAS, and a thinMulticopterAutopilotaddingHOVER/DELIVERmission primitives plus low-speed capture-radius clamping. New commands:MCOPT,YAW,YAWRATE.MulticopterPerf(OpenAP): electric power/battery model builtfrom vendored PyThrust data (Apache 2.0) — APC prop tables already tabulate thrust and power, so a self-contained gen script emits small per-type interpolation maps. No runtime dependency added. Fills the rotor-thrust TODO inperfoap.py.Key enablers already in the codebase: OpenAP's rotor path accepts
SPD 0(negativevminenvelopes), and theSELECTIMPLreplaceable pattern handles hot-swapping. The plan includes acceptance criteria per phase and a decision log (naming, helicopter exclusion, dependency-free data pipeline).TODO
KinematicsfromTrafficas a replaceable entity (core, behaviour-preserving)multicopterplugin: membership, kinematics,MulticopterAutopilot(HOVER/DELIVER), commandsMulticopterPerf:vendored PyThrust data,gen script, battery/SoC modelDetailed per-phase checklists live in
docs/multicopter-plan.md.Review notes
Kinematicsextraction is the main thing needing a design nod, since it touchesminisky/traffic/traffic.py.🤖 Generated with Claude Code
Note: Pythrust has been removed from plan and will be added in future.