Executive Mode Executor - Weber-State-UAV-Program/Documentation-2024-2025 GitHub Wiki

The Executive Mode Executor is responsible for managing and activating the difference flight modes that are necessary to complete a full mission sequence. It becomes active when the Mission - Precision Landing mode is activated from QGroundControl.

There are six different modes that the mode executor runs through in order to complete a mission sequence. As illustrated in the Executive Mode Executor FSM shown below

Upon activation, the mode executor begins running the above state machine. The state machine is implemented with a runState() function that accepts the result of the previous state, as well as which state to run.

  void onActivate() override
  {
    runState(State::OpenDoors, px4_ros2::Result::Success);
  }

This onActivate() function is overridden from the ROS2-PX4 interface libraries ModeExecutorBase class. It is called when the mode is activated from QGroundControl.

runState()

The runState() function is called each time a state or mode needs to be run. When a mode is called, it is passed a callback function that is executed upon completion of that mode. This allows the mode to take over control, and then run the next mode when it is complete.


  void runState(State state, px4_ros2::Result previous_result)
  {
    if (previous_result != px4_ros2::Result::Success) {
      RCLCPP_ERROR(
        _node.get_logger(), "State %i: previous state failed: %s", (int)state,
        resultToString(previous_result));
      return;
    }

    RCLCPP_DEBUG(_node.get_logger(), "Executing state %i", (int)state);

    switch (state) {
      case State::Reset:
        break;

      case State::OpenDoors: {
        RCLCPP_INFO(_node.get_logger(), "Opening doors...");

        _open_doors_callback = [this](px4_ros2::Result result) {
          runState(State::Arm, result);
        };
        
        _open_doors_timer = _node.create_wall_timer(
          100ms,
          std::bind(&MissionWithPrecisionLanding::openDoorsTimerCallback, this)
        );
        break;
      }
 
      case State::Arm: {
        _arm_attempts = 0;
        tryArm([this](px4_ros2::Result result) {
          runState(State::Mission, result);
        });
        break;
      }
  
      case State::Mission:
        scheduleMode(
          _mission_id, [this](px4_ros2::Result result) {
            runState(State::PrecisionLand, result);
          });
        break;

      case State::PrecisionLand:
        scheduleMode(
          ownedMode().id(), [this](px4_ros2::Result result) {
            runState(State::CloseDoors, result);
          });
        break;

      case State::WaitUntilDisarmed:
        waitUntilDisarmed(
          [this](px4_ros2::Result result) {
            runState(State::CloseDoors, result);
          });
        break;

      case State::CloseDoors: {
        RCLCPP_INFO(_node.get_logger(), "Closing doors...");

        _close_doors_callback = [this](px4_ros2::Result result) {
          RCLCPP_INFO(_node.get_logger(), "All states complete (%s)", resultToString(result));
        };
        
        _close_doors_timer = _node.create_wall_timer(
          100ms,
          std::bind(&MissionWithPrecisionLanding::closeDoorsTimerCallback, this)
        );
        break;
      }
    }
  }

Modes are activated by using the scheduleMode method. You must pass the ID of the mode to be activated, as well as a callback to execute upon completion of the mode. In this case, we are activating mission mode

scheduleMode(
  _mission_id, [this](px4_ros2::Result result) {
  runState(State::PrecisionLand, result);
});

INFO: More information on mode executors and custom modes can be found here

INFO: Some examples and additional information on using the ROS2-PX4 Interface library can be found here