Skip to content

feat: preliminary multicopter plugin - #17

Merged
amorfinv merged 17 commits into
mainfrom
multicopter-plan
Aug 9, 2026
Merged

feat: preliminary multicopter plugin#17
amorfinv merged 17 commits into
mainfrom
multicopter-plan

Conversation

@amorfinv

@amorfinv amorfinv commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

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

  • Hover and yaw: change heading at zero speed, limited by a yaw rate instead of the bank-angle turn rate
  • Decoupled track and heading: change direction of travel without rotating the body (thrust redirection, no turn radius)
  • Electric performance: battery SoC, power vs speed/thrust, envelope that degrades as the battery sags

Approach (4 phases)

  1. Core refactor (only core change): extract update_airspeed/update_groundspeed/update_pos from Traffic into a first-level Kinematics(TrafficArrays) entity so it auto-registers as replaceable (SELECTIMPL KINEMATICS ...). Behaviour-preserving.
  2. plugins/multicopter.py: typecode-based membership (OpenAP rotor list minus helicopters — deliberately named multicopter, EC35 excluded), MulticopterKinematics, MulticopterAPorASAS, and a thin MulticopterAutopilot adding HOVER/DELIVER mission primitives plus low-speed capture-radius clamping. New commands: MCOPT, YAW, YAWRATE.
  3. MulticopterPerf(OpenAP): electric power/battery model built from 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 in perfoap.py.
  4. Docs, delivery scenario, commands-reference regen.

Key enablers already in the codebase: OpenAP's rotor path accepts SPD 0 (negative vmin envelopes), and the SELECTIMPL replaceable pattern handles hot-swapping. The plan includes acceptance criteria per phase and a decision log (naming, helicopter exclusion, dependency-free data pipeline).

TODO

  • Review and agree on the plan
  • Phase 1 — extract Kinematics from Traffic as a replaceable entity (core, behaviour-preserving)
  • Phase 2 — multicopter plugin: membership, kinematics, MulticopterAutopilot (HOVER/DELIVER), commands
  • Phase 3 — MulticopterPerf: vendored PyThrust data, gen script, battery/SoC model
  • Phase 4 — docs, delivery scenario, commands-reference regen

Detailed per-phase checklists live in docs/multicopter-plan.md.

Review notes

  • The Phase 1 Kinematics extraction is the main thing needing a design nod, since it touches minisky/traffic/traffic.py.

🤖 Generated with Claude Code

Note: Pythrust has been removed from plan and will be added in future.

@amorfinv amorfinv self-assigned this Jul 17, 2026
@amorfinv amorfinv added the enhancement New feature or request label Jul 17, 2026
@skyrock36
skyrock36 force-pushed the multicopter-plan branch 2 times, most recently from c87c3d2 to 81a73ca Compare July 28, 2026 18:45
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
minisky 25c1a8a Aug 07 2026, 04:27 PM

amorfinv and others added 10 commits August 1, 2026 23:00
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.
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.
@amorfinv
amorfinv marked this pull request as ready for review August 6, 2026 15:22
@amorfinv

amorfinv commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

I think this is ready to merge to main. There is an elementary battery model. Eventually I would like to use pythrust or another energy model to make it more realistic.

Here is a nice animation of the example scenario:

multicopter_delivery_3d.mp4

shall we do a 0.1.0 release @abc8747 @xoolive ?

@amorfinv
amorfinv merged commit ad0a349 into main Aug 9, 2026
2 checks passed
@amorfinv
amorfinv deleted the multicopter-plan branch August 9, 2026 05:45
Comment thread docs/guides/multicopters.md
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)`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use KaTeX (I will enable the arithmatex plugin soon)

@amorfinv amorfinv Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #45

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`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make this configurable, the core supports plugin-scoped settings (see minisky_tangram)

Comment thread packages/minisky-multicopter/src/minisky_multicopter/activewp.py
Comment thread packages/minisky-multicopter/src/minisky_multicopter/perf.py


@plugin_api.replacement
class MulticopterPerf(OpenAP):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread packages/minisky-multicopter/src/minisky_multicopter/entity.py
Comment thread packages/minisky-multicopter/src/minisky_multicopter/kinematics.py
@abc8747

abc8747 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

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

@abc8747 abc8747 changed the title Multicopter support plan feat: preliminary multicopter plugin Aug 9, 2026
abc8747 added a commit that referenced this pull request Aug 9, 2026
- migrates all legacy `bool, str` with `Result`
  #17 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants