Building an AI drone software stack requires orchestrating low-level flight control with high-level perception and spatial navigation. The core architecture relies on ROS 2 as the middleware connecting your edge compute hardware (e.g., NVIDIA Jetson Orin) to a flight controller running PX4 Autopilot or ArduPilot.

Core System Architecture

 [Sensors: RGB-D / LiDAR / IMU]
               │
               ▼
   [ROS 2 Node: OpenCV / TensorRT] ──► (Inference & Tracking)
               │
               ▼
   [ROS 2 Node: Nav2 / VIO / SLAM] ──► (Pose & Path Planning)
               │
               ▼  (Micro XRCE-DDS / MAVROS)
   [PX4 / ArduPilot Flight Controller]
               │
               ▼
    [Motors & Actuators]

Software Layer Breakdown

1. Perception & Computer Vision Pipeline

2. State Estimation & GPS-Denied Navigation

3. Flight Controller Bridge & Offboard Control

Minimal ROS 2 Offboard Command Node (Python)

Python

import rclpy
from rclpy.node import Node
from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint, VehicleCommand

class AIAutonomousDrone(Node):
    def __init__(self):
        super().__init__('ai_drone_offboard_node')
        
        # Publishers to PX4 via Micro XRCE-DDS
        self.offboard_control_mode_pub = self.create_publisher(
            OffboardControlMode, '/fmu/in/offboard_control_mode', 10)
        self.trajectory_setpoint_pub = self.create_publisher(
            TrajectorySetpoint, '/fmu/in/trajectory_setpoint', 10)
        self.vehicle_command_pub = self.create_publisher(
            VehicleCommand, '/fmu/in/vehicle_command', 10)

        # 20Hz Loop for streaming setpoints to PX4
        self.timer = self.create_timer(0.05, self.cmd_loop)
        self.offboard_counter = 0

    def cmd_loop(self):
        # 1. Publish Heartbeat required by PX4 for Offboard Mode
        offboard_msg = OffboardControlMode()
        offboard_msg.position = True
        offboard_msg.timestamp = int(self.get_clock().now().nanoseconds / 1000)
        self.offboard_control_mode_pub.publish(offboard_msg)

        # 2. Set Target Position (x, y, z in NED frame)
        setpoint = TrajectorySetpoint()
        setpoint.position = [0.0, 0.0, -2.0]  # Hover at 2m altitude
        setpoint.timestamp = int(self.get_clock().now().nanoseconds / 1000)
        self.trajectory_setpoint_pub.publish(setpoint)

        # 3. Request Offboard mode after establishing setpoint stream
        if self.offboard_counter == 10:
            self.send_vehicle_command(VehicleCommand.VEHICLE_CMD_DO_SET_MODE, 1.0, 6.0)
            self.arm()
        if self.offboard_counter < 11:
            self.offboard_counter += 1

    def arm(self):
        self.send_vehicle_command(VehicleCommand.VEHICLE_CMD_COMPONENT_ARM_DISARM, 1.0)

    def send_vehicle_command(self, command, param1=0.0, param2=0.0):
        cmd = VehicleCommand()
        cmd.command = command
        cmd.param1 = param1
        cmd.param2 = param2
        cmd.target_system = 1
        cmd.target_component = 1
        cmd.from_external = True
        cmd.timestamp = int(self.get_clock().now().nanoseconds / 1000)
        self.vehicle_command_pub.publish(cmd)

def main(args=None):
    rclpy.init(args=args)
    node = AIAutonomousDrone()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

Development & Simulation Tools