gazebo_motor_model.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland
00003  * Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland
00004  * Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland
00005  * Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland
00006  * Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland
00007  * Copyright 2016 Geoffrey Hunter <gbmhunter@gmail.com>
00008  *
00009  * Licensed under the Apache License, Version 2.0 (the "License");
00010  * you may not use this file except in compliance with the License.
00011  * You may obtain a copy of the License at
00012  *
00013  *     http://www.apache.org/licenses/LICENSE-2.0
00014 
00015  * Unless required by applicable law or agreed to in writing, software
00016  * distributed under the License is distributed on an "AS IS" BASIS,
00017  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.M
00018  * See the License for the specific language governing permissions and
00019  * limitations under the License.
00020  */
00021 
00022 #include "rotors_gazebo_plugins/gazebo_motor_model.h"
00023 
00024 #include "ConnectGazeboToRosTopic.pb.h"
00025 #include "ConnectRosToGazeboTopic.pb.h"
00026 
00027 namespace gazebo {
00028 
00029 GazeboMotorModel::~GazeboMotorModel() {
00030 }
00031 
00032 void GazeboMotorModel::InitializeParams() {}
00033 
00034 void GazeboMotorModel::Publish() {
00035   if (publish_speed_) {
00036     turning_velocity_msg_.set_data(joint_->GetVelocity(0));
00037     motor_velocity_pub_->Publish(turning_velocity_msg_);
00038   }
00039   if (publish_position_) {
00040     position_msg_.set_data(joint_->Position(0));
00041     motor_position_pub_->Publish(position_msg_);
00042   }
00043   if (publish_force_) {
00044     force_msg_.set_data(joint_->GetForce(0));
00045     motor_force_pub_->Publish(force_msg_);
00046   }
00047 }
00048 
00049 void GazeboMotorModel::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {
00050   if (kPrintOnPluginLoad) {
00051     gzdbg << __FUNCTION__ << "() called." << std::endl;
00052   }
00053 
00054   model_ = _model;
00055 
00056   namespace_.clear();
00057 
00058   if (_sdf->HasElement("robotNamespace"))
00059     namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>();
00060   else
00061     gzerr << "[gazebo_motor_model] Please specify a robotNamespace.\n";
00062 
00063   node_handle_ = gazebo::transport::NodePtr(new transport::Node());
00064 
00065   // Initialise with default namespace (typically /gazebo/default/)
00066   node_handle_->Init();
00067 
00068   if (_sdf->HasElement("jointName"))
00069     joint_name_ = _sdf->GetElement("jointName")->Get<std::string>();
00070   else
00071     gzerr << "[gazebo_motor_model] Please specify a jointName, where the rotor "
00072              "is attached.\n";
00073 
00074   // Get the pointer to the joint.
00075   joint_ = model_->GetJoint(joint_name_);
00076   if (joint_ == NULL)
00077     gzthrow(
00078         "[gazebo_motor_model] Couldn't find specified joint \"" << joint_name_
00079                                                                 << "\".");
00080 
00081   if (_sdf->HasElement("linkName"))
00082     link_name_ = _sdf->GetElement("linkName")->Get<std::string>();
00083   else
00084     gzerr << "[gazebo_motor_model] Please specify a linkName of the rotor.\n";
00085   link_ = model_->GetLink(link_name_);
00086   if (link_ == NULL)
00087     gzthrow(
00088         "[gazebo_motor_model] Couldn't find specified link \"" << link_name_
00089                                                                << "\".");
00090 
00091   if (_sdf->HasElement("motorNumber"))
00092     motor_number_ = _sdf->GetElement("motorNumber")->Get<int>();
00093   else
00094     gzerr << "[gazebo_motor_model] Please specify a motorNumber.\n";
00095 
00096   if (_sdf->HasElement("turningDirection")) {
00097     std::string turning_direction =
00098         _sdf->GetElement("turningDirection")->Get<std::string>();
00099     if (turning_direction == "cw")
00100       turning_direction_ = turning_direction::CW;
00101     else if (turning_direction == "ccw")
00102       turning_direction_ = turning_direction::CCW;
00103     else
00104       gzerr << "[gazebo_motor_model] Please only use 'cw' or 'ccw' as "
00105                "turningDirection.\n";
00106   } else
00107     gzerr << "[gazebo_motor_model] Please specify a turning direction ('cw' or "
00108              "'ccw').\n";
00109 
00110   if (_sdf->HasElement("motorType")) {
00111     std::string motor_type = _sdf->GetElement("motorType")->Get<std::string>();
00112     if (motor_type == "velocity")
00113       motor_type_ = MotorType::kVelocity;
00114     else if (motor_type == "position")
00115       motor_type_ = MotorType::kPosition;
00116     else if (motor_type == "force") {
00117       motor_type_ = MotorType::kForce;
00118     } else
00119       gzerr << "[gazebo_motor_model] Please only use 'velocity', 'position' or "
00120                "'force' as motorType.\n";
00121   } else {
00122     gzwarn << "[gazebo_motor_model] motorType not specified, using velocity.\n";
00123     motor_type_ = MotorType::kVelocity;
00124   }
00125 
00126   // Set up joint control PID to control joint.
00127   if (motor_type_ == MotorType::kPosition) {
00128     if (_sdf->HasElement("joint_control_pid")) {
00129       sdf::ElementPtr pid = _sdf->GetElement("joint_control_pid");
00130       double p = 0;
00131       if (pid->HasElement("p"))
00132         p = pid->Get<double>("p");
00133       double i = 0;
00134       if (pid->HasElement("i"))
00135         i = pid->Get<double>("i");
00136       double d = 0;
00137       if (pid->HasElement("d"))
00138         d = pid->Get<double>("d");
00139       double iMax = 0;
00140       if (pid->HasElement("iMax"))
00141         iMax = pid->Get<double>("iMax");
00142       double iMin = 0;
00143       if (pid->HasElement("iMin"))
00144         iMin = pid->Get<double>("iMin");
00145       double cmdMax = 0;
00146       if (pid->HasElement("cmdMax"))
00147         cmdMax = pid->Get<double>("cmdMax");
00148       double cmdMin = 0;
00149       if (pid->HasElement("cmdMin"))
00150         cmdMin = pid->Get<double>("cmdMin");
00151       pids_.Init(p, i, d, iMax, iMin, cmdMax, cmdMin);
00152     } else {
00153       pids_.Init(0, 0, 0, 0, 0, 0, 0);
00154       gzerr << "[gazebo_motor_model] PID values not found, Setting all values "
00155                "to zero!\n";
00156     }
00157   } else {
00158     pids_.Init(0, 0, 0, 0, 0, 0, 0);
00159   }
00160 
00161   getSdfParam<std::string>(
00162       _sdf, "commandSubTopic", command_sub_topic_, command_sub_topic_);
00163   getSdfParam<std::string>(
00164       _sdf, "windSpeedSubTopic", wind_speed_sub_topic_, wind_speed_sub_topic_);
00165   getSdfParam<std::string>(
00166       _sdf, "motorSpeedPubTopic", motor_speed_pub_topic_,
00167       motor_speed_pub_topic_);
00168 
00169   // Only publish position and force messages if a topic is specified in sdf.
00170   if (_sdf->HasElement("motorPositionPubTopic")) {
00171     publish_position_ = true;
00172     motor_position_pub_topic_ =
00173         _sdf->GetElement("motorPositionPubTopic")->Get<std::string>();
00174   }
00175   if (_sdf->HasElement("motorForcePubTopic")) {
00176     publish_force_ = true;
00177     motor_force_pub_topic_ =
00178         _sdf->GetElement("motorForcePubTopic")->Get<std::string>();
00179   }
00180 
00181   getSdfParam<double>(
00182       _sdf, "rotorDragCoefficient", rotor_drag_coefficient_,
00183       rotor_drag_coefficient_);
00184   getSdfParam<double>(
00185       _sdf, "rollingMomentCoefficient", rolling_moment_coefficient_,
00186       rolling_moment_coefficient_);
00187   getSdfParam<double>(
00188       _sdf, "maxRotVelocity", max_rot_velocity_, max_rot_velocity_);
00189   getSdfParam<double>(_sdf, "motorConstant", motor_constant_, motor_constant_);
00190   getSdfParam<double>(
00191       _sdf, "momentConstant", moment_constant_, moment_constant_);
00192 
00193   getSdfParam<double>(
00194       _sdf, "timeConstantUp", time_constant_up_, time_constant_up_);
00195   getSdfParam<double>(
00196       _sdf, "timeConstantDown", time_constant_down_, time_constant_down_);
00197   getSdfParam<double>(
00198       _sdf, "rotorVelocitySlowdownSim", rotor_velocity_slowdown_sim_, 10);
00199 
00200   // Listen to the update event. This event is broadcast every
00201   // simulation iteration.
00202   updateConnection_ = event::Events::ConnectWorldUpdateBegin(
00203       boost::bind(&GazeboMotorModel::OnUpdate, this, _1));
00204 
00205   // Create the first order filter.
00206   rotor_velocity_filter_.reset(
00207       new FirstOrderFilter<double>(
00208           time_constant_up_, time_constant_down_, ref_motor_input_));
00209 }
00210 
00211 // This gets called by the world update start event.
00212 void GazeboMotorModel::OnUpdate(const common::UpdateInfo& _info) {
00213   if (kPrintOnUpdates) {
00214     gzdbg << __FUNCTION__ << "() called." << std::endl;
00215   }
00216 
00217   if (!pubs_and_subs_created_) {
00218     CreatePubsAndSubs();
00219     pubs_and_subs_created_ = true;
00220   }
00221 
00222   sampling_time_ = _info.simTime.Double() - prev_sim_time_;
00223   prev_sim_time_ = _info.simTime.Double();
00224   UpdateForcesAndMoments();
00225   Publish();
00226 }
00227 
00228 void GazeboMotorModel::CreatePubsAndSubs() {
00229   gzdbg << __PRETTY_FUNCTION__ << " called." << std::endl;
00230 
00231   // Create temporary "ConnectGazeboToRosTopic" publisher and message
00232   gazebo::transport::PublisherPtr gz_connect_gazebo_to_ros_topic_pub =
00233       node_handle_->Advertise<gz_std_msgs::ConnectGazeboToRosTopic>(
00234           "~/" + kConnectGazeboToRosSubtopic, 1);
00235   gz_std_msgs::ConnectGazeboToRosTopic connect_gazebo_to_ros_topic_msg;
00236 
00237   // Create temporary "ConnectRosToGazeboTopic" publisher and message
00238   gazebo::transport::PublisherPtr gz_connect_ros_to_gazebo_topic_pub =
00239       node_handle_->Advertise<gz_std_msgs::ConnectRosToGazeboTopic>(
00240           "~/" + kConnectRosToGazeboSubtopic, 1);
00241   gz_std_msgs::ConnectRosToGazeboTopic connect_ros_to_gazebo_topic_msg;
00242 
00243   // ============================================ //
00244   //  ACTUAL MOTOR SPEED MSG SETUP (GAZEBO->ROS)  //
00245   // ============================================ //
00246   if (publish_speed_) {
00247     motor_velocity_pub_ = node_handle_->Advertise<gz_std_msgs::Float32>(
00248         "~/" + namespace_ + "/" + motor_speed_pub_topic_, 1);
00249 
00250     connect_gazebo_to_ros_topic_msg.set_gazebo_topic(
00251         "~/" + namespace_ + "/" + motor_speed_pub_topic_);
00252     connect_gazebo_to_ros_topic_msg.set_ros_topic(
00253         namespace_ + "/" + motor_speed_pub_topic_);
00254     connect_gazebo_to_ros_topic_msg.set_msgtype(
00255         gz_std_msgs::ConnectGazeboToRosTopic::FLOAT_32);
00256     gz_connect_gazebo_to_ros_topic_pub->Publish(
00257         connect_gazebo_to_ros_topic_msg, true);
00258   }
00259 
00260   // =============================================== //
00261   //  ACTUAL MOTOR POSITION MSG SETUP (GAZEBO->ROS)  //
00262   // =============================================== //
00263 
00264   if (publish_position_) {
00265     motor_position_pub_ = node_handle_->Advertise<gz_std_msgs::Float32>(
00266         "~/" + namespace_ + "/" + motor_position_pub_topic_, 1);
00267 
00268     connect_gazebo_to_ros_topic_msg.set_gazebo_topic(
00269         "~/" + namespace_ + "/" + motor_position_pub_topic_);
00270     connect_gazebo_to_ros_topic_msg.set_ros_topic(
00271         namespace_ + "/" + motor_position_pub_topic_);
00272     connect_gazebo_to_ros_topic_msg.set_msgtype(
00273         gz_std_msgs::ConnectGazeboToRosTopic::FLOAT_32);
00274     gz_connect_gazebo_to_ros_topic_pub->Publish(
00275         connect_gazebo_to_ros_topic_msg, true);
00276   }
00277 
00278   // ============================================ //
00279   //  ACTUAL MOTOR FORCE MSG SETUP (GAZEBO->ROS)  //
00280   // ============================================ //
00281 
00282   if (publish_force_) {
00283     motor_force_pub_ = node_handle_->Advertise<gz_std_msgs::Float32>(
00284         "~/" + namespace_ + "/" + motor_force_pub_topic_, 1);
00285 
00286     connect_gazebo_to_ros_topic_msg.set_gazebo_topic(
00287         "~/" + namespace_ + "/" + motor_force_pub_topic_);
00288     connect_gazebo_to_ros_topic_msg.set_ros_topic(
00289         namespace_ + "/" + motor_force_pub_topic_);
00290     connect_gazebo_to_ros_topic_msg.set_msgtype(
00291         gz_std_msgs::ConnectGazeboToRosTopic::FLOAT_32);
00292     gz_connect_gazebo_to_ros_topic_pub->Publish(
00293         connect_gazebo_to_ros_topic_msg, true);
00294   }
00295 
00296   // ============================================ //
00297   // = CONTROL COMMAND MSG SETUP (ROS->GAZEBO) = //
00298   // ============================================ //
00299 
00300   command_sub_ = node_handle_->Subscribe(
00301       "~/" + namespace_ + "/" + command_sub_topic_,
00302       &GazeboMotorModel::ControlCommandCallback, this);
00303 
00304   connect_ros_to_gazebo_topic_msg.set_ros_topic(
00305       namespace_ + "/" + command_sub_topic_);
00306   connect_ros_to_gazebo_topic_msg.set_gazebo_topic(
00307       "~/" + namespace_ + "/" + command_sub_topic_);
00308   connect_ros_to_gazebo_topic_msg.set_msgtype(
00309       gz_std_msgs::ConnectRosToGazeboTopic::COMMAND_MOTOR_SPEED);
00310   gz_connect_ros_to_gazebo_topic_pub->Publish(
00311       connect_ros_to_gazebo_topic_msg, true);
00312 
00313   // ============================================ //
00314   // ==== WIND SPEED MSG SETUP (ROS->GAZEBO) ==== //
00315   // ============================================ //
00316 
00318   wind_speed_sub_ = node_handle_->Subscribe(
00319       "~/" + namespace_ + "/" + wind_speed_sub_topic_,
00320       &GazeboMotorModel::WindSpeedCallback, this);
00321 
00322   connect_ros_to_gazebo_topic_msg.set_ros_topic(
00323       namespace_ + "/" + wind_speed_sub_topic_);
00324   connect_ros_to_gazebo_topic_msg.set_gazebo_topic(
00325       "~/" + namespace_ + "/" + wind_speed_sub_topic_);
00326   connect_ros_to_gazebo_topic_msg.set_msgtype(
00327       gz_std_msgs::ConnectRosToGazeboTopic::WIND_SPEED);
00328   gz_connect_ros_to_gazebo_topic_pub->Publish(
00329       connect_ros_to_gazebo_topic_msg, true);
00330 }
00331 
00332 void GazeboMotorModel::ControlCommandCallback(
00333     GzCommandMotorInputMsgPtr& command_motor_input_msg) {
00334   if (kPrintOnMsgCallback) {
00335     gzdbg << __FUNCTION__ << "() called." << std::endl;
00336   }
00337 
00338   if (motor_number_ > command_motor_input_msg->motor_speed_size() - 1) {
00339     gzerr << "You tried to access index " << motor_number_
00340           << " of the MotorSpeed message array which is of size "
00341           << command_motor_input_msg->motor_speed_size();
00342   }
00343 
00344   if (motor_type_ == MotorType::kVelocity) {
00345     ref_motor_input_ = std::min(
00346         static_cast<double>(
00347             command_motor_input_msg->motor_speed(motor_number_)),
00348         static_cast<double>(max_rot_velocity_));
00349   } else if (motor_type_ == MotorType::kPosition) {
00350     ref_motor_input_ = command_motor_input_msg->motor_speed(motor_number_);
00351   } else {  // if (motor_type_ == MotorType::kForce) {
00352     ref_motor_input_ = std::min(
00353         static_cast<double>(
00354             command_motor_input_msg->motor_speed(motor_number_)),
00355         static_cast<double>(max_force_));
00356   }
00357 }
00358 
00359 void GazeboMotorModel::WindSpeedCallback(GzWindSpeedMsgPtr& wind_speed_msg) {
00360   if (kPrintOnMsgCallback) {
00361     gzdbg << __FUNCTION__ << "() called." << std::endl;
00362   }
00363 
00364   // TODO(burrimi): Transform velocity to world frame if frame_id is set to
00365   // something else.
00366   wind_speed_W_.X() = wind_speed_msg->velocity().x();
00367   wind_speed_W_.Y() = wind_speed_msg->velocity().y();
00368   wind_speed_W_.Z() = wind_speed_msg->velocity().z();
00369 }
00370 
00371 double GazeboMotorModel::NormalizeAngle(double input){
00372       // Constrain magnitude to be max 2*M_PI.
00373       double wrapped = std::fmod(std::abs(input), 2*M_PI);
00374       wrapped = std::copysign(wrapped, input);
00375 
00376      // Constrain result to be element of [0, 2*pi).
00377      // Set angle to zero if sufficiently close to 2*pi.
00378      if(std::abs(wrapped - 2*M_PI) < 1e-8){
00379        wrapped = 0;
00380      }
00381 
00382      // Ensure angle is positive.
00383      if(wrapped < 0){
00384         wrapped += 2*M_PI;
00385      }
00386 
00387      return wrapped;
00388 }
00389 
00390 void GazeboMotorModel::UpdateForcesAndMoments() {
00391   switch (motor_type_) {
00392     case (MotorType::kPosition): {
00393       double err = NormalizeAngle(joint_->Position(0)) - NormalizeAngle(ref_motor_input_);
00394 
00395       // Angles are element of [0..2pi).
00396       // Constrain difference of angles to be in [-pi..pi).
00397       if (err > M_PI){
00398         err -= 2*M_PI;
00399       }
00400       if (err < -M_PI){
00401         err += 2*M_PI;
00402       }
00403       if(std::abs(err - M_PI) < 1e-8){
00404         err = -M_PI;
00405       }
00406 
00407       double force = pids_.Update(err, sampling_time_);
00408       joint_->SetForce(0, force);
00409       break;
00410     }
00411     case (MotorType::kForce): {
00412       joint_->SetForce(0, ref_motor_input_);
00413       break;
00414     }
00415     default:  // MotorType::kVelocity
00416     {
00417       motor_rot_vel_ = joint_->GetVelocity(0);
00418       if (motor_rot_vel_ / (2 * M_PI) > 1 / (2 * sampling_time_)) {
00419         gzerr << "Aliasing on motor [" << motor_number_
00420               << "] might occur. Consider making smaller simulation time "
00421                  "steps or raising the rotor_velocity_slowdown_sim_ param.\n";
00422       }
00423       double real_motor_velocity =
00424           motor_rot_vel_ * rotor_velocity_slowdown_sim_;
00425       // Get the direction of the rotor rotation.
00426       int real_motor_velocity_sign =
00427           (real_motor_velocity > 0) - (real_motor_velocity < 0);
00428       // Assuming symmetric propellers (or rotors) for the thrust calculation.
00429       double thrust = turning_direction_ * real_motor_velocity_sign *
00430                       real_motor_velocity * real_motor_velocity *
00431                       motor_constant_;
00432 
00433       // Apply a force to the link.
00434       link_->AddRelativeForce(ignition::math::Vector3d (0, 0, thrust));
00435 
00436       // Forces from Philppe Martin's and Erwan Salaün's
00437       // 2010 IEEE Conference on Robotics and Automation paper
00438       // The True Role of Accelerometer Feedback in Quadrotor Control
00439       // - \omega * \lambda_1 * V_A^{\perp}
00440       ignition::math::Vector3d joint_axis = joint_->GlobalAxis(0);
00441       ignition::math::Vector3d body_velocity_W = link_->WorldLinearVel();
00442       ignition::math::Vector3d relative_wind_velocity_W = body_velocity_W - wind_speed_W_;
00443       ignition::math::Vector3d body_velocity_perpendicular =
00444           relative_wind_velocity_W -
00445           (relative_wind_velocity_W.Dot(joint_axis) * joint_axis);
00446       ignition::math::Vector3d air_drag = -std::abs(real_motor_velocity) *
00447                                rotor_drag_coefficient_ *
00448                                body_velocity_perpendicular;
00449 
00450       // Apply air_drag to link.
00451       link_->AddForce(air_drag);
00452       // Moments get the parent link, such that the resulting torques can be
00453       // applied.
00454       physics::Link_V parent_links = link_->GetParentJointsLinks();
00455       // The tansformation from the parent_link to the link_.
00456       ignition::math::Pose3d pose_difference =
00457           link_->WorldCoGPose() - parent_links.at(0)->WorldCoGPose();
00458       ignition::math::Vector3d drag_torque(
00459           0, 0, -turning_direction_ * thrust * moment_constant_);
00460       // Transforming the drag torque into the parent frame to handle
00461       // arbitrary rotor orientations.
00462       ignition::math::Vector3d drag_torque_parent_frame =
00463           pose_difference.Rot().RotateVector(drag_torque);
00464       parent_links.at(0)->AddRelativeTorque(drag_torque_parent_frame);
00465 
00466       ignition::math::Vector3d rolling_moment;
00467       // - \omega * \mu_1 * V_A^{\perp}
00468       rolling_moment = -std::abs(real_motor_velocity) *
00469                        rolling_moment_coefficient_ *
00470                        body_velocity_perpendicular;
00471       parent_links.at(0)->AddTorque(rolling_moment);
00472       // Apply the filter on the motor's velocity.
00473       double ref_motor_rot_vel;
00474       ref_motor_rot_vel = rotor_velocity_filter_->updateFilter(
00475           ref_motor_input_, sampling_time_);
00476 
00477       // Make sure max force is set, as it may be reset to 0 by a world reset any
00478       // time. (This cannot be done during Reset() because the change will be undone
00479       // by the Joint's reset function afterwards.)
00480       joint_->SetVelocity(
00481           0, turning_direction_ * ref_motor_rot_vel /
00482                  rotor_velocity_slowdown_sim_);
00483     }
00484   }
00485 }
00486 
00487 GZ_REGISTER_MODEL_PLUGIN(GazeboMotorModel);
00488 }


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