00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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
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
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
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
00201
00202 updateConnection_ = event::Events::ConnectWorldUpdateBegin(
00203 boost::bind(&GazeboMotorModel::OnUpdate, this, _1));
00204
00205
00206 rotor_velocity_filter_.reset(
00207 new FirstOrderFilter<double>(
00208 time_constant_up_, time_constant_down_, ref_motor_input_));
00209 }
00210
00211
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
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
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
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
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
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
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
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 {
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
00365
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
00373 double wrapped = std::fmod(std::abs(input), 2*M_PI);
00374 wrapped = std::copysign(wrapped, input);
00375
00376
00377
00378 if(std::abs(wrapped - 2*M_PI) < 1e-8){
00379 wrapped = 0;
00380 }
00381
00382
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
00396
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:
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
00426 int real_motor_velocity_sign =
00427 (real_motor_velocity > 0) - (real_motor_velocity < 0);
00428
00429 double thrust = turning_direction_ * real_motor_velocity_sign *
00430 real_motor_velocity * real_motor_velocity *
00431 motor_constant_;
00432
00433
00434 link_->AddRelativeForce(ignition::math::Vector3d (0, 0, thrust));
00435
00436
00437
00438
00439
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
00451 link_->AddForce(air_drag);
00452
00453
00454 physics::Link_V parent_links = link_->GetParentJointsLinks();
00455
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
00461
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
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
00473 double ref_motor_rot_vel;
00474 ref_motor_rot_vel = rotor_velocity_filter_->updateFilter(
00475 ref_motor_input_, sampling_time_);
00476
00477
00478
00479
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 }