Back

/ 4 min read

ArduPilot SITL + Gazebo on Ubuntu 24.04

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

Terminal window
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl wget build-essential cmake python3-pip python3-venv python-is-python3

python-is-python3 matters - some ArduPilot scripts call python directly and will fail without it.

ArduPilot

Terminal window
git clone --recurse-submodules https://github.com/ArduPilot/ardupilot.git ~/ardupilot
cd ~/ardupilot
Tools/environment_install/install-prereqs-ubuntu.sh -y

Close and reopen your terminal afterwards so PATH changes take effect.

If mavproxy.py or sim_vehicle.py come up missing later, do this:

Terminal window
pip install MAVProxy future --break-system-packages
echo 'export PATH=$PATH:$HOME/.local/bin:$HOME/ardupilot/Tools/autotest' >> ~/.bashrc
source ~/.bashrc

The 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.

Terminal window
sudo apt install -y software-properties-common
sudo add-apt-repository universe
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "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/null
sudo apt update
sudo apt install -y ros-jazzy-desktop ros-jazzy-ros-gz

Then add ROS to your shell so gz sim is available everywhere:

Terminal window
echo 'source /opt/ros/jazzy/setup.bash' >> ~/.bashrc
source ~/.bashrc

GStreamer dev libs

The plugin depends on these and the cmake error you get without them is unhelpful.

Terminal window
sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

Building 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.

Terminal window
git clone https://github.com/ArduPilot/ardupilot_gazebo.git ~/ardupilot_gazebo
cd ~/ardupilot_gazebo
mkdir build && cd build
export GZ_VERSION=harmonic
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
make -j$(nproc)

Sanity check that it actually built:

Terminal window
ls ~/ardupilot_gazebo/build/*.so

You 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

Terminal window
echo 'export GZ_VERSION=harmonic' >> ~/.bashrc
echo 'export GZ_SIM_SYSTEM_PLUGIN_PATH=$HOME/ardupilot_gazebo/build:${GZ_SIM_SYSTEM_PLUGIN_PATH}' >> ~/.bashrc
echo 'export GZ_SIM_RESOURCE_PATH=$HOME/ardupilot_gazebo/models:$HOME/ardupilot_gazebo/worlds:${GZ_SIM_RESOURCE_PATH}' >> ~/.bashrc
source ~/.bashrc

Running it

Order matters - start Gazebo first, then SITL.

Terminal 1:

Terminal window
gz sim -v4 -r ~/ardupilot_gazebo/worlds/iris_runway.sdf

Wait until you see the Iris on the runway and the sim clock is ticking up at the bottom.

Terminal 2:

Terminal window
cd ~/ardupilot/ArduCopter
sim_vehicle.py -v ArduCopter -f quad --model JSON --console --map

This 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 0
Received 1370 parameters
Saved 1370 parameters to mav.parm

Actually flying it

In the MAVProxy console, run these three commands as fast as you can - paste them as a single block if possible:

mode guided
arm throttle
takeoff 10

Copter 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 land

Versions 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!