Skip to the content
FixTheRobot Explore Robotics / UAV / Module 6 / M6-T11

UAV Systems Engineering · Module 6

M6-T11: Offboard control

Offboard mode hands the vehicle to your software, and PX4 will take it back the moment your software goes quiet. Build the handshake carefully.

  • Lesson 11 of 16
  • UAV Software and Flight-Controller Integration
  • Implementation-specific
  • About 5 minutes to read

Offboard control is how autonomy code on the companion flies the vehicle. The interface is deliberately strict: it only works while a stream of setpoints is arriving, which makes the link itself a safety mechanism. Understanding that rule turns most “it won’t arm” and “it dropped out of offboard” problems into one-line fixes.

Learning objectives

After this lesson, you should be able to:

  • Describe the offboard handshake and the role of the setpoint stream.
  • Choose the control mode flags in OffboardControlMode that match the setpoints being sent.
  • Send position and velocity setpoints in NED and handle the mode and arm commands.
  • List the failsafes that apply when the stream is lost.

Conceptual explanation

Implementation-specific This follows the PX4 offboard documentation and ROS 2 examples for recent releases. Message fields, parameter names and defaults vary between versions: check the documentation for the one you fly.

In offboard mode PX4 follows setpoints supplied by an external system instead of the pilot or a stored mission. Three ingredients are required:

  • OffboardControlMode messages say which quantities the external system controls (position, velocity, acceleration, attitude, body rate…).
  • A setpoint message of the matching type (for example TrajectorySetpoint for position, velocity or acceleration).
  • A continuous stream of both: PX4’s documentation states a rate above 2 Hz; in practice 10–50 Hz is typical. If the stream stops, PX4 exits offboard mode and applies its failsafe.
Offboard handshakeThe companion computer must stream OffboardControlMode and a setpoint continuously before PX4 will accept the switch to offboard mode. After the mode command and arm command the stream must continue; if it stops, PX4 leaves offboard mode and applies its failsafe.Companion computerPX4 OffboardControlMode + TrajectorySetpoint, streaming (> 2 Hz) VehicleCommand: set mode OFFBOARD, then ARM (after > 1 s of stream) vehicle_status: nav_state = OFFBOARD, arming_state = ARMED stream stops → PX4 leaves offboard mode (failsafe action)
Start streaming first, then switch mode and arm, then keep streaming.
Animation of the offboard handshake as a timeline. The companion starts streaming setpoints at 20 hertz. Before one second of streaming has elapsed, a mode-switch request is refused. After more than one second the request is accepted and the mode becomes offboard and the vehicle is armed. Later the setpoint stream stops for a stall; after the half-second timeout the vehicle leaves offboard and enters its failsafe mode, and the strips show the stream, the offboard mode state and the age of the last setpoint.
PX4 offboard rules: a setpoint stream above 2 Hz must exist before offboard can be entered, and it must continue; if it stops the vehicle leaves offboard after a timeout and applies the failsafe action. The stream is the heartbeat of the interface. Timings are illustrative (switch accepted after 1 s of stream, 0.5 s timeout); a model of the logic, not PX4 code. (Looping, about 6 seconds; drawn and computed for this course with illustrative values.)

Engineering intuition

The stream is both the command and the heartbeat. PX4 refuses to enter offboard mode without it, so a node that sends the mode command immediately at start-up is rejected: stream for a second or so first. It also means your autonomy software’s timing health is a flight-safety property: a node that stalls for 500 ms during a garbage-collection pause or an expensive planning step makes the vehicle drop out of offboard. Run the setpoint publisher in its own thread or node with its own timer, and let the planner feed it, instead of publishing from inside the planner.

Mathematical formulation

Position setpoints are in the NED frame with zz negative above the home position. A velocity setpoint in NED that is computed from a ROS ENU velocity vENU=(vE, vN, vU)v_{\text{ENU}}=(v_{E},\,v_{N},\,v_{U}) is (vN, vE, −vU)(v_{N},\,v_{E},\,-v_{U}). A smooth reference prevents the position controller from receiving unreachable steps; limit setpoint changes by the vehicle’s acceleration limit:

Rate-limited setpoint
psp(t+Δt)=psp(t)+clip⁡ ⁣(pgoal−psp(t), ±vmax⁡ Δt)p_{\text{sp}}(t + \Delta t) = p_{\text{sp}}(t) + \operatorname{clip}\!\left(p_{\text{goal}} - p_{\text{sp}}(t),\ \pm v_{\max}\,\Delta t\right)

Timeout arithmetic: with a stream at ff Hz and an allowed gap of 0.5 s PX4 tolerates losing about 0.5 ff consecutive messages: at 20 Hz, ten. Choose the rate so that a single dropped message does not matter and a stall does.

Architecture and data flow

Companion planner → trajectory setpoint publisher (own timer, constant rate) → /fmu/in/offboard_control_mode and /fmu/in/trajectory_setpoint → PX4 position controller → attitude → motors. Commands (arm, mode) go to /fmu/in/vehicle_command; status returns on /fmu/out/vehicle_status so the node can confirm the mode switched rather than assuming it did. Failsafe behaviour on loss of the offboard stream is set by PX4 parameters such as the offboard loss timeout and action (check the PX4 failsafe documentation for the parameter names of your version).

Real-world UAV applications

  • Running a custom planner or MPC on the companion computer.
  • Visual servoing and precision landing logic.
  • Executing waypoint missions generated by autonomy software in SITL and on hardware.
  • Research controllers that bypass PX4’s position loop by sending attitude or body-rate setpoints.

Implementation example

A minimal ROS 2 node that follows the documented pattern: stream for ten cycles, then set offboard mode and arm, then fly to a position. It is syntax-checked but not run by the tests (it needs ROS 2, px4_msgs and a PX4 instance); run it in SITL first. Check field and constant names against your px4_msgs version.

import math, rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy, HistoryPolicy
from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint, VehicleCommand

class OffboardDemo(Node):
    def __init__(self):
        super().__init__('offboard_demo')
        qos = QoSProfile(reliability=ReliabilityPolicy.BEST_EFFORT, durability=DurabilityPolicy.TRANSIENT_LOCAL,
                         history=HistoryPolicy.KEEP_LAST, depth=1)
        self.mode_pub = self.create_publisher(OffboardControlMode, '/fmu/in/offboard_control_mode', qos)
        self.sp_pub = self.create_publisher(TrajectorySetpoint, '/fmu/in/trajectory_setpoint', qos)
        self.cmd_pub = self.create_publisher(VehicleCommand, '/fmu/in/vehicle_command', qos)
        self.n = 0
        self.create_timer(0.05, self.tick)                      # 20 Hz stream, on its own timer

    def now_us(self):
        return int(self.get_clock().now().nanoseconds // 1000)

    def command(self, cmd, p1=0.0, p2=0.0):
        m = VehicleCommand()
        m.timestamp = self.now_us(); m.command = cmd; m.param1 = p1; m.param2 = p2
        m.target_system = 1; m.target_component = 1; m.source_system = 1; m.source_component = 1; m.from_external = True
        self.cmd_pub.publish(m)

    def tick(self):
        mode = OffboardControlMode(); mode.timestamp = self.now_us(); mode.position = True   # we control position only
        self.mode_pub.publish(mode)
        sp = TrajectorySetpoint(); sp.timestamp = self.now_us()
        sp.position = [0.0, 0.0, -5.0]                         # NED: 5 m above the home position
        sp.yaw = 0.0                                           # facing north
        self.sp_pub.publish(sp)
        if self.n == 20:                                       # after about 1 s of streaming
            self.command(VehicleCommand.VEHICLE_CMD_DO_SET_MODE, 1.0, 6.0)          # custom mode: offboard (see PX4 docs)
            self.command(VehicleCommand.VEHICLE_CMD_COMPONENT_ARM_DISARM, 1.0)
        self.n += 1

rclpy.init(); rclpy.spin(OffboardDemo())
Offboard handshake: stream first, then switch mode and arm, and keep streaming. Run in SITL first.

Common engineering mistakes

Sending the mode command before streaming

PX4 rejects offboard mode without an existing setpoint stream.

Publishing setpoints from a slow planner callback

A planning stall stops the stream and drops offboard mode: decouple the publisher.

Frame errors on setpoints

NED with zz negative up; a positive zz asks the vehicle to go underground.

Not checking vehicle_status

Assuming the mode switched; read back nav_state and arming_state.

No plan for stream loss

Decide and test what happens when the link or the node dies (hold, return, land).

Failure modes

Failure modes, symptoms and causes
Failure modeHow it shows upLikely cause
Will not enter offboardMode command ignoredNo stream, stream below the minimum rate, wrong control-mode flags, or pre-arm checks failing
Drops out of offboard in flightVehicle switches mode and hovers or returnsStream stalled (CPU load, GC pause, network)
Vehicle climbs or descends the wrong wayAltitude control invertedSign of zz in NED
Jerky motionVelocity steps or oscillationUnsmoothed setpoints; setpoint rate too low

Debugging approach

  1. Watch ros2 topic hz on the two /fmu/in topics: the rate must be steady.
  2. Read vehicle_status for nav_state and arming_state; check the PX4 console for the reason a mode switch was refused.
  3. Plot the setpoint against the measured local position in the log.
  4. Inject a deliberate stream pause in SITL and confirm the failsafe behaves as configured.

Practical exercises

  1. Modify the node to fly a 5 m square in NED, rate-limiting the setpoint to 2 m/s.
  2. Measure the longest stall of your node’s timer using a monotonic clock and compare with the offboard timeout.
  3. Switch the node to velocity control and set the matching flags in OffboardControlMode.
  4. Add a status subscriber that logs mode changes and prints a warning on leaving offboard.

Interview questions

Write an answer in your own words, then reveal the model answer. Nothing is sent anywhere.

Question 1

Why does PX4 require a setpoint stream before offboard mode?

Model answer

The stream proves the external controller is alive. PX4 refuses to enter offboard without it and exits offboard if it stops, giving a built-in failsafe against a dead companion.

Question 2

What does OffboardControlMode tell PX4?

Model answer

Which control quantities the external system supplies (position, velocity, acceleration, attitude, body rate…), so PX4 knows which controllers to run and which setpoint fields to use.

Question 3

How do you make offboard control robust against a slow planner?

Model answer

Publish setpoints from a dedicated fixed-rate timer or node, feed it from the planner through a buffer, and test deliberate stalls against the configured failsafe.

References and further reading

Primary papers, official documentation and standards. Each claim above that is specific to a tool or version points here.

What’s next

Mark this lesson

Saved in this browser and synced to your account when you are signed in.

Practice this lesson

M6-L5: Send position and velocity commands

Send offboard setpoints.

Next lesson

M6-T12: QGroundControl

Use QGroundControl for calibration, parameter management, mission planning and live monitoring of a vehicle.