Architecture
Process tree
make px4_sitl_newton
cmake -E env ... bin/px4
px4-rc.newtonsim (sourced by PX4 init)
newton_run.sh &
setsid uv run rerun & (local only, own session)
uv run python3 -m px4_newton_bridge.main
simulator_mavlink start -c 4560
rerun_logger start -s newton
Communication
PX4 (simulator_mavlink) <──TCP:4560──> Bridge (MAVLinkInterface)
PX4 (rerun_logger) ──gRPC:9876──> Rerun viewer
Bridge (ViewerRerun) ──gRPC:9876──> Rerun viewer
PX4 and the bridge communicate via MAVLink over TCP in lockstep: the bridge sends sensor data, then blocks waiting for actuator controls (with a 2-second timeout for disconnect detection).
Both the PX4 rerun_logger module and the bridge log to the Rerun viewer via
gRPC on port 9876.
Startup
Running make px4_sitl_newton newton_<vehicle> starts PX4, which sources
px4-rc.newtonsim during init. This script:
- Kills any leftover bridge process from a previous session. This is necessary because a stale bridge still listening on port 4560 would cause the new PX4 instance to connect to it instead of waiting for the fresh bridge.
- Launches
newton_run.shin the background, passingPX4_PID=$$so the script knows which PX4 instance started it. - Starts
simulator_mavlinkin TCP client mode (PX4 retries connecting to the bridge on port 4560 until the bridge is ready). - Starts
rerun_logger, which connects to the Rerun viewer on port 9876.
newton_run.sh then:
- Installs Python dependencies via
uv sync. - Starts the Rerun viewer in its own session via
setsid(local only — skipped in Docker since the viewer runs on the host viarun_docker.sh). The separate session prevents Ctrl-C from killing the viewer along with PX4, so it persists across sim restarts. - Launches the bridge (
px4_newton_bridge.main), with stdout/stderr redirected tostartup.log.
The bridge initializes Warp (CUDA kernel compilation), builds the physics model,
and stabilizes the simulation. It then listens for PX4 on TCP port 4560 via
wait_for_px4(), which has a 2-second timeout. Once PX4 connects and starts
sending actuator controls, lockstep simulation begins.
Console output
The bridge's stdout/stderr is redirected to startup.log so that Warp kernel
compilation messages don't pollute the PX4 console. If PX4 exits before the
bridge connects (e.g. Ctrl-C during init), the bridge's output won't overwrite
the terminal prompt. Bridge logs (warnings, errors, status) go to the Rerun
viewer via a custom Python logging handler.
Shutdown
Docker
In Docker, all processes (PX4, bridge, Rerun logger) run inside a single
container started by docker compose run. Ctrl-C sends SIGINT to the container,
which terminates all processes at once. The Rerun viewer runs on the host and is
unaffected.
Local
PX4, the bridge, and newton_run.sh all run in the same terminal but as
separate processes. The bridge runs as a background process (launched via
./newton_run.sh & in px4-rc.newtonsim), so it does not receive SIGINT
from Ctrl-C.
Ctrl-C after MAVLink connection is established
- Ctrl-C sends SIGINT to PX4 (foreground process).
- PX4 shuts down, closing the TCP connection on port 4560.
- The bridge detects the closed connection within its 2-second MAVLink timeout
and raises
ConnectionError. - The bridge exits,
viewer.close()disconnects from Rerun. newton_run.shsends SIGTERM to the specific PX4 instance that launched it (viaPX4_PID, already dead at this point). The Rerun viewer stays open.
Ctrl-C before MAVLink connection (during Warp init)
- Ctrl-C sends SIGINT to PX4.
- PX4 shuts down, but the bridge is still initializing. Warp kernel compilation runs in native CUDA code that blocks Python signal handling, so the bridge cannot respond to signals during this phase.
- The bridge finishes init and enters
wait_for_px4(), which has a 2-second timeout. Since PX4 is dead, the timeout expires andConnectionErroris raised. - The bridge exits cleanly.
newton_run.shsends SIGTERM to its specific PX4 instance (viaPX4_PID). The Rerun viewer stays open.
Note
newton_run.sh uses the PX4_PID environment variable (set by
px4-rc.newtonsim) to kill only its own PX4 instance, not any other
bin/px4 process. This prevents a stale newton_run.sh from killing a
newly started PX4 session.
Local vs Docker
| Aspect | Local | Docker |
|---|---|---|
| Rerun viewer | Started by newton_run.sh |
Started by run_docker.sh on the host |
| Warp kernel cache | ~/.cache/warp |
Docker volume (warp-cache) |
| Python venv | .venv/ |
Docker volume (venv) at /var/cache/venv |
| Rerun gRPC errors | Suppressed by redirecting bridge output to startup.log |
Suppressed via RUST_LOG in docker-compose.yaml |