MobilityBasePlugin.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2014-2017, Dataspeed Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of Dataspeed Inc. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include <mobility_base_gazebo_plugins/MobilityBasePlugin.h>
00036 
00037 namespace gazebo
00038 {
00039 
00040 MobilityBasePlugin::MobilityBasePlugin() :
00041     nh_(NULL), spinner_thread_(NULL), tf_broadcaster_(NULL), first_update_(true), fast_(true)
00042 {
00043   omni_a_ = 1.0 / WHEEL_RADIUS;
00044   omni_b_ = 1.0 / WHEEL_RADIUS;
00045   omni_c_ = 0.5 * (WHEEL_BASE_WIDTH + WHEEL_BASE_LENGTH) / WHEEL_RADIUS;
00046   frame_id_ = "/base_footprint";
00047   mode_ = mobility_base_core_msgs::Mode::MODE_DISABLED;
00048 }
00049 MobilityBasePlugin::~MobilityBasePlugin()
00050 {
00051   if (nh_) {
00052     delete nh_;
00053   }
00054   if (spinner_thread_) {
00055     delete spinner_thread_;
00056   }
00057 }
00058 void MobilityBasePlugin::FiniChild()
00059 {
00060   nh_->shutdown();
00061   spinner_thread_->join();
00062 }
00063 
00064 void MobilityBasePlugin::omniFromCartesian(double vx, double vy, double wz, double w[4]) const {
00065     w[0] = omni_a_ * vx - omni_b_ * vy - omni_c_ * wz;
00066     w[1] = omni_a_ * vx + omni_b_ * vy + omni_c_ * wz;
00067     w[2] = omni_a_ * vx + omni_b_ * vy - omni_c_ * wz;
00068     w[3] = omni_a_ * vx - omni_b_ * vy + omni_c_ * wz;
00069 }
00070 
00071 // generated with MATLAB omni_invert(9.842519685, 9.842519685, 5.836279527)
00072 //const double slip_inv_[3][4] = {
00073 //  {+0.0254000000,+0.0254000000,+0.0254000000,+0.0254000000,},
00074 //  {-0.0254000000,+0.0254000000,+0.0254000000,-0.0254000000,},
00075 //  {-0.0428355083,+0.0428355083,-0.0428355083,+0.0428355083,},
00076 //};
00077 
00078 void MobilityBasePlugin::omniToCartesian(const double w[4], double *vx, double *vy, double *wz) const {
00079 //    *vx = slip_inv_[0][0] * w[0] + slip_inv_[0][1] * w[1] + slip_inv_[0][2] * w[2] + slip_inv_[0][3] * w[3];
00080 //    *vy = slip_inv_[1][0] * w[0] + slip_inv_[1][1] * w[1] + slip_inv_[1][2] * w[2] + slip_inv_[1][3] * w[3];
00081 //    *wz = slip_inv_[2][0] * w[0] + slip_inv_[2][1] * w[1] + slip_inv_[2][2] * w[2] + slip_inv_[2][3] * w[3];
00082 }
00083 
00084 void MobilityBasePlugin::Load(physics::ModelPtr parent, sdf::ElementPtr sdf)
00085 {
00086   first_update_ = true;
00087 
00088   // Store the pointer to the model and world
00089   model_ = parent;
00090   world_ = model_->GetWorld();
00091 
00092   // Get then name of the parent model
00093   std::string model_name = sdf->GetParent()->Get<std::string>("name");
00094   gzdbg << "MobilityBasePlugin loaded for model '" << model_name << "'\n";
00095 
00096   // Get parameters
00097   if (sdf->HasElement("fast")) {
00098     std::string comp = "true";
00099     fast_ = comp.compare(sdf->GetElement("fast")->Get<std::string>()) ? false : true;
00100   }
00101   if (sdf->HasElement("parent_frame_id")) {
00102     parent_frame_id_ = sdf->GetElement("parent_frame_id")->Get<std::string>();
00103     if (sdf->HasElement("child_frame_id")) {
00104       child_frame_id_ = sdf->GetElement("child_frame_id")->Get<std::string>();
00105     } else {
00106       child_frame_id_ = frame_id_;
00107     }
00108     tf_broadcaster_ = new tf::TransformBroadcaster();
00109   }
00110 
00111   // Get the pointer to the base_link
00112   link_base_footprint_ = model_->GetLink("base_footprint");
00113 
00114   joint_state_wheels_.name.push_back("wheel_fl");
00115   joint_state_wheels_.name.push_back("wheel_fr");
00116   joint_state_wheels_.name.push_back("wheel_rl");
00117   joint_state_wheels_.name.push_back("wheel_rr");
00118 
00119   // Setup joints
00120   for (unsigned int i = 0; i < NUM_WHEELS; i++) {
00121     // Wheel joint
00122     const std::string &wheel = joint_state_wheels_.name[i];
00123     joint_wheels_[i] = parent->GetJoint(wheel);
00124     if (joint_wheels_[i]) {
00125       joint_wheels_[i]->SetDamping(0, 0.1);
00126       joint_state_wheels_.position.push_back(0);
00127       joint_state_wheels_.velocity.push_back(0);
00128       joint_state_wheels_.effort.push_back(0);
00129     } else {
00130       gzerr << "Failed to find joint '" << wheel << "' in the model\n";
00131       return;
00132     }
00133     if (!fast_) {
00134     // Roller joints
00135       for (unsigned int j = 0; j < NUM_ROLLERS; j++) {
00136         std::stringstream ss;
00137         ss << wheel << "_roller_" << j;
00138         std::string roller = ss.str();
00139         joint_rollers_[i][j] = parent->GetJoint(roller);
00140         if (joint_rollers_[i][j]) {
00141           joint_rollers_[i][j]->SetDamping(0, 0.0005);
00142           joint_state_rollers_.name.push_back(roller);
00143           joint_state_rollers_.position.push_back(0);
00144           joint_state_rollers_.velocity.push_back(0);
00145           joint_state_rollers_.effort.push_back(0);
00146         } else {
00147           gzerr << "Failed to find joint '" << roller << "' in the model\n";
00148           return;
00149         }
00150       }
00151     }
00152   }
00153 
00154   // Initialize the ROS node
00155   int argc = 0;
00156   char** argv = NULL;
00157   ros::init(argc, argv, "gazebo_mobility_base", ros::init_options::NoSigintHandler | ros::init_options::AnonymousName);
00158   nh_ = new ros::NodeHandle("/mobility_base");
00159 
00160   // Set up Publishers
00161   pub_imu_data_ = nh_->advertise<sensor_msgs::Imu>("imu/data_raw", 10, false);
00162   pub_imu_mag_ = nh_->advertise<sensor_msgs::MagneticField>("imu/mag", 10, false);
00163   pub_imu_is_calibrated_ = nh_->advertise<std_msgs::Bool>("imu/is_calibrated", 1, true);
00164   pub_twist_ = nh_->advertise<geometry_msgs::TwistStamped>("twist", 10, false);
00165   pub_wrench_ = nh_->advertise<geometry_msgs::WrenchStamped>("wrench", 10, false);
00166   pub_joint_states_ = nh_->advertise<sensor_msgs::JointState>("joint_states", 10, false);
00167   pub_joystick_ = nh_->advertise<geometry_msgs::TwistStamped>("joystick", 10, false);
00168   pub_bumper_states_ = nh_->advertise<mobility_base_core_msgs::BumperState>("bumper_states", 10, true);
00169   pub_mode_ = nh_->advertise<mobility_base_core_msgs::Mode>("mode", 1, true);
00170 
00171   // Set up Publisher Queues
00172   pmq_.startServiceThread();
00173   pmq_imu_data_ = pmq_.addPub<sensor_msgs::Imu>();
00174   pmq_imu_mag_ = pmq_.addPub<sensor_msgs::MagneticField>();
00175   pmq_imu_is_calibrated_ = pmq_.addPub<std_msgs::Bool>();
00176   pmq_twist_ = pmq_.addPub<geometry_msgs::TwistStamped>();
00177   pmq_wrench_ = pmq_.addPub<geometry_msgs::WrenchStamped>();
00178   pmq_joint_states_ = pmq_.addPub<sensor_msgs::JointState>();
00179   pmq_joystick_ = pmq_.addPub<geometry_msgs::TwistStamped>();
00180   pmq_bumper_states_ = pmq_.addPub<mobility_base_core_msgs::BumperState>();
00181   pmq_mode_ = pmq_.addPub<mobility_base_core_msgs::Mode>();
00182 
00183   // Set up Subscribers
00184   sub_cmd_vel_ = nh_->subscribe("cmd_vel", 1, &MobilityBasePlugin::recvCmdVel, this);
00185   sub_cmd_vel_raw_ = nh_->subscribe("cmd_vel_raw", 1, &MobilityBasePlugin::recvCmdVelRaw, this);
00186 
00187   // Publish latched topics 'mode' and 'imu/is_calibrated'
00188   publishMode(ros::Time(0));
00189   std_msgs::Bool msg;
00190   msg.data = true;
00191   pmq_imu_is_calibrated_->push(msg, pub_imu_is_calibrated_);
00192 
00193   // Listen to the update event. This event is broadcast every simulation iteration.
00194   spinner_thread_ = new boost::thread(boost::bind( &MobilityBasePlugin::spin, this));
00195   update_connection_ = event::Events::ConnectWorldUpdateBegin(boost::bind(&MobilityBasePlugin::UpdateChild, this, _1));
00196 }
00197 
00198 // Called by the world update start event
00199 void MobilityBasePlugin::UpdateChild(const common::UpdateInfo & _info)
00200 {
00201   const common::Time gstamp = world_->GetSimTime();
00202   const ros::Time rstamp(gstamp.sec, gstamp.nsec);
00203   const double ts = (gstamp - previous_stamp_).Double();
00204 
00205   // Header for all messages
00206   std_msgs::Header header;
00207   header.stamp = rstamp;
00208   header.frame_id = frame_id_;
00209 
00210   // Grab model states from Gazebo
00211   math::Vector3 linear_vel;
00212   math::Vector3 angular_vel;
00213   math::Vector3 linear_accel;
00214   math::Vector3 angular_pos;
00215   math::Quaternion orientation;
00216   math::Vector3 position;
00217 
00218   linear_vel = model_->GetRelativeLinearVel();
00219   angular_vel = model_->GetRelativeAngularVel();
00220   linear_accel = model_->GetRelativeLinearAccel();
00221   angular_pos = model_->GetWorldPose().rot.GetAsEuler();
00222   orientation = model_->GetWorldPose().rot;
00223   position = model_->GetWorldPose().pos;
00224 
00225   const math::Vector3 fb_vel(linear_vel.x, linear_vel.y, angular_vel.z);
00226 
00227   // Command wheel velocity and torque to meet robot velocity request
00228   if (first_update_) {
00229     stamp_vehicle_ = gstamp;
00230     stamp_imu_ = gstamp;
00231     stamp_joystick_ = gstamp;
00232     stamp_bumpers_ = gstamp;
00233     stamp_mode_ = gstamp;
00234     cmd_vel_history_ = math::Vector3::Zero;
00235 
00236   } else {
00237     // Select command source
00238     math::Vector3 cmd = math::Vector3::Zero;
00239     mobility_base_core_msgs::Mode::_mode_type mode = mobility_base_core_msgs::Mode::MODE_TIMEOUT;
00240     {
00241       boost::lock_guard<boost::mutex> lock1(cmd_vel_mutex_);
00242       boost::lock_guard<boost::mutex> lock2(cmd_vel_raw_mutex_);
00243       if (cmd_vel_stamp_ > cmd_vel_raw_stamp_) {
00244         if (gstamp - cmd_vel_stamp_ < common::Time(CMD_TIMEOUT)) {
00245           cmd = cmd_vel_;
00246           mode = mobility_base_core_msgs::Mode::MODE_VELOCITY;
00247         }
00248       } else {
00249         if (gstamp - cmd_vel_raw_stamp_ < common::Time(CMD_TIMEOUT)) {
00250           cmd = cmd_vel_raw_;
00251           mode = mobility_base_core_msgs::Mode::MODE_VELOCITY_RAW;
00252         }
00253       }
00254     }
00255 
00256     // Select acceleration limits
00257     math::Vector3 accel_limit;
00258     switch (mode) {
00259       case mobility_base_core_msgs::Mode::MODE_VELOCITY:
00260         accel_limit.x = ACCEL_LIMIT_SLOW_VXY;
00261         accel_limit.y = ACCEL_LIMIT_SLOW_VXY;
00262         accel_limit.z = ACCEL_LIMIT_SLOW_WZ;
00263         break;
00264       case mobility_base_core_msgs::Mode::MODE_VELOCITY_RAW:
00265       case mobility_base_core_msgs::Mode::MODE_TIMEOUT:
00266       default:
00267         accel_limit.x = ACCEL_LIMIT_FAST_VXY;
00268         accel_limit.y = ACCEL_LIMIT_FAST_VXY;
00269         accel_limit.z = ACCEL_LIMIT_FAST_WZ;
00270         break;
00271     }
00272 
00273     // Initialize feedback for velocity change calculation
00274     if (mode != mode_) {
00275       cmd_vel_history_ = fb_vel;
00276     }
00277 
00278     // Limit change in velocity from previous command
00279     math::Vector3 temp;
00280     temp.x = limitDelta(cmd.x, cmd_vel_history_.x, accel_limit.x * ts); // m/s^2
00281     temp.y = limitDelta(cmd.y, cmd_vel_history_.y, accel_limit.y * ts); // m/s^2
00282     temp.z = limitDelta(cmd.z, cmd_vel_history_.z, accel_limit.z * ts); // rad/s^2
00283 
00284     // Limit change in velocity from feedback
00285     temp.x = limitDelta(temp.x, fb_vel.x, ACCEL_INSTANT_VXY); // m/s
00286     temp.y = limitDelta(temp.y, fb_vel.y, ACCEL_INSTANT_VXY); // m/s
00287     temp.z = limitDelta(temp.z, fb_vel.z, ACCEL_INSTANT_WZ); // rad/s
00288 
00289     // Compute motor velocity commands
00290     double speed[4];
00291     omniFromCartesian(temp.x, temp.y, temp.z, speed);
00292     if (omniSaturate(RADIANS_PER_SECOND_MAX, speed)) {
00293       cmd_vel_history_ = fb_vel;
00294     } else {
00295       cmd_vel_history_ = temp;
00296     }
00297 
00298     if (fast_) {
00299       // Apply force and torque to base_footprint to control mobility_base
00300       math::Vector3 linear_vel = orientation.RotateVector(math::Vector3(temp.x, temp.y, 0.0));
00301       math::Vector3 linear_vel_orig = link_base_footprint_->GetRelativeLinearVel();
00302       math::Vector3 angular_vel_orig = link_base_footprint_->GetRelativeAngularVel();
00303       link_base_footprint_->SetLinearVel(math::Vector3(GAIN_X * linear_vel.x, GAIN_Y * linear_vel.y, linear_vel_orig.z + linear_vel.z));
00304       link_base_footprint_->SetAngularVel(math::Vector3(angular_vel_orig.x, angular_vel_orig.y, GAIN_Z * temp.z));
00305     }
00306 
00307     // Command the wheel motors
00308     for (unsigned int i = 0; i < NUM_WHEELS; i++) {
00309       joint_wheels_[i]->SetVelocity(0, speed[i]);
00310 #if GAZEBO_MAJOR_VERSION >= 7
00311       joint_wheels_[i]->SetEffortLimit(0, TORQUE_MAX_GLOBAL);
00312 #else
00313       joint_wheels_[i]->SetMaxForce(0, TORQUE_MAX_GLOBAL);
00314 #endif
00315     }
00316 
00317     // Publish vehicle feedback
00318     if (gstamp - stamp_vehicle_ >= common::Time(1.0 / PUB_FREQ_VEHICLE)) {
00319       stamp_vehicle_ += common::Time(1.0 / PUB_FREQ_VEHICLE);
00320 
00321       // Publish twist
00322       geometry_msgs::TwistStamped msg_twist;
00323       msg_twist.header = header;
00324       msg_twist.twist.linear.x = linear_vel.x;
00325       msg_twist.twist.linear.y = linear_vel.y;
00326       msg_twist.twist.linear.z = linear_vel.z;
00327       msg_twist.twist.angular.x = angular_vel.x;
00328       msg_twist.twist.angular.y = angular_vel.y;
00329       msg_twist.twist.angular.z = angular_vel.z;
00330       pmq_twist_->push(msg_twist, pub_twist_);
00331 
00332       // Publish wrench
00333       geometry_msgs::WrenchStamped msg_wrench;
00334       msg_wrench.header = header;
00335       msg_wrench.wrench.force.x = 0.0;
00336       msg_wrench.wrench.force.y = 0.0;
00337       msg_wrench.wrench.torque.z = 0.0;
00338       pmq_wrench_->push(msg_wrench, pub_wrench_);
00339 
00340       // Publish joint_states
00341       joint_state_wheels_.header = header;
00342       if (!fast_)
00343         joint_state_rollers_.header = header;
00344       for (unsigned int i = 0; i < NUM_WHEELS; i++) {
00345         joint_state_wheels_.position[i] = joint_wheels_[i]->GetAngle(0).Radian();
00346         joint_state_wheels_.velocity[i] = joint_wheels_[i]->GetVelocity(0);
00347 
00348         if (!fast_) {
00349           for (unsigned int j = 0; j < NUM_ROLLERS; j++) {
00350             joint_state_rollers_.position[i * NUM_ROLLERS + j] = joint_rollers_[i][j]->GetAngle(0).Radian();
00351             joint_state_rollers_.velocity[i * NUM_ROLLERS + j] = joint_rollers_[i][j]->GetVelocity(0);
00352           }
00353         }
00354       }
00355       pmq_joint_states_->push(joint_state_wheels_, pub_joint_states_);
00356       if (!fast_)
00357         pmq_joint_states_->push(joint_state_rollers_, pub_joint_states_);
00358 
00359       // Optionally publish tf transform
00360       if (tf_broadcaster_) {
00361         geometry_msgs::TransformStamped transform;
00362         transform.header.stamp = rstamp;
00363         transform.header.frame_id = parent_frame_id_;
00364         transform.child_frame_id = child_frame_id_;
00365         transform.transform.translation.x = position.x;
00366         transform.transform.translation.y = position.y;
00367         transform.transform.translation.z = position.z;
00368         transform.transform.rotation.w = orientation.w;
00369         transform.transform.rotation.x = orientation.x;
00370         transform.transform.rotation.y = orientation.y;
00371         transform.transform.rotation.z = orientation.z;
00372         tf_broadcaster_->sendTransform(transform);
00373       }
00374     }
00375 
00376     // Publish imu feedback
00377     if (gstamp - stamp_imu_ >= common::Time(1.0 / PUB_FREQ_IMU)) {
00378       stamp_imu_ += common::Time(1.0 / PUB_FREQ_IMU);
00379 
00380       // Publish imu_data_
00381       sensor_msgs::Imu msg_imu;
00382       msg_imu.header = header;
00383       msg_imu.linear_acceleration.x = linear_accel.x;
00384       msg_imu.linear_acceleration.y = linear_accel.y;
00385       msg_imu.linear_acceleration.z = linear_accel.z;
00386       msg_imu.orientation_covariance[0] = -1;
00387       msg_imu.angular_velocity.x = angular_vel.x;
00388       msg_imu.angular_velocity.y = angular_vel.y;
00389       msg_imu.angular_velocity.z = angular_vel.z;
00390       msg_imu.angular_velocity_covariance[0] = -1;
00391       pmq_imu_data_->push(msg_imu, pub_imu_data_);
00392 
00393       sensor_msgs::MagneticField msg_mag;
00394       msg_mag.header = header;
00395       msg_mag.magnetic_field.x = 0.0;
00396       msg_mag.magnetic_field.y = 0.0;
00397       msg_mag.magnetic_field.z = 0.0;
00398       msg_mag.magnetic_field_covariance[0] = -1;
00399       pmq_imu_mag_->push(msg_mag, pub_imu_mag_);
00400     }
00401 
00402     // Publish joystick feedback
00403     if (gstamp - stamp_joystick_ >= common::Time(1.0 / PUB_FREQ_JOYSTICK)) {
00404       stamp_joystick_ += common::Time(1.0 / PUB_FREQ_JOYSTICK);
00405       geometry_msgs::TwistStamped msg;
00406       msg.header = header;
00407       pmq_joystick_->push(msg, pub_joystick_);
00408     }
00409 
00410     // Publish bumper_states feedback
00411     if (gstamp - stamp_bumpers_ >= common::Time(1.0 / PUB_FREQ_BUMPERS)) {
00412       stamp_bumpers_ += common::Time(1.0 / PUB_FREQ_BUMPERS);
00413       mobility_base_core_msgs::BumperState msg;
00414       msg.front_left = 0;
00415       msg.front_right = 0;
00416       msg.rear_left = 0;
00417       msg.rear_right = 0;
00418       pmq_bumper_states_->push(msg, pub_bumper_states_);
00419     }
00420 
00421     // Publish mode feedback
00422     if (mode != mode_) {
00423       stamp_mode_ = gstamp;
00424       publishMode(rstamp);
00425     } else if (gstamp - stamp_mode_ >= common::Time(1.0 / PUB_FREQ_MODE)) {
00426       stamp_mode_ += common::Time(1.0 / PUB_FREQ_MODE);
00427       publishMode(rstamp);
00428     }
00429 
00430     mode_ = mode;
00431   }
00432 
00433   previous_stamp_ = gstamp;
00434   first_update_ = false;
00435 }
00436 
00437 void MobilityBasePlugin::recvCmdVel(const geometry_msgs::Twist::ConstPtr& msg)
00438 {
00439   boost::lock_guard<boost::mutex> lock(cmd_vel_mutex_);
00440   cmd_vel_stamp_ = world_->GetSimTime();
00441   cmd_vel_.x = msg->linear.x;
00442   cmd_vel_.y = msg->linear.y;
00443   cmd_vel_.z = msg->angular.z;
00444 }
00445 
00446 void MobilityBasePlugin::recvCmdVelRaw(const geometry_msgs::Twist::ConstPtr& msg)
00447 {
00448   boost::lock_guard<boost::mutex> lock(cmd_vel_raw_mutex_);
00449   cmd_vel_raw_stamp_ = world_->GetSimTime();
00450   cmd_vel_raw_.x = msg->linear.x;
00451   cmd_vel_raw_.y = msg->linear.y;
00452   cmd_vel_raw_.z = msg->angular.z;
00453 }
00454 
00455 void MobilityBasePlugin::publishMode(const ros::Time &stamp)
00456 {
00457   mobility_base_core_msgs::Mode msg;
00458   msg.header.frame_id = frame_id_;
00459   msg.header.stamp = stamp;
00460   msg.mode = mode_;
00461   pmq_mode_->push(msg, pub_mode_);
00462 }
00463 
00464 void MobilityBasePlugin::spin()
00465 {
00466   while (ros::ok()) {
00467     ros::spinOnce();
00468   }
00469 }
00470 
00471 // Register this plugin with the simulator
00472 GZ_REGISTER_MODEL_PLUGIN(MobilityBasePlugin)
00473 }


mobility_base_gazebo_plugins
Author(s): Dataspeed Inc.
autogenerated on Thu Jun 6 2019 20:44:12