UAV Systems Engineering · Module 6
M6-T8: ROS 2 and PX4 integration
The connection is easy to make and easy to get subtly wrong: frames, QoS, message versions and time are where integrations fail.
Connecting ROS 2 to PX4 is a half-hour job; making it correct is the real task. Four things decide whether the integration is trustworthy: matching message versions, compatible QoS, correct frame conversion and consistent time. This lesson is the checklist, with a conversion you can test.
Learning objectives
After this lesson, you should be able to:
- Compare the native uXRCE-DDS path with MAVROS and choose between them.
- Convert positions, velocities and orientations between ENU/FLU (ROS) and NED/FRD (PX4).
- Choose QoS settings that match the PX4 DDS bridge.
- Verify the integration with rate, frame and timestamp checks.
Conceptual explanation
Implementation-specific The details of the PX4 ROS 2 interface (message package px4_msgs, topic names with the /fmu/in and /fmu/out prefixes, QoS) follow recent PX4 documentation. The px4_msgs package must match the PX4 firmware version; mismatches cause silent or noisy failures.
Two integration paths exist:
- Native (uXRCE-DDS): a DDS client on PX4 exchanges selected uORB topics with a micro-XRCE-DDS agent on the companion computer; ROS 2 nodes use the
px4_msgspackage whose message types mirror the uORB definitions. It exposes PX4’s internal topics with low overhead, and is the path PX4 documents for ROS 2. - MAVROS: a ROS node that translates MAVLink into ROS topics and services (and provides ENU/NED conversions). It works with PX4 and ArduPilot and gives a stable, MAVLink-level interface, at the price of the translation layer and the MAVLink message set.
Whichever path you pick, handle four issues explicitly:
- Frames: ROS uses ENU world frames and FLU body frames (REP-103); PX4 uses NED world and FRD body. A conversion is needed on every pose, velocity, and setpoint, and the quaternion needs care.
- QoS: PX4’s publishers use best-effort reliability with a small history (and the bridge uses transient-local durability for some topics); a subscriber with an incompatible QoS silently receives nothing.
- Versions:
px4_msgsand the firmware must come from the same release (or branch). - Time: PX4 timestamps are in microseconds on the vehicle’s clock; the bridge can synchronise time, but you must verify offsets before fusing anything.

Engineering intuition
The conversion is just a fixed rotation between two right-handed conventions: ENU and NED differ by swapping and and flipping ; FLU and FRD differ by flipping and . Everything that goes wrong is forgetting which side a number is on. Write the frame in the variable name (p_enu, v_ned) and convert at one place, in one tested function, at the boundary.
QoS problems are especially annoying because the node starts, the topic exists, and nothing arrives. Check ros2 topic info -v for the publisher’s QoS and match it, rather than relying on the default (reliable) subscription.
Mathematical formulation
Position and velocity vectors convert as and the inverse is the same swap. Body vectors convert FLU → FRD by (, −, −). For orientation, if RENU←FLU is the ROS attitude then the PX4 attitude is
Both matrices are orthogonal with determinant +1, so each is a proper rotation (frd_flu is a half-turn about the axis, and Cned_enu swaps and and flips , which is a half-turn about the axis ), and the product is a rotation too. Yaw converts as (wrapped to ±). In quaternion form, apply the same transformation through the rotation matrix or use a tested library; do not permute components by hand.
- _enu
- world-frame axis permutation and sign flip.
- _flu
- body-frame sign flips.
- yaw angle.
Architecture and data flow
Native: PX4 uORB ↔ uXRCE-DDS client ↔ serial or UDP ↔ MicroXRCEAgent on the companion ↔ DDS ↔ ROS 2 nodes using px4_msgs (/fmu/out/* topics from PX4, /fmu/in/* topics to PX4). MAVROS: PX4 MAVLink module ↔ serial or UDP ↔ MAVROS node ↔ ROS topics (/mavros/*). A conversion node or library function sits at the boundary and the application works in ROS frames internally.
Real-world UAV applications
- Perception and planning nodes feeding PX4 offboard setpoints.
- Publishing VIO or lidar odometry into EKF2.
- Recording PX4 state in ROS 2 bags alongside sensor data.
- Hardware-in-the-loop tests with the full ROS 2 stack.
Implementation example
This runs as shown. The frame conversion functions with round-trip and handedness checks, including the quaternion conversion done through rotation matrices.
import numpy as np
C_ned_enu = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1.0]]) # world: ENU -> NED (also NED -> ENU)
C_frd_flu = np.diag([1.0, -1.0, -1.0]) # body: FLU -> FRD (also FRD -> FLU)
def quat_to_R(q): # q = (w, x, y, z)
w, x, y, z = q
return np.array([[1 - 2*(y*y+z*z), 2*(x*y - z*w), 2*(x*z + y*w)],
[2*(x*y + z*w), 1 - 2*(x*x+z*z), 2*(y*z - x*w)],
[2*(x*z - y*w), 2*(y*z + x*w), 1 - 2*(x*x+y*y)]])
def R_to_quat(R):
t = np.trace(R)
if t > 0:
s = 2 * np.sqrt(1 + t); return np.array([s / 4, (R[2, 1] - R[1, 2]) / s, (R[0, 2] - R[2, 0]) / s, (R[1, 0] - R[0, 1]) / s])
i = int(np.argmax(np.diag(R))); j, k = (i + 1) % 3, (i + 2) % 3
s = 2 * np.sqrt(1 + R[i, i] - R[j, j] - R[k, k]); q = np.zeros(4); q[1 + i] = s / 4
q[0] = (R[k, j] - R[j, k]) / s; q[1 + j] = (R[j, i] + R[i, j]) / s; q[1 + k] = (R[k, i] + R[i, k]) / s
return q
def pose_enu_flu_to_ned_frd(p_enu, q_enu_flu):
R = C_ned_enu @ quat_to_R(q_enu_flu) @ C_frd_flu
q = R_to_quat(R); q = q if q[0] >= 0 else -q
return C_ned_enu @ p_enu, q
yaw_enu = np.radians(30.0) # vehicle facing 30 deg from East towards North (ENU yaw)
q_enu = np.array([np.cos(yaw_enu / 2), 0, 0, np.sin(yaw_enu / 2)])
p_ned, q_ned = pose_enu_flu_to_ned_frd(np.array([1.0, 2.0, 3.0]), q_enu)
R_ned = quat_to_R(q_ned); yaw_ned = np.arctan2(R_ned[1, 0], R_ned[0, 0])
print(p_ned, f"yaw NED {np.degrees(yaw_ned):.1f} deg (expected {np.degrees(np.pi / 2 - yaw_enu):.1f})")
assert np.allclose(p_ned, [2.0, 1.0, -3.0]) and abs(yaw_ned - (np.pi / 2 - yaw_enu)) < 1e-12
assert abs(np.linalg.det(R_ned) - 1) < 1e-12 and np.allclose(R_ned @ R_ned.T, np.eye(3), atol=1e-12)Check the QoS match on the live system:
ros2 topic list | grep fmu
ros2 topic info /fmu/out/vehicle_local_position --verbose # publisher QoS: reliability, durability, depth
ros2 topic hz /fmu/out/vehicle_local_positionCommon engineering mistakes
Frame conversion scattered through the code
Convert in one tested function at the boundary.
Permuting quaternion components by hand
Convert through rotation matrices or a tested library.
px4_msgs from a different release than the firmware
Messages parse wrongly or are ignored; pin both.
Default (reliable) QoS subscriber
No data from best-effort publishers; set QoS explicitly.
Assuming clocks are aligned
Check the time offset between companion and vehicle before fusing.
Failure modes
| Failure mode | How it shows up | Likely cause |
|---|---|---|
| Topics visible but empty | ros2 topic echo shows nothing | QoS mismatch |
| Vehicle moves the wrong way | Mirrored motion | ENU/NED sign or swap error |
| Message errors on the agent | Agent logs deserialisation problems | px4_msgs version mismatch |
| Setpoints seem delayed | Control lag | Time-base mismatch or network buffering |
Debugging approach
- Test the conversion with known vectors (East, North, Up) and a known yaw.
- Inspect QoS with ros2 topic info -v.
- Compare px4_msgs and firmware commit hashes.
- Measure topic rates and latency with timestamps.
Practical exercises
- Convert the East, North and Up unit vectors to NED and check against the figure in REP-103.
- Write a unit test for the quaternion conversion for yaws of , , , −.
- Subscribe with matching QoS to vehicle_local_position and print , , .
- Measure the end-to-end rate and the clock offset between the companion and PX4.
Interview questions
Write an answer in your own words, then reveal the model answer. Nothing is sent anywhere.
Question 1
How do ROS and PX4 differ in frame conventions?
Model answer
ROS uses ENU world and FLU body frames; PX4 uses NED world and FRD body. Positions and velocities swap and and flip ; body vectors flip and ; yaw converts as .
Question 2
Why can a subscriber receive nothing from an existing topic?
Model answer
A QoS mismatch: for example a reliable subscriber to a best-effort publisher is not matched. Match the publisher’s reliability and durability.
Question 3
When would you choose MAVROS over the native bridge?
Model answer
When you need a MAVLink-level interface that works across autopilots or existing MAVROS-based software; the native bridge is preferred for direct access to PX4 uORB topics with lower overhead.
References and further reading
Primary papers, official documentation and standards. Each claim above that is specific to a tool or version points here.