Program Listing for File pid_speed_controller.cpp

Return to documentation for file (/tmp/ws/src/aerostack2/as2_motion_controller/plugins/pid_speed_controller/src/pid_speed_controller.cpp)

// Copyright 2023 Universidad Politécnica de Madrid
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the Universidad Politécnica de Madrid nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

/*!*******************************************************************************************
 *  \file       pid_speed_controller_plugin.cpp
 *  \brief      Speed PID controller plugin for the Aerostack framework.
 *  \authors    Rafael Pérez Seguí
 *              Miguel Fernández Cortizas
 ********************************************************************************************/

#include "pid_speed_controller.hpp"

namespace pid_speed_controller
{

void Plugin::ownInitialize()
{
  speed_limits_ = Eigen::Vector3d::Zero();

  tf_handler_ = std::make_shared<as2::tf::TfHandler>(node_ptr_);

  enu_frame_id_ = as2::tf::generateTfName(node_ptr_, enu_frame_id_);
  flu_frame_id_ = as2::tf::generateTfName(node_ptr_, flu_frame_id_);

  input_pose_frame_id_ = as2::tf::generateTfName(node_ptr_, input_pose_frame_id_);
  input_twist_frame_id_ = as2::tf::generateTfName(node_ptr_, input_twist_frame_id_);

  output_twist_frame_id_ = as2::tf::generateTfName(node_ptr_, output_twist_frame_id_);

  reset();
  return;
}

void Plugin::checkParamList(
  const std::string & param,
  std::vector<std::string> & _params_list,
  bool & _all_params_read)
{
  if (find(_params_list.begin(), _params_list.end(), param) != _params_list.end()) {
    // Remove the parameter from the list of parameters to be read
    _params_list.erase(
      std::remove(_params_list.begin(), _params_list.end(), param),
      _params_list.end());
  }
  if (_params_list.size() == 0) {
    _all_params_read = true;
  }
}

bool Plugin::updateParams(const std::vector<rclcpp::Parameter> & parameters)
{
  for (auto & param : parameters) {
    std::string param_name = param.get_name();

    RCLCPP_DEBUG(node_ptr_->get_logger(), "Updating parameter %s", param_name.c_str());

    if (param.get_name() == "proportional_limitation") {
      proportional_limitation_ = param.get_value<bool>();
      if (!flags_.plugin_parameters_read) {
        checkParamList(param_name, plugin_parameters_to_read_, flags_.plugin_parameters_read);
      }
    } else if (param.get_name() == "use_bypass") {
      use_bypass_ = param.get_value<bool>();
      if (!flags_.plugin_parameters_read) {
        checkParamList(param_name, plugin_parameters_to_read_, flags_.plugin_parameters_read);
      }
    } else {
      std::string controller = param_name.substr(0, param_name.find("."));
      std::string param_subname = param_name.substr(param_name.find(".") + 1);
      if (controller == "position_control") {
        updateController3DParameter(pid_3D_position_handler_, param_subname, param);
        if (!flags_.position_controller_parameters_read) {
          checkParamList(
            param_name, position_control_parameters_to_read_,
            flags_.position_controller_parameters_read);
        }
      } else if (controller == "speed_control") {
        updateController3DParameter(pid_3D_velocity_handler_, param_subname, param);
        if (!flags_.velocity_controller_parameters_read) {
          checkParamList(
            param_name, velocity_control_parameters_to_read_,
            flags_.velocity_controller_parameters_read);
        }
      } else if (controller == "speed_in_a_plane_control") {
        updateSpeedInAPlaneParameter(
          pid_1D_speed_in_a_plane_handler_,
          pid_3D_speed_in_a_plane_handler_, param_subname, param);
        if (!flags_.speed_in_a_plane_controller_parameters_read) {
          checkParamList(
            param_name, speed_in_a_plane_control_parameters_to_read_,
            flags_.speed_in_a_plane_controller_parameters_read);
        }
      } else if (controller == "trajectory_control") {
        updateController3DParameter(pid_3D_trajectory_handler_, param_subname, param);
        if (!flags_.trajectory_controller_parameters_read) {
          checkParamList(
            param_name, trajectory_control_parameters_to_read_,
            flags_.trajectory_controller_parameters_read);
        }
      } else if (controller == "yaw_control") {
        updateControllerParameter(pid_yaw_handler_, param_subname, param);
        if (!flags_.yaw_controller_parameters_read) {
          checkParamList(
            param_name, yaw_control_parameters_to_read_,
            flags_.yaw_controller_parameters_read);
        }
      }
    }
  }
  return true;
}

void Plugin::updateControllerParameter(
  PID_1D & _pid_handler,
  const std::string & _parameter_name,
  const rclcpp::Parameter & _param)
{
  if (_parameter_name == "reset_integral") {
    _pid_handler.set_reset_integral_saturation_flag(_param.get_value<bool>());
  } else if (_parameter_name == "antiwindup_cte") {
    _pid_handler.set_anti_windup(_param.get_value<double>());
  } else if (_parameter_name == "alpha") {
    _pid_handler.set_alpha(_param.get_value<double>());
  } else if (_parameter_name == "kp") {
    double kp, ki, kd;
    _pid_handler.get_gains(kp, ki, kd);
    _pid_handler.set_gains(_param.get_value<double>(), ki, kd);
  } else if (_parameter_name == "ki") {
    double kp, ki, kd;
    _pid_handler.get_gains(kp, ki, kd);
    _pid_handler.set_gains(kp, _param.get_value<double>(), kd);
  } else if (_parameter_name == "kd") {
    double kp, ki, kd;
    _pid_handler.get_gains(kp, ki, kd);
    _pid_handler.set_gains(kp, ki, _param.get_value<double>());
  }
  return;
}

void Plugin::updateController3DParameter(
  PID & _pid_handler,
  const std::string & _parameter_name,
  const rclcpp::Parameter & _param)
{
  if (_parameter_name == "reset_integral") {
    _pid_handler.set_reset_integral_saturation_flag(_param.get_value<bool>());
  } else if (_parameter_name == "antiwindup_cte") {
    Eigen::Vector3d anti_windup = Eigen::Vector3d::Constant(_param.get_value<double>());
    _pid_handler.set_anti_windup(anti_windup);
  } else if (_parameter_name == "alpha") {
    Eigen::Vector3d alpha = Eigen::Vector3d::Constant(_param.get_value<double>());
    _pid_handler.set_alpha(alpha);
  } else if (_parameter_name == "kp.x") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_kp();
    current_gains.x() = _param.get_value<double>();
    _pid_handler.set_gains_kp(current_gains);
  } else if (_parameter_name == "kp.y") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_kp();
    current_gains.y() = _param.get_value<double>();
    _pid_handler.set_gains_kp(current_gains);
  } else if (_parameter_name == "kp.z") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_kp();
    current_gains.z() = _param.get_value<double>();
    _pid_handler.set_gains_kp(current_gains);
  } else if (_parameter_name == "ki.x") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_ki();
    current_gains.x() = _param.get_value<double>();
    _pid_handler.set_gains_ki(current_gains);
  } else if (_parameter_name == "ki.y") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_ki();
    current_gains.y() = _param.get_value<double>();
    _pid_handler.set_gains_ki(current_gains);
  } else if (_parameter_name == "ki.z") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_ki();
    current_gains.z() = _param.get_value<double>();
    _pid_handler.set_gains_ki(current_gains);
  } else if (_parameter_name == "kd.x") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_kd();
    current_gains.x() = _param.get_value<double>();
    _pid_handler.set_gains_kd(current_gains);
  } else if (_parameter_name == "kd.y") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_kd();
    current_gains.y() = _param.get_value<double>();
    _pid_handler.set_gains_kd(current_gains);
  } else if (_parameter_name == "kd.z") {
    Eigen::Vector3d current_gains = _pid_handler.get_gains_kd();
    current_gains.z() = _param.get_value<double>();
    _pid_handler.set_gains_kd(current_gains);
  }
  return;
}

void Plugin::updateSpeedInAPlaneParameter(
  PID_1D & _pid_1d_handler,
  PID & _pid_3d_handler,
  const std::string & _parameter_name,
  const rclcpp::Parameter & _param)
{
  if (_parameter_name == "reset_integral") {
    _pid_1d_handler.set_reset_integral_saturation_flag(_param.get_value<bool>());
    _pid_3d_handler.set_reset_integral_saturation_flag(_param.get_value<bool>());
  } else if (_parameter_name == "antiwindup_cte") {
    _pid_1d_handler.set_anti_windup(_param.get_value<double>());
    Eigen::Vector3d anti_windup = Eigen::Vector3d::Constant(_param.get_value<double>());
    _pid_3d_handler.set_alpha(anti_windup);
  } else if (_parameter_name == "alpha") {
    _pid_1d_handler.set_alpha(_param.get_value<double>());
    Eigen::Vector3d alpha = Eigen::Vector3d::Constant(_param.get_value<double>());
    _pid_3d_handler.set_alpha(alpha);
  } else if (_parameter_name == "height.kp") {
    double kp, ki, kd;
    _pid_1d_handler.get_gains(kp, ki, kd);
    _pid_1d_handler.set_gains(_param.get_value<double>(), ki, kd);
  } else if (_parameter_name == "height.ki") {
    double kp, ki, kd;
    _pid_1d_handler.get_gains(kp, ki, kd);
    _pid_1d_handler.set_gains(kp, _param.get_value<double>(), kd);
  } else if (_parameter_name == "height.kd") {
    double kp, ki, kd;
    _pid_1d_handler.get_gains(kp, ki, kd);
    _pid_1d_handler.set_gains(kp, ki, _param.get_value<double>());
  } else if (_parameter_name == "speed.kp.x") {
    Eigen::Vector3d current_gains = _pid_3d_handler.get_gains_kp();
    current_gains.x() = _param.get_value<double>();
    _pid_3d_handler.set_gains_kp(current_gains);
  } else if (_parameter_name == "speed.kp.y") {
    Eigen::Vector3d current_gains = _pid_3d_handler.get_gains_kp();
    current_gains.y() = _param.get_value<double>();
    _pid_3d_handler.set_gains_kp(current_gains);
  } else if (_parameter_name == "speed.ki.x") {
    Eigen::Vector3d current_gains = _pid_3d_handler.get_gains_ki();
    current_gains.x() = _param.get_value<double>();
    _pid_3d_handler.set_gains_ki(current_gains);
  } else if (_parameter_name == "speed.ki.y") {
    Eigen::Vector3d current_gains = _pid_3d_handler.get_gains_ki();
    current_gains.y() = _param.get_value<double>();
    _pid_3d_handler.set_gains_ki(current_gains);
  } else if (_parameter_name == "speed.kd.x") {
    Eigen::Vector3d current_gains = _pid_3d_handler.get_gains_kd();
    current_gains.x() = _param.get_value<double>();
    _pid_3d_handler.set_gains_kd(current_gains);
  } else if (_parameter_name == "speed.kd.y") {
    Eigen::Vector3d current_gains = _pid_3d_handler.get_gains_kd();
    current_gains.y() = _param.get_value<double>();
    _pid_3d_handler.set_gains_kd(current_gains);
  }
  return;
}

void Plugin::reset()
{
  resetReferences();
  resetState();
  resetCommands();
  pid_yaw_handler_.reset_controller();
  pid_3D_position_handler_.reset_controller();
  pid_3D_velocity_handler_.reset_controller();
  pid_3D_trajectory_handler_.reset_controller();
  // Info: Yaw rate limit could be set if needed
  // pid_yaw_handler_.set_output_saturation(yaw_speed_limit_);
}

void Plugin::resetState()
{
  uav_state_ = UAV_state();
  return;
}

void Plugin::resetReferences()
{
  control_ref_.position = uav_state_.position;
  control_ref_.velocity = Eigen::Vector3d::Zero();
  control_ref_.yaw = uav_state_.yaw;
  return;
}

void Plugin::resetCommands()
{
  control_command_.velocity = Eigen::Vector3d::Zero();
  control_command_.yaw_speed = 0.0;
  return;
}

void Plugin::updateState(
  const geometry_msgs::msg::PoseStamped & pose_msg,
  const geometry_msgs::msg::TwistStamped & twist_msg)
{
  if (pose_msg.header.frame_id != input_pose_frame_id_ &&
    twist_msg.header.frame_id != input_twist_frame_id_)
  {
    RCLCPP_ERROR(node_ptr_->get_logger(), "Pose and Twist frame_id are not desired ones");
    RCLCPP_ERROR(
      node_ptr_->get_logger(), "Recived: %s, %s", pose_msg.header.frame_id.c_str(),
      twist_msg.header.frame_id.c_str());
    RCLCPP_ERROR(
      node_ptr_->get_logger(), "Desired: %s, %s", input_pose_frame_id_.c_str(),
      input_twist_frame_id_.c_str());
    return;
  }

  uav_state_.position =
    Eigen::Vector3d(pose_msg.pose.position.x, pose_msg.pose.position.y, pose_msg.pose.position.z);
  uav_state_.velocity =
    Eigen::Vector3d(twist_msg.twist.linear.x, twist_msg.twist.linear.y, twist_msg.twist.linear.z);
  uav_state_.yaw.x() = as2::frame::getYawFromQuaternion(pose_msg.pose.orientation);

  if (hover_flag_) {
    resetReferences();
    flags_.ref_received = true;
    hover_flag_ = false;
  }

  flags_.state_received = true;
  return;
}

void Plugin::updateReference(const geometry_msgs::msg::PoseStamped & pose_msg)
{
  if (control_mode_in_.control_mode == as2_msgs::msg::ControlMode::POSITION ||
    control_mode_in_.control_mode == as2_msgs::msg::ControlMode::SPEED_IN_A_PLANE)
  {
    control_ref_.position = Eigen::Vector3d(
      pose_msg.pose.position.x, pose_msg.pose.position.y,
      pose_msg.pose.position.z);
    flags_.ref_received = true;
  }

  if ((control_mode_in_.control_mode == as2_msgs::msg::ControlMode::SPEED ||
    control_mode_in_.control_mode == as2_msgs::msg::ControlMode::POSITION ||
    control_mode_in_.control_mode == as2_msgs::msg::ControlMode::SPEED_IN_A_PLANE) &&
    control_mode_in_.yaw_mode == as2_msgs::msg::ControlMode::YAW_ANGLE)
  {
    control_ref_.yaw.x() = as2::frame::getYawFromQuaternion(pose_msg.pose.orientation);
  }

  return;
}

void Plugin::updateReference(const geometry_msgs::msg::TwistStamped & twist_msg)
{
  if (control_mode_in_.control_mode == as2_msgs::msg::ControlMode::POSITION) {
    speed_limits_ = Eigen::Vector3d(
      twist_msg.twist.linear.x, twist_msg.twist.linear.y,
      twist_msg.twist.linear.z);
    pid_3D_position_handler_.set_output_saturation(
      speed_limits_, -speed_limits_,
      proportional_limitation_);
    pid_3D_velocity_handler_.set_output_saturation(
      speed_limits_, -speed_limits_,
      proportional_limitation_);
    pid_3D_trajectory_handler_.set_output_saturation(
      speed_limits_, -speed_limits_,
      proportional_limitation_);
    return;
  }

  if (control_mode_in_.control_mode != as2_msgs::msg::ControlMode::SPEED &&
    control_mode_in_.control_mode != as2_msgs::msg::ControlMode::SPEED_IN_A_PLANE)
  {
    return;
  }

  control_ref_.velocity =
    Eigen::Vector3d(twist_msg.twist.linear.x, twist_msg.twist.linear.y, twist_msg.twist.linear.z);

  if (control_mode_in_.yaw_mode == as2_msgs::msg::ControlMode::YAW_SPEED) {
    control_ref_.yaw.y() = twist_msg.twist.angular.z;
  }

  flags_.ref_received = true;
  return;
}

void Plugin::updateReference(const as2_msgs::msg::TrajectoryPoint & traj_msg)
{
  if (control_mode_in_.control_mode != as2_msgs::msg::ControlMode::TRAJECTORY) {
    return;
  }

  control_ref_.position =
    Eigen::Vector3d(traj_msg.position.x, traj_msg.position.y, traj_msg.position.z);

  control_ref_.velocity = Eigen::Vector3d(traj_msg.twist.x, traj_msg.twist.y, traj_msg.twist.z);

  control_ref_.yaw.x() = traj_msg.yaw_angle;

  flags_.ref_received = true;
  return;
}

bool Plugin::setMode(
  const as2_msgs::msg::ControlMode & in_mode,
  const as2_msgs::msg::ControlMode & out_mode)
{
  if (!flags_.plugin_parameters_read) {
    RCLCPP_WARN(node_ptr_->get_logger(), "Plugin parameters not read yet, can not set mode");
    return false;
  }

  if (!flags_.position_controller_parameters_read) {
    RCLCPP_WARN(
      node_ptr_->get_logger(),
      "Position controller parameters not read, can not set mode");
    for (auto & param : position_control_parameters_to_read_) {
      RCLCPP_WARN(node_ptr_->get_logger(), "Parameter %s not read", param.c_str());
    }
    return false;
  }

  if (in_mode.control_mode == as2_msgs::msg::ControlMode::TRAJECTORY &&
    !flags_.trajectory_controller_parameters_read)
  {
    RCLCPP_WARN(
      node_ptr_->get_logger(),
      "Trajectory controller parameters not read yet, can not set mode to TRAJECTORY");
    for (auto & param : trajectory_control_parameters_to_read_) {
      RCLCPP_WARN(node_ptr_->get_logger(), "Parameter %s not read", param.c_str());
    }
    return false;
    // TODO(RPS98): Check lint
  } else if ((in_mode.control_mode == as2_msgs::msg::ControlMode::SPEED || // NOLINT
    in_mode.control_mode == as2_msgs::msg::ControlMode::SPEED_IN_A_PLANE) &&
    (!flags_.velocity_controller_parameters_read && !use_bypass_))
  {
    RCLCPP_WARN(
      node_ptr_->get_logger(),
      "Velocity controller parameters not read yet and bypass is not used, can not set "
      "mode to SPEED or SPEED_IN_A_PLANE");
    if (in_mode.control_mode == as2_msgs::msg::ControlMode::SPEED) {
      for (auto & param : velocity_control_parameters_to_read_) {
        RCLCPP_WARN(node_ptr_->get_logger(), "Parameter %s not read", param.c_str());
      }
    } else {
      for (auto & param : speed_in_a_plane_control_parameters_to_read_) {
        RCLCPP_WARN(node_ptr_->get_logger(), "Parameter %s not read", param.c_str());
      }
    }
    return false;
  }

  if (in_mode.yaw_mode == as2_msgs::msg::ControlMode::YAW_ANGLE &&
    !flags_.yaw_controller_parameters_read)
  {
    RCLCPP_WARN(
      node_ptr_->get_logger(),
      "Yaw controller parameters not read yet, can not set mode to YAW_ANGLE");
    return false;
  }

  if (in_mode.control_mode == as2_msgs::msg::ControlMode::HOVER) {
    control_mode_in_.control_mode = in_mode.control_mode;
    control_mode_in_.yaw_mode = as2_msgs::msg::ControlMode::YAW_ANGLE;
    control_mode_in_.reference_frame = as2_msgs::msg::ControlMode::LOCAL_ENU_FRAME;
    hover_flag_ = true;
  } else {
    control_mode_in_ = in_mode;
  }

  flags_.state_received = false;
  flags_.ref_received = false;
  control_mode_out_ = out_mode;

  if (control_mode_in_.control_mode == as2_msgs::msg::ControlMode::HOVER ||
    control_mode_in_.control_mode == as2_msgs::msg::ControlMode::POSITION ||
    control_mode_in_.control_mode == as2_msgs::msg::ControlMode::TRAJECTORY)
  {
    input_pose_frame_id_ = enu_frame_id_;
    output_twist_frame_id_ = enu_frame_id_;
    // TODO(RPS98): Check lint
  } else if (control_mode_in_.control_mode == as2_msgs::msg::ControlMode::SPEED || // NOLINT
    control_mode_in_.control_mode == as2_msgs::msg::ControlMode::SPEED_IN_A_PLANE)
  {
    input_pose_frame_id_ = enu_frame_id_;
    switch (control_mode_out_.reference_frame) {
      case as2_msgs::msg::ControlMode::BODY_FLU_FRAME:
        input_twist_frame_id_ = flu_frame_id_;
        output_twist_frame_id_ = flu_frame_id_;
        break;
      case as2_msgs::msg::ControlMode::LOCAL_ENU_FRAME:
      default:
        input_twist_frame_id_ = enu_frame_id_;
        output_twist_frame_id_ = enu_frame_id_;
        break;
    }
  }

  return true;
}

std::string Plugin::getDesiredPoseFrameId() {return input_pose_frame_id_;}

std::string Plugin::getDesiredTwistFrameId() {return input_twist_frame_id_;}

bool Plugin::computeOutput(
  double dt,
  geometry_msgs::msg::PoseStamped & pose,
  geometry_msgs::msg::TwistStamped & twist,
  as2_msgs::msg::Thrust & thrust)
{
  if (!flags_.state_received) {
    auto & clk = *node_ptr_->get_clock();
    RCLCPP_WARN_THROTTLE(node_ptr_->get_logger(), clk, 5000, "State not received yet");
    return false;
  }

  if (!flags_.ref_received) {
    auto & clk = *node_ptr_->get_clock();
    RCLCPP_WARN_THROTTLE(
      node_ptr_->get_logger(), clk, 5000,
      "State changed, but ref not recived yet");
    return false;
  }

  resetCommands();

  switch (control_mode_in_.control_mode) {
    case as2_msgs::msg::ControlMode::HOVER:
    case as2_msgs::msg::ControlMode::POSITION: {
        Eigen::Vector3d position_error =
          pid_3D_position_handler_.get_error(uav_state_.position, control_ref_.position);
        control_command_.velocity = pid_3D_position_handler_.compute_control(dt, position_error);
        break;
      }
    case as2_msgs::msg::ControlMode::SPEED: {
        if (use_bypass_) {
          control_command_.velocity = control_ref_.velocity;
        } else {
          Eigen::Vector3d velocity_error =
            pid_3D_velocity_handler_.get_error(uav_state_.velocity, control_ref_.velocity);
          control_command_.velocity = pid_3D_velocity_handler_.compute_control(dt, velocity_error);
        }
        break;
      }
    case as2_msgs::msg::ControlMode::SPEED_IN_A_PLANE: {
        if (use_bypass_) {
          control_command_.velocity = control_ref_.velocity;
        } else {
          Eigen::Vector3d velocity_error =
            pid_3D_speed_in_a_plane_handler_.get_error(uav_state_.velocity, control_ref_.velocity);
          control_command_.velocity =
            pid_3D_speed_in_a_plane_handler_.compute_control(dt, velocity_error);
        }

        double position_error = pid_1D_speed_in_a_plane_handler_.get_error(
          uav_state_.position.z(),
          control_ref_.position.z());
        control_command_.velocity.z() =
          pid_1D_speed_in_a_plane_handler_.compute_control(dt, position_error);

        break;
      }
    case as2_msgs::msg::ControlMode::TRAJECTORY: {
        Eigen::Vector3d position_error =
          pid_3D_trajectory_handler_.get_error(uav_state_.position, control_ref_.position);
        Eigen::Vector3d velocity_error =
          pid_3D_trajectory_handler_.get_error(uav_state_.velocity, control_ref_.velocity);
        control_command_.velocity =
          pid_3D_trajectory_handler_.compute_control(dt, position_error, velocity_error);
        break;
      }
    default:
      auto & clk = *node_ptr_->get_clock();
      RCLCPP_ERROR_THROTTLE(node_ptr_->get_logger(), clk, 5000, "Unknown control mode");
      return false;
      break;
  }

  switch (control_mode_in_.yaw_mode) {
    case as2_msgs::msg::ControlMode::YAW_ANGLE: {
        double yaw_error = as2::frame::angleMinError(control_ref_.yaw.x(), uav_state_.yaw.x());
        control_command_.yaw_speed = pid_yaw_handler_.compute_control(dt, yaw_error);
        break;
      }
    case as2_msgs::msg::ControlMode::YAW_SPEED: {
        control_command_.yaw_speed = control_ref_.yaw.y();
        break;
      }
    default:
      auto & clk = *node_ptr_->get_clock();
      RCLCPP_ERROR_THROTTLE(node_ptr_->get_logger(), clk, 5000, "Unknown yaw mode");
      return false;
      break;
  }

  return getOutput(twist);
}

bool Plugin::getOutput(geometry_msgs::msg::TwistStamped & _twist_msg)
{
  _twist_msg.header.frame_id = output_twist_frame_id_;

  _twist_msg.twist.linear.x = control_command_.velocity.x();
  _twist_msg.twist.linear.y = control_command_.velocity.y();
  _twist_msg.twist.linear.z = control_command_.velocity.z();

  _twist_msg.twist.angular.x = 0;
  _twist_msg.twist.angular.y = 0;
  _twist_msg.twist.angular.z = control_command_.yaw_speed;
  return true;
}

}  // namespace pid_speed_controller

#include <pluginlib/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(
  pid_speed_controller::Plugin,
  as2_motion_controller_plugin_base::ControllerBase)