$search
00001 //================================================================================================= 00002 // Copyright (c) 2011, Stefan Kohlbrecher, TU Darmstadt 00003 // All rights reserved. 00004 00005 // Redistribution and use in source and binary forms, with or without 00006 // modification, are permitted provided that the following conditions are met: 00007 // * Redistributions of source code must retain the above copyright 00008 // notice, this list of conditions and the following disclaimer. 00009 // * Redistributions in binary form must reproduce the above copyright 00010 // notice, this list of conditions and the following disclaimer in the 00011 // documentation and/or other materials provided with the distribution. 00012 // * Neither the name of the Simulation, Systems Optimization and Robotics 00013 // group, TU Darmstadt nor the names of its contributors may be used to 00014 // endorse or promote products derived from this software without 00015 // specific prior written permission. 00016 00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00018 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00019 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00020 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00027 //================================================================================================= 00028 00033 #include <algorithm> 00034 #include <assert.h> 00035 00036 #include <hector_gazebo_plugins/diffdrive_plugin_6w.h> 00037 00038 #include <gazebo/gazebo.h> 00039 #include <gazebo/GazeboError.hh> 00040 #include <gazebo/Quatern.hh> 00041 #include <gazebo/Controller.hh> 00042 #include <gazebo/Body.hh> 00043 #include <gazebo/World.hh> 00044 #include <gazebo/Simulator.hh> 00045 #include <gazebo/Global.hh> 00046 #include <gazebo/XMLConfig.hh> 00047 #include <gazebo/ControllerFactory.hh> 00048 #include <gazebo/PhysicsEngine.hh> 00049 00050 #include <ros/ros.h> 00051 #include <tf/transform_broadcaster.h> 00052 #include <tf/transform_listener.h> 00053 #include <geometry_msgs/Twist.h> 00054 #include <nav_msgs/Odometry.h> 00055 #include <boost/bind.hpp> 00056 00057 using namespace gazebo; 00058 00059 GZ_REGISTER_DYNAMIC_CONTROLLER("diffdrive_plugin_6w", DiffDrivePlugin6W); 00060 00061 enum 00062 { 00063 FRONT_LEFT, 00064 FRONT_RIGHT, 00065 MID_LEFT, 00066 MID_RIGHT, 00067 REAR_LEFT, 00068 REAR_RIGHT, 00069 NUM_WHEELS 00070 }; 00071 00072 // Constructor 00073 DiffDrivePlugin6W::DiffDrivePlugin6W(Entity *parent) : 00074 Controller(parent) 00075 { 00076 parent_ = dynamic_cast<Model*> (parent); 00077 00078 if (!parent_) 00079 gzthrow("Differential_Position2d controller requires a Model as its parent"); 00080 00081 enableMotors = true; 00082 00083 for (size_t i = 0; i < 2; ++i){ 00084 wheelSpeed[i] = 0; 00085 } 00086 00087 prevUpdateTime = Simulator::Instance()->GetSimTime(); 00088 00089 Param::Begin(¶meters); 00090 frontLeftJointNameP = new ParamT<std::string> ("frontLeftJoint", "", 1); 00091 frontRightJointNameP = new ParamT<std::string> ("frontRightJoint", "", 1); 00092 midLeftJointNameP = new ParamT<std::string> ("midLeftJoint", "", 1); 00093 midRightJointNameP = new ParamT<std::string> ("midRightJoint", "", 1); 00094 rearLeftJointNameP = new ParamT<std::string> ("rearLeftJoint", "", 1); 00095 rearRightJointNameP = new ParamT<std::string> ("rearRightJoint", "", 1); 00096 00097 wheelSepP = new ParamT<float> ("wheelSeparation", 0.34, 1); 00098 wheelDiamP = new ParamT<float> ("wheelDiameter", 0.15, 1); 00099 torqueP = new ParamT<float> ("torque", 10.0, 1); 00100 robotNamespaceP = new ParamT<std::string> ("robotNamespace", "", 0); 00101 topicNameP = new ParamT<std::string> ("topicName", "", 1); 00102 Param::End(); 00103 00104 x_ = 0; 00105 rot_ = 0; 00106 alive_ = true; 00107 } 00108 00109 // Destructor 00110 DiffDrivePlugin6W::~DiffDrivePlugin6W() 00111 { 00112 00113 delete frontLeftJointNameP; 00114 delete frontRightJointNameP; 00115 delete midLeftJointNameP; 00116 delete midRightJointNameP; 00117 delete rearLeftJointNameP; 00118 delete rearRightJointNameP; 00119 00120 delete wheelSepP; 00121 delete wheelDiamP; 00122 delete torqueP; 00123 delete robotNamespaceP; 00124 delete topicNameP; 00125 delete callback_queue_thread_; 00126 delete rosnode_; 00127 delete transform_broadcaster_; 00128 } 00129 00130 // Load the controller 00131 void DiffDrivePlugin6W::LoadChild(XMLConfigNode *node) 00132 { 00133 pos_iface_ = dynamic_cast<libgazebo::PositionIface*> (GetIface("position")); 00134 00135 // the defaults are from pioneer2dx 00136 wheelSepP->Load(node); 00137 wheelDiamP->Load(node); 00138 torqueP->Load(node); 00139 00140 //leftJointNameP->Load(node); 00141 //rightJointNameP->Load(node); 00142 00143 frontLeftJointNameP->Load(node); 00144 frontRightJointNameP->Load(node); 00145 midLeftJointNameP->Load(node); 00146 midRightJointNameP->Load(node); 00147 rearLeftJointNameP->Load(node); 00148 rearRightJointNameP->Load(node); 00149 00150 joints[FRONT_LEFT] = parent_->GetJoint(**frontLeftJointNameP); 00151 joints[FRONT_RIGHT] = parent_->GetJoint(**frontRightJointNameP); 00152 joints[MID_LEFT] = parent_->GetJoint(**midLeftJointNameP); 00153 joints[MID_RIGHT] = parent_->GetJoint(**midRightJointNameP); 00154 joints[REAR_LEFT] = parent_->GetJoint(**rearLeftJointNameP); 00155 joints[REAR_RIGHT] = parent_->GetJoint(**rearRightJointNameP); 00156 00157 //if (!joints[LEFT]) 00158 // gzthrow("The controller couldn't get left hinge joint"); 00159 00160 //if (!joints[RIGHT]) 00161 // gzthrow("The controller couldn't get right hinge joint"); 00162 00163 // Initialize the ROS node and subscribe to cmd_vel 00164 00165 robotNamespaceP->Load(node); 00166 robotNamespace = robotNamespaceP->GetValue(); 00167 00168 int argc = 0; 00169 char** argv = NULL; 00170 ros::init(argc, argv, "diff_drive_plugin", ros::init_options::NoSigintHandler | ros::init_options::AnonymousName); 00171 rosnode_ = new ros::NodeHandle(robotNamespace); 00172 00173 tf_prefix_ = tf::getPrefixParam(*rosnode_); 00174 transform_broadcaster_ = new tf::TransformBroadcaster(); 00175 00176 topicNameP->Load(node); 00177 topicName = topicNameP->GetValue(); 00178 00179 // ROS: Subscribe to the velocity command topic (usually "cmd_vel") 00180 ros::SubscribeOptions so = 00181 ros::SubscribeOptions::create<geometry_msgs::Twist>(topicName, 1, 00182 boost::bind(&DiffDrivePlugin6W::cmdVelCallback, this, _1), 00183 ros::VoidPtr(), &queue_); 00184 sub_ = rosnode_->subscribe(so); 00185 pub_ = rosnode_->advertise<nav_msgs::Odometry>("odom", 1); 00186 } 00187 00188 // Initialize the controller 00189 void DiffDrivePlugin6W::InitChild() 00190 { 00191 // Reset odometric pose 00192 odomPose[0] = 0.0; 00193 odomPose[1] = 0.0; 00194 odomPose[2] = 0.0; 00195 00196 odomVel[0] = 0.0; 00197 odomVel[1] = 0.0; 00198 odomVel[2] = 0.0; 00199 00200 callback_queue_thread_ = new boost::thread(boost::bind(&DiffDrivePlugin6W::QueueThread, this)); 00201 } 00202 00203 // Load the controller 00204 void DiffDrivePlugin6W::SaveChild(std::string &prefix, std::ostream &stream) 00205 { 00206 stream << prefix << *(frontLeftJointNameP) << "\n"; 00207 stream << prefix << *(frontRightJointNameP) << "\n"; 00208 stream << prefix << *(midLeftJointNameP) << "\n"; 00209 stream << prefix << *(midRightJointNameP) << "\n"; 00210 stream << prefix << *(rearLeftJointNameP) << "\n"; 00211 stream << prefix << *(rearRightJointNameP) << "\n"; 00212 00213 stream << prefix << *(torqueP) << "\n"; 00214 stream << prefix << *(wheelDiamP) << "\n"; 00215 stream << prefix << *(wheelSepP) << "\n"; 00216 } 00217 00218 // Reset 00219 void DiffDrivePlugin6W::ResetChild() 00220 { 00221 // Reset odometric pose 00222 odomPose[0] = 0.0; 00223 odomPose[1] = 0.0; 00224 odomPose[2] = 0.0; 00225 00226 odomVel[0] = 0.0; 00227 odomVel[1] = 0.0; 00228 odomVel[2] = 0.0; 00229 } 00230 00231 // Update the controller 00232 void DiffDrivePlugin6W::UpdateChild() 00233 { 00234 // TODO: Step should be in a parameter of this function 00235 double wd, ws; 00236 double d1, d2; 00237 double dr, da; 00238 Time stepTime; 00239 00240 //myIface->Lock(1); 00241 00242 GetPositionCmd(); 00243 00244 wd = **(wheelDiamP); 00245 ws = **(wheelSepP); 00246 00247 //stepTime = World::Instance()->GetPhysicsEngine()->GetStepTime(); 00248 stepTime = Simulator::Instance()->GetSimTime() - prevUpdateTime; 00249 prevUpdateTime = Simulator::Instance()->GetSimTime(); 00250 00251 // Distance travelled by front wheels 00252 d1 = stepTime.Double() * wd / 2 * joints[MID_LEFT]->GetVelocity(0); 00253 d2 = stepTime.Double() * wd / 2 * joints[MID_RIGHT]->GetVelocity(0); 00254 00255 dr = (d1 + d2) / 2; 00256 da = (d1 - d2) / ws; 00257 00258 // Compute odometric pose 00259 odomPose[0] += dr * cos(odomPose[2]); 00260 odomPose[1] += dr * sin(odomPose[2]); 00261 odomPose[2] += da; 00262 00263 // Compute odometric instantaneous velocity 00264 odomVel[0] = dr / stepTime.Double(); 00265 odomVel[1] = 0.0; 00266 odomVel[2] = da / stepTime.Double(); 00267 00268 if (enableMotors) 00269 { 00270 joints[FRONT_LEFT]->SetVelocity(0, wheelSpeed[0] / (**(wheelDiamP) / 2.0)); 00271 joints[MID_LEFT]->SetVelocity(0, wheelSpeed[0] / (**(wheelDiamP) / 2.0)); 00272 joints[REAR_LEFT]->SetVelocity(0, wheelSpeed[0] / (**(wheelDiamP) / 2.0)); 00273 00274 joints[FRONT_RIGHT]->SetVelocity(0, wheelSpeed[1] / (**(wheelDiamP) / 2.0)); 00275 joints[MID_RIGHT]->SetVelocity(0, wheelSpeed[1] / (**(wheelDiamP) / 2.0)); 00276 joints[REAR_RIGHT]->SetVelocity(0, wheelSpeed[1] / (**(wheelDiamP) / 2.0)); 00277 00278 joints[FRONT_LEFT]->SetMaxForce(0, **(torqueP)); 00279 joints[MID_LEFT]->SetMaxForce(0, **(torqueP)); 00280 joints[REAR_LEFT]->SetMaxForce(0, **(torqueP)); 00281 00282 joints[FRONT_RIGHT]->SetMaxForce(0, **(torqueP)); 00283 joints[MID_RIGHT]->SetMaxForce(0, **(torqueP)); 00284 joints[REAR_RIGHT]->SetMaxForce(0, **(torqueP)); 00285 } 00286 00287 write_position_data(); 00288 //publish_odometry(); 00289 00290 //myIface->Unlock(); 00291 } 00292 00293 // Finalize the controller 00294 void DiffDrivePlugin6W::FiniChild() 00295 { 00296 //std::cout << "ENTERING FINALIZE\n"; 00297 alive_ = false; 00298 // Custom Callback Queue 00299 queue_.clear(); 00300 queue_.disable(); 00301 rosnode_->shutdown(); 00302 callback_queue_thread_->join(); 00303 //std::cout << "EXITING FINALIZE\n"; 00304 } 00305 00306 // NEW: Now uses the target velocities from the ROS message, not the Iface 00307 void DiffDrivePlugin6W::GetPositionCmd() 00308 { 00309 lock.lock(); 00310 00311 double vr, va; 00312 00313 vr = x_; //myIface->data->cmdVelocity.pos.x; 00314 va = rot_; //myIface->data->cmdVelocity.yaw; 00315 00316 //std::cout << "X: [" << x_ << "] ROT: [" << rot_ << "]" << std::endl; 00317 00318 // Changed motors to be always on, which is probably what we want anyway 00319 enableMotors = true; //myIface->data->cmdEnableMotors > 0; 00320 00321 //std::cout << enableMotors << std::endl; 00322 00323 wheelSpeed[0] = vr + va * **(wheelSepP) / 2; 00324 wheelSpeed[1] = vr - va * **(wheelSepP) / 2; 00325 00326 lock.unlock(); 00327 } 00328 00329 // NEW: Store the velocities from the ROS message 00330 void DiffDrivePlugin6W::cmdVelCallback(const geometry_msgs::Twist::ConstPtr& cmd_msg) 00331 { 00332 //std::cout << "BEGIN CALLBACK\n"; 00333 00334 lock.lock(); 00335 00336 x_ = cmd_msg->linear.x; 00337 rot_ = cmd_msg->angular.z; 00338 00339 lock.unlock(); 00340 00341 //std::cout << "END CALLBACK\n"; 00342 } 00343 00344 // NEW: custom callback queue thread 00345 void DiffDrivePlugin6W::QueueThread() 00346 { 00347 static const double timeout = 0.01; 00348 00349 while (alive_ && rosnode_->ok()) 00350 { 00351 // std::cout << "CALLING STUFF\n"; 00352 queue_.callAvailable(ros::WallDuration(timeout)); 00353 } 00354 } 00355 00356 // NEW: Update this to publish odometry topic 00357 void DiffDrivePlugin6W::publish_odometry() 00358 { 00359 // get current time 00360 ros::Time current_time_((Simulator::Instance()->GetSimTime()).sec, (Simulator::Instance()->GetSimTime()).nsec); 00361 00362 // getting data for base_footprint to odom transform 00363 btQuaternion qt; 00364 // TODO: Is there something wrong here? RVIZ has a problem? 00365 qt.setEulerZYX(pos_iface_->data->pose.yaw, pos_iface_->data->pose.pitch, pos_iface_->data->pose.roll); 00366 btVector3 vt(pos_iface_->data->pose.pos.x, pos_iface_->data->pose.pos.y, pos_iface_->data->pose.pos.z); 00367 tf::Transform base_footprint_to_odom(qt, vt); 00368 00369 transform_broadcaster_->sendTransform(tf::StampedTransform(base_footprint_to_odom, 00370 current_time_, 00371 "odom", 00372 "base_footprint")); 00373 00374 // publish odom topic 00375 odom_.pose.pose.position.x = pos_iface_->data->pose.pos.x; 00376 odom_.pose.pose.position.y = pos_iface_->data->pose.pos.y; 00377 00378 gazebo::Quatern rot; 00379 rot.SetFromEuler(gazebo::Vector3(pos_iface_->data->pose.roll, pos_iface_->data->pose.pitch, pos_iface_->data->pose.yaw)); 00380 00381 odom_.pose.pose.orientation.x = rot.x; 00382 odom_.pose.pose.orientation.y = rot.y; 00383 odom_.pose.pose.orientation.z = rot.z; 00384 odom_.pose.pose.orientation.w = rot.u; 00385 00386 odom_.twist.twist.linear.x = pos_iface_->data->velocity.pos.x; 00387 odom_.twist.twist.linear.y = pos_iface_->data->velocity.pos.y; 00388 odom_.twist.twist.angular.z = pos_iface_->data->velocity.yaw; 00389 00390 odom_.header.frame_id = tf::resolve(tf_prefix_, "odom"); 00391 odom_.child_frame_id = "base_footprint"; 00392 00393 //odom_.header.stamp = current_time_; 00394 odom_.header.stamp.sec = (Simulator::Instance()->GetSimTime()).sec; 00395 odom_.header.stamp.nsec = (Simulator::Instance()->GetSimTime()).nsec; 00396 00397 pub_.publish(odom_); 00398 } 00399 00400 // Update the data in the interface 00401 void DiffDrivePlugin6W::write_position_data() 00402 { 00403 // TODO: Data timestamp 00404 pos_iface_->data->head.time = Simulator::Instance()->GetSimTime().Double(); 00405 00406 pos_iface_->data->pose.pos.x = odomPose[0]; 00407 pos_iface_->data->pose.pos.y = odomPose[1]; 00408 pos_iface_->data->pose.yaw = NORMALIZE(odomPose[2]); 00409 00410 pos_iface_->data->velocity.pos.x = odomVel[0]; 00411 pos_iface_->data->velocity.yaw = odomVel[2]; 00412 00413 // TODO 00414 pos_iface_->data->stall = 0; 00415 } 00416