Skip to main content

Simulation Container

docker pull bedrockdynamics/substrate-sim:px4-gazebo-humble
The container bundles PX4 SITL, Gazebo Harmonic, and the MAVLink gRPC bridge.
PortProtocolPurpose
9090gRPCros2-bridge (telemetry streaming + MAVLink command relay)

Flight Sequence

PX4 requires a specific command sequence to enter velocity control. The agent (or MCP tools) must follow these steps in order:
1

ARM

Send the arm command. The flight controller enables the motors.
2

TAKEOFF

Command takeoff to a target altitude. PX4 handles the climb autonomously.
3

OFFBOARD

Switch to OFFBOARD mode. PX4 now accepts external velocity setpoints. The bridge must stream setpoints at 20Hz or PX4 will revert to the previous mode.
4

Velocity Control

WASM controllers write body velocity commands at 100Hz. The bridge relays these as MAVLink SET_POSITION_TARGET_LOCAL_NED messages at 20Hz.
5

LAND

Command landing. PX4 handles the descent and motor disarm.

WASM Channels

The ChannelManifest::quadcopter() manifest defines 4 body velocity command channels and 4 position state channels at 100Hz.

Command Channels (4)

IndexNameUnitLimitsMax Rate of Change
0body/velocity.xm/s-5.0 to 5.02.0 m/s per tick
1body/velocity.ym/s-5.0 to 5.02.0 m/s per tick
2body/velocity.zm/s-3.0 to 3.01.5 m/s per tick
3body/yaw_raterad/s-pi/2 to pi/21.0 rad/s per tick

State Channels (4)

IndexNameUnitType
0body/position.xmPosition
1body/position.ymPosition
2body/position.zmPosition
3body/yawradPosition

NED Frame Convention

PX4 uses the NED (North-East-Down) coordinate frame where +Z points downward. The roz channel interface uses a body frame where +Z points upward. The bridge negates vz when converting between the two frames. Writing a positive body/velocity.z in your WASM controller makes the drone go up.
The gRPC bridge handles all MAVLink protocol details:
  • Companion heartbeat: The bridge sends a heartbeat on the onboard port so PX4 recognizes it as a companion computer.
  • 20Hz setpoint streaming: OFFBOARD mode requires continuous setpoints. The bridge maintains a 20Hz stream, using the latest WASM output each cycle.
  • COMMAND_LONG: Flight mode transitions (ARM, TAKEOFF, LAND, OFFBOARD) use COMMAND_LONG messages. The bridge handles acknowledgment and retry.
PX4 requires NaN for unused COMMAND_LONG parameters. The bridge fills unused parameter slots with f32::NAN — do not use 0.0 as a placeholder.

Example

A WASM controller that moves the drone forward and up:
;; Move forward at 1 m/s
(call $set_command (i32.const 0) (f64.const 1.0))
;; Climb at 0.5 m/s (bridge negates for NED)
(call $set_command (i32.const 2) (f64.const 0.5))

Real Hardware

For a physical PX4 drone (no Docker container):
  1. Connect via MAVLink over serial (TELEM2) or UDP to the flight controller
  2. The bridge communicates on PX4’s onboard MAVLink instance (companion port)
  3. Same WASM channels and safety filter apply — velocity commands are clamped before reaching the flight controller
Real hardware support is under active development. The safety guarantees are currently simulation-validated only. Always test in simulation first and have a manual override (RC transmitter) ready.

Source Code