Skip to main content
The simulation container packages PX4 SITL, Gazebo Harmonic, ROS 2 Humble, and the substrate-sim-bridge into a single image. Substrate pulls and manages this container automatically, but you can also run it manually or customize it.

Image Tags

TagDescription
px4-gazebo-humblePX4 autopilot + Gazebo Harmonic + ROS 2 Humble (default)
ardupilot-gazebo-humbleArduPilot SITL + Gazebo (planned)
moveit-gazebo-humbleMoveIt2 manipulation + Gazebo (planned)
latestAlias for the most recent build
bedrockdynamics/substrate-sim

Installed Software

SoftwareVersion
Ubuntu22.04
PX4 SITLv1.16.1
GazeboHarmonic (stable)
ROS 2Humble
Python3.x
MAVSDK (Python)2.0.1
DroneKit2.9.2
pymavlinklatest
ROS 2 packages included: ros-humble-ros-base, geometry-msgs, sensor-msgs, nav-msgs, tf2-ros, tf2-geometry-msgs, mavros, mavros-msgs.

Ports

PortProtocolService
14550UDPMAVLink GCS (ground control)
14540UDPMAVLink Offboard (MAVSDK / external control)
9090TCPScene gRPC (bridge)
9091TCPTelemetry gRPC
Multi-instance port allocation: each additional instance offsets MAVLink ports by +10 and bridge ports by +2.

Environment Variables

VariableDefaultDescription
PX4_MODELx500Vehicle model: x500, x500_depth, rc_cessna, standard_vtol, r1_rover, boat
PX4_WORLDdefaultGazebo world: default, empty, or a Gazebo Fuel world name
GZ_WORLD(none)Alternative world override (takes precedence over PX4_WORLD)
VEHICLE_COUNT1Number of vehicles to spawn
SPAWN_OFFSET2.0Y-axis spacing in meters between vehicles
SITL_INSTANCE0Instance number for multi-instance setups
MAVLINK_GCS_PORT14550GCS MAVLink port
MAVLINK_OFFBOARD_PORT14540Offboard MAVLink port
BRIDGE_PORT9090gRPC bridge port
BRIDGE_WAIT_FOR_MODEL(auto)Model name the bridge waits for. Default: {PX4_MODEL}_{INSTANCE}
HEADLESS1Disable Gazebo GUI (always 1 in container)
Do not set PX4_GZ_MODEL_NAME — it enables attach mode which skips sensor spawning, causing EKF to have no data and the autopilot to remain stuck at MAV_STATE_UNINIT.

Volume Mounts

MountPurpose
~/.gz:/root/.gz:rwGazebo Fuel cache (auto-mounted by Substrate)
<workspace>:/workspaces/<folder>:rwIDE workspace integration
/custom/worldsUser-provided SDF world files (read-only)
/custom/modelsUser-provided model files (read-only)

Resource Limits

Default allocation: 4 CPUs, 4 GB memory. Configurable via Substrate’s DockerSimConfig.

Startup Sequence

  1. Source ROS 2 environment
  2. Validate world file (warns if Fuel download is needed)
  3. Launch PX4 SITL (make px4_sitl gz_{model})
  4. Wait for PX4 + Gazebo + MAVLink port (up to 120 s)
  5. Configure SITL parameters via MAVSDK (disable battery checks, supply check bypass)
  6. Start substrate-sim-bridge
  7. Enter process monitoring loop (5 s health checks, graceful shutdown on SIGTERM)
For multi-instance setups, instance 0 spawns Gazebo. Instances 1+ use PX4_GZ_STANDALONE=1 and apply a Y-axis offset based on SPAWN_OFFSET.

Manual Usage

# Pull the image
docker pull bedrockdynamics/substrate-sim:px4-gazebo-humble

# Run with default settings
docker run -d \
  -p 14550:14550/udp \
  -p 14540:14540/udp \
  -p 9090:9090 \
  -p 9091:9091 \
  -e PX4_MODEL=x500 \
  -e PX4_WORLD=default \
  --add-host host.docker.internal:host-gateway \
  bedrockdynamics/substrate-sim:px4-gazebo-humble

# Run with custom world
docker run -d \
  -p 14550:14550/udp \
  -p 9090:9090 \
  -e PX4_MODEL=x500 \
  -e PX4_WORLD=baylands \
  -v ~/.gz:/root/.gz:rw \
  --add-host host.docker.internal:host-gateway \
  bedrockdynamics/substrate-sim:px4-gazebo-humble