husky_controller.cpp
Go to the documentation of this file.
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  * Desc: ROS interface to a Position controller for a Differential drive Husky Robot
00023  * Author: Siddhant Ahuja/Ryan Gariepy (adapted from Daniel Hewlett)
00024  */
00025 
00026 #include <algorithm>
00027 #include <assert.h>
00028 
00029 #include "husky_controller/husky_controller.h"
00030 
00031 #include <gazebo/gazebo.h>
00032 #include <gazebo/GazeboError.hh>
00033 #include <gazebo/Quatern.hh>
00034 #include <gazebo/Controller.hh>
00035 #include <gazebo/Body.hh>
00036 #include <gazebo/World.hh>
00037 #include <gazebo/Simulator.hh>
00038 #include <gazebo/Global.hh>
00039 #include <gazebo/XMLConfig.hh>
00040 #include <gazebo/ControllerFactory.hh>
00041 #include <gazebo/PhysicsEngine.hh>
00042 
00043 #include <ros/ros.h>
00044 #include <tf/transform_broadcaster.h>
00045 #include <tf/transform_listener.h>
00046 #include <geometry_msgs/Twist.h>
00047 #include <nav_msgs/Odometry.h>
00048 #include <boost/bind.hpp>
00049 
00050 using namespace gazebo;
00051 
00052 GZ_REGISTER_DYNAMIC_CONTROLLER("husky_plugin", DiffDrivePlugin); 
00053 
00054 enum
00055 {
00056         BACKRIGHT, BACKLEFT, FRONTRIGHT, FRONTLEFT
00057 };
00058 
00059 // Constructor
00060 DiffDrivePlugin::DiffDrivePlugin(Entity *parent) : Controller(parent)
00061 {
00062         parent_ = dynamic_cast<Model*> (parent);
00063 
00064         if (!parent_)
00065                 gzthrow("Husky controller requires a Model as its parent");
00066 
00067         enableMotors = true;
00068 
00069         wheelSpeed[BACKRIGHT] = 0;
00070         wheelSpeed[BACKLEFT] = 0;
00071         wheelSpeed[FRONTRIGHT] = 0;
00072         wheelSpeed[FRONTLEFT] = 0;
00073 
00074         prevUpdateTime = Simulator::Instance()->GetSimTime();
00075 
00076         Param::Begin(&parameters);
00077         backLeftJointNameP = new ParamT<std::string> ("backLeftJoint", "", 1);
00078         backRightJointNameP = new ParamT<std::string> ("backRightJoint", "", 1);
00079         frontLeftJointNameP = new ParamT<std::string> ("frontLeftJoint", "", 1);
00080         frontRightJointNameP = new ParamT<std::string> ("frontRightJoint", "", 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         topicNameP = new ParamT<std::string> ("topicName", "", 1);
00086         Param::End();
00087 
00088         x_ = 0;
00089         rot_ = 0;
00090         alive_ = true;
00091 }
00092 
00093 // Destructor
00094 DiffDrivePlugin::~DiffDrivePlugin()
00095 {
00096         delete backLeftJointNameP;
00097         delete backRightJointNameP;
00098         delete frontLeftJointNameP;
00099         delete frontRightJointNameP;
00100         delete wheelSepP;
00101         delete wheelDiamP;
00102         delete torqueP;
00103         delete robotNamespaceP;
00104         delete topicNameP;
00105         delete callback_queue_thread_;
00106         delete rosnode_;
00107         delete transform_broadcaster_;
00108 }
00109 
00110 // Load the controller
00111 void DiffDrivePlugin::LoadChild(XMLConfigNode *node)
00112 {
00113         pos_iface_ = dynamic_cast<libgazebo::PositionIface*> (GetIface("position"));
00114 
00115         // the defaults are from pioneer2dx
00116         wheelSepP->Load(node);
00117         wheelDiamP->Load(node);
00118         torqueP->Load(node);
00119 
00120         backLeftJointNameP->Load(node);
00121         backRightJointNameP->Load(node);
00122         frontLeftJointNameP->Load(node);
00123         frontRightJointNameP->Load(node);
00124 
00125         joints[BACKLEFT] = parent_->GetJoint(**backLeftJointNameP);
00126         joints[BACKRIGHT] = parent_->GetJoint(**backRightJointNameP);
00127         joints[FRONTLEFT] = parent_->GetJoint(**frontLeftJointNameP);
00128         joints[FRONTRIGHT] = parent_->GetJoint(**frontRightJointNameP);
00129 
00130         if (!joints[BACKLEFT])
00131                 gzthrow("The controller couldn't get back left hinge joint");
00132 
00133         if (!joints[BACKRIGHT])
00134                 gzthrow("The controller couldn't get back right hinge joint");
00135 
00136         if (!joints[FRONTLEFT])
00137                 gzthrow("The controller couldn't get front left hinge joint");
00138 
00139         if (!joints[FRONTRIGHT])
00140                 gzthrow("The controller couldn't get front right hinge joint");
00141 
00142         // Initialize the ROS node and subscribe to cmd_vel
00143 
00144         robotNamespaceP->Load(node);
00145         robotNamespace = robotNamespaceP->GetValue();
00146 
00147         int argc = 0;
00148         char** argv = NULL;
00149         ros::init(argc, argv, "husky_description", ros::init_options::NoSigintHandler | ros::init_options::AnonymousName);
00150         rosnode_ = new ros::NodeHandle(robotNamespace);
00151 
00152         tf_prefix_ = tf::getPrefixParam(*rosnode_);
00153         transform_broadcaster_ = new tf::TransformBroadcaster();
00154 
00155         topicNameP->Load(node);
00156         topicName = topicNameP->GetValue();
00157 
00158         // ROS: Subscribe to the velocity command topic (usually "cmd_vel")
00159         ros::SubscribeOptions so =
00160         ros::SubscribeOptions::create<geometry_msgs::Twist>(topicName, 1,
00161                                                           boost::bind(&DiffDrivePlugin::cmdVelCallback, this, _1),
00162                                                           ros::VoidPtr(), &queue_);
00163         sub_ = rosnode_->subscribe(so);
00164         pub_ = rosnode_->advertise<nav_msgs::Odometry>("odom", 1);
00165 }
00166 
00167 // Initialize the controller
00168 void DiffDrivePlugin::InitChild()
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         callback_queue_thread_ = new boost::thread(boost::bind(&DiffDrivePlugin::QueueThread, this));
00180 }
00181 
00182 // Load the controller
00183 void DiffDrivePlugin::SaveChild(std::string &prefix, std::ostream &stream)
00184 {
00185         stream << prefix << *(backLeftJointNameP) << "\n";
00186         stream << prefix << *(backRightJointNameP) << "\n";
00187         stream << prefix << *(frontLeftJointNameP) << "\n";
00188         stream << prefix << *(frontRightJointNameP) << "\n";
00189         stream << prefix << *(torqueP) << "\n";
00190         stream << prefix << *(wheelDiamP) << "\n";
00191         stream << prefix << *(wheelSepP) << "\n";
00192 }
00193 
00194 // Reset
00195 void DiffDrivePlugin::ResetChild()
00196 {
00197         // Reset odometric pose
00198         odomPose[0] = 0.0;
00199         odomPose[1] = 0.0;
00200         odomPose[2] = 0.0;
00201 
00202         odomVel[0] = 0.0;
00203         odomVel[1] = 0.0;
00204         odomVel[2] = 0.0;
00205 }
00206 
00207 // Update the controller
00208 void DiffDrivePlugin::UpdateChild()
00209 {
00210         // TODO: Step should be in a parameter of this function
00211         double wd, ws;
00212         double d1, d2, d3, d4;
00213         double d_avg_left, d_avg_right;
00214         double dr, da;
00215         Time stepTime;
00216 
00217         //myIface->Lock(1);
00218 
00219         GetPositionCmd();
00220 
00221         wd = **(wheelDiamP);
00222         ws = **(wheelSepP);
00223 
00224         //stepTime = World::Instance()->GetPhysicsEngine()->GetStepTime();
00225         stepTime = Simulator::Instance()->GetSimTime() - prevUpdateTime;
00226         prevUpdateTime = Simulator::Instance()->GetSimTime();
00227 
00228         // Distance travelled by back wheels
00229         d1 = stepTime.Double() * wd / 2 * joints[BACKLEFT]->GetVelocity(0);
00230         d2 = stepTime.Double() * wd / 2 * joints[BACKRIGHT]->GetVelocity(0);
00231 
00232         // Distance travelled by front wheels
00233         d3 = stepTime.Double() * wd / 2 * joints[FRONTLEFT]->GetVelocity(0);
00234         d4 = stepTime.Double() * wd / 2 * joints[FRONTRIGHT]->GetVelocity(0);
00235         
00236         d_avg_left = (d3 + d1) / 2 ;
00237         d_avg_right = (d4 + d2) / 2 ;
00238         
00239         dr = (d_avg_left + d_avg_right) / 2;
00240         da = (d_avg_right - d_avg_left) / ws;
00241 
00242         // Compute odometric pose
00243         odomPose[0] += dr * cos(odomPose[2]);
00244         odomPose[1] += dr * sin(odomPose[2]);
00245         odomPose[2] += da;
00246 
00247         // Compute odometric instantaneous velocity
00248         odomVel[0] = dr / stepTime.Double();
00249         odomVel[1] = 0.0;
00250         odomVel[2] = da / stepTime.Double();
00251 
00252         if (enableMotors)
00253         {
00254                 joints[BACKLEFT]->SetVelocity(0, wheelSpeed[BACKLEFT] / (**(wheelDiamP) / 2.0));
00255 
00256                 joints[BACKRIGHT]->SetVelocity(0, wheelSpeed[BACKRIGHT] / (**(wheelDiamP) / 2.0));
00257 
00258                 joints[FRONTLEFT]->SetVelocity(0, wheelSpeed[FRONTLEFT] / (**(wheelDiamP) / 2.0));
00259 
00260                 joints[FRONTRIGHT]->SetVelocity(0, wheelSpeed[FRONTRIGHT] / (**(wheelDiamP) / 2.0));
00261 
00262                 joints[BACKLEFT]->SetMaxForce(0, **(torqueP));
00263                 joints[BACKRIGHT]->SetMaxForce(0, **(torqueP));
00264                 joints[FRONTLEFT]->SetMaxForce(0, **(torqueP));
00265                 joints[FRONTRIGHT]->SetMaxForce(0, **(torqueP));
00266         }
00267 
00268         write_position_data();
00269         publish_odometry();
00270 
00271         //myIface->Unlock();
00272 }
00273 
00274 // Finalize the controller
00275 void DiffDrivePlugin::FiniChild()
00276 {
00277         //std::cout << "ENTERING FINALIZE\n";
00278         alive_ = false;
00279         // Custom Callback Queue
00280         queue_.clear();
00281         queue_.disable();
00282         rosnode_->shutdown();
00283         callback_queue_thread_->join();
00284         //std::cout << "EXITING FINALIZE\n";
00285 }
00286 
00287 // NEW: Now uses the target velocities from the ROS message, not the Iface 
00288 void DiffDrivePlugin::GetPositionCmd()
00289 {
00290         lock.lock();
00291 
00292         double vr, va;
00293 
00294         vr = x_; 
00295         va = rot_; 
00296 
00297         // Changed motors to be always on, which is probably what we want anyway
00298         enableMotors = true; 
00299 
00300         wheelSpeed[BACKLEFT] = vr - va * **(wheelSepP) / 2;
00301         wheelSpeed[BACKRIGHT] = vr + va * **(wheelSepP) / 2;
00302         wheelSpeed[FRONTLEFT] = vr - va * **(wheelSepP) / 2;
00303     wheelSpeed[FRONTRIGHT] = vr + va * **(wheelSepP) / 2;
00304     lock.unlock();
00305 }
00306 
00307 // Store the velocities from the ROS message
00308 void DiffDrivePlugin::cmdVelCallback(const geometry_msgs::Twist::ConstPtr& cmd_msg)
00309 {
00310         lock.lock();
00311 
00312         x_ = cmd_msg->linear.x;
00313         rot_ = cmd_msg->angular.z;
00314 
00315         lock.unlock();
00316 
00317 }
00318 
00319 // NEW: custom callback queue thread
00320 void DiffDrivePlugin::QueueThread()
00321 {
00322         static const double timeout = 0.01;
00323 
00324         while (alive_ && rosnode_->ok())
00325         {
00326                 queue_.callAvailable(ros::WallDuration(timeout));
00327         }
00328 }
00329 
00330 // Publish odometry topic
00331 void DiffDrivePlugin::publish_odometry()
00332 {
00333         // get current time
00334         ros::Time current_time_((Simulator::Instance()->GetSimTime()).sec, (Simulator::Instance()->GetSimTime()).nsec); 
00335 
00336         // getting data for base_footprint to odom transform
00337         btQuaternion qt;
00338         qt.setEulerZYX(pos_iface_->data->pose.yaw, pos_iface_->data->pose.pitch, pos_iface_->data->pose.roll);
00339         btVector3 vt(pos_iface_->data->pose.pos.x, pos_iface_->data->pose.pos.y, pos_iface_->data->pose.pos.z);
00340         tf::Transform base_footprint_to_odom(qt, vt);
00341 
00342         transform_broadcaster_->sendTransform(tf::StampedTransform(base_footprint_to_odom,
00343                                                             current_time_,
00344                                                             "odom",
00345                                                             "base_footprint"));
00346 
00347         // publish odom topic
00348         odom_.pose.pose.position.x = pos_iface_->data->pose.pos.x;
00349         odom_.pose.pose.position.y = pos_iface_->data->pose.pos.y;
00350 
00351         gazebo::Quatern rot;
00352         rot.SetFromEuler(gazebo::Vector3(pos_iface_->data->pose.roll, pos_iface_->data->pose.pitch, pos_iface_->data->pose.yaw));
00353 
00354         odom_.pose.pose.orientation.x = rot.x;
00355         odom_.pose.pose.orientation.y = rot.y;
00356         odom_.pose.pose.orientation.z = rot.z;
00357         odom_.pose.pose.orientation.w = rot.u;
00358 
00359         odom_.twist.twist.linear.x = pos_iface_->data->velocity.pos.x;
00360         odom_.twist.twist.linear.y = pos_iface_->data->velocity.pos.y;
00361         odom_.twist.twist.angular.z = pos_iface_->data->velocity.yaw;
00362 
00363         odom_.header.frame_id = tf::resolve(tf_prefix_, "odom");
00364         odom_.child_frame_id = "base_footprint";
00365 
00366         odom_.header.stamp.sec = (Simulator::Instance()->GetSimTime()).sec;
00367         odom_.header.stamp.nsec = (Simulator::Instance()->GetSimTime()).nsec;
00368 
00369         pub_.publish(odom_);
00370 }
00371 
00372 // Update the data in the interface
00373 void DiffDrivePlugin::write_position_data()
00374 {
00375         pos_iface_->data->head.time = Simulator::Instance()->GetSimTime().Double();
00376 
00377         pos_iface_->data->pose.pos.x = odomPose[0];
00378         pos_iface_->data->pose.pos.y = odomPose[1];
00379         pos_iface_->data->pose.yaw = NORMALIZE(odomPose[2]);
00380 
00381         pos_iface_->data->velocity.pos.x = odomVel[0];
00382         pos_iface_->data->velocity.yaw = odomVel[2];
00383 
00384         // TODO
00385         pos_iface_->data->stall = 0;
00386 }
00387 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends


husky_gazebo_plugins
Author(s): Ryan Gariepy/Clearpath Robotics
autogenerated on Tue Sep 3 2013 10:23:58