gazebo_fw_dynamics_plugin.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2017 Pavel Vechersky, ASL, ETH Zurich, Switzerland
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009 
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 // MODULE HEADER
00018 #include "rotors_gazebo_plugins/gazebo_fw_dynamics_plugin.h"
00019 
00020 #include "ConnectRosToGazeboTopic.pb.h"
00021 
00022 namespace gazebo {
00023 
00024 GazeboFwDynamicsPlugin::GazeboFwDynamicsPlugin()
00025     : ModelPlugin(),
00026       node_handle_(0),
00027       W_wind_speed_W_B_(0, 0, 0),
00028       delta_aileron_left_(0.0),
00029       delta_aileron_right_(0.0),
00030       delta_elevator_(0.0),
00031       delta_flap_(0.0),
00032       delta_rudder_(0.0),
00033       throttle_(0.0),
00034       pubs_and_subs_created_(false) {
00035 }
00036 
00037 GazeboFwDynamicsPlugin::~GazeboFwDynamicsPlugin() {
00038 }
00039 
00040 void GazeboFwDynamicsPlugin::Load(physics::ModelPtr _model,
00041                                   sdf::ElementPtr _sdf) {
00042   if (kPrintOnPluginLoad) {
00043     gzdbg << __FUNCTION__ << "() called." << std::endl;
00044   }
00045 
00046   gzdbg << "_model = " << _model->GetName() << std::endl;
00047 
00048   // Store the pointer to the model.
00049   model_ = _model;
00050   world_ = model_->GetWorld();
00051 
00052   namespace_.clear();
00053 
00054   // Get the robot namespace.
00055   if (_sdf->HasElement("robotNamespace"))
00056     namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>();
00057   else
00058     gzerr << "[gazebo_fw_dynamics_plugin] Please specify a robotNamespace.\n";
00059 
00060   // Create the node handle.
00061   node_handle_ = transport::NodePtr(new transport::Node());
00062 
00063   // Initialise with default namespace (typically /gazebo/default/).
00064   node_handle_->Init();
00065 
00066   // Get the link name.
00067   std::string link_name;
00068   if (_sdf->HasElement("linkName"))
00069     link_name = _sdf->GetElement("linkName")->Get<std::string>();
00070   else
00071     gzerr << "[gazebo_fw_dynamics_plugin] Please specify a linkName.\n";
00072   // Get the pointer to the link.
00073   link_ = model_->GetLink(link_name);
00074   if (link_ == NULL) {
00075     gzthrow("[gazebo_fw_dynamics_plugin] Couldn't find specified link \""
00076             << link_name << "\".");
00077   }
00078 
00079   // Get the path to fixed-wing aerodynamics parameters YAML file. If not
00080   // provided, default Techpod parameters are used.
00081   if (_sdf->HasElement("aeroParamsYAML")) {
00082     std::string aero_params_yaml =
00083         _sdf->GetElement("aeroParamsYAML")->Get<std::string>();
00084 
00085     aero_params_.LoadAeroParamsYAML(aero_params_yaml);
00086   } else {
00087     gzwarn << "[gazebo_fw_dynamics_plugin] No aerodynamic paramaters YAML file"
00088         << " specified, using default Techpod parameters.\n";
00089   }
00090 
00091   // Get the path to fixed-wing vehicle parameters YAML file. If not provided,
00092   // default Techpod parameters are used.
00093   if (_sdf->HasElement("vehicleParamsYAML")) {
00094     std::string vehicle_params_yaml =
00095         _sdf->GetElement("vehicleParamsYAML")->Get<std::string>();
00096 
00097     vehicle_params_.LoadVehicleParamsYAML(vehicle_params_yaml);
00098   } else {
00099     gzwarn << "[gazebo_fw_dynamics_plugin] No vehicle paramaters YAML file"
00100         << " specified, using default Techpod parameters.\n";
00101   }
00102 
00103   // Get the rest of the sdf parameters.
00104   getSdfParam<bool>(_sdf, "isInputJoystick", is_input_joystick_,
00105                     kDefaultIsInputJoystick);
00106   getSdfParam<std::string>(_sdf, "actuatorsSubTopic",
00107                            actuators_sub_topic_,
00108                            mav_msgs::default_topics::COMMAND_ACTUATORS);
00109   getSdfParam<std::string>(_sdf, "rollPitchYawrateThrustSubTopic",
00110                            roll_pitch_yawrate_thrust_sub_topic_,
00111                            mav_msgs::default_topics::
00112                                COMMAND_ROLL_PITCH_YAWRATE_THRUST);
00113   getSdfParam<std::string>(_sdf, "windSpeedSubTopic",
00114                            wind_speed_sub_topic_,
00115                            mav_msgs::default_topics::WIND_SPEED);
00116 
00117   // Listen to the update event. This event is broadcast every
00118   // simulation iteration.
00119   this->updateConnection_ = event::Events::ConnectWorldUpdateBegin(
00120           boost::bind(&GazeboFwDynamicsPlugin::OnUpdate, this, _1));
00121 }
00122 
00123 void GazeboFwDynamicsPlugin::OnUpdate(const common::UpdateInfo& _info) {
00124   if (kPrintOnUpdates) {
00125     gzdbg << __FUNCTION__ << "() called." << std::endl;
00126   }
00127 
00128   if (!pubs_and_subs_created_) {
00129     CreatePubsAndSubs();
00130     pubs_and_subs_created_ = true;
00131   }
00132 
00133   UpdateForcesAndMoments();
00134 }
00135 
00136 void GazeboFwDynamicsPlugin::UpdateForcesAndMoments() {
00137   // Express the air speed and angular velocity in the body frame.
00138   // B denotes body frame and W world frame ... e.g., W_rot_W_B denotes
00139   // rotation of B wrt. W expressed in W.
00140   ignition::math::Quaterniond W_rot_W_B = link_->WorldPose().Rot();
00141   ignition::math::Vector3d B_air_speed_W_B = W_rot_W_B.RotateVectorReverse(
00142       link_->WorldLinearVel() - W_wind_speed_W_B_);
00143   ignition::math::Vector3d B_angular_velocity_W_B = link_->RelativeAngularVel();
00144 
00145   // Traditionally, fixed-wing aerodynamics use NED (North-East-Down) frame,
00146   // but since our model's body frame is in North-West-Up frame we rotate the
00147   // linear and angular velocities by 180 degrees around the X axis.
00148   double u = B_air_speed_W_B.X();
00149   double v = -B_air_speed_W_B.Y();
00150   double w = -B_air_speed_W_B.Z();
00151 
00152   double p = B_angular_velocity_W_B.X();
00153   double q = -B_angular_velocity_W_B.Y();
00154   double r = -B_angular_velocity_W_B.Z();
00155 
00156   // Compute the angle of attack (alpha) and the sideslip angle (beta). To
00157   // avoid division by zero, there is a minimum air speed threshold below which
00158   // alpha and beta are zero.
00159   double V = B_air_speed_W_B.Length();
00160   double beta = (V < kMinAirSpeedThresh) ? 0.0 : asin(v / V);
00161   double alpha = (u < kMinAirSpeedThresh) ? 0.0 : atan(w / u);
00162 
00163   // Bound the angle of attack.
00164   if (alpha > aero_params_.alpha_max)
00165     alpha = aero_params_.alpha_max;
00166   else if (alpha < aero_params_.alpha_min)
00167     alpha = aero_params_.alpha_min;
00168 
00169   // Pre-compute the common component in the force and moment calculations.
00170   const double q_bar_S = 0.5 * kAirDensity * V * V * vehicle_params_.wing_surface;
00171 
00172   // Combine some of the control surface deflections.
00173   double aileron_sum = delta_aileron_left_ + delta_aileron_right_;
00174   double aileron_diff = delta_aileron_left_ - delta_aileron_right_;
00175   double flap_sum = 2.0 * delta_flap_;
00176   double flap_diff = 0.0;
00177 
00178   // Compute the forces in the wind frame.
00179   const double drag = q_bar_S *
00180       (aero_params_.c_drag_alpha.dot(
00181            Eigen::Vector3d(1.0, alpha, alpha * alpha)) +
00182        aero_params_.c_drag_beta.dot(
00183            Eigen::Vector3d(0.0, beta, beta * beta)) +
00184        aero_params_.c_drag_delta_ail.dot(
00185            Eigen::Vector3d(0.0, aileron_sum, aileron_sum * aileron_sum)) +
00186        aero_params_.c_drag_delta_flp.dot(
00187            Eigen::Vector3d(0.0, flap_sum, flap_sum * flap_sum)));
00188 
00189   const double side_force = q_bar_S *
00190       (aero_params_.c_side_force_beta.dot(
00191            Eigen::Vector2d(0.0, beta)));
00192 
00193   const double lift = q_bar_S *
00194       (aero_params_.c_lift_alpha.dot(
00195            Eigen::Vector4d(1.0, alpha, alpha * alpha, alpha * alpha * alpha)) +
00196        aero_params_.c_lift_delta_ail.dot(
00197            Eigen::Vector2d(0.0, aileron_sum)) +
00198        aero_params_.c_lift_delta_flp.dot(
00199            Eigen::Vector2d(0.0, flap_sum)));
00200 
00201   const Eigen::Vector3d forces_Wind(-drag, side_force, -lift);
00202 
00203   // Non-dimensionalize the angular rates for inclusion in the computation of
00204   // moments. To avoid division by zero, there is a minimum air speed threshold
00205   // below which the values are zero.
00206   const double p_hat = (V < kMinAirSpeedThresh) ? 0.0 :
00207       p * vehicle_params_.wing_span / (2.0 * V);
00208   const double q_hat = (V < kMinAirSpeedThresh) ? 0.0 :
00209       q * vehicle_params_.chord_length / (2.0 * V);
00210   const double r_hat = (V < kMinAirSpeedThresh) ? 0.0 :
00211       r * vehicle_params_.wing_span / (2.0 * V);
00212 
00213   // Compute the moments in the wind frame.
00214   const double rolling_moment = q_bar_S * vehicle_params_.wing_span *
00215       (aero_params_.c_roll_moment_beta.dot(
00216            Eigen::Vector2d(0.0, beta)) +
00217        aero_params_.c_roll_moment_p.dot(
00218            Eigen::Vector2d(0.0, p_hat)) +
00219        aero_params_.c_roll_moment_r.dot(
00220            Eigen::Vector2d(0.0, r_hat)) +
00221        aero_params_.c_roll_moment_delta_ail.dot(
00222            Eigen::Vector2d(0.0, aileron_diff)) +
00223        aero_params_.c_roll_moment_delta_flp.dot(
00224            Eigen::Vector2d(0.0, flap_diff)));
00225 
00226   const double pitching_moment = q_bar_S * vehicle_params_.chord_length *
00227       (aero_params_.c_pitch_moment_alpha.dot(
00228            Eigen::Vector2d(1.0, alpha)) +
00229        aero_params_.c_pitch_moment_q.dot(
00230            Eigen::Vector2d(0.0, q_hat)) +
00231        aero_params_.c_pitch_moment_delta_elv.dot(
00232            Eigen::Vector2d(0.0, delta_elevator_)));
00233 
00234   const double yawing_moment = q_bar_S * vehicle_params_.wing_span *
00235       (aero_params_.c_yaw_moment_beta.dot(
00236            Eigen::Vector2d(0.0, beta)) +
00237        aero_params_.c_yaw_moment_r.dot(
00238            Eigen::Vector2d(0.0, r_hat)) +
00239        aero_params_.c_yaw_moment_delta_rud.dot(
00240            Eigen::Vector2d(0.0, delta_rudder_)));
00241 
00242   const Eigen::Vector3d moments_Wind(rolling_moment,
00243                                      pitching_moment,
00244                                      yawing_moment);
00245 
00246   // Compute the thrust force in the body frame.
00247   const double thrust = aero_params_.c_thrust.dot(
00248       Eigen::Vector3d(1.0, throttle_, throttle_ * throttle_));
00249 
00250   const Eigen::Vector3d force_thrust_B = thrust * Eigen::Vector3d(
00251       cos(vehicle_params_.thrust_inclination),
00252       0.0,
00253       sin(vehicle_params_.thrust_inclination));
00254 
00255   // Compute the transform between the body frame and the wind frame.
00256   double ca = cos(alpha);
00257   double sa = sin(alpha);
00258   double cb = cos(beta);
00259   double sb = sin(beta);
00260 
00261   Eigen::Matrix3d R_Wind_B;
00262   R_Wind_B << ca * cb, sb, sa * cb,
00263               -sb * ca, cb, -sa * sb,
00264               -sa, 0.0, ca;
00265 
00266   const Eigen::Matrix3d R_Wind_B_t = R_Wind_B.transpose();
00267 
00268   // Transform all the forces and moments into the body frame
00269   const Eigen::Vector3d forces_B = R_Wind_B_t * forces_Wind + force_thrust_B;
00270   const Eigen::Vector3d moments_B = R_Wind_B_t * moments_Wind;
00271 
00272   // Once again account for the difference between our body frame orientation
00273   // and the traditional aerodynamics frame.
00274   const ignition::math::Vector3d forces =
00275       ignition::math::Vector3d (forces_B[0], -forces_B[1], -forces_B[2]);
00276   const ignition::math::Vector3d moments =
00277       ignition::math::Vector3d (moments_B[0], -moments_B[1], -moments_B[2]);
00278 
00279   // Apply the calculated forced and moments to the main body link.
00280   link_->AddRelativeForce(forces);
00281   link_->AddRelativeTorque(moments);
00282 }
00283 
00284 double GazeboFwDynamicsPlugin::NormalizedInputToAngle(
00285     const ControlSurface& surface, double input) {
00286   return (surface.deflection_max + surface.deflection_min) * 0.5 +
00287       (surface.deflection_max - surface.deflection_min) * 0.5 * input;
00288 }
00289 
00290 void GazeboFwDynamicsPlugin::CreatePubsAndSubs() {
00291   gzdbg << __PRETTY_FUNCTION__ << " called." << std::endl;
00292 
00293   // Create temporary "ConnectRosToGazeboTopic" publisher and message
00294   gazebo::transport::PublisherPtr gz_connect_ros_to_gazebo_topic_pub =
00295       node_handle_->Advertise<gz_std_msgs::ConnectRosToGazeboTopic>(
00296           "~/" + kConnectRosToGazeboSubtopic, 1);
00297   gz_std_msgs::ConnectRosToGazeboTopic connect_ros_to_gazebo_topic_msg;
00298 
00299   // ============================================ //
00300   // ==== WIND SPEED MSG SETUP (ROS->GAZEBO) ==== //
00301   // ============================================ //
00302 
00303   // Wind speed subscriber (Gazebo).
00304   wind_speed_sub_ =
00305       node_handle_->Subscribe("~/" + namespace_ + "/" + wind_speed_sub_topic_,
00306                               &GazeboFwDynamicsPlugin::WindSpeedCallback,
00307                               this);
00308 
00309   connect_ros_to_gazebo_topic_msg.set_ros_topic(namespace_ + "/" +
00310                                                 wind_speed_sub_topic_);
00311   connect_ros_to_gazebo_topic_msg.set_gazebo_topic("~/" + namespace_ + "/" +
00312                                                    wind_speed_sub_topic_);
00313   connect_ros_to_gazebo_topic_msg.set_msgtype(
00314       gz_std_msgs::ConnectRosToGazeboTopic::WIND_SPEED);
00315   gz_connect_ros_to_gazebo_topic_pub->Publish(connect_ros_to_gazebo_topic_msg,
00316                                               true);
00317 
00318   // If we are using a joystick for control inputs then subscribe to the
00319   // RollPitchYawrateThrust msgs, otherwise subscribe to the Actuator msgs.
00320   if (is_input_joystick_) {
00321     // ========================================================= //
00322     // === ROLL_PITCH_YAWRATE_THRUST MSG SETUP (ROS->GAZEBO) === //
00323     // ========================================================= //
00324 
00325     roll_pitch_yawrate_thrust_sub_ =
00326         node_handle_->Subscribe("~/" + namespace_ + "/" +
00327             roll_pitch_yawrate_thrust_sub_topic_,
00328             &GazeboFwDynamicsPlugin::RollPitchYawrateThrustCallback, this);
00329 
00330     connect_ros_to_gazebo_topic_msg.set_ros_topic(namespace_ + "/" +
00331         roll_pitch_yawrate_thrust_sub_topic_);
00332     connect_ros_to_gazebo_topic_msg.set_gazebo_topic("~/" + namespace_ + "/" +
00333         roll_pitch_yawrate_thrust_sub_topic_);
00334     connect_ros_to_gazebo_topic_msg.set_msgtype(
00335         gz_std_msgs::ConnectRosToGazeboTopic::ROLL_PITCH_YAWRATE_THRUST);
00336   } else {
00337     // ============================================ //
00338     // ===== ACTUATORS MSG SETUP (ROS->GAZEBO) ==== //
00339     // ============================================ //
00340 
00341     actuators_sub_ =
00342         node_handle_->Subscribe("~/" + namespace_ + "/" + actuators_sub_topic_,
00343                                 &GazeboFwDynamicsPlugin::ActuatorsCallback,
00344                                 this);
00345 
00346     connect_ros_to_gazebo_topic_msg.set_ros_topic(namespace_ + "/" +
00347                                                   actuators_sub_topic_);
00348     connect_ros_to_gazebo_topic_msg.set_gazebo_topic("~/" + namespace_ + "/" +
00349                                                      actuators_sub_topic_);
00350     connect_ros_to_gazebo_topic_msg.set_msgtype(
00351         gz_std_msgs::ConnectRosToGazeboTopic::ACTUATORS);
00352   }
00353 
00354   gz_connect_ros_to_gazebo_topic_pub->Publish(connect_ros_to_gazebo_topic_msg,
00355                                               true);
00356 }
00357 
00358 void GazeboFwDynamicsPlugin::ActuatorsCallback(
00359     GzActuatorsMsgPtr &actuators_msg) {
00360   if (kPrintOnMsgCallback) {
00361     gzdbg << __FUNCTION__ << "() called." << std::endl;
00362   }
00363 
00364   delta_aileron_left_ = NormalizedInputToAngle(vehicle_params_.aileron_left,
00365       actuators_msg->normalized(vehicle_params_.aileron_left.channel));
00366   delta_aileron_right_ = NormalizedInputToAngle(vehicle_params_.aileron_right,
00367       actuators_msg->normalized(vehicle_params_.aileron_right.channel));
00368   delta_elevator_ = NormalizedInputToAngle(vehicle_params_.elevator,
00369       actuators_msg->normalized(vehicle_params_.elevator.channel));
00370   delta_flap_ = NormalizedInputToAngle(vehicle_params_.flap,
00371       actuators_msg->normalized(vehicle_params_.flap.channel));
00372   delta_rudder_ = NormalizedInputToAngle(vehicle_params_.rudder,
00373       actuators_msg->normalized(vehicle_params_.rudder.channel));
00374 
00375   throttle_ = actuators_msg->normalized(vehicle_params_.throttle_channel);
00376 }
00377 
00378 void GazeboFwDynamicsPlugin::RollPitchYawrateThrustCallback(
00379     GzRollPitchYawrateThrustMsgPtr& roll_pitch_yawrate_thrust_msg) {
00380   if (kPrintOnMsgCallback) {
00381     gzdbg << __FUNCTION__ << "() called." << std::endl;
00382   }
00383 
00384   delta_aileron_left_ = NormalizedInputToAngle(vehicle_params_.aileron_left,
00385       roll_pitch_yawrate_thrust_msg->roll());
00386   delta_aileron_right_ = -NormalizedInputToAngle(vehicle_params_.aileron_right,
00387       roll_pitch_yawrate_thrust_msg->roll());
00388   delta_elevator_ = NormalizedInputToAngle(vehicle_params_.elevator,
00389       roll_pitch_yawrate_thrust_msg->pitch());
00390   delta_rudder_ = NormalizedInputToAngle(vehicle_params_.rudder,
00391       roll_pitch_yawrate_thrust_msg->yaw_rate());
00392 
00393   throttle_ = roll_pitch_yawrate_thrust_msg->thrust().x();
00394 }
00395 
00396 void GazeboFwDynamicsPlugin::WindSpeedCallback(
00397     GzWindSpeedMsgPtr& wind_speed_msg) {
00398   if (kPrintOnMsgCallback) {
00399     gzdbg << __FUNCTION__ << "() called." << std::endl;
00400   }
00401 
00402   W_wind_speed_W_B_.X() = wind_speed_msg->velocity().x();
00403   W_wind_speed_W_B_.Y() = wind_speed_msg->velocity().y();
00404   W_wind_speed_W_B_.Z() = wind_speed_msg->velocity().z();
00405 }
00406 
00407 GZ_REGISTER_MODEL_PLUGIN(GazeboFwDynamicsPlugin);
00408 
00409 }  // namespace gazebo


rotors_gazebo_plugins
Author(s): Fadri Furrer, Michael Burri, Mina Kamel, Janosch Nikolic, Markus Achtelik
autogenerated on Thu Apr 18 2019 02:43:43