gazebo_ros_diffdrive_uos.cpp
Go to the documentation of this file.
00001 #include <diffdrive_gazebo_plugin/gazebo_ros_diffdrive_uos.h>
00002 #include <nav_msgs/Odometry.h>
00003 #include <geometry_msgs/Twist.h>
00004 
00005 #include <ros/time.h>
00006 
00007 #include <tf/transform_broadcaster.h>
00008 
00009 using namespace gazebo;
00010 
00011 const double gazebo::GazeboRosDiffdrive::CMD_VEL_TIMEOUT = 0.6;
00012 
00013 GazeboRosDiffdrive::GazeboRosDiffdrive() :
00014   wheel_speed_right_(0.0),
00015   wheel_speed_left_(0.0)
00016 {
00017   this->spinner_thread_ = new boost::thread( boost::bind( &GazeboRosDiffdrive::spin, this) );
00018 }
00019 
00020 GazeboRosDiffdrive::~GazeboRosDiffdrive()
00021 {
00022   rosnode_->shutdown();
00023   this->spinner_thread_->join();
00024   delete this->spinner_thread_;
00025   delete rosnode_;
00026 }
00027 
00028 void GazeboRosDiffdrive::Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf )
00029 {
00030   this->my_world_ = _parent->GetWorld();
00031 
00032   this->my_parent_ = _parent;
00033   if (!this->my_parent_)
00034   {
00035     ROS_FATAL("Gazebo_ROS_Create controller requires a Model as its parent");
00036     return;
00037   }
00038 
00039 
00040   // this MUST be called 'robotNamespace' for proper remapping to work
00041   // TODO: could be 'node_namespace' now
00042   this->node_namespace_ = "/";
00043   if (_sdf->HasElement("robotNamespace"))
00044     this->node_namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>() + "/";
00045 
00046 
00047   cmd_vel_topic_name_ = "/cmd_vel";
00048   if (_sdf->HasElement("cmd_vel_topic_name"))
00049     cmd_vel_topic_name_ = _sdf->GetElement("cmd_vel_topic_name")->Get<std::string>();
00050 
00051   odom_topic_name_ = "/odom";
00052   if (_sdf->HasElement("odom_topic_name"))
00053     odom_topic_name_ = _sdf->GetElement("odom_topic_name")->Get<std::string>();
00054 
00055   joint_states_topic_name_ = "/joint_states";
00056   if (_sdf->HasElement("joint_states_topic_name"))
00057     joint_states_topic_name_ = _sdf->GetElement("joint_states_topic_name")->Get<std::string>();
00058 
00059   if (!ros::isInitialized())
00060   {
00061     int argc = 0;
00062     char** argv = NULL;
00063     ros::init(argc, argv, "gazebo_ros_diffdrive_uos", ros::init_options::NoSigintHandler|ros::init_options::AnonymousName);
00064   }
00065 
00066   rosnode_ = new ros::NodeHandle(node_namespace_);
00067 
00068   cmd_vel_sub_ = rosnode_->subscribe(cmd_vel_topic_name_, 1, &GazeboRosDiffdrive::OnCmdVel, this);
00069   odom_pub_ = rosnode_->advertise<nav_msgs::Odometry> (odom_topic_name_, 1);
00070   joint_state_pub_ = rosnode_->advertise<sensor_msgs::JointState> (joint_states_topic_name_, 1);
00071 
00072   enum
00073   {
00074     LF = 0, // left front wheel
00075     LM = 1, // left middle wheel
00076     LR = 2, // left rear wheel
00077     RF = 3, // right front wheel
00078     RM = 4, // right middle wheel
00079     RR = 5  // right rear wheel
00080   };
00081 
00082   std::vector<std::string> joint_names(6);
00083 
00084   joint_names[LF] = "left_front_wheel_joint";
00085   joint_names[LM] = "left_middle_wheel_joint";
00086   joint_names[LR] = "left_rear_wheel_joint";
00087   joint_names[RF] = "right_front_wheel_joint",
00088   joint_names[RM] = "right_middle_wheel_joint";
00089   joint_names[RR] = "right_rear_wheel_joint";
00090 
00091   std::vector<physics::JointPtr> joints_tmp(6);
00092 
00093   for(size_t i=0; i<joint_names.size(); i++)
00094   {
00095     // overwrite names, if configured in sdf
00096     if(_sdf->HasElement(joint_names[i]))
00097     {
00098       joint_names[i] = _sdf->GetElement(joint_names[i])->Get<std::string>();
00099     }
00100 
00101     // get all joint objects, some of them might not exist in the model
00102     joints_tmp[i] = my_parent_->GetJoint(joint_names[i]);
00103   }
00104 
00105   //check for availability
00106 
00107   std::vector<int> missing_joints;
00108 
00109   // left front, rear and right front and rear are required for a four wheel diffdrive robot
00110   if(!joints_tmp[LF]) missing_joints.push_back(LF);
00111   if(!joints_tmp[LR]) missing_joints.push_back(LR);
00112   if(!joints_tmp[RF]) missing_joints.push_back(RF);
00113   if(!joints_tmp[RR]) missing_joints.push_back(RR);
00114 
00115   // if one of the middle joints is available than the other should be available, too.
00116   if( !joints_tmp[LM] != !joints_tmp[RM] )
00117   {
00118     missing_joints.push_back(LM);
00119     missing_joints.push_back(RM);
00120   }
00121 
00122   if(!missing_joints.empty())
00123   {
00124     // build helpful error message with the missing joints
00125     std::string missing_err = "The controller couldn't get the joint(s): ";
00126     for(size_t i=0; i<missing_joints.size(); i++)
00127     {
00128       missing_err += joint_names[missing_joints[i]] + (i != missing_joints.size()-1 ? ", " : "");
00129     }
00130     gzthrow("This plugin supports either a four or a six wheeled robot, due to that the middle wheels are optional. "
00131                 + missing_err);
00132   }
00133 
00134   // Determine the number of joints, the type of robot, either four wheeled or six wheeled.
00135   bool six_wheeled = joints_tmp[LM] && joints_tmp[RM];
00136   num_joints_ = six_wheeled ? 6 : 4;
00137 
00138   int six_wheels[] = {LF, LM, LR, RF, RM, RR};
00139   int four_wheels[] = {LF, LR, RF, RR};
00140   int* wheels = six_wheeled ? six_wheels : four_wheels;
00141 
00142   if ( six_wheeled ) ROS_INFO_STREAM("The robot is six wheeled.");
00143   else ROS_INFO_STREAM("The robot is four wheeled");
00144 
00145   left = six_wheeled ? 1 : 0;
00146   right = six_wheeled ? 4 : 2;
00147 
00148   js_.name.resize(num_joints_);
00149   js_.position.resize(num_joints_);
00150   js_.velocity.resize(num_joints_);
00151   js_.effort.resize(num_joints_);
00152   joints_.resize(num_joints_);
00153 
00154   for (size_t i = 0; i < num_joints_; ++i)
00155   {
00156     js_.position[i] = 0;
00157     js_.velocity[i] = 0;
00158     js_.effort[i] = 0;
00159     js_.name[i] = joint_names[wheels[i]];
00160     joints_[i] = joints_tmp[wheels[i]];
00161   }
00162 
00163   wheel_sep_ = 0.34;
00164   if (_sdf->HasElement("wheel_separation"))
00165     wheel_sep_ = _sdf->GetElement("wheel_separation")->Get<double>();
00166 
00167   turning_adaptation_ = 0.15;
00168   if (_sdf->HasElement("turning_adaptation"))
00169     turning_adaptation_ = _sdf->GetElement("turning_adaptation")->Get<double>();
00170 
00171   wheel_diam_ = 0.15;
00172   if (_sdf->HasElement("wheel_diameter"))
00173     wheel_diam_ = _sdf->GetElement("wheel_diameter")->Get<double>();
00174 
00175   torque_ = 4.0;
00176   if (_sdf->HasElement("torque"))
00177     torque_ = _sdf->GetElement("torque")->Get<double>();
00178 
00179   max_velocity_ = 4.0;
00180   if (_sdf->HasElement("max_velocity"))
00181     max_velocity_ = _sdf->GetElement("max_velocity")->Get<double>();
00182 
00183   //initialize time and odometry position
00184 #if GAZEBO_MAJOR_VERSION > 8
00185   prev_update_time_ = last_cmd_vel_time_ = this->my_world_->SimTime();
00186 #else
00187   prev_update_time_ = last_cmd_vel_time_ = this->my_world_->GetSimTime();
00188 #endif
00189   odom_pose_[0] = 0.0;
00190   odom_pose_[1] = 0.0;
00191   odom_pose_[2] = 0.0;
00192 
00193   // Get then name of the parent model
00194   std::string modelName = _sdf->GetParent()->Get<std::string>("name");
00195 
00196   // Listen to the update event. This event is broadcast every
00197   // simulation iteration.
00198   this->updateConnection = event::Events::ConnectWorldUpdateBegin(
00199       boost::bind(&GazeboRosDiffdrive::UpdateChild, this));
00200   gzdbg << "plugin model name: " << modelName << "\n";
00201 
00202   ROS_INFO_STREAM("Plugin gazebo_ros_diffdrive_uos_uos initialized.");
00203 }
00204 
00205 void GazeboRosDiffdrive::UpdateChild()
00206 {
00207 #if GAZEBO_MAJOR_VERSION > 8
00208   common::Time time_now = this->my_world_->SimTime();
00209 #else
00210   common::Time time_now = this->my_world_->GetSimTime();
00211 #endif
00212   common::Time step_time = time_now - prev_update_time_;
00213   prev_update_time_ = time_now;
00214 
00215   double wd, ws;
00216   double d1, d2;
00217   double dr, da;
00218   double turning_adaptation;
00219 
00220   wd = wheel_diam_;
00221   ws = wheel_sep_;
00222   turning_adaptation = turning_adaptation_;
00223 
00224   d1 = d2 = 0;
00225   dr = da = 0;
00226 
00227   // Distance travelled by middle (six wheeled robot) or by front wheels (four wheeled robot)
00228   d1 = step_time.Double() * (wd / 2) * joints_[left]->GetVelocity(0);
00229   d2 = step_time.Double() * (wd / 2) * joints_[right]->GetVelocity(0);
00230 
00231   // Can see NaN values here, just zero them out if needed
00232   if (std::isnan(d1)) {
00233     ROS_WARN_THROTTLE(0.1, "gazebo_ros_diffdrive_uos: NaN in d1. Step time: %.2f. WD: %.2f. Velocity: %.2f", step_time.Double(), wd, joints_[left]->GetVelocity(0));
00234     d1 = 0;
00235   }
00236 
00237   if (std::isnan(d2)) {
00238     ROS_WARN_THROTTLE(0.1, "gazebo_ros_diffdrive_uos: NaN in d2. Step time: %.2f. WD: %.2f. Velocity: %.2f", step_time.Double(), wd, joints_[right]->GetVelocity(0));
00239     d2 = 0;
00240   }
00241 
00242   dr = (d1 + d2) / 2;
00243   da = (d2 - d1) / ws * turning_adaptation;
00244 
00245   // Compute odometric pose
00246   odom_pose_[0] += dr * cos(odom_pose_[2]);
00247   odom_pose_[1] += dr * sin(odom_pose_[2]);
00248   odom_pose_[2] += da;
00249 
00250   // Compute odometric instantaneous velocity
00251   odom_vel_[0] = dr / step_time.Double();
00252   odom_vel_[1] = 0.0;
00253   odom_vel_[2] = da / step_time.Double();
00254 
00255 #if GAZEBO_MAJOR_VERSION > 8
00256   if (this->my_world_->SimTime() > last_cmd_vel_time_ + common::Time(CMD_VEL_TIMEOUT))
00257 #else
00258   if (this->my_world_->GetSimTime() > last_cmd_vel_time_ + common::Time(CMD_VEL_TIMEOUT))
00259 #endif
00260   {
00261 #if GAZEBO_MAJOR_VERSION > 8
00262     ROS_DEBUG("gazebo_ros_diffdrive_uos: cmd_vel timeout - current: %f, last cmd_vel: %f, timeout: %f", this->my_world_->SimTime().Double(), last_cmd_vel_time_.Double(), common::Time(CMD_VEL_TIMEOUT).Double());
00263 #else
00264     ROS_DEBUG("gazebo_ros_diffdrive_uos: cmd_vel timeout - current: %f, last cmd_vel: %f, timeout: %f", this->my_world_->GetSimTime().Double(), last_cmd_vel_time_.Double(), common::Time(CMD_VEL_TIMEOUT).Double());
00265 #endif
00266     wheel_speed_left_ = wheel_speed_right_ = 0.0;
00267   }
00268 
00269   ROS_DEBUG("gazebo_ros_diffdrive_uos: setting wheel speeds (left; %f, right: %f)", wheel_speed_left_ / (wd / 2.0), wheel_speed_right_ / (wd / 2.0));
00270 
00271   // turn left wheels
00272   for (unsigned short i = 0; i < num_joints_/2; i++)
00273   {
00274     joints_[i]->SetVelocity(0, wheel_speed_left_ / (wd / 2.0));
00275 #if GAZEBO_MAJOR_VERSION < 5
00276     joints_[i]->SetMaxForce(0, torque_);
00277 #endif
00278   }
00279 
00280   // turn right wheels
00281   for (unsigned short i = num_joints_/2; i < num_joints_; i++)
00282   {
00283     joints_[i]->SetVelocity(0, wheel_speed_right_ / (wd / 2.0));
00284 #if GAZEBO_MAJOR_VERSION < 5
00285     joints_[i]->SetMaxForce(0, torque_);
00286 #endif
00287   }
00288 
00289   nav_msgs::Odometry odom;
00290   odom.header.stamp.sec = time_now.sec;
00291   odom.header.stamp.nsec = time_now.nsec;
00292   odom.header.frame_id = "odom_combined";
00293   odom.child_frame_id = "base_footprint";
00294   odom.pose.pose.position.x = odom_pose_[0];
00295   odom.pose.pose.position.y = odom_pose_[1];
00296   odom.pose.pose.position.z = 0;
00297 
00298   tf::Quaternion qt;
00299   qt.setEuler(0, 0, odom_pose_[2]);
00300 
00301   odom.pose.pose.orientation.x = qt.getX();
00302   odom.pose.pose.orientation.y = qt.getY();
00303   odom.pose.pose.orientation.z = qt.getZ();
00304   odom.pose.pose.orientation.w = qt.getW();
00305 
00306   double pose_cov[36] = { 1e-3, 0, 0, 0, 0, 0,
00307                           0, 1e-3, 0, 0, 0, 0,
00308                           0, 0, 1e6, 0, 0, 0,
00309                           0, 0, 0, 1e6, 0, 0,
00310                           0, 0, 0, 0, 1e6, 0,
00311                           0, 0, 0, 0, 0, 1e3};
00312 
00313   memcpy(&odom.pose.covariance[0], pose_cov, sizeof(double) * 36);
00314   memcpy(&odom.twist.covariance[0], pose_cov, sizeof(double) * 36);
00315 
00316   odom.twist.twist.linear.x = odom_vel_[0];
00317   odom.twist.twist.linear.y = 0;
00318   odom.twist.twist.linear.z = 0;
00319 
00320   odom.twist.twist.angular.x = 0;
00321   odom.twist.twist.angular.y = 0;
00322   odom.twist.twist.angular.z = odom_vel_[2];
00323 
00324   odom_pub_.publish(odom);
00325 
00326   js_.header.stamp.sec = time_now.sec;
00327   js_.header.stamp.nsec = time_now.nsec;
00328 
00329   for (size_t i = 0; i < num_joints_; ++i)
00330   {
00331 #if GAZEBO_MAJOR_VERSION > 8
00332     js_.position[i] = joints_[i]->Position(0);
00333 #else
00334     js_.position[i] = joints_[i]->GetAngle(0).Radian();
00335 #endif
00336     js_.velocity[i] = joints_[i]->GetVelocity(0);
00337   }
00338 
00339   joint_state_pub_.publish(js_);
00340 }
00341 
00342 void GazeboRosDiffdrive::OnCmdVel(const geometry_msgs::TwistConstPtr &msg)
00343 {
00344 #if GAZEBO_MAJOR_VERSION > 8
00345   last_cmd_vel_time_ = this->my_world_->SimTime();
00346 #else
00347   last_cmd_vel_time_ = this->my_world_->GetSimTime();
00348 #endif
00349   double vr, va;
00350   vr = msg->linear.x;
00351   va = msg->angular.z;
00352 
00353   wheel_speed_left_ = vr - va * wheel_sep_ / 2;
00354   wheel_speed_right_ = vr + va * wheel_sep_ / 2;
00355 
00356   // limit wheel speed
00357   if (fabs(wheel_speed_left_) > max_velocity_)
00358     wheel_speed_left_ = copysign(max_velocity_, wheel_speed_left_);
00359   if (fabs(wheel_speed_right_) > max_velocity_)
00360     wheel_speed_right_ = copysign(max_velocity_, wheel_speed_right_);
00361 }
00362 
00363 void GazeboRosDiffdrive::spin()
00364 {
00365   while(ros::ok()) ros::spinOnce();
00366 }
00367 
00368 GZ_REGISTER_MODEL_PLUGIN(GazeboRosDiffdrive);


diffdrive_gazebo_plugin
Author(s): Martin Guenther
autogenerated on Thu Jun 6 2019 18:32:09