Project SkySim, Part 2: One Physics Core, Three Ways to Run It
At the end of the first part of this series, I made a promise that sounds slightly contradictory. Sk 2026-9-29 15:14:55 Author: hackernoon.com(查看原文) 阅读量:3 收藏

At the end of the first part of this series, I made a promise that sounds slightly contradictory. SkySim should run in a browser tab with nothing to install, and it should run real aerodynamics and hand the vehicle to actual flight-controller firmware. Accessible and serious, at the same time.

If you try to satisfy both goals by picking a single technology, you lose. Build everything inside a heavyweight game engine and you get gorgeous 3D but you're back to installers, GPUs, and platform lock-in. Build everything in JavaScript for the browser and you get the easy URL but a physics model too thin to trust. Neither extreme gives you both halves.

The way out is to stop treating "the simulator" as one monolithic thing. It isn't. It's a physics core and a set of places that core can run. Once you separate those two ideas cleanly, the contradiction dissolves.

The one rule: the core knows nothing about rendering

The single most important design rule in SkySim is that the simulation core has no idea it's being watched.

The core is pure C++20. It integrates rigid-body state, computes the forces and torques on the airframe, models the rotors, the atmosphere, ground effect, vortex ring state, turbulence, and the battery. What it does not do is know about a window, a camera, a GPU, a game engine, or a browser. It takes a state and some control inputs, advances time, and hands you back a new state. That's the whole contract.

That sounds like an obvious separation of concerns, and it is, but in simulation work it's the difference between a project that runs in exactly one environment and one that runs everywhere. Because the core has no rendering dependencies, the same compiled logic can be hosted by three completely different things without changing a line of the physics.

                     ┌─────────────────────────────┐
                     │   SkySim physics core (C++20) │
                     │   state · forces · rotors ·    │
                     │   atmosphere · ground effect · │
                     │   VRS · turbulence · battery   │
                     │   (no renderer, no engine)     │
                     └───────────────┬───────────────┘
                                     │  same core, three hosts
        ┌────────────────────────────┼────────────────────────────┐
        ▼                            ▼                            ▼
┌───────────────┐          ┌──────────────────┐          ┌────────────────┐
│  Godot 4       │          │  Browser (WASM)   │          │  Headless /     │
│  GDExtension   │          │  Emscripten +     │          │  native         │
│  native desktop│          │  Three.js render  │          │  no GPU, no      │
│  Win/mac/Linux │          │  keyboard or JS   │          │  engine —        │
│                │          │  control function │          │  tests & CI      │
└───────┬────────┘          └─────────┬────────┘          └────────┬───────┘
        │                             │                            │
        └──────────────┬──────────────┴──────────────┬────────────┘
                        ▼                             ▼
              ┌───────────────────┐        ┌────────────────────────┐
              │  Agent interface   │        │  Firmware bridges (SITL)│
              │  WebSocket JSON     │        │  ArduPilot (JSON)       │
              │  lockstep · gym env │        │  PX4 (MAVLink HIL)      │
              │  PID / PPO examples │        │  Betaflight (MSP)       │
              └───────────────────┘        └────────────────────────┘

Let me walk through the three hosts, because each one exists to serve a different person.

Host 1:- Native desktop, through Godot

The first target is the one that looks most like a "normal" simulator: a native desktop application with real-time 3D, running on Windows, Linux, and macOS, including Apple Silicon.

Here the physics core is bound into Godot 4 as a GDExtension, that a compiled native extension, linked through godot-cpp, rather than gameplay logic written in Godot's scripting language. The core plugs into Godot's physics step (the _integrate_forces callback) so the C++ does the aerodynamic work every tick and Godot handles what game engines are actually good at: rendering, scene management, input, and the window.

The design rule I care about most here is that there is zero scripting-language code in the hot path. GDScript is convenient for wiring up a scene, but you do not want an interpreted language deciding rotor thrust sixty or more times a second. The engine renders; the C++ core flies the aircraft. That boundary is deliberate and it's where the performance lives.

This host is for the person who wants the full, rich experience on their own machine, and it's what you get from the pre-built downloads on the Releases page, so even the "native" path doesn't require you to compile anything.

Host 2:- The browser, through WebAssembly

This is the target from Part 1: fly a drone by opening a page.

The same C++ core compiles to WebAssembly via Emscripten and runs entirely client-side. There's no server doing the physics for you as the simulation happens in your tab, on your machine, using the exact same core logic that runs natively. Three.js draws the scene. You can fly with the keyboard, or/and this is the part that matters for algorithm work, and drop in a JavaScript control function and let your code fly the aircraft.

I want to be precise and honest about the boundaries of this tier, because overpromising here would undo the trust the whole project depends on. The in-browser flight experience is a preview. And the browser is deliberately not where the heavy work happens: large-scale machine-learning training and full firmware software-in-the-loop run against the native agent server, not in the tab. The browser tier is for interactive flight and lighter, classical control, and the on-ramp and the demos. The demanding workloads stay native, running against the same physics core. Same physics, different ceiling. Saying that plainly is more useful than pretending a browser tab can train a policy network.

Host 3:- Headless, for testing and CI

The third host has no renderer at all, and it might be the most important one.

Because the core is engine-agnostic, it can run as a plain native program with no GPU and no game engine, which means you can actually test the physics. You can compile it, run it in continuous integration, assert that a hover holds, that energy behaves, that a maneuver produces the state you expect, and catch a regression the moment it's introduced. The web physics core is validated in exactly this decoupled, native form before it's ever trusted in a browser.

This is the quiet payoff of the "core knows nothing about rendering" rule. A simulator welded to a game engine is painful to test, because you can't separate a physics bug from a rendering quirk. Split them, and the physics becomes ordinary software you can verify like any other library.

Talking to the sim: many front doors, one core

Running the physics is only half the story. The other half is how your algorithm talks to it, and here too the pattern is one core with several interfaces layered on top.

For programmatic control there's an agent interface built on a WebSocket JSON protocol that runs in lockstep, and the sim and your agent advance together, one step at a time, so nothing races ahead. On top of that sits a Python client shaped like a standard reinforcement-learning environment (a Gymnasium-style API), with ready-made tasks like hover and waypoint following and worked examples for both a classical PID controller and a PPO agent. If you've trained an RL policy before, this will feel familiar within minutes, which is the entire point.

For the firmware path, SkySim speaks the same protocols real drones use: ArduPilot over its JSON SITL interface, PX4 over MAVLink hardware-in-the-loop, and Betaflight over MSP. This is what lets the control logic you validate in simulation be the same control logic you deploy on hardware, which is the sim-to-real thread this whole series keeps pulling on.

The crucial thing is that none of these interfaces re-implement the physics. They're all adapters onto the same core. Add a new front door and the aircraft still flies by the same equations.

Why determinism is a feature, not a detail

That lockstep design isn't just about tidiness, and it's about reproducibility. If you can't reproduce a run, you can't trust a result, and you certainly can't use a simulation result to make claims about real hardware. SkySim treats determinism as a first-class property, with a harness whose job is to confirm that the same inputs produce the same trajectory. When the eventual goal is sim-to-real transfer, a simulator that quietly gives different answers on different runs is worse than useless. Boring, repeatable, and checkable beats impressive and flaky.

Two gotchas I paid for so you don't have to

Architecture articles that only describe the clean final design are lying by omission. Two problems cost me real time, and both are the kind of thing you'd never guess from documentation.

GCC-13 is stricter than Clang about nested-struct default arguments. Code that compiled fine under Clang and under Emscripten was flat-out rejected by GCC-13. Because SkySim has to build across all of those toolchains, including native, browser, and CI, that I couldn't just pick the lenient compiler. The fix was a delegating-constructor workaround that keeps every toolchain happy. If you're building cross-platform C++ in 2026, assume your compilers disagree and build against all of them early, not late.

Godot's .tscn scene files will silently hand you a null. If you hand-author a scene file and try to assign a typed-node @export property through a NodePath, it can produce null at runtime with no error and the scene loads, the reference just isn't there. That's a miserable bug to chase because nothing complains. The reliable fix is to resolve the node in code with get_node() inside _ready() rather than trusting the hand-authored path. Lesson: in Godot, prefer resolving critical references in code over wiring them by hand in a text scene file.

The case against this architecture

Per my own habit, here are the strongest objections and my honest answers.

"Why not just pick one engine and be done? This decoupling is over-engineering." It would be, for a simulator meant to run in one place. But the entire premise from Part 1 is that it has to run in several, including a browser for the newcomer, native for the power user, headless for the machine. The moment you have more than one target, the engine-agnostic core stops being over-engineering and starts being the only thing that keeps you sane. The cost is one clean boundary; the payoff is three environments from one codebase.

"WASM physics and native physics will drift apart, and then your browser results are lies." This is the objection I take most seriously, and it's precisely why the web physics core is validated in decoupled native form and why determinism gets a harness rather than a hope. It's the same source compiled to different targets, not two implementations. but "same source" is a claim you have to keep testing, not assert once. I'd rather name the risk than paper over it.

"A game engine as a rendering layer is a heavy dependency for something that's supposed to be lightweight." Fair, which is part of why the browser tier renders with Three.js and doesn't drag Godot along, and why the physics never depended on the engine in the first place. Godot earns its place on the native desktop host, where you actually want its strengths. Where you don't, it isn't in the room.

What's next

This was the skeleton and how the pieces fit. The next article gets into the muscle: the physics itself. Blade-element theory for the rotors, ground effect, vortex ring state, an ISA atmosphere, and the honest placeholders I've left in the model where the math still needs validation. If you've ever wondered why a drone gets twitchy near the ground or drops out of the sky descending into its own downwash, that one's for you.

SkySim is open-source and on GitHub: https://github.com/vishwagw/Sky-Sim-drone-simulator-3.0. If clean architecture boundaries are your thing, the core-versus-hosts split is a good place to poke holes, and I'm actively looking for contributors who will.


文章来源: https://hackernoon.com/project-skysim-part-2-one-physics-core-three-ways-to-run-it?source=rss
如有侵权请联系:admin#unsafe.sh