$search
00001 /* 00002 * Gazebo - Outdoor Multi-Robot Simulator 00003 * Copyright (C) 2003 00004 * Nate Koenig & Andrew Howard 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 * 00020 */ 00021 00022 /* 00023 * Desc: ROS interface to a Position2d controller for a Differential drive with covariance information in odom message 00024 * Author: Jose Capriles (adapted from Daniel Hewlett and Nathan Koenig) 00025 */ 00026 00027 00028 #include <algorithm> 00029 #include <assert.h> 00030 00031 #include <reem_controller_plugin/reem_controller_plugin.h> 00032 00033 #include <gazebo/gazebo.h> 00034 #include <gazebo/GazeboError.hh> 00035 #include <gazebo/Quatern.hh> 00036 #include <gazebo/Controller.hh> 00037 #include <gazebo/Body.hh> 00038 #include <gazebo/World.hh> 00039 #include <gazebo/Simulator.hh> 00040 #include <gazebo/Global.hh> 00041 #include <gazebo/XMLConfig.hh> 00042 #include <gazebo/ControllerFactory.hh> 00043 #include <gazebo/PhysicsEngine.hh> 00044 00045 #include <ros/ros.h> 00046 #include <tf/transform_broadcaster.h> 00047 #include <tf/transform_listener.h> 00048 #include <geometry_msgs/Twist.h> 00049 #include <nav_msgs/Odometry.h> 00050 #include <boost/bind.hpp> 00051 #include <boost/assign/list_of.hpp> 00052 00053 using namespace gazebo; 00054 00055 //Register the new gazebo lybrary 00056 GZ_REGISTER_DYNAMIC_CONTROLLER("reem_controller_plugin", reemControllerPlugin); 00057 00058 enum 00059 { 00060 RIGHT, LEFT 00061 }; 00062 00063 // Constructor 00064 reemControllerPlugin::reemControllerPlugin(Entity *parent) : Controller(parent) 00065 { 00066 parent_ = dynamic_cast<Model*> (parent); 00067 00068 if (!parent_) 00069 gzthrow("Reem differential controller requires a Model as its parent"); 00070 00071 enableMotors = true; 00072 00073 wheelSpeed[RIGHT] = 0; 00074 wheelSpeed[LEFT] = 0; 00075 00076 prevUpdateTime = Simulator::Instance()->GetSimTime(); 00077 00078 Param::Begin(¶meters); 00079 leftJointNameP = new ParamT<std::string> ("leftJoint", "", 1); 00080 rightJointNameP = new ParamT<std::string> ("rightJoint", "", 1); 00081 wheelSepP = new ParamT<float> ("wheelSeparation", 0.34, 1); 00082 wheelDiamP = new ParamT<float> ("wheelDiameter", 0.15, 1); 00083 torqueP = new ParamT<float> ("torque", 10.0, 1); 00084 robotNamespaceP = new ParamT<std::string> ("robotNamespace", "/", 0); 00085 Param::End(); 00086 00087 x_ = 0; 00088 rot_ = 0; 00089 alive_ = true; 00090 } 00091 00092 // Destructor 00093 reemControllerPlugin::~reemControllerPlugin() 00094 { 00095 delete leftJointNameP; 00096 delete rightJointNameP; 00097 delete wheelSepP; 00098 delete wheelDiamP; 00099 delete torqueP; 00100 delete robotNamespaceP; 00101 delete callback_queue_thread_; 00102 delete rosnode_; 00103 delete transform_broadcaster_; 00104 } 00105 00106 // Load the controller 00107 void reemControllerPlugin::LoadChild(XMLConfigNode *node) 00108 { 00109 ROS_INFO("Reem controller plugin"); 00110 pos_iface_ = dynamic_cast<libgazebo::PositionIface*> (GetIface("position")); 00111 00112 wheelSepP->Load(node); 00113 wheelDiamP->Load(node); 00114 torqueP->Load(node); 00115 leftJointNameP->Load(node); 00116 rightJointNameP->Load(node); 00117 robotNamespaceP->Load(node); 00118 robotNamespace = robotNamespaceP->GetValue(); 00119 00120 joints[LEFT] = parent_->GetJoint(**leftJointNameP); 00121 joints[RIGHT] = parent_->GetJoint(**rightJointNameP); 00122 00123 if (!joints[LEFT]) 00124 gzthrow("The controller couldn't get left hinge joint"); 00125 00126 if (!joints[RIGHT]) 00127 gzthrow("The controller couldn't get right hinge joint"); 00128 00129 int argc = 0; 00130 char** argv = NULL; 00131 ros::init(argc, argv, "reem_controller_plugin", ros::init_options::NoSigintHandler | ros::init_options::AnonymousName); 00132 rosnode_ = new ros::NodeHandle(robotNamespace); 00133 00134 tf_prefix_ = tf::getPrefixParam(*rosnode_); 00135 transform_broadcaster_ = new tf::TransformBroadcaster(); 00136 00137 ros::SubscribeOptions so = ros::SubscribeOptions::create<geometry_msgs::Twist>("/cmd_vel", 100, boost::bind(&reemControllerPlugin::cmdVelCallback, this, _1), ros::VoidPtr(), &queue_); 00138 sub_ = rosnode_->subscribe(so); 00139 pub_ = rosnode_->advertise<nav_msgs::Odometry>("odom", 1); 00140 } 00141 00142 // Initialize the controller 00143 void reemControllerPlugin::InitChild() 00144 { 00145 // Initialize odometric pose 00146 odomPose[0] = 0.0; 00147 odomPose[1] = 0.0; 00148 odomPose[2] = 0.0; 00149 00150 odomVel[0] = 0.0; 00151 odomVel[1] = 0.0; 00152 odomVel[2] = 0.0; 00153 00154 callback_queue_thread_ = new boost::thread(boost::bind(&reemControllerPlugin::QueueThread, this)); 00155 } 00156 00157 // Load the controller 00158 void reemControllerPlugin::SaveChild(std::string &prefix, std::ostream &stream) 00159 { 00160 stream << prefix << *(leftJointNameP) << "\n"; 00161 stream << prefix << *(rightJointNameP) << "\n"; 00162 stream << prefix << *(torqueP) << "\n"; 00163 stream << prefix << *(wheelDiamP) << "\n"; 00164 stream << prefix << *(wheelSepP) << "\n"; 00165 } 00166 00167 // Reset 00168 void reemControllerPlugin::ResetChild() 00169 { 00170 // Reset odometric pose 00171 odomPose[0] = 0.0; 00172 odomPose[1] = 0.0; 00173 odomPose[2] = 0.0; 00174 00175 odomVel[0] = 0.0; 00176 odomVel[1] = 0.0; 00177 odomVel[2] = 0.0; 00178 } 00179 00180 // Update the controller 00181 void reemControllerPlugin::UpdateChild() 00182 { 00183 double wd, ws; 00184 double d1, d2; 00185 double dr, da; 00186 Time stepTime; 00187 00188 //myIface->Lock(1); 00189 00190 GetPositionCmd(); 00191 00192 wd = **(wheelDiamP); 00193 ws = **(wheelSepP); 00194 00195 //stepTime = World::Instance()->GetPhysicsEngine()->GetStepTime(); 00196 stepTime = Simulator::Instance()->GetSimTime() - prevUpdateTime; 00197 prevUpdateTime = Simulator::Instance()->GetSimTime(); 00198 00199 // Distance travelled by front wheels 00200 d1 = stepTime.Double() * wd / 2 * joints[LEFT]->GetVelocity(0); 00201 d2 = stepTime.Double() * wd / 2 * joints[RIGHT]->GetVelocity(0); 00202 00203 dr = (d1 + d2) / 2; 00204 da = (d1 - d2) / ws; 00205 00206 // Compute odometric pose 00207 odomPose[0] += dr * cos(odomPose[2]); 00208 odomPose[1] += dr * sin(odomPose[2]); 00209 odomPose[2] += da; 00210 00211 // Compute odometric instantaneous velocity 00212 odomVel[0] = dr / stepTime.Double(); 00213 odomVel[1] = 0.0; 00214 odomVel[2] = da / stepTime.Double(); 00215 00216 if (enableMotors) 00217 { 00218 joints[LEFT]->SetVelocity(0, wheelSpeed[LEFT] / (**(wheelDiamP) / 2.0)); 00219 00220 joints[RIGHT]->SetVelocity(0, wheelSpeed[RIGHT] / (**(wheelDiamP) / 2.0)); 00221 00222 joints[LEFT]->SetMaxForce(0, **(torqueP)); 00223 joints[RIGHT]->SetMaxForce(0, **(torqueP)); 00224 } 00225 00226 write_position_data(); 00227 publish_odometry(); 00228 00229 //myIface->Unlock(); 00230 } 00231 00232 // Finalize the controller 00233 void reemControllerPlugin::FiniChild() 00234 { 00235 //std::cout << "ENTERING FINALIZE\n"; 00236 alive_ = false; 00237 // Custom Callback Queue 00238 queue_.clear(); 00239 queue_.disable(); 00240 rosnode_->shutdown(); 00241 callback_queue_thread_->join(); 00242 //std::cout << "EXITING FINALIZE\n"; 00243 } 00244 00245 // NEW: Now uses the target velocities from the ROS message, not the Iface 00246 void reemControllerPlugin::GetPositionCmd() 00247 { 00248 lock.lock(); 00249 00250 double vr, va; 00251 00252 vr = x_; //myIface->data->cmdVelocity.pos.x; 00253 va = rot_; //myIface->data->cmdVelocity.yaw; 00254 00255 //std::cout << "X: [" << x_ << "] ROT: [" << rot_ << "]" << std::endl; 00256 00257 // Changed motors to be always on, which is probably what we want anyway 00258 enableMotors = true; //myIface->data->cmdEnableMotors > 0; 00259 00260 //std::cout << enableMotors << std::endl; 00261 00262 wheelSpeed[LEFT] = vr + va * **(wheelSepP) / 2; 00263 wheelSpeed[RIGHT] = vr - va * **(wheelSepP) / 2; 00264 00265 lock.unlock(); 00266 } 00267 00268 // NEW: Store the velocities from the ROS message 00269 void reemControllerPlugin::cmdVelCallback(const geometry_msgs::Twist::ConstPtr& cmd_msg) 00270 { 00271 //std::cout << "BEGIN CALLBACK\n"; 00272 00273 lock.lock(); 00274 00275 x_ = cmd_msg->linear.x; 00276 rot_ = cmd_msg->angular.z; 00277 00278 lock.unlock(); 00279 00280 //std::cout << "END CALLBACK\n"; 00281 } 00282 00283 // NEW: custom callback queue thread 00284 void reemControllerPlugin::QueueThread() 00285 { 00286 static const double timeout = 0.01; 00287 00288 while (alive_ && rosnode_->ok()) 00289 { 00290 // std::cout << "CALLING STUFF\n"; 00291 queue_.callAvailable(ros::WallDuration(timeout)); 00292 } 00293 } 00294 00295 // NEW: Update this to publish odometry topic 00296 void reemControllerPlugin::publish_odometry() 00297 { 00298 // get current time 00299 ros::Time current_time_((Simulator::Instance()->GetSimTime()).sec, (Simulator::Instance()->GetSimTime()).nsec); 00300 00301 // getting data for base_footprint to odom transform 00302 btQuaternion qt; 00303 // TODO: Is there something wrong here? RVIZ has a problem? 00304 qt.setEulerZYX(pos_iface_->data->pose.yaw, pos_iface_->data->pose.pitch, pos_iface_->data->pose.roll); 00305 btVector3 vt(pos_iface_->data->pose.pos.x, pos_iface_->data->pose.pos.y, pos_iface_->data->pose.pos.z); 00306 tf::Transform base_footprint_to_odom(qt, vt); 00307 00308 transform_broadcaster_->sendTransform(tf::StampedTransform(base_footprint_to_odom, 00309 current_time_, 00310 "odom", 00311 "base_footprint")); 00312 00313 // publish odom topic 00314 odom_.pose.pose.position.x = pos_iface_->data->pose.pos.x; 00315 odom_.pose.pose.position.y = pos_iface_->data->pose.pos.y; 00316 00317 gazebo::Quatern rot; 00318 rot.SetFromEuler(gazebo::Vector3(pos_iface_->data->pose.roll, pos_iface_->data->pose.pitch, pos_iface_->data->pose.yaw)); 00319 00320 odom_.pose.pose.orientation.x = rot.x; 00321 odom_.pose.pose.orientation.y = rot.y; 00322 odom_.pose.pose.orientation.z = rot.z; 00323 odom_.pose.pose.orientation.w = rot.u; 00324 00325 odom_.pose.covariance = boost::assign::list_of(1e-3) (0) (0) (0) (0) (0) 00326 (0) (1e-3) (0) (0) (0) (0) 00327 (0) (0) (1e6) (0) (0) (0) 00328 (0) (0) (0) (1e6) (0) (0) 00329 (0) (0) (0) (0) (1e6) (0) 00330 (0) (0) (0) (0) (0) (1e3) ; 00331 00332 odom_.twist.twist.linear.x = pos_iface_->data->velocity.pos.x; 00333 odom_.twist.twist.linear.y = pos_iface_->data->velocity.pos.y; 00334 odom_.twist.twist.angular.z = pos_iface_->data->velocity.yaw; 00335 00336 odom_.twist.covariance = boost::assign::list_of(1e-3) (0) (0) (0) (0) (0) 00337 (0) (1e-3) (0) (0) (0) (0) 00338 (0) (0) (1e6) (0) (0) (0) 00339 (0) (0) (0) (1e6) (0) (0) 00340 (0) (0) (0) (0) (1e6) (0) 00341 (0) (0) (0) (0) (0) (1e3) ; 00342 00343 odom_.header.frame_id = tf::resolve(tf_prefix_, "odom"); 00344 odom_.child_frame_id = "base_footprint"; 00345 00346 //odom_.header.stamp = current_time_; 00347 odom_.header.stamp.sec = (Simulator::Instance()->GetSimTime()).sec; 00348 odom_.header.stamp.nsec = (Simulator::Instance()->GetSimTime()).nsec; 00349 00350 pub_.publish(odom_); 00351 } 00352 00353 // Update the data in the interface 00354 void reemControllerPlugin::write_position_data() 00355 { 00356 // TODO: Data timestamp 00357 pos_iface_->data->head.time = Simulator::Instance()->GetSimTime().Double(); 00358 00359 pos_iface_->data->pose.pos.x = odomPose[0]; 00360 pos_iface_->data->pose.pos.y = odomPose[1]; 00361 pos_iface_->data->pose.yaw = NORMALIZE(odomPose[2]); 00362 00363 pos_iface_->data->velocity.pos.x = odomVel[0]; 00364 pos_iface_->data->velocity.yaw = odomVel[2]; 00365 00366 // TODO 00367 pos_iface_->data->stall = 0; 00368 } 00369