I wanted to share an architectural pattern for handling autonomous drone navigation using ROS 2 (Jazzy) and PX4 Autopilot via Micro XRCE-DDS, specifically regarding the Offboard mode failsafe.
The Problem: When developing an autonomous flight node in C++, transitioning to PX4’s Offboard mode can be tricky. PX4 implements a strict failsafe: it will reject any VEHICLE_CMD_DO_SET_MODE to Offboard unless it detects a continuous stream of setpoints (e.g., TrajectorySetpoint and OffboardControlMode) at >2Hz prior to the request.
A common beginner mistake is using while loops with std::this_thread::sleep_for() to pre-load these setpoints. This is an anti-pattern in ROS 2 because it blocks the executor’s thread, preventing the node from processing incoming telemetry from the drone.
The Solution: Instead of blocking the thread, we implemented a non-blocking State Machine driven by the ROS 2 Executor using rclcpp::Node::create_wall_timer.
By setting a timer to fire exactly every 50ms (20Hz), we delegate the time-keeping to the Executor. Inside the timer’s single callback, we manage a state machine that handles the initialization sequence safely:
-
Pre-flight Heartbeat: The callback counts up to 20 cycles (1 second) publishing dummy setpoints to satisfy the PX4 failsafe.
-
Mode Switch: On the 21st cycle, it publishes the Offboard command.
-
Arming: It sends the Arm command.
-
Mission Loop: It continues publishing the actual trajectory setpoints at 20Hz without ever blocking the main thread.
Resources: rclcpp: rclcpp::node::Node Class Reference , Offboard Mode | PX4 User Guide , Create wall timer using callback with parameters.