I just spent solid 3 hours on getting to work ArduPilot SITL together with Gazebo on current Ubuntu LTS 24.04. For this special occasion I bought recently Beelink SER5 Pro - just to get moving with my drone work.
Whole post assumes you’re working on a clean state Ubuntu that was just installed. Let’s get started.
Base system
sudo apt update && sudo apt upgrade -ysudo apt install -y git curl wget build-essential cmake python3-pip python3-venv python-is-python3python-is-python3 matters - some ArduPilot scripts call python directly and will fail without it.
ArduPilot
git clone --recurse-submodules https://github.com/ArduPilot/ardupilot.git ~/ardupilotcd ~/ardupilotTools/environment_install/install-prereqs-ubuntu.sh -yClose and reopen your terminal afterwards so PATH changes take effect.
If mavproxy.py or sim_vehicle.py come up missing later, do this:
pip install MAVProxy future --break-system-packagesecho 'export PATH=$PATH:$HOME/.local/bin:$HOME/ardupilot/Tools/autotest' >> ~/.bashrcsource ~/.bashrcThe future package isn’t pulled in by MAVProxy’s deps for some reason, but MAVProxy crashes on startup without it. Install it preemptively.
ROS 2 Jazzy + Gazebo Harmonic
The cleanest way to get Gazebo Harmonic on 24.04 is via the ROS 2 Jazzy repo, since Jazzy ships Harmonic as its default sim.
sudo apt install -y software-properties-commonsudo add-apt-repository universesudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpgecho "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/nullsudo apt updatesudo apt install -y ros-jazzy-desktop ros-jazzy-ros-gzThen add ROS to your shell so gz sim is available everywhere:
echo 'source /opt/ros/jazzy/setup.bash' >> ~/.bashrcsource ~/.bashrcGStreamer dev libs
The plugin depends on these and the cmake error you get without them is unhelpful.
sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-devBuilding the ArduPilot Gazebo plugin
This is where things get fragile. The GZ_VERSION env var has to be set before you run cmake, otherwise the plugin builds against the wrong gz-sim headers and you’ll spend hours debugging “protocol magic” warnings later. Whatever that means. Magic.
git clone https://github.com/ArduPilot/ardupilot_gazebo.git ~/ardupilot_gazebocd ~/ardupilot_gazebomkdir build && cd buildexport GZ_VERSION=harmoniccmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfomake -j$(nproc)Sanity check that it actually built:
ls ~/ardupilot_gazebo/build/*.soYou should see libArduPilotPlugin.so. If you don’t, scroll back through the make output for errors - if cmake silently misconfigured itself, the build will look fine but produce no plugin.
Environment variables, made permanent
echo 'export GZ_VERSION=harmonic' >> ~/.bashrcecho 'export GZ_SIM_SYSTEM_PLUGIN_PATH=$HOME/ardupilot_gazebo/build:${GZ_SIM_SYSTEM_PLUGIN_PATH}' >> ~/.bashrcecho 'export GZ_SIM_RESOURCE_PATH=$HOME/ardupilot_gazebo/models:$HOME/ardupilot_gazebo/worlds:${GZ_SIM_RESOURCE_PATH}' >> ~/.bashrcsource ~/.bashrcRunning it
Order matters - start Gazebo first, then SITL.
Terminal 1:
gz sim -v4 -r ~/ardupilot_gazebo/worlds/iris_runway.sdfWait until you see the Iris on the runway and the sim clock is ticking up at the bottom.
Terminal 2:
cd ~/ardupilot/ArduCoptersim_vehicle.py -v ArduCopter -f quad --model JSON --console --mapThis is the part that I spent the most time on. Use -f quad --model JSON. Do not use --frame gazebo-iris, even though that’s what most older tutorials and forum posts suggest. The gazebo-iris frame in sim_vehicle.py invokes ArduPilot’s legacy Gazebo Classic backend, which sends UDP packets in a totally different format than what the modern ardupilot_gazebo plugin expects. The plugin will print Incorrect protocol magic 0 should be 18458 forever and nothing will connect.
The new plugin uses a JSON-over-UDP protocol with a magic header (18458). To get SITL to speak that protocol, you need --model JSON. That’s the entire fix. I wasted hours on port swaps, branch downgrades, and rebuilds before finding this.
If everything’s right, MAVProxy should report something like:
Detected vehicle 1:1 on link 0Received 1370 parametersSaved 1370 parameters to mav.parmActually flying it
In the MAVProxy console, run these three commands as fast as you can - paste them as a single block if possible:
mode guidedarm throttletakeoff 10Copter auto-disarms within 15 seconds of arming if no takeoff command follows. If you type slowly, the motors disarm between arm throttle and takeoff 10, and you get NAV_TAKEOFF: FAILED. Pasting all three at once is the most reliable way.
If everything works, switch to the Gazebo window and watch the Iris climb to 10m and hover. To bring it down:
mode landVersions that worked for me (April 2026)
- Ubuntu 24.04
- ArduPilot master
- ardupilot_gazebo main
- Gazebo Harmonic (gz-sim 8.10) via ROS 2 Jazzy
- Python 3.12
Why I wrote this
Hope this saves someone the evening it cost me. Have fun!