The data path between ArduPilot SITL and a ROS 2 node has three legs:
- ArduPilot ↔ MAVROS over MAVLink/TCP — SITL listens on TCP 5760; MAVROS connects as a client.
- MAVROS ↔ ROS 2 over DDS — MAVROS publishes/subscribes ROS 2 topics in the standard DDS namespace.
- ArduPilot ↔ Gazebo over JSON/UDP — separate path; the FDM lock-step described in ardupilot_gazebo plugin.
This page is a pointer-and-summary because the actual configuration sits in three different places: MAVLink in sim_vehicle.py flags, DDS in ROS 2 settings, and JSON-UDP in the ArduPilot Gazebo plugin’s <plugin> SDF element.
Default port map
| Path | Port | Protocol |
|---|---|---|
| ArduPilot ↔ MAVProxy / MAVROS | 5760 | TCP |
| ArduPilot ↔ Gazebo plugin (FDM out) | 9002 | UDP |
| ArduPilot ↔ Gazebo plugin (FDM in) | 9003 | UDP |
| Optional GCS UDP forward | 14550 | UDP |
Parallel-instance port offsets
When running multiple SITL instances (-I 0, -I 1, …), sim_vehicle.py offsets the MAVLink port by +10 * instance automatically. So instance 1 listens on TCP 5770; instance 2 on 5780. The Gazebo FDM ports, however, are not offset by SITL — they’re hardcoded in the SDF. That’s the cause of the parallel-instance bug documented in Parallel SITL instances.
MAVROS connection
ros2 launch mavros apm.launch.py fcu_url:=tcp://localhost:5760
Once connected, MAVROS exposes:
/mavros/state— armed, mode, connected flags./mavros/local_position/pose— EKF-fused position./mavros/setpoint_position/local— write to send position setpoints./mavros/cmd/arming,/mavros/cmd/takeoff,/mavros/set_mode— services.
The full list runs to ~80 topics and services — ros2 topic list and ros2 service list after MAVROS connects.
Where to go next
- SITL hub — sibling pages
- Parallel SITL instances — the multi-instance gotcha
- MAVROS theory — the conceptual side