Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/PX4/PX4-Autopilot/llms.txt

Use this file to discover all available pages before exploring further.

uORB (Micro Object Request Broker) is PX4’s internal publish-subscribe message bus. Every piece of data that flows between modules — sensor readings, state estimates, setpoints, actuator commands — travels as a typed uORB message over this bus. The design is asynchronous: publishers write new data whenever it is ready, and subscribers are notified immediately without polling. This keeps the system reactive and ensures modules stay decoupled from one another.

How uORB works

uORB operates through shared memory. All PX4 modules run in a single address space (on NuttX), so publishing a message is a fast, lock-minimized write to a shared buffer. Subscribers register interest in a topic by its ID and can either poll for updates or be woken up by a callback when new data arrives. The bus is started automatically early in the PX4 boot sequence with uorb start. You can inspect live topic activity at any time:
# Show all active topics and their publish rates
uorb top

# Show detailed info for a specific topic
uorb status vehicle_local_position

Message definitions

Every uORB topic is defined by a .msg file in the msg/ directory. The build system automatically generates the C/C++ headers from these definitions, so you never write serialization code by hand. A message definition file uses CamelCase naming (e.g., VehicleLocalPosition.msg) and maps to a snake_case topic ID (vehicle_local_position). The format is straightforward:
# Vehicle local position estimate in NED frame.

uint64 timestamp          # [us] Time since system start (required).
uint64 timestamp_sample   # [us] Timestamp of the data sample.

bool xy_valid             # True if x and y position estimates are valid.
bool z_valid              # True if z position estimate is valid.
bool v_xy_valid           # True if x and y velocity estimates are valid.
bool v_z_valid            # True if z velocity estimate is valid.

float32 x                 # [m] North position in NED frame.
float32 y                 # [m] East position in NED frame.
float32 z                 # [m] Down position in NED frame.

float32 vx                # [m/s] North velocity.
float32 vy                # [m/s] East velocity.
float32 vz                # [m/s] Down velocity.
Every message definition must include a uint64 timestamp field, and publishers must populate it before calling orb_publish(). The logger requires this field to record topics correctly.

Supported field types

TypeDescription
boolBoolean (true/false)
uint8, int88-bit integers
uint16, int1616-bit integers
uint32, int3232-bit integers
uint64, int6464-bit integers
float3232-bit IEEE float
float6464-bit IEEE float
charSingle character
TypeName[N]Fixed-size array of N elements

Multi-topic messages

One message definition can produce several topic IDs. This is useful when the same data structure is shared by multiple subsystems — for example, ActuatorOutputs.msg defines three topics at once:
# TOPICS actuator_outputs actuator_outputs_sim actuator_outputs_debug

Nested messages

Message definitions can reference other message types to build complex structures:
# PositionSetpointTriplet.msg
uint64 timestamp
PositionSetpoint previous
PositionSetpoint current
PositionSetpoint next

Subscribing in C++

The recommended subscription API uses the uORB::Subscription template class. Call update() in your module’s update loop — it returns true only when new data has arrived since the last call.
#include <uORB/topics/vehicle_local_position.h>

uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)};
vehicle_local_position_s local_pos{};

if (_vehicle_local_position_sub.update(&local_pos)) {
    // New data is available — local_pos is populated.
    float north = local_pos.x;
    float east  = local_pos.y;
    float down  = local_pos.z;
}
For modules that run on a work queue and need to wake up only when a specific topic changes, use uORB::SubscriptionCallbackWorkItem:
#include <uORB/SubscriptionCallback.hpp>
#include <uORB/topics/sensor_combined.h>

uORB::SubscriptionCallbackWorkItem _sensor_sub{this, ORB_ID(sensor_combined)};

bool MyModule::init()
{
    // Register callback: the work item is scheduled whenever sensor_combined is published.
    if (!_sensor_sub.registerCallback()) {
        return false;
    }
    return true;
}

Publishing in C++

To publish a topic, declare a uORB::Publication and call publish() with your populated struct:
#include <uORB/Publication.hpp>
#include <uORB/topics/vehicle_attitude.h>

uORB::Publication<vehicle_attitude_s> _attitude_pub{ORB_ID(vehicle_attitude)};

vehicle_attitude_s att{};
att.timestamp = hrt_absolute_time();
att.q[0] = w;
att.q[1] = x;
att.q[2] = y;
att.q[3] = z;

_attitude_pub.publish(att);
Always set timestamp using hrt_absolute_time() immediately before publishing. This gives the logger and downstream subscribers accurate timing information.

Key topics reference

Published by ekf2. Contains the vehicle’s estimated position and velocity in the local NED (North-East-Down) frame.Key fields: x, y, z (position in meters), vx, vy, vz (velocity in m/s), xy_valid, z_valid, v_xy_valid, v_z_valid.Consumed by: mc_pos_control, fw_pos_control, navigator, mavlink (telemetry stream).
Published by ekf2 (and attitude_estimator_q as a fallback). Contains the vehicle’s estimated orientation as a quaternion.Key fields: q[4] (unit quaternion, w-x-y-z order), timestamp, timestamp_sample.Consumed by: mc_att_control, fw_att_control, mavlink.
Published by the sensor module. Contains pre-processed, calibrated IMU data (accelerometer and gyroscope) from the primary sensor.Key fields: gyro_rad[3] (rad/s), accelerometer_m_s2[3] (m/s²), timestamp, gyro_integral_dt, accelerometer_integral_dt.Consumed by: ekf2, attitude_estimator_q, logging.
Published by the output driver after mixing. Contains the final normalized output values (typically 0–1 for motors, -1–1 for servos) sent to hardware.Defined by multi-topic message: also creates actuator_outputs_sim and actuator_outputs_debug.Consumed by: output drivers (PWM, DroneCAN), simulation bridges, logging.
Published by the actuator allocation module. Contains the desired motor throttle and servo positions before output driver remapping. These are the post-mixing, pre-output-driver commands.Consumed by: PWM output driver, DroneCAN output driver.

Multi-instance topics

Some topics support multiple instances — for example, when a vehicle has more than one GPS receiver. Use uORB::SubscriptionMultiArray to subscribe to all instances simultaneously:
#include <uORB/SubscriptionMultiArray.hpp>
#include <uORB/topics/sensor_gps.h>

uORB::SubscriptionMultiArray<sensor_gps_s> _gps_subs{ORB_ID::sensor_gps};

for (auto &sub : _gps_subs) {
    sensor_gps_s gps{};
    if (sub.update(&gps)) {
        // Process this GPS instance.
    }
}
To publish to a specific instance, pass the instance index to uORB::PublicationMulti:
uORB::PublicationMulti<sensor_gps_s> _gps_pub{ORB_ID(sensor_gps)};

Adding a new topic

1

Create the .msg file

Create a new file in msg/ using CamelCase naming, e.g., MyNewTopic.msg. Include uint64 timestamp as the first field, then define your data fields with inline comments.
# Brief description of what this topic carries.

uint64 timestamp    # [us] Time since system start.
float32 value_a     # Description of value_a.
float32 value_b     # Description of value_b.
2

Register in CMakeLists.txt

Add your new .msg filename to msg/CMakeLists.txt in the alphabetically sorted list of message files.
set(msg_files
    ...
    MyNewTopic.msg
    ...
)
3

Include the generated header

After building, include the auto-generated header in your module source. The header name is the snake_case version of the message name.
#include <uORB/topics/my_new_topic.h>
4

Publish or subscribe

Declare a uORB::Publication or uORB::Subscription using ORB_ID(my_new_topic) and use it in your module’s run loop.

Build docs developers (and LLMs) love