Program Listing for File as2_platform_multirotor_simulator.cpp

Return to documentation for file (/tmp/ws/src/aerostack2/as2_aerial_platforms/as2_platform_multirotor_simulator/src/as2_platform_multirotor_simulator.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.

#include "as2_platform_multirotor_simulator/as2_platform_multirotor_simulator.hpp"

#include "as2_core/utils/frame_utils.hpp"
#include "as2_core/utils/tf_utils.hpp"
#include "as2_core/utils/control_mode_utils.hpp"

namespace as2_platform_multirotor_simulator
{

MultirotorSimulatorPlatform::MultirotorSimulatorPlatform(const rclcpp::NodeOptions & options)
: as2::AerialPlatform(options)
{
  RCLCPP_INFO(this->get_logger(), "Initializing MultirotorSimulatorPlatform...");

  // Read parameters
  readParams(platform_params_);

  // Timers
  RCLCPP_INFO(this->get_logger(), "Using update freq: %f", platform_params_.update_freq);
  RCLCPP_INFO(this->get_logger(), "Using control freq: %f", platform_params_.control_freq);
  RCLCPP_INFO(this->get_logger(), "Using state pub freq: %f", platform_params_.state_freq);
  RCLCPP_INFO(
    this->get_logger(), "GPS origin: %f, %f, %f", platform_params_.latitude,
    platform_params_.longitude, platform_params_.altitude);

  simulator_timer_ = this->create_timer(
    std::chrono::duration<double>(1.0 / platform_params_.update_freq),
    std::bind(&MultirotorSimulatorPlatform::simulatorTimerCallback, this));
  simulator_control_timer_ = this->create_timer(
    std::chrono::duration<double>(1.0 / platform_params_.control_freq),
    std::bind(&MultirotorSimulatorPlatform::simulatorControlTimerCallback, this));
  simulator_state_pub_timer_ = this->create_timer(
    std::chrono::duration<double>(1.0 / platform_params_.imu_pub_freq),
    std::bind(&MultirotorSimulatorPlatform::simulatorStateTimerCallback, this));

  gps_handler_.setOrigin(
    platform_params_.latitude, platform_params_.longitude,
    platform_params_.altitude);  // Set origin for GPS

  // Configure sensors
  configureSensors();
}

MultirotorSimulatorPlatform::~MultirotorSimulatorPlatform()
{
  // Timers
  simulator_timer_.reset();
  simulator_control_timer_.reset();
  simulator_state_pub_timer_.reset();

  // Sensors
  sensor_ground_truth_ptr_.reset();
  sensor_odom_estimate_ptr_.reset();
  sensor_imu_ptr_.reset();
  sensor_gps_ptr_.reset();

  // Handlers
  tf_handler_.reset();
}

void MultirotorSimulatorPlatform::configureSensors()
{
  getParam("global_ref_frame", frame_id_earth_);
  getParam("odom_frame", frame_id_odom_);
  getParam("base_frame", frame_id_baselink_);
  frame_id_odom_ = as2::tf::generateTfName(this, frame_id_odom_);
  frame_id_baselink_ = as2::tf::generateTfName(this, frame_id_baselink_);

  // Get gimbal name
  std::string gimbal_name = "gimbal";
  std::string gimbal_base_name = "gimbal_base";
  getParam("gimbal_frame_id", gimbal_name);
  getParam("gimbal_base_frame_id", gimbal_base_name);

  RCLCPP_INFO(this->get_logger(), "Ground truth freq: %f", platform_params_.ground_truth_pub_freq);
  RCLCPP_INFO(this->get_logger(), "Odometry freq: %f", platform_params_.odometry_pub_freq);
  RCLCPP_INFO(this->get_logger(), "IMU freq: %f", platform_params_.imu_pub_freq);
  RCLCPP_INFO(this->get_logger(), "GPS freq: %f", platform_params_.gps_pub_freq);

  // Ground truth
  sensor_ground_truth_ptr_ = std::make_unique<as2::sensors::GroundTruth>(
    this, platform_params_.ground_truth_pub_freq);
  // Odometry
  sensor_odom_estimate_ptr_ = std::make_unique<as2::sensors::Sensor<nav_msgs::msg::Odometry>>(
    as2_names::topics::sensor_measurements::odom, this, platform_params_.odometry_pub_freq);
  // IMU
  sensor_imu_ptr_ = std::make_unique<as2::sensors::Sensor<sensor_msgs::msg::Imu>>(
    as2_names::topics::sensor_measurements::imu, this, platform_params_.imu_pub_freq);
  // GPS
  sensor_gps_ptr_ = std::make_unique<as2::sensors::Sensor<sensor_msgs::msg::NavSatFix>>(
    as2_names::topics::sensor_measurements::gps, this, platform_params_.gps_pub_freq);
  // Gimbal
  sensor_gimbal_ptr_ = std::make_unique<as2::sensors::Gimbal>(
    gimbal_name, gimbal_base_name, this, platform_params_.gimbal_pub_freq);
  gimbal_control_sub_ = this->create_subscription<as2_msgs::msg::GimbalControl>(
    "platform/" + gimbal_name + "/gimbal_command", 10,
    std::bind(&MultirotorSimulatorPlatform::gimbalControlCallback, this, std::placeholders::_1));

  geometry_msgs::msg::Transform gimbal_transform;
  getParam("gimbal_base_transform.x", gimbal_transform.translation.x);
  getParam("gimbal_base_transform.y", gimbal_transform.translation.y);
  getParam("gimbal_base_transform.z", gimbal_transform.translation.z);
  sensor_gimbal_ptr_->setGimbalBaseTransform(gimbal_transform);
}

bool MultirotorSimulatorPlatform::ownSetArmingState(bool state)
{
  state ? simulator_.arm() : simulator_.disarm();
  RCLCPP_INFO(this->get_logger(), "Arming state set to %d.", state);
  return true;
}

bool MultirotorSimulatorPlatform::ownSetOffboardControl(bool offboard)
{
  RCLCPP_INFO(this->get_logger(), "Offboard state set to %d.", offboard);
  return true;
}

bool MultirotorSimulatorPlatform::ownSetPlatformControlMode(const as2_msgs::msg::ControlMode & msg)
{
  if (platform_info_msg_.current_control_mode.control_mode == msg.control_mode) {
    RCLCPP_INFO(
      this->get_logger(), "Control mode already set to [%s]",
      as2::control_mode::controlModeToString(msg).c_str());
    return true;
  }

  multirotor::YawControlMode yaw_mode = multirotor::YawControlMode::ANGLE;
  if (msg.yaw_mode == as2_msgs::msg::ControlMode::YAW_SPEED) {
    yaw_mode = multirotor::YawControlMode::RATE;
  }

  switch (msg.control_mode) {
    case as2_msgs::msg::ControlMode::UNSET:
    case as2_msgs::msg::ControlMode::HOVER:
      {
        simulator_.set_control_mode(multirotor::ControlMode::HOVER);
        break;
      }
    case as2_msgs::msg::ControlMode::POSITION:
      {
        simulator_.set_control_mode(multirotor::ControlMode::POSITION, yaw_mode);
        break;
      }
    case as2_msgs::msg::ControlMode::SPEED:
      {
        simulator_.set_control_mode(multirotor::ControlMode::VELOCITY, yaw_mode);
        break;
      }
    case as2_msgs::msg::ControlMode::TRAJECTORY:
      {
        simulator_.set_control_mode(multirotor::ControlMode::TRAJECTORY, yaw_mode);
        break;
      }
    case as2_msgs::msg::ControlMode::ACRO:
      {
        simulator_.set_control_mode(multirotor::ControlMode::ACRO);
        break;
      }
    default:
      {
        RCLCPP_ERROR(
          this->get_logger(), "Desired control mode not supported: [%s]",
          as2::control_mode::controlModeToString(msg).c_str());
        return false;
        break;
      }
  }
  RCLCPP_INFO(
    this->get_logger(), "Control mode set to [%s]", as2::control_mode::controlModeToString(
      msg).c_str());
  return true;
}

bool MultirotorSimulatorPlatform::ownSendCommand()
{
  const as2_msgs::msg::ControlMode current_control_mode = platform_info_msg_.current_control_mode;

  switch (current_control_mode.control_mode) {
    case as2_msgs::msg::ControlMode::UNSET:
    case as2_msgs::msg::ControlMode::HOVER:
      {
        // Hovering
        break;
      }
    case as2_msgs::msg::ControlMode::POSITION:
      {
        Eigen::Vector3d position;
        position.x() = command_pose_msg_.pose.position.x;
        position.y() = command_pose_msg_.pose.position.y;
        position.z() = command_pose_msg_.pose.position.z;
        simulator_.set_reference_position(position);

        // Velocity limits
        Eigen::Vector3d velocity;
        velocity.x() = command_twist_msg_.twist.linear.x;
        velocity.y() = command_twist_msg_.twist.linear.y;
        velocity.z() = command_twist_msg_.twist.linear.z;
        if (velocity.norm() > 0.0) {
          const bool proportional_saturation_flag =
            simulator_.get_controller_const().get_position_controller_const().
            get_proportional_saturation_flag();
          simulator_.get_controller().get_position_controller().set_output_saturation(
            velocity,
            -velocity,
            proportional_saturation_flag);
        }
        break;
      }
    case as2_msgs::msg::ControlMode::SPEED:
      {
        Eigen::Vector3d velocity;
        velocity.x() = command_twist_msg_.twist.linear.x;
        velocity.y() = command_twist_msg_.twist.linear.y;
        velocity.z() = command_twist_msg_.twist.linear.z;
        simulator_.set_reference_velocity(velocity);
        break;
      }
    case as2_msgs::msg::ControlMode::TRAJECTORY:
      {
        Eigen::Vector3d position, velocity, acceleration;
        position.x() = command_trajectory_msg_.position.x;
        position.y() = command_trajectory_msg_.position.y;
        position.z() = command_trajectory_msg_.position.z;
        velocity.x() = command_trajectory_msg_.twist.x;
        velocity.y() = command_trajectory_msg_.twist.y;
        velocity.z() = command_trajectory_msg_.twist.z;
        acceleration.x() = command_trajectory_msg_.acceleration.x;
        acceleration.y() = command_trajectory_msg_.acceleration.y;
        acceleration.z() = command_trajectory_msg_.acceleration.z;

        simulator_.set_reference_trajectory(
          position, velocity, acceleration);
        break;
      }
    case as2_msgs::msg::ControlMode::ACRO:
      {
        double thrust = command_thrust_msg_.thrust;
        Eigen::Vector3d angular_velocity;
        angular_velocity.x() = command_twist_msg_.twist.angular.x;
        angular_velocity.y() = command_twist_msg_.twist.angular.y;
        angular_velocity.z() = command_twist_msg_.twist.angular.z;

        simulator_.set_reference_acro(thrust, angular_velocity);
        break;
      }
    default:
      RCLCPP_ERROR(
        this->get_logger(), "Control mode %d not supported.",
        platform_info_msg_.current_control_mode.control_mode);
      return false;
      break;
  }

  switch (current_control_mode.yaw_mode) {
    case as2_msgs::msg::ControlMode::YAW_SPEED:
      {
        simulator_.set_reference_yaw_rate(command_twist_msg_.twist.angular.z);
        break;
      }
    case as2_msgs::msg::ControlMode::YAW_ANGLE:
    default:
      {
        double roll, pitch, yaw;
        as2::frame::quaternionToEuler(command_pose_msg_.pose.orientation, roll, pitch, yaw);
        if (current_control_mode.control_mode ==
          as2_msgs::msg::ControlMode::TRAJECTORY)
        {
          yaw = command_trajectory_msg_.yaw_angle;
        } else {
          as2::frame::quaternionToEuler(command_pose_msg_.pose.orientation, roll, pitch, yaw);
        }
        simulator_.set_reference_yaw_angle(yaw);
        break;
      }
  }

  return true;
}

void MultirotorSimulatorPlatform::ownStopPlatform()
{
  // Send hover to platform here
  as2_msgs::msg::ControlMode control_mode_msg;
  control_mode_msg.control_mode = as2_msgs::msg::ControlMode::HOVER;
  setPlatformControlMode(control_mode_msg);
}

void MultirotorSimulatorPlatform::ownKillSwitch()
{
  // Switch off motors
  simulator_.disarm();
}

bool MultirotorSimulatorPlatform::ownTakeoff()
{
  // Send takeoff to platform here

  // Set control mode to position
  as2_msgs::msg::ControlMode control_mode_msg;
  control_mode_msg.control_mode = as2_msgs::msg::ControlMode::POSITION;
  control_mode_msg.yaw_mode = as2_msgs::msg::ControlMode::YAW_ANGLE;
  control_mode_msg.reference_frame = as2_msgs::msg::ControlMode::LOCAL_ENU_FRAME;
  setPlatformControlMode(control_mode_msg);

  // Set reference position to current position and 1m above
  command_pose_msg_.pose.position.x = simulator_.get_state().kinematics.position.x();
  command_pose_msg_.pose.position.y = simulator_.get_state().kinematics.position.y();
  const double takeoff_height = simulator_.get_floor_height() + 1.0;
  command_pose_msg_.pose.position.z = takeoff_height;
  command_pose_msg_.pose.orientation.w = simulator_.get_state().kinematics.orientation.w();
  command_pose_msg_.pose.orientation.x = simulator_.get_state().kinematics.orientation.x();
  command_pose_msg_.pose.orientation.y = simulator_.get_state().kinematics.orientation.y();
  command_pose_msg_.pose.orientation.z = simulator_.get_state().kinematics.orientation.z();

  // Set reference velocity to 1m/s to speed limit
  command_twist_msg_.twist.linear.x = 1.0;
  command_twist_msg_.twist.linear.y = 1.0;
  command_twist_msg_.twist.linear.z = 1.0;

  // Set references
  if (!ownSendCommand()) {
    return false;
  }

  // TODO(RPS98): Use multithread execution
  while (rclcpp::ok() &&
    std::abs(simulator_.get_state().kinematics.position.z() - takeoff_height) > 0.2)
  {
    // Spin timers
    simulatorTimerCallback();
    simulatorControlTimerCallback();
    simulatorStateTimerCallback();
  }
  return true;
}

bool MultirotorSimulatorPlatform::ownLand()
{
  // Send land to platform here

  // Set control mode to position
  as2_msgs::msg::ControlMode control_mode_msg;
  control_mode_msg.control_mode = as2_msgs::msg::ControlMode::POSITION;
  control_mode_msg.yaw_mode = as2_msgs::msg::ControlMode::YAW_ANGLE;
  control_mode_msg.reference_frame = as2_msgs::msg::ControlMode::LOCAL_ENU_FRAME;
  setPlatformControlMode(control_mode_msg);

  // Set reference position to current position and 1m above
  command_pose_msg_.pose.position.x = simulator_.get_state().kinematics.position.x();
  command_pose_msg_.pose.position.y = simulator_.get_state().kinematics.position.y();
  const double land_height = simulator_.get_floor_height();
  command_pose_msg_.pose.position.z = land_height;
  command_pose_msg_.pose.orientation.w = simulator_.get_state().kinematics.orientation.w();
  command_pose_msg_.pose.orientation.x = simulator_.get_state().kinematics.orientation.x();
  command_pose_msg_.pose.orientation.y = simulator_.get_state().kinematics.orientation.y();
  command_pose_msg_.pose.orientation.z = simulator_.get_state().kinematics.orientation.z();

  // Set reference velocity to 1m/s to speed limit
  command_twist_msg_.twist.linear.x = 1.0;
  command_twist_msg_.twist.linear.y = 1.0;
  command_twist_msg_.twist.linear.z = 1.0;

  // Set references
  if (!ownSendCommand()) {
    return false;
  }

  // TODO(RPS98): Use multithread execution
  while (rclcpp::ok() &&
    std::abs(simulator_.get_state().kinematics.position.z() - land_height) > 0.2)
  {
    // Call timers
    simulatorTimerCallback();
    simulatorControlTimerCallback();
    simulatorStateTimerCallback();
  }
  return true;
}

void MultirotorSimulatorPlatform::gimbalControlCallback(
  const as2_msgs::msg::GimbalControl::SharedPtr msg)
{
  double roll, pitch, yaw;
  switch (msg->control_mode) {
    case as2_msgs::msg::GimbalControl::POSITION_MODE:
      {
        roll = msg->target.vector.x;
        pitch = msg->target.vector.y;
        yaw = msg->target.vector.z;
        break;
      }
    case as2_msgs::msg::GimbalControl::SPEED_MODE:
    // TODO(RPS98): Implement speed mode
    // {
    //   as2::frame::quaternionToEuler(gimbal_desired_orientation_.quaternion, roll, pitch, yaw);
    //   double dt = 1.0 / platform_params_.gimbal_pub_freq;
    //   roll += msg->target.vector.x * dt;
    //   pitch += msg->target.vector.y * dt;
    //   yaw += msg->target.vector.z * dt;
    //   break;
    // }
    default:
      {
        RCLCPP_ERROR(
          this->get_logger(), "Gimbal control mode %d not supported.", msg->control_mode);
        break;
      }
  }
  as2::frame::eulerToQuaternion(
    roll, pitch, yaw, gimbal_desired_orientation_.quaternion);
}

Eigen::Vector3d MultirotorSimulatorPlatform::readVectorParams(const std::string & param_name)
{
  Eigen::Vector3d default_value = Eigen::Vector3d::Zero();  // Default value

  try {
    std::vector<double> vec;
    this->getParam(param_name, vec);

    if (vec.size() != 3) {
      RCLCPP_ERROR(
        this->get_logger(), "Parameter '%s' is not a vector of size 3.", param_name.c_str());
      // Print vector
      RCLCPP_ERROR(this->get_logger(), "Vector: ");
      for (auto & v : vec) {
        RCLCPP_ERROR(this->get_logger(), "%f", v);
      }
      return default_value;
    }

    return Eigen::Vector3d(vec[0], vec[1], vec[2]);
  } catch (const std::exception & e) {
    RCLCPP_ERROR(
      this->get_logger(), "Error getting parameter %s: %s", param_name.c_str(), e.what());
    return default_value;
  }
}

void MultirotorSimulatorPlatform::readParams(PlatformParams & platform_params)
{
  RCLCPP_INFO(this->get_logger(), "Reading parameters...");
  // Platform Parameters
  getParam("imu_pub_freq", platform_params.imu_pub_freq);
  getParam("odometry_pub_freq", platform_params.odometry_pub_freq);
  getParam("ground_truth_pub_freq", platform_params.ground_truth_pub_freq);
  getParam("gps_pub_freq", platform_params.gps_pub_freq);
  getParam("gimbal_pub_freq", platform_params.gimbal_pub_freq);

  // Get max frequency
  platform_params.state_freq = std::max(
    std::max(platform_params.imu_pub_freq, platform_params.odometry_pub_freq),
    std::max(platform_params.ground_truth_pub_freq, platform_params.gps_pub_freq));

  // GPS Origin
  getParam("gps_origin.latitude", platform_params.latitude);
  getParam("gps_origin.longitude", platform_params.longitude);
  getParam("gps_origin.altitude", platform_params.altitude);

  // Simulator params
  double floor_height;
  getParam("floor_height", floor_height);
  getParam("simulation_params.simulator_update_freq", platform_params.update_freq);
  getParam("simulation_params.simulator_control_freq", platform_params.control_freq);

  // Dynamics params
  // Dynamics::State params
  SimulatorParams::DynamicsParams & dp = simulator_params_.dynamics_params;
  Eigen::Vector3d initial_position;
  getParam("vehicle_initial_pose.x", initial_position.x());
  getParam("vehicle_initial_pose.y", initial_position.y());
  getParam("vehicle_initial_pose.z", initial_position.z());
  double roll, pitch, yaw;
  getParam("vehicle_initial_pose.yaw", yaw);
  getParam("vehicle_initial_pose.pitch", pitch);
  getParam("vehicle_initial_pose.roll", roll);
  Eigen::Quaterniond initial_orientation;
  as2::frame::eulerToQuaternion(roll, pitch, yaw, initial_orientation);
  dp.state.kinematics.position = initial_position;
  dp.state.kinematics.orientation = initial_orientation;

  // Dynamics::Model params
  dp.model_params.gravity = readVectorParams("multirotor.dynamics.model.gravity");
  getParam("multirotor.dynamics.model.vehicle_mass", dp.model_params.vehicle_mass);
  dp.model_params.vehicle_inertia =
    readVectorParams("multirotor.dynamics.model.vehicle_inertia").asDiagonal();
  getParam(
    "multirotor.dynamics.model.vehicle_drag_coefficient", dp.model_params.vehicle_drag_coefficient);
  dp.model_params.vehicle_aero_moment_coefficient =
    readVectorParams("multirotor.dynamics.model.vehicle_aero_moment_coefficient").asDiagonal();
  getParam(
    "multirotor.dynamics.model.force_process_noise_auto_correlation",
    dp.model_params.moment_process_noise_auto_correlation);
  getParam(
    "multirotor.dynamics.model.moment_process_noise_auto_correlation",
    dp.model_params.moment_process_noise_auto_correlation);

  double thrust_coefficient, torque_coefficient, x_dist, y_dist, min_speed, max_speed,
    time_constant, rotational_inertia;
  getParam("multirotor.dynamics.model.motors_params.thrust_coefficient", thrust_coefficient);
  getParam("multirotor.dynamics.model.motors_params.torque_coefficient", torque_coefficient);
  getParam("multirotor.dynamics.model.motors_params.x_dist", x_dist);
  getParam("multirotor.dynamics.model.motors_params.y_dist", y_dist);
  getParam("multirotor.dynamics.model.motors_params.min_speed", min_speed);
  getParam("multirotor.dynamics.model.motors_params.max_speed", max_speed);
  getParam("multirotor.dynamics.model.motors_params.time_constant", time_constant);
  getParam("multirotor.dynamics.model.motors_params.rotational_inertia", rotational_inertia);
  dp.model_params.motors_params = multirotor::model::Model<double, 4>::create_quadrotor_x_config(
    thrust_coefficient, torque_coefficient, x_dist, y_dist, min_speed, max_speed, time_constant,
    rotational_inertia);

  // Controller params Indi
  SimulatorParams::ControllerParams & cp = simulator_params_.controller_params;
  cp.indi_controller_params.inertia = dp.model_params.vehicle_inertia;
  auto mixing_matrix_6D_4rotors =
    multirotor::model::Model<double, 4>::compute_mixer_matrix<6>(dp.model_params.motors_params);
  cp.indi_controller_params.mixer_matrix_inverse =
    indi_controller::compute_quadrotor_mixer_matrix_inverse(mixing_matrix_6D_4rotors);

  cp.indi_controller_params.pid_params.Kp_gains =
    readVectorParams("multirotor.controller.indi.kp");
  cp.indi_controller_params.pid_params.Ki_gains =
    readVectorParams("multirotor.controller.indi.ki");
  cp.indi_controller_params.pid_params.Kd_gains =
    readVectorParams("multirotor.controller.indi.kd");
  cp.indi_controller_params.pid_params.alpha =
    readVectorParams("multirotor.controller.indi.alpha");
  cp.indi_controller_params.pid_params.antiwindup_cte =
    readVectorParams("multirotor.controller.indi.antiwindup_cte");
  Eigen::Vector3d angular_acceleration_limit =
    readVectorParams("multirotor.controller.indi.angular_acceleration_limit");
  cp.indi_controller_params.pid_params.upper_output_saturation = angular_acceleration_limit;
  cp.indi_controller_params.pid_params.lower_output_saturation = -angular_acceleration_limit;
  cp.indi_controller_params.pid_params.proportional_saturation_flag = true;

  // Controller params Acro
  cp.acro_controller_params.gravity = dp.model_params.gravity;
  cp.acro_controller_params.vehicle_mass = dp.model_params.vehicle_mass;
  cp.acro_controller_params.kp_rot =
    readVectorParams("multirotor.controller.acro.kp_rot").asDiagonal();

  // Controller params Trajectory
  cp.trajectory_controller_params.pid_params.Kp_gains =
    readVectorParams("multirotor.controller.trajectory.kp");
  cp.trajectory_controller_params.pid_params.Ki_gains =
    readVectorParams("multirotor.controller.trajectory.ki");
  cp.trajectory_controller_params.pid_params.Kd_gains =
    readVectorParams("multirotor.controller.trajectory.kd");
  cp.trajectory_controller_params.pid_params.alpha =
    readVectorParams("multirotor.controller.trajectory.alpha");
  cp.trajectory_controller_params.pid_params.antiwindup_cte =
    readVectorParams("multirotor.controller.trajectory.antiwindup_cte");
  Eigen::Vector3d linear_acceleration_limit =
    readVectorParams("multirotor.controller.trajectory.linear_acceleration_limit");
  cp.trajectory_controller_params.pid_params.upper_output_saturation = linear_acceleration_limit;
  cp.trajectory_controller_params.pid_params.lower_output_saturation = -linear_acceleration_limit;
  cp.trajectory_controller_params.pid_params.proportional_saturation_flag = true;

  // Controller params Velocity
  cp.velocity_controller_params.pid_params.Kp_gains =
    readVectorParams("multirotor.controller.velocity.kp");
  cp.velocity_controller_params.pid_params.Ki_gains =
    readVectorParams("multirotor.controller.velocity.ki");
  cp.velocity_controller_params.pid_params.Kd_gains =
    readVectorParams("multirotor.controller.velocity.kd");
  cp.velocity_controller_params.pid_params.alpha =
    readVectorParams("multirotor.controller.velocity.alpha");
  cp.velocity_controller_params.pid_params.antiwindup_cte =
    readVectorParams("multirotor.controller.velocity.antiwindup_cte");
  linear_acceleration_limit =
    readVectorParams("multirotor.controller.velocity.linear_acceleration_limit");
  cp.velocity_controller_params.pid_params.upper_output_saturation = linear_acceleration_limit;
  cp.velocity_controller_params.pid_params.lower_output_saturation = -linear_acceleration_limit;
  cp.velocity_controller_params.pid_params.proportional_saturation_flag = true;

  // Controller params Position
  cp.position_controller_params.pid_params.Kp_gains =
    readVectorParams("multirotor.controller.position.kp");
  cp.position_controller_params.pid_params.Ki_gains =
    readVectorParams("multirotor.controller.position.ki");
  cp.position_controller_params.pid_params.Kd_gains =
    readVectorParams("multirotor.controller.position.kd");
  cp.position_controller_params.pid_params.alpha =
    readVectorParams("multirotor.controller.position.alpha");
  cp.position_controller_params.pid_params.antiwindup_cte =
    readVectorParams("multirotor.controller.position.antiwindup_cte");
  Eigen::Vector3d inear_velocity_limit =
    readVectorParams("multirotor.controller.position.linear_velocity_limit");
  cp.position_controller_params.pid_params.upper_output_saturation = inear_velocity_limit;
  cp.position_controller_params.pid_params.lower_output_saturation = -inear_velocity_limit;
  cp.position_controller_params.pid_params.proportional_saturation_flag = true;

  // IMU params
  getParam("multirotor.imu.gyro_noise_var", simulator_params_.imu_params.gyro_noise_var);
  getParam("multirotor.imu.accel_noise_var", simulator_params_.imu_params.accel_noise_var);
  getParam(
    "multirotor.imu.gyro_bias_noise_autocorr_time",
    simulator_params_.imu_params.gyro_bias_noise_autocorr_time);
  getParam(
    "multirotor.imu.accel_bias_noise_autocorr_time",
    simulator_params_.imu_params.accel_bias_noise_autocorr_time);

  // Inertial Odometry params
  getParam("multirotor.inertial_odometry.alpha", simulator_params_.inertial_odometry_params.alpha);
  simulator_params_.inertial_odometry_params.initial_world_orientation = initial_orientation;

  simulator_ = Simulator(simulator_params_);
  simulator_.enable_floor_collision(floor_height);
  RCLCPP_INFO(this->get_logger(), "Parameters read.");
}

void MultirotorSimulatorPlatform::simulatorTimerCallback()
{
  // Get time
  rclcpp::Time current_time = this->now();
  static rclcpp::Time last_time_dynamics = current_time;
  double dt = (current_time - last_time_dynamics).seconds();
  last_time_dynamics = current_time;

  if (dt <= 0.0) {
    return;
  }

  // Update simulator
  simulator_.update_dynamics(dt);
  simulator_.update_imu(dt);
}

void MultirotorSimulatorPlatform::simulatorControlTimerCallback()
{
  // Get time
  rclcpp::Time current_time = this->now();
  static rclcpp::Time last_time_control = current_time;
  double dt = (current_time - last_time_control).seconds();
  last_time_control = current_time;

  if (dt <= 0.0) {
    return;
  }
  simulator_.update_controller(dt);
  simulator_.update_inertial_odometry(dt);
}

void MultirotorSimulatorPlatform::simulatorStateTimerCallback()
{
  // Get time
  rclcpp::Time current_time = this->now();

  // Get odometry simulator state for imu orientation
  const multirotor::state::internal::Kinematics odometry_kinematics = simulator_.get_odometry();

  // Get imu simulator
  Eigen::Vector3d imu_angular_velocity, imu_acceleration;
  simulator_.get_imu_measurement(imu_angular_velocity, imu_angular_velocity);
  geometry_msgs::msg::Vector3 imu_angular_velocity_msg;
  imu_angular_velocity_msg.x = imu_angular_velocity.x();
  imu_angular_velocity_msg.y = imu_angular_velocity.y();
  imu_angular_velocity_msg.z = imu_angular_velocity.z();
  geometry_msgs::msg::Vector3 imu_acceleration_msg;
  imu_acceleration_msg.x = imu_acceleration.x();
  imu_acceleration_msg.y = imu_acceleration.y();
  imu_acceleration_msg.z = imu_acceleration.z();

  sensor_msgs::msg::Imu imu_msg;
  imu_msg.header.stamp = current_time;
  imu_msg.header.frame_id = frame_id_baselink_;
  imu_msg.angular_velocity = imu_angular_velocity_msg;
  imu_msg.linear_acceleration = imu_acceleration_msg;
  imu_msg.orientation.w = odometry_kinematics.orientation.w();
  imu_msg.orientation.x = odometry_kinematics.orientation.x();
  imu_msg.orientation.y = odometry_kinematics.orientation.y();
  imu_msg.orientation.z = odometry_kinematics.orientation.z();
  sensor_imu_ptr_->updateData(imu_msg);

  // Get odometry simulator state
  nav_msgs::msg::Odometry odometry;
  odometry.header.stamp = current_time;
  odometry.header.frame_id = frame_id_odom_;
  odometry.child_frame_id = frame_id_baselink_;
  odometry.pose.pose.position.x = odometry_kinematics.position.x();
  odometry.pose.pose.position.y = odometry_kinematics.position.y();
  odometry.pose.pose.position.z = odometry_kinematics.position.z();
  odometry.pose.pose.orientation.w = odometry_kinematics.orientation.w();
  odometry.pose.pose.orientation.x = odometry_kinematics.orientation.x();
  odometry.pose.pose.orientation.y = odometry_kinematics.orientation.y();
  odometry.pose.pose.orientation.z = odometry_kinematics.orientation.z();
  Eigen::Vector3d odom_linear_velocity_flu =
    as2::frame::transform(odometry_kinematics.orientation, odometry_kinematics.linear_velocity);
  odometry.twist.twist.linear.x = odom_linear_velocity_flu.x();
  odometry.twist.twist.linear.y = odom_linear_velocity_flu.y();
  odometry.twist.twist.linear.z = odom_linear_velocity_flu.z();
  odometry.twist.twist.angular.x = odometry_kinematics.angular_velocity.x();
  odometry.twist.twist.angular.y = odometry_kinematics.angular_velocity.y();
  odometry.twist.twist.angular.z = odometry_kinematics.angular_velocity.z();
  sensor_odom_estimate_ptr_->updateData(odometry);

  // Get ground truth simulator state
  const multirotor::state::internal::Kinematics kinematics =
    simulator_.get_state().kinematics;

  geometry_msgs::msg::Quaternion ground_truth_orientation;
  ground_truth_orientation.w = kinematics.orientation.w();
  ground_truth_orientation.x = kinematics.orientation.x();
  ground_truth_orientation.y = kinematics.orientation.y();
  ground_truth_orientation.z = kinematics.orientation.z();

  geometry_msgs::msg::PoseStamped ground_truth_pose;
  ground_truth_pose.header.stamp = current_time;
  ground_truth_pose.header.frame_id = frame_id_earth_;
  ground_truth_pose.pose.position.x = kinematics.position.x();
  ground_truth_pose.pose.position.y = kinematics.position.y();
  ground_truth_pose.pose.position.z = kinematics.position.z();
  ground_truth_pose.pose.orientation = ground_truth_orientation;

  geometry_msgs::msg::TwistStamped ground_truth_twist;
  ground_truth_twist.header.stamp = current_time;
  ground_truth_twist.header.frame_id = frame_id_baselink_;
  Eigen::Vector3d gt_linear_velocity_flu =
    as2::frame::transform(kinematics.orientation, kinematics.linear_velocity);
  ground_truth_twist.twist.linear.x = gt_linear_velocity_flu.x();
  ground_truth_twist.twist.linear.y = gt_linear_velocity_flu.y();
  ground_truth_twist.twist.linear.z = gt_linear_velocity_flu.z();
  ground_truth_twist.twist.angular.x = kinematics.angular_velocity.x();
  ground_truth_twist.twist.angular.y = kinematics.angular_velocity.y();
  ground_truth_twist.twist.angular.z = kinematics.angular_velocity.z();

  sensor_ground_truth_ptr_->updateData(ground_truth_pose, ground_truth_twist);

  // Convert to GPS
  double lat, lon, alt;
  gps_handler_.Local2LatLon(
    kinematics.position.x(), kinematics.position.y(),
    kinematics.position.z(), lat, lon, alt);

  sensor_msgs::msg::NavSatFix gps_msg;
  gps_msg.header.stamp = current_time;
  gps_msg.header.frame_id = frame_id_earth_;
  gps_msg.latitude = lat;
  gps_msg.longitude = lon;
  gps_msg.altitude = alt;
  sensor_gps_ptr_->updateData(gps_msg);

  // Move Gimbal
  gimbal_desired_orientation_.header.stamp = current_time;
  sensor_gimbal_ptr_->updateData(gimbal_desired_orientation_);
}

}  // namespace as2_platform_multirotor_simulator