Appendix C — Appendix C: Toolchain Setup Guide
This appendix is a procedural reference for installing the simulation toolchain used throughout the book: PX4 Autopilot v1.16.2 (built from source and run as software-in-the-loop, SITL, with the Gazebo simulator) and QGroundControl (QGC), the ground control station. Three host environments are covered:
- macOS — a Docker-container build with the Gazebo window forwarded to the Mac via XQuartz (plus a VNC fallback if X11 forwarding will not cooperate).
- Windows — the Windows Subsystem for Linux (WSL 2) with Ubuntu 22.04, building natively inside the WSL distro, with WSLg rendering the GUI windows on the Windows desktop.
- Native Ubuntu 22.04 — the same native build path as WSL, without the WSL-specific setup.
Every path pins the same PX4 release (v1.16.2), builds the same SITL target (make px4_sitl gz_x500), and ends at the same place: a pxh> console, a Gazebo window showing an X500 quadcopter, and QGC connected over UDP port 14550. Because the endpoint is identical, the pre-arm troubleshooting and takeoff procedure is written once, in the shared section at the end, rather than repeated per operating system.
ROS 2 is not installed here. The chapters that use ROS 2 assume a standard installation from the official ROS 2 documentation (the apt package repositories for your distribution, plus rosdep for dependency resolution); that upstream guide is authoritative and kept current, so this appendix does not duplicate it.
C.1 Getting the PX4 source (all platforms)
Every path builds the same source tree, cloned from GitHub and pinned to the v1.16.2 tag with all submodules initialized:
cd ~
git clone https://github.com/PX4/PX4-Autopilot.git --recursive
cd PX4-Autopilot
git checkout v1.16.2
git submodule update --init --recursiveWhere you run this differs by platform:
- macOS: run it on the Mac host. The source tree is then mounted into the build container, so build artifacts persist on your machine between container runs.
- Windows (WSL): run it inside the Ubuntu 22.04 WSL shell, in your WSL home directory.
- Native Ubuntu: run it in your home directory.
The git submodule update --init --recursive step after checkout matters: the tag checkout can leave submodules pointing at the wrong revisions, and a build from mismatched submodules fails in confusing ways.
C.2 macOS
On macOS, PX4 is built inside the official PX4 development container (px4io/px4-dev-simulation-jammy, an Ubuntu 22.04 image with the toolchain preinstalled), while QGC runs natively on the Mac. The Gazebo window escapes the container through X11 forwarding to XQuartz.
C.2.1 Core tools
Install Homebrew if it is not already present:
brew --version # check first
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"Then install Docker Desktop, XQuartz, and git:
brew install --cask docker
brew install --cask xquartz
brew install gitOpen Docker Desktop and let it finish initializing (the whale icon appears in the menu bar), then verify:
open /Applications/Docker.app
docker --versionXQuartz needs one configuration change, and it is the step most people miss:
- Launch XQuartz:
open -a XQuartz - Go to XQuartz → Settings (Preferences) → Security
- Tick “Allow connections from network clients”
- Quit and restart XQuartz for the change to take effect (a full logout/restart is the surest way).
C.2.2 Clone the source on the host
Run the clone sequence from Getting the PX4 source above on the Mac itself, in your home directory. The next step mounts ~/PX4-Autopilot into the container.
C.2.3 X11 forwarding
Each session, with XQuartz running, allow the local connection:
xhost +localhostIf xhost is not found, make sure XQuartz is running and that /opt/X11/bin is on your PATH (open a fresh terminal after installing XQuartz).
C.2.4 Build and run SITL in the dev container
Start the container with the source mounted, the display forwarded, and the MAVLink UDP ports published:
docker run --rm -it \
-e DISPLAY=host.docker.internal:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v ~/PX4-Autopilot:/home/user/PX4-Autopilot \
-p 14550:14550/udp \
-p 14540:14540/udp \
--workdir /home/user/PX4-Autopilot \
px4io/px4-dev-simulation-jammy:latest \
bashFlags explained:
-e DISPLAY=host.docker.internal:0— points the container at XQuartz on the Mac.-v /tmp/.X11-unix:/tmp/.X11-unix— mounts the X11 socket.-v ~/PX4-Autopilot:/home/user/PX4-Autopilot— mounts your source tree, so build artifacts persist on the host.-p 14550:14550/udp— forwards the port QGC uses to reach the sim (needed later).--workdir— drops you straight into the source directory.
This opens a bash shell inside the container — not the pxh> prompt yet. If the build later complains about missing dependencies, run the in-tree setup script once inside the container, then re-open the shell:
bash ./Tools/setup/ubuntu.shInside the container shell, build and run:
make px4_sitl gz_x500The first build takes several minutes. When it finishes, Gazebo launches (its window appears on the Mac via XQuartz) and you land at the pxh> prompt:
INFO [px4] Startup script returned successfully
pxh>
Startup warnings such as Preflight Fail: No connection to the GCS and brief timestamp errors are normal at this point and resolve on their own or via the shared troubleshooting section below.
C.2.5 If the Gazebo window does not appear
- Confirm XQuartz is running and “Allow connections from network clients” is ticked.
- Re-run
xhost +localhoston the Mac, then restart the container. - Give Gazebo 10–15 seconds after PX4 boots to start rendering.
- If you still get an X11/display error, use the VNC fallback below.
C.2.6 QGroundControl on macOS
Unlike the simulator, which runs inside the Linux container, QGC installs natively on macOS. It reaches the sim through the -p 14550:14550/udp port mapping in the docker run command — make sure that flag is present.
brew install --cask qgroundcontrol
open /Applications/QGroundControl.appQGC auto-connects to the running SITL instance on UDP port 14550, and the “No connection to the ground control station” pre-arm failure clears. Start QGC after the sim is up, or leave it running and it will pick up the connection when the sim starts. On first launch, macOS Gatekeeper may warn about an unidentified developer; allow it via System Settings → Privacy & Security, or install the official .dmg from qgroundcontrol.com if you prefer not to use Homebrew.
C.2.7 VNC fallback (only if X11 forwarding fails)
This method is laggier, with less responsive mouse control, but it avoids X11 entirely: the container runs a virtual display (Xvfb) and a VNC server, and the Mac connects with the built-in Screen Sharing client.
Create a build directory and Dockerfile that extend the dev image:
mkdir -p ~/px4-vnc
cat <<'EOF' > ~/px4-vnc/Dockerfile
FROM px4io/px4-dev-simulation-jammy:latest
USER root
RUN apt-get update && apt-get install -y \
xvfb \
x11vnc \
&& rm -rf /var/lib/apt/lists/*
COPY start.sh /start.sh
RUN chmod +x /start.sh
ENTRYPOINT []
CMD ["/start.sh"]
EOFCreate the startup script — it launches the virtual display and VNC server, then drops into a build shell in the mounted source tree:
cat <<'EOF' > ~/px4-vnc/start.sh
#!/bin/bash
# Start virtual display
Xvfb :1 -screen 0 1280x720x24 &
sleep 2
export DISPLAY=:1
# Create VNC password and start VNC server on port 5900
mkdir -p ~/.vnc
x11vnc -storepasswd 1234 ~/.vnc/passwd
x11vnc -display :1 -forever -rfbauth ~/.vnc/passwd -rfbport 5900 &
sleep 1
# Drop into a shell in the mounted PX4 source tree
cd /home/user/PX4-Autopilot
exec bash
EOFBuild the image once, then run it with the source mounted:
cd ~/px4-vnc
docker build -t px4-build-vnc .
docker run --rm -it \
-v ~/PX4-Autopilot:/home/user/PX4-Autopilot \
-p 14550:14550/udp \
-p 14540:14540/udp \
-p 5900:5900 \
px4-build-vncInside the container shell, build and run SITL as before (make px4_sitl gz_x500). Then, in a new terminal tab on the Mac, connect to the Gazebo GUI via Screen Sharing:
open vnc://localhost:5900The password is 1234. If the view is black or grey, give Gazebo 10–15 seconds to start rendering. Pre-arm fixes and takeoff are identical to the shared section below.
C.2.8 macOS quick reference
# One-time per session: allow local X11 connections (XQuartz must be running)
xhost +localhost
# Start the dev container with source + display mounted
docker run --rm -it \
-e DISPLAY=host.docker.internal:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v ~/PX4-Autopilot:/home/user/PX4-Autopilot \
-p 14550:14550/udp -p 14540:14540/udp \
--workdir /home/user/PX4-Autopilot \
px4io/px4-dev-simulation-jammy:latest bash
# Inside the container: build + run
make px4_sitl gz_x500C.3 Windows (WSL 2 + Ubuntu 22.04)
On Windows, everything — PX4 and QGC — runs inside a WSL 2 Ubuntu 22.04 distro. WSLg (WSL’s built-in GUI support) renders the Gazebo and QGC windows on the Windows desktop with no X server setup.
C.3.1 Install WSL with Ubuntu 22.04
Open PowerShell as Administrator and install:
wsl --install -d Ubuntu-22.04Restart if prompted, then set your UNIX username and password when Ubuntu first launches.
Update the system inside the Ubuntu shell:
sudo apt update && sudo apt upgrade -yEnsure you are on WSL 2 with WSLg (needed for the Gazebo GUI). In PowerShell:
wsl --set-version Ubuntu-22.04 2 wsl --update
Launching the right Ubuntu. If you have more than one distro installed, the bare ubuntu command may open the wrong one (for example, 20.04). To check and fix:
wsl -l -v # list distros + versions; * marks default
wsl -d Ubuntu-22.04 # launch 22.04 specifically
wsl --set-default Ubuntu-22.04 # make 22.04 the default for plain `wsl`If Ubuntu-22.04 is not listed, it did not install — rerun wsl --install -d Ubuntu-22.04.
C.3.2 Install and build PX4 v1.16.2
Inside the Ubuntu 22.04 shell, install git and run the clone sequence from Getting the PX4 source above:
sudo apt install -y gitThen run the Ubuntu setup script, which installs the toolchain and Gazebo dependencies:
cd ~/PX4-Autopilot
bash ./Tools/setup/ubuntu.shClose and reopen the WSL terminal (or run source ~/.bashrc), then build and run SITL to verify:
cd ~/PX4-Autopilot
make px4_sitl gz_x500A Gazebo Sim window should open on the Windows desktop with an X500 quadcopter.
C.3.3 QGroundControl on WSL
PX4 needs a ground control station connection before it will arm. On WSL, install QGC inside the WSL distro using the official AppImage — WSLg renders its window on the Windows desktop:
cd ~
wget https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-x86_64.AppImage
chmod +x QGroundControl-x86_64.AppImage
./QGroundControl-x86_64.AppImage(If you download the AppImage with a Windows browser instead, the official QGroundControl downloads page has the current builds. Windows Explorer can reach your WSL home directory via \\wsl.localhost\<distro-name>\home\<your-username> — for example, run explorer.exe \\wsl.localhost\Ubuntu-22.04\home\<your-username> from PowerShell — and you can copy the AppImage there before making it executable.)
QGC auto-connects to the running SITL instance on UDP port 14550, and the GCS pre-arm failure clears.
Notes:
Run QGC alongside the SITL sim — start it after the sim is up, or leave it running and it will pick up the connection when the sim starts.
If the AppImage fails with a FUSE error, run it in extracted mode or install FUSE:
./QGroundControl-x86_64.AppImage --appimage-extract-and-run # or: sudo apt install -y libfuse2If the window does not appear, confirm WSLg is working (
echo $DISPLAYshould return something like:0) and that you are on WSL 2 withwsl --updateapplied.If QGC renders badly under WSL’s GPU passthrough, a small launcher script that forces software rendering is a reliable workaround:
cat > ~/launch-qgc.sh << 'EOF' #!/bin/bash export LIBGL_ALWAYS_SOFTWARE=1 export GALLIUM_DRIVER=llvmpipe exec ~/QGroundControl-x86_64.AppImage "$@" EOF chmod +x ~/launch-qgc.sh ./launch-qgc.sh
C.4 Native Ubuntu 22.04
On native Ubuntu 22.04 the procedure is the WSL procedure minus the WSL setup: everything runs directly on the host.
C.4.1 Prepare the system
sudo apt update && sudo apt upgrade -y
sudo apt install -y git wget curl
git --versionC.4.2 Install and build PX4 v1.16.2
Run the clone sequence from Getting the PX4 source above in your home directory, then install the dependencies and reload the shell:
cd ~/PX4-Autopilot
bash ./Tools/setup/ubuntu.sh
source ~/.bashrcLaunch SITL with the default (blank) simulation world:
cd ~/PX4-Autopilot
make px4_sitl gz_x500Or launch with the Baylands world instead:
PX4_GZ_WORLD=baylands make px4_sitl gz_x500C.4.3 QGroundControl on Ubuntu
Same AppImage as on WSL, downloaded from the official build server:
cd ~
wget https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-x86_64.AppImage
chmod +x QGroundControl-x86_64.AppImage
./QGroundControl-x86_64.AppImageThe FUSE-error fallback (--appimage-extract-and-run or sudo apt install -y libfuse2) and the software-rendering launcher script from the Windows section apply unchanged on native Ubuntu if you hit the same symptoms.
C.4.4 Verification checklist
C.4.5 Resulting directory structure
~
├── PX4-Autopilot/
├── QGroundControl-x86_64.AppImage
└── launch-qgc.sh
C.5 Clearing pre-arm failures and taking off (all platforms)
Whichever host you built on, you end at the same pxh> console, and the same two pre-arm failures are the common blockers. Both fixes are entered at the pxh> prompt.
C.5.1 “ekf2 missing data”
PX4’s EKF2 module (an extended-Kalman-filter state estimator) needs a few seconds to converge — this usually clears on its own within ~10–30 s. If it does not:
- If software rendering is dragging the simulation’s real-time factor down, convergence simply takes longer — wait it out.
- Check the real-time factor in the Gazebo GUI. Heavy
.daemeshes can drop it badly and starve EKF2 — simplify meshes or reduce texture sizes if it is well under ~0.5.
C.5.2 “No connection to the ground control station”
The clean fix is to start QGC (previous sections) — the failure clears on its own once the GCS link is up. For a headless run, the check can instead be relaxed from the pxh> prompt:
param set NAV_DLL_ACT 0
param set COM_RCL_EXCEPT 4
param set COM_RC_IN_MODE 1These parameters disable the data-link-loss failsafe action and the RC-loss/RC-input checks. They are simulation-only relaxations: restore the defaults (param reset NAV_DLL_ACT, and likewise for the others) before reusing a parameter set anywhere near real hardware.
C.5.3 Take off
Once the checks are green, the console reports “ready to take off” (you may have to press Enter in the terminal running the sim to see a fresh prompt). Then:
commander takeoffThe drone takes off, hovers, and lands — visible both in the terminal logs and in the Gazebo 3D window. From here, QGC’s map view, telemetry panes, and flight commands are live against the simulated vehicle.