Article

An odometry quaternion can look valid while giving a drone the wrong yaw

A position estimate can pass a basic sanity check while its attitude points into the wrong coordinate system. That is the failure behind a PX4 change merged on 31 October 2026: the MAVLink receiver converted position and velocity from ENU/FLU conventions into the frames expected by the flight stack, but copied the orientation quaternion without the corresponding rotation. The result could remain hidden until the estimator used external-vision yaw.

The bug is a useful case study in an integration problem that extends beyond PX4. A number can be internally consistent, correctly transmitted and timestamped, yet describe the wrong physical direction because the sender and receiver disagree about its reference frame.

The pose has a world frame and a body frame

ROS REP 103 specifies an East-North-Up (ENU) geographic convention and a Forward-Left-Up (FLU) body convention. PX4 generally uses North-East-Down (NED) and Forward-Right-Down (FRD), as documented in its ROS 2 interface guide.

The first conversion is easy to visualize for a position vector. An ENU position (east, north, up) becomes (north, east, -up) in NED. A body vector (forward, left, up) becomes (forward, -left, -up) in FRD. Both coordinate systems remain right-handed. Merely changing the field labels or flipping the sign of the altitude does not turn an orientation into the new convention.

A quaternion describing attitude relates two frames. For an attitude received in ENU with an FLU body, the conversion must act on both sides of the orientation:

q_NED_FRD = q_NED_ENU * q_ENU_FLU * q_FLU_FRD

The left factor changes the world-frame basis; the right factor changes the body-frame basis. Quaternion order matters. Applying only one factor, swapping position axes while leaving the quaternion intact, or treating the quaternion's four stored numbers as a vector does not implement this transformation. The MAVLink ODOMETRY specification identifies a pose frame, a velocity frame, a quaternion and covariance fields; the ROS aerial-vehicle interface convention also distinguishes the pose's parent frame from the twist's child frame.

Why the previous PX4 path looked healthy

The PX4 correction describes an asymmetry in the MAVLink ODOMETRY receiver. When the message used MAV_FRAME_LOCAL_ENU or MAV_FRAME_LOCAL_FLU, the receiver converted the position and velocity and labeled the resulting internal visual-odometry pose as NED or FRD. It did not rotate the quaternion.

That made a position plot a weak diagnostic. A robot moved east, and the translated east/north coordinates could still have the expected magnitude and direction. Yet the orientation that accompanied them could be interpreted under another axis convention. The EKF's external-vision yaw path then extracted heading from the incorrectly labeled quaternion.

The contributor's simulation test sent a pose equivalent to 1.000 rad of NED yaw. Before the correction, the internal odometry output reported 0.571 rad for the ENU input, equal to approximately pi/2 - 1. The LOCAL_FLU case reported -1.000 rad. After the change, both yielded 1.000 rad. The reported position conversion was correct before and after the patch. These are the contributor's SIH and numerical test results, not independent flight measurements.

The practical consequence depends on configuration. PX4's EKF2 documentation identifies bit 3 of EKF2_EV_CTRL as external-vision yaw fusion. If that measurement is fused under a mistaken coordinate basis, the estimator can be driven toward an incorrect heading. The PR author warns of circling or loss of position control in that configuration. The public evidence establishes the conversion bug and its simulated yaw effect; it does not establish how often such a failure occurred in the field.

Angle errors are not always a constant offset

A tempting workaround is to add 90 degrees to a displayed heading. For this particular pre-fix ENU path, the reported relation was approximately pi/2 - yaw, which includes a sign reversal. It is not a fixed additive correction across all headings.

It is also unsafe to repair a full three-dimensional attitude with a yaw-only offset. Roll and pitch interact with the orientation representation. Camera mounting orientation, vehicle motion and the choice of local frame can expose mistakes that a level, north-facing bench test misses.

The merged change transforms the attitude for the two affected frame choices and also converts body angular rates to FRD, reversing the relevant pitch and yaw rate signs. The PR says orientation variances were left unchanged because the transformation only changes signs of their axes. That statement applies to the variances in this implementation, not to every possible covariance matrix: a full covariance with cross-terms needs a consistent basis transformation.

Frame compatibility is separate from estimator quality

A correctly converted quaternion does not guarantee useful external-vision navigation. The local VIO origin may have no relationship to true north, and the external estimator may reset or drift. The PX4 VIO guidance treats source selection, delay, sensor position and yaw fusion as separate configuration concerns. The MAVLink message also carries timing and uncertainty metadata.

This distinction matters in fault analysis. A pose can have small reported covariance but wrong coordinate metadata; conversely, perfect frame conversion cannot repair a biased or delayed vision estimate. Neither problem is revealed by simply observing that MAVLink packets arrive and position values change.

What a useful integration test should show

Before allowing external-vision yaw into the flight estimator, an integrator can build an acceptance test in simulation around several known orientations. Start with a stationary, level vehicle, then check nonzero yaw in both directions and combined roll/pitch cases. Compare transformed quaternions or rotation matrices, not only the heading displayed on a ground station. Repeat with the intended frame_id and child_frame_id choices, and verify the mapping between raw MAVLink values and the logged vehicle_visual_odometry message.

For the ENU world position example, one expected transform is:

Incoming ENU position Expected NED position
(1 m east, 2 m north, 3 m up) (2 m north, 1 m east, -3 m down)

That vector check is necessary but insufficient. The corresponding attitude must describe the same physical aircraft after both the world and body frames have been transformed. A discrepancy should block closed-loop flight testing until its source is understood.

A regression suite should also retain explicit cases for estimator resets, stale timestamps and uncertain measurements. They are different faults from axis conversion, but can produce superficially similar symptoms in a position-hold test. The PX4 VIO test card provides a broader staged validation framework; its flight checks belong after bench and simulator evidence supports them.

What the October fix establishes

PX4 merged the code change into its main development history on 31 October 2026, according to PR #29327. The author reports numerical rotation-matrix comparisons and SIH tests. That is meaningful evidence for the specific code path, but it is not confirmation that every stable firmware branch or installed aircraft has the change, and it does not replace validation of a particular ROS/MAVLink bridge.

The general engineering lesson is narrower and more useful than a claim that an external-vision stack is now safe. Every odometry integration needs an explicit contract for world frame, body frame, message frame identifiers, orientation direction, timestamps and covariance. If position is transformed but attitude is not, a successful-looking navigation trace may be the last reassuring signal before yaw fusion exposes the mismatch.