Introduction#
PX4, as one of the most popular open-source flight controllers worldwide, provides convenience for developers in drone design and control. ROS, as a robot operating system, simplifies the drone formation simulation workflow through its distributed communication architecture, while the Gazebo physics simulation engine provides a realistic simulation environment.
ROS has now been updated to ROS2, which offers greater applicability and maintainability compared to ROS1. There are currently few ROS2+PX4 simulation tutorials available, and continuous updates of both platforms bring many environment configuration challenges. This article provides a personal development workflow.
Installing Ubuntu#
This environment is built on Ubuntu 22.04. You can install it as a virtual machine or set up a dual-boot system alongside Windows. Here is a video tutorial for installing Ubuntu 22.04 as a dual-boot system on Windows 11:
Now we begin the environment setup process. For reference, here is another developer’s workflow that is similar to ours and quite concise. If you only need single-drone simulation, this tutorial is also a good reference:
Note: Before starting the following steps, ensure you have reliable internet access (with proper network tools if needed), otherwise cloning repositories from GitHub may fail midway.
Downloading & Compiling PX4 Source#
First, clone the PX4 source code from GitHub and update all submodules:
git clone https://github.com/PX4/PX4-Autopilot.git --recursivebashSince the subsequent environment setup will install many packages, and it may be difficult to fully clean them up if something goes wrong, it is recommended to back up the ~/PX4-Autopilot directory at this point, so you don’t need to re-clone the entire repository if the environment needs a fresh start:
zip -r PX4-Autopilot.zip PX4-Autopilot/bashNext, install the required dependencies:
bash ./PX4-Autopilot/Tools/setup/ubuntu.shbashAt this point, the PX4 source code is downloaded and all dependencies are installed. We can now try compiling and launching the simulation:
cd ~/PX4-Autopilot/
make px4_sitl # Build the SITL firmware first (required!)
make px4_sitl jmavsim # Once built, launch jmavsimbashCommon error: If you run
make px4_sitl jmavsimdirectly and encounterninja: error: unknown target 'jmavsim', it means the PX4 CMake/Ninja build system hasn’t generated the jmavsim target yet. You must runmake px4_sitlfirst to complete the initial build, thenmake px4_sitl jmavsimto launch the simulator. After this first-time setup,make px4_sitl jmavsimwill work directly.
You will see the PX4 source code compile and the jmavsim physics simulation engine start up. This works because the ubuntu.sh dependency script already installed jmavsim and Gazebo-classic for us. If you see green ready for takeoff text in the terminal and the jmavsim window appears, compilation is successful. If this step fails, check your network tools.
Now press Enter in the terminal, and at the pxh> prompt, enter PX4 commands:
commander takeoff/land/shutdownplaintextThese commands control the drone’s takeoff, landing, and shutdown of the simulation. You should see the drone taking off and landing in the jmavsim UI. At this point, the PX4 source download and compilation are complete.
Installing ROS2 and Related Dependencies#
We use ROS2 Humble for development. The installation and environment configuration for ROS2 and its dependencies can be somewhat tedious, so I strongly recommend using the “FishROS” one-click installation tool. Many thanks to FishROS for providing this convenience for the ROS community. Simply run the following command in your Ubuntu terminal and follow the on-screen instructions:
wget http://fishros.com/install -O fishros && . fishrosbashThe one-click installation tool also includes options for Docker and other tools — feel free to install them as needed. Again, many thanks. Additionally, developers using the ROS2 Foxy distribution can also use this tool for installation and environment configuration.
Installing Gazebo Sim 8.9.0#
According to the official PX4 documentation, the former Gazebo Ignition has been renamed to Gazebo, and the old Gazebo is now called Gazebo-classic. Ubuntu 22.04 supports the Gazebo (Ignition) series. When we installed PX4 dependencies earlier, ubuntu.sh automatically installed jmavsim and Gazebo-classic, but for multi-drone simulation we need Gazebo Harmonic (v8.x, i.e., Gazebo Sim 8.9.0).
Important: The Gazebo version and the ROS2 bridge package must match. Mixing multiple versions is the primary cause of environment crashes. Gazebo Garden (v7), Harmonic (v8), and Ionic (v9) cannot coexist.
First, add the OSRF repository and install Gazebo Harmonic:
cd ~/PX4-Autopilot
sudo apt install wget
sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
sudo apt update
sudo apt-get install gz-harmonicbashThen install the corresponding Gazebo-ROS2 communication bridge (Harmonic maps to gzharmonic):
sudo apt install ros-humble-ros-gzharmonicbashAfter installation, verify version uniqueness (only one version number should appear):
gz sim --versions # Should only show 8.9.0bashCommon mistake: Do not install
gz-gardenorros-humble-ros-gzgarden. That is Gazebo Garden (v7), which is incompatible with PX4’s currentgz-sim8compilation target.
Installing MicroXRCE-DDS Agent#
According to the official documentation, in ROS2, PX4 uses the uXRCE-DDS middleware to allow publishing and subscribing to uORB messages on the companion computer, replacing MAVROS. We follow the official example to download, compile, and install.
Download the source:
git clone -b v2.4.2 https://github.com/eProsima/Micro-XRCE-DDS-Agent.gitbashCompile:
cd Micro-XRCE-DDS-Agent
mkdir build
cd build
cmake ..
makebashThere will likely be a build error here. Go to
build/fastdds/tmp/fastdds-gitclone.cmake, change2.12.xtov2.12.1, then re-runmake.
The compilation process takes a while, so be patient. After compilation finishes, install:
sudo make install
sudo ldconfig /usr/local/lib/bashInstalling QGC Ground Station#
When starting the PX4+Gazebo simulation, losing connection to the ground station will prevent PX4 from controlling the drone. Therefore, you need to install QGC Ground Station. Here is a CSDN guide that walks through the steps:
Single Drone Offboard Test and Development Guide#
All development environment components are now set up. Let’s verify PX4 Offboard mode.
Offboard mode is a special flight mode in PX4 that allows external systems (such as companion computers, ROS2 nodes, etc.) to send control commands directly via MAVLink or uXRCE-DDS, enabling real-time control of the drone’s attitude, position, or velocity for autonomous flight or advanced missions.
First, create a ROS2 workspace:
mkdir -p ~/ros2_ws/srcbashThen download the source code (make sure your network is working):
cd ~/ros2_ws/src
git clone https://github.com/PX4/px4_msgs.git -b release/1.14
git clone https://github.com/PX4/px4_ros_com.git -b release/v1.14bashCompile:
cd ~/ros2_ws
colcon buildbashUpdate environment and configure environment variables:
echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc
echo "export GZ_SIM_RESOURCE_PATH=\$HOME/.simulation-gazebo/models:\$GZ_SIM_RESOURCE_PATH" >> ~/.bashrc
source ~/.bashrcbashLaunching the Offboard Simulation#
Now we start the Offboard simulation. First, open the already installed QGC Ground Station.
Terminal 1 — Start communication:
MicroXRCEAgent udp4 -p 8888bash
Terminal 2 — Start simulation:
cd ~/PX4-Autopilot
make px4_sitl gz_x500bashAt this point, the ground station will connect to PX4, Gazebo will launch, and the x500 drone will appear. When the PX4 terminal shows green ready for takeoff, the simulation has started successfully. We will explain this make-based simulation startup in more detail in the multi-drone simulation section.
Terminal 3 — Run Offboard control source:
ros2 run px4_ros_com offboard_controlbashYou should see the drone ascend 5 meters in Gazebo, confirming Offboard mode control is successful.
Offboard Mode Development Workflow#
In the ~/ros2_ws workspace we just created, navigate to the px4_ros_com directory and open CMakeLists.txt. Find these three lines:
add_executable(offboard_control src/examples/offboard/offboard_control.cpp)
ament_target_dependencies(offboard_control rclcpp px4_msgs)
install(TARGETS offboard_control DESTINATION lib/${PROJECT_NAME})cmakeThis compiles offboard_control.cpp from src/examples/offboard into an executable named offboard_control, with its dependency on px4_msgs. You can open the src/examples/offboard directory to find the offboard_control.cpp source code.
Therefore, to develop single-drone Offboard mode simulations, follow this workflow:
- Inside the already created and configured
~/ros2_wsROS2 workspace, create a new package (at the same level as thepx4_ros_comdirectory). - In the new package, write your Offboard mode control code (C++/Python) under the
/srcdirectory. - In the new package’s
CMakeLists.txt, find these lines:
add_executable(offboard_control src/examples/offboard/offboard_control.cpp)
ament_target_dependencies(offboard_control rclcpp px4_msgs)
install(TARGETS offboard_control DESTINATION lib/${PROJECT_NAME})cmakeChange the path to the location of your C++/Python code, and give it an executable name. Before running the simulation, you must recompile the ROS2 workspace!
cd ~/ros2_ws
colcon buildbashFinally, following the earlier method, open QGC Ground Station, start communication and simulation, then open a new terminal and run:
ros2 run <package_name> <executable_name>bashIf you see the drone flying according to your code logic in Gazebo, the simulation is successful.
Supplementary Notes#
Supplement 1: When using the
make px4_sitl gz_x500command to start the simulation, the automatically launchedgz simsimulator world is the default world. Its sdf file is no longer in~/.gz/worlds, but in./PX4-Autopilot/Tools/simulation/gz/worlds. Similarly, all models are also located underTools/simulation. Therefore, when building your simulation environment, you should go to these directories to directly modify the sdf files.
Supplement 2: When writing C++/Python code for Offboard mode control, pay attention to coordinate system transformations. In PX4, the coordinate system is NED (North-East-Down), meaning the x-axis points north, the y-axis points east, and the z-axis points down (all altitudes are negative). In Gazebo Ignition (now Gazebo), the coordinate system is ENU (East-North-Up), meaning the x-axis points east, the y-axis points north, and the z-axis points up (altitudes are positive). Make sure to account for coordinate conversions when writing your control logic.
At this point, the basic workflow for ROS2+PX4 single-drone Offboard mode simulation is fully covered. If you have successfully run through the above workflow, you can now proceed with further single-drone simulation development.
Multi-Drone (Formation) Development Workflow#
In the previous section, we completed the single-drone simulation environment setup using ROS2+PX4. Now we proceed to the multi-drone environment setup in preparation for formation control logic simulation.
Reviewing Single-Drone Startup#
Let’s first review the primary command for starting a single-drone simulation:
cd ~/PX4-Autopilot
make px4_sitl gz_x500bashThis make command actually performs three tasks:
- Compiles the PX4 source code
- Starts PX4
- Launches the Gazebo simulation
However, for multi-drone simulation, we cannot simply run make repeatedly — that would cause each x500 drone to launch its own separate Gazebo instance, which is not what we want. Therefore, for multi-drone simulation in the Gazebo environment, our goals are:
- Start Gazebo simulation separately
- Launch PX4 instances individually, adding drones to the Gazebo environment
- Establish communication
Starting Gazebo Separately#
First, we need to download the Gazebo launch script (simulation-gazebo):
wget https://raw.githubusercontent.com/PX4/PX4-gazebo-models/main/simulation-gazebobashThen, in the directory where the script is located, run:
python3 simulation-gazebobashTroubleshooting: If the Gazebo window fails to open (crash or black screen), it’s usually due to incompatible GPU drivers or a VM without GPU passthrough. Prefix the command with
LIBGL_ALWAYS_SOFTWARE=1to force CPU software rendering:bashLIBGL_ALWAYS_SOFTWARE=1 python3 simulation-gazeboThis environment variable tells Mesa/OpenGL to use the LLVMpipe software rasterizer instead of hardware GPU acceleration. The tradeoff is lower frame rate, but simulation functionality is unaffected.
The first time you run the script, it will download some components into .simulation-gazebo (use Ctrl+H in your home directory to reveal hidden folders). The /worlds/default.sdf file inside is the simulation world sdf file — you will modify this file later.
Supplement 3: Note the distinction from Supplement 1 in the single-drone simulation section. This is a significant difference between single-drone and multi-drone setup. When starting a single-drone simulation, we use the
makecommand, and the world sdf file is in./PX4-Autopilot/Tools/simulation/gz/worlds. However, if you examine thesimulation-gazebo.pyscript we downloaded for multi-drone simulation, you will find that the world sdf file is under.simulation-gazebo. Make sure to distinguish between these two.
Adding Multiple Drones#
After starting the Gazebo environment separately, we add drones to it. This time we do not use the make command, but the following command instead:
cd ~/PX4-Autopilot
PX4_GZ_STANDALONE=1 PX4_SYS_AUTOSTART=4001 PX4_GZ_MODEL_POSE="0,2" PX4_SIM_MODEL=gz_x500 ./build/px4_sitl_default/bin/px4 -i 0bashParameter explanation:
| Parameter | Description |
|---|---|
PX4_GZ_STANDALONE=1 | Use standalone mode (start only PX4 without launching its own simulation, connect to the separately started simulation) |
PX4_SYS_AUTOSTART=4001 | Required airframe configuration field |
PX4_GZ_MODEL_POSE="0,2" | Initial drone position — different drones should have different positions |
PX4_SIM_MODEL=gz_x500 | Drone model is x500 |
-i 0 | Drone instance ID (ID 0) |
After running this command, you should see a drone appear in the previously launched Gazebo window.
Now add a second drone (ID -i 1, position "0,1"):
PX4_GZ_STANDALONE=1 PX4_SYS_AUTOSTART=4001 PX4_GZ_MODEL_POSE="0,1" PX4_SIM_MODEL=gz_x500 ./build/px4_sitl_default/bin/px4 -i 1bashTo add more drones, follow the same pattern, or write a unified script to add them all at once.
Now you should see two drones side by side in Gazebo. In each drone’s PX4 terminal, you can enter:
commander takeoff/landplaintextto observe each drone’s takeoff and landing. Finally, add the communication:
MicroXRCEAgent udp4 -p 8888bash
At this point, the entire environment setup for ROS2+PX4 single-drone and multi-drone simulation in Gazebo is complete. In the next article, we build on this environment to implement ROS2+PX4 Multi-Drone Offboard Control, achieving dual-drone formation flight using a leader-follower algorithm. Beyond that, you can also pursue target recognition, path planning, and many other projects. The first step is always the hardest — I hope this article can contribute even a small amount to research in this direction.
If you still encounter simulation issues, please refer to the official PX4 documentation and related GitHub projects. Also, if you’re interested in Betaflight, check out the Betaflight + Gazebo SITL Tutorial for an alternative SITL simulation approach. Environment configuration and workflow familiarization is a simple but tedious process. As a beginner, I have provided my personal development workflow, and there are certainly better approaches out there. Corrections and suggestions for better methods are always welcome — they will help those who come after us.
Gazebo Environment Troubleshooting Guide#
Based on bugs reported over the past year, here is a summary of troubleshooting and repair methods for dependency conflicts caused by multiple coexisting Gazebo versions.
1. Check Which Gazebo Versions Are Installed#
# List all Gazebo-related packages
dpkg -l | grep -E "^ii" | awk '{print $2}' | grep -E "^gz-|^libgz-|^sdformat|^libsdformat|python3-gz" | sort
# Check available Gazebo Sim versions (multiple versions = conflict)
gz sim --versions
# Check the current default version
gz sim --versionbashHealthy state:
gz sim --versionsshould output only one version number (e.g.,8.9.0), and alllibgz-*-Xshould have the same major version (e.g., all 7 or all 8).
2. Typical Symptoms of Version Conflicts#
# Symptom 1: Multiple versions coexist
gz sim --versions
# Output:
# 8.9.0
# 7.9.0 ← Garden and Harmonic coexist -- conflict!
# Symptom 2: CMake finds the wrong version
grep "gz-sim_DIR" ~/PX4-Autopilot/build/px4_sitl_default/CMakeCache.txt
# Should match gz sim --version: Harmonic → gz-sim8, Garden → gz-sim7
# Symptom 3: ROS2 bridge package version mismatch
dpkg -l | grep ros-humble-ros-gz
# Should only have gzharmonic or only gzgarden, not both simultaneouslybash3. Fix: Unify to a Single Gazebo Version#
Using Harmonic (v8) as an example (recommended for ROS2 Humble, as it has a corresponding bridge package):
# 1. Remove all Garden (v7) packages
sudo apt-get purge -y gz-garden gz-sim7-cli gz-launch6-cli gz-transport12-cli \
libgz-sim7 libgz-sim7-dev libgz-sim7-plugins \
libgz-launch6 libgz-launch6-dev \
libgz-transport12 libgz-transport12-dev \
libgz-fuel-tools8 libgz-fuel-tools8-dev \
libgz-gui7 libgz-gui7-dev \
libgz-msgs9 libgz-msgs9-dev \
libgz-physics6 libgz-physics6-dev \
libgz-rendering7 libgz-rendering7-dev \
libgz-sensors7 libgz-sensors7-dev \
libsdformat13 libsdformat13-dev sdformat13-sdf \
python3-gz-sim7
# 2. Remove Garden ROS2 bridge
sudo apt-get purge -y ros-humble-ros-gzgarden*
# 3. Install Harmonic ROS2 bridge
sudo apt-get install -y ros-humble-ros-gzharmonic
# 4. Verify
gz sim --versions # Should only show 8.9.0
dpkg -l | grep gz-garden # Should output nothing
dpkg -l | grep gz-harmonic # Should show as installedbash4. Common .bashrc Issues#
# Problem 1: Duplicate source entries (repeated appending inflates PATH)
grep "source.*setup.bash" ~/.bashrc | sort | uniq -c
# If any line appears more than once, manually clean up ~/.bashrc
# Problem 2: Incorrect ROS2 loading order
# Correct order: source /opt/ros/humble/setup.bash first, then source ~/ros2_ws/install/setup.bash
# The workspace overlay must come after the base environment
# Problem 3: Model path variable
# Gazebo Garden/Harmonic use GZ_SIM_RESOURCE_PATH, not GAZEBO_MODEL_PATH
echo $GZ_SIM_RESOURCE_PATH # Should point to your model directorybash5. Check if PX Build Target Matches Gazebo Version#
# PX4 CMake cache records the gz-sim version
grep "gz-sim_DIR" ~/PX4-Autopilot/build/px4_sitl_default/CMakeCache.txt
# gz-sim8 → Harmonic
# gz-sim7 → Garden
# If the version does not match the installed Gazebo, recompile PX4:
cd ~/PX4-Autopilot
make clean
make px4_sitl gz_x500bash6. Check ROS2 Bridge Package Dependencies#
# Confirm the bridge package's SDFormat dependency matches the Gazebo version
apt-cache depends ros-humble-ros-gzharmonic | grep sdformat
# Harmonic → libsdformat14-dev
# Confirm no residual Garden bridge packages
dpkg -l ros-humble-ros-gzgarden* 2>/dev/null
# Should show "no packages found" or status "un" (uninstalled)bash