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
00041
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,
00075 LM = 1,
00076 LR = 2,
00077 RF = 3,
00078 RM = 4,
00079 RR = 5
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
00096 if(_sdf->HasElement(joint_names[i]))
00097 {
00098 joint_names[i] = _sdf->GetElement(joint_names[i])->Get<std::string>();
00099 }
00100
00101
00102 joints_tmp[i] = my_parent_->GetJoint(joint_names[i]);
00103 }
00104
00105
00106
00107 std::vector<int> missing_joints;
00108
00109
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
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
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
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
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
00194 std::string modelName = _sdf->GetParent()->Get<std::string>("name");
00195
00196
00197
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
00228 d1 = step_time.Double() * (wd / 2) * joints_[left]->GetVelocity(0);
00229 d2 = step_time.Double() * (wd / 2) * joints_[right]->GetVelocity(0);
00230
00231
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
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
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
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
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
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);