Tutorials/plugins/converting old plugins/examples

To demonstrate how to update a plugin from gazebo's pre-1.0.0 release to the latest revision, `gazebo::GazeboRosLaser` plugin as implemented in the gazebo\_plugins package is used as an example of the plugin conversions process. Below are snippets of change highlights between the codes in the old electric laser scan ros plugin and its equivalent updated fuerte ros laser plugin.
 * 1) Example ##


 * 1) Include Files ###

Old plugin API:

#include        // XML parameter parsing #include  #include  // Controller Plugin Registration #include  #include 

New plugin API:

#include "gazebo.h"       // data types declarations #include "sensors/SensorTypes.hh" // if parent is a sensor #include "physics/PhysicsTypes.hh" // if parent is a Model or World


 * 1) Class Inhertance ###

Old plugin API:

class GazeboRosLaser : public Controller

New plugin API: In this example, the plugin's parent is a `gazebo::Sensor`.

class GazeboRosLaser : public SensorPlugin


 * 1) Constructor ###

Old plugin API:

////////////////////////////////////////////////////////////////////////////////       // Constructor GazeboRosLaser::GazeboRosLaser(Entity *parent) : Controller(parent) {         this->myParent = dynamic_cast(this->parent); if (!this->myParent) gzthrow("GazeboRosLaser controller requires a Ray Sensor as its parent"); Param::Begin(&this->parameters); this->robotNamespaceP = new ParamT("robotNamespace", "/", 0); this->hokuyoMinIntensityP = new ParamT ("hokuyoMinIntensity", 101.0, 0); this->gaussianNoiseP = new ParamT ("gaussianNoise", 0.0, 0); this->topicNameP = new ParamT("topicName", "", 1); this->deprecatedTopicNameP = new ParamT("deprecatedTopicName", "", 0); this->frameNameP = new ParamT("frameName", "default_gazebo_ros_laser_frame", 0); Param::End; this->laserConnectCount = 0; this->deprecatedLaserConnectCount = 0; }

New plugin API: No need to declare `Param` here with SDF parsing. Pointer to parent entity is provided in `Load(...)` function instead.

////////////////////////////////////////////////////////////////////////////////       // Constructor GazeboRosLaser::GazeboRosLaser {       }


 * 1) Loading ###

Old plugin API:

////////////////////////////////////////////////////////////////////////////////       // Load the controller void GazeboRosLaser::LoadChild(XMLConfigNode *node) {         this->robotNamespaceP->Load(node); this->robotNamespace = this->robotNamespaceP->GetValue; if (!ros::isInitialized) {           int argc = 0; char** argv = NULL; ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName); }         this->rosnode_ = new ros::NodeHandle(this->robotNamespace); this->hokuyoMinIntensityP->Load(node); this->hokuyoMinIntensity = this->hokuyoMinIntensityP->GetValue; ROS_INFO("INFO: gazebo_ros_laser plugin artifically sets minimum intensity to %f due to cutoff in hokuyo filters.", this->hokuyoMinIntensity); this->topicNameP->Load(node); this->topicName = this->topicNameP->GetValue; this->deprecatedTopicNameP->Load(node); this->deprecatedTopicName = this->deprecatedTopicNameP->GetValue; this->frameNameP->Load(node); this->frameName = this->frameNameP->GetValue; this->gaussianNoiseP->Load(node); this->gaussianNoise = this->gaussianNoiseP->GetValue; if (this->topicName != "") {       #ifdef USE_CBQ ros::AdvertiseOptions ao = ros::AdvertiseOptions::create(             this->topicName,1,              boost::bind( &GazeboRosLaser::LaserConnect,this),              boost::bind( &GazeboRosLaser::LaserDisconnect,this), ros::VoidPtr, &this->laser_queue_); this->pub_ = this->rosnode_->advertise(ao); #else this->pub_ = this->rosnode_->advertise(this->topicName,1,             boost::bind( &GazeboRosLaser::LaserConnect, this),              boost::bind( &GazeboRosLaser::LaserDisconnect, this)); #endif }         if (this->deprecatedTopicName != "") {       #ifdef USE_CBQ ros::AdvertiseOptions ao = ros::AdvertiseOptions::create(             this->deprecatedTopicName,1,              boost::bind( &GazeboRosLaser::DeprecatedLaserConnect,this),              boost::bind( &GazeboRosLaser::DeprecatedLaserDisconnect,this), ros::VoidPtr, &this->laser_queue_); this->deprecated_pub_ = this->rosnode_->advertise(ao); #else this->deprecated_pub_ = this->rosnode_->advertise(this->deprecatedTopicName,1,             boost::bind( &GazeboRosLaser::DeprecatedLaserConnect, this),              boost::bind( &GazeboRosLaser::DeprecatedLaserDisconnect, this)); #endif }       }

New plugin API: Pointer to parent object is given here instead of through the constructor. `XMLConfigNode` is replaced by new SDF class.

////////////////////////////////////////////////////////////////////////////////       // Load the controller void GazeboRosLaser::Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf) {         // Get then name of the parent sensor this->parentSensor = _parent; // Get the world name. std::string worldName = _sdf->GetWorldName; this->world = physics::get_world(worldName); this->lastUpdateTime = common::Time(0); gzdbg << "plugin parent sensor name: " << this->parentSensor->GetName << "\n"; // node cannot be initialized here, leads to deadlock with sensor manager update // node not used anyways //this->node = transport::NodePtr(new transport::Node); //this->node->Init(worldName); //this->statsSub = this->node->Subscribe("~/world_stats", &GazeboRosLaser::OnStats, this); this->parentRaySensor = boost::shared_dynamic_cast(this->parentSensor); if (!this->parentRaySensor) gzthrow("GazeboRosLaser controller requires a Ray Sensor as its parent"); this->robotNamespace = ""; if (_sdf->HasElement("robotNamespace")) this->robotNamespace = _sdf->GetElement("robotNamespace")->GetValueString + "/"; if (!_sdf->HasElement("frameName")) {           ROS_WARN("Laser plugin missing , defaults to /world"); this->frameName = "/world"; }         else this->frameName = _sdf->GetElement("frameName")->GetValueString; if (!_sdf->HasElement("topicName")) {           ROS_WARN("Laser plugin missing <topicName>, defaults to /world"); this->topicName = "/world"; }         else this->topicName = _sdf->GetElement("topicName")->GetValueString; if (!_sdf->HasElement("gaussianNoise")) {           ROS_WARN("Laser plugin missing <gaussianNoise>, defaults to 0.0"); this->gaussianNoise = 0; }         else this->gaussianNoise = _sdf->GetElement("gaussianNoise")->GetValueDouble; if (!_sdf->HasElement("hokuyoMinIntensity")) {           ROS_WARN("Laser plugin missing <hokuyoMinIntensity>, defaults to 101"); this->hokuyoMinIntensity = 101; }         else this->hokuyoMinIntensity = _sdf->GetElement("hokuyoMinIntensity")->GetValueDouble; ROS_INFO("INFO: gazebo_ros_laser plugin should set minimum intensity to %f due to cutoff in hokuyo filters.", this->hokuyoMinIntensity); if (!_sdf->GetElement("updateRate")) {           ROS_WARN("Laser plugin missing <updateRate>, defaults to 0"); this->updateRate = 0; }         else this->updateRate = _sdf->GetElement("updateRate")->GetValueDouble; // set parent sensor update rate this->parentSensor->SetUpdateRate(this->updateRate); // prepare to throttle this plugin at the same rate // ideally, we should invoke a plugin update when the sensor updates, // have to think about how to do that properly later if (this->updateRate > 0.0) this->updatePeriod = 1.0/this->updateRate; else this->updatePeriod = 0.0; this->laserConnectCount = 0; if (!ros::isInitialized) {           int argc = 0; char** argv = NULL; ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName); }         this->rosnode_ = new ros::NodeHandle(this->robotNamespace); if (this->topicName != "") {           ros::AdvertiseOptions ao = ros::AdvertiseOptions::create<sensor_msgs::LaserScan>(              this->topicName,1,              boost::bind( &GazeboRosLaser::LaserConnect,this),              boost::bind( &GazeboRosLaser::LaserDisconnect,this), ros::VoidPtr, &this->laser_queue_); this->pub_ = this->rosnode_->advertise(ao); }         // Initialize the controller // sensor generation off by default this->parentRaySensor->SetActive(false); // start custom queue for laser this->callback_queue_thread_ = boost::thread( boost::bind( &GazeboRosLaser::LaserQueueThread,this ) ); // Listen to the update event. This event is broadcast every // simulation iteration. this->updateConnection = event::Events::ConnectWorldUpdateStart(             boost::bind(&GazeboRosLaser::UpdateChild, this)); }


 * 1) Initialization ###

Old plugin API:

////////////////////////////////////////////////////////////////////////////////       // Initialize the controller void GazeboRosLaser::InitChild {         // sensor generation off by default this->myParent->SetActive(false); #ifdef USE_CBQ // start custom queue for laser this->callback_queue_thread_ = boost::thread( boost::bind( &GazeboRosLaser::LaserQueueThread,this ) ); #endif }

New plugin API: Not used in current example. In this conversion of the laser plugin, the above code has been moved into `Load(sensors::SensorPtr &_parent, sdf::ElementPtr &_sdf)`


 * 1) Parameter Parsing ###

Here's a closer look at the changes in plugin parameter parsing, from `gazebo::XMLConfigNode` to `gazebo::SDF`.

Old plugin API:

Param::Begin(&this->parameters); this->robotNamespaceP = new ParamT<std::string>("robotNamespace", "/", 0); this->hokuyoMinIntensityP = new ParamT ("hokuyoMinIntensity", 101.0, 0); this->gaussianNoiseP = new ParamT ("gaussianNoise", 0.0, 0); this->topicNameP = new ParamT<std::string>("topicName", "", 1); this->deprecatedTopicNameP = new ParamT<std::string>("deprecatedTopicName", "", 0); this->frameNameP = new ParamT<std::string>("frameName", "default_gazebo_ros_laser_frame", 0); Param::End;

New plugin API: Using `SDF` parser.

this->robotNamespace = ""; if (_sdf->HasElement("robotNamespace")) this->robotNamespace = _sdf->GetElement("robotNamespace")->GetValueString + "/"; if (!_sdf->HasElement("frameName")) {         ROS_WARN("Laser plugin missing <frameName>, defaults to /world"); this->frameName = "/world"; }       else this->frameName = _sdf->GetElement("frameName")->GetValueString; if (!_sdf->HasElement("topicName")) {         ROS_WARN("Laser plugin missing <topicName>, defaults to /world"); this->topicName = "/world"; }       else this->topicName = _sdf->GetElement("topicName")->GetValueString; if (!_sdf->HasElement("gaussianNoise")) {         ROS_WARN("Laser plugin missing <gaussianNoise>, defaults to 0.0"); this->gaussianNoise = 0; }       else this->gaussianNoise = _sdf->GetElement("gaussianNoise")->GetValueDouble; if (!_sdf->HasElement("hokuyoMinIntensity")) {         ROS_WARN("Laser plugin missing <hokuyoMinIntensity>, defaults to 101"); this->hokuyoMinIntensity = 101; }       else this->hokuyoMinIntensity = _sdf->GetElement("hokuyoMinIntensity")->GetValueDouble; ROS_INFO("INFO: gazebo_ros_laser plugin should set minimum intensity to %f due to cutoff in hokuyo filters.", this->hokuyoMinIntensity); if (!_sdf->GetElement("updateRate")) {         ROS_WARN("Laser plugin missing <updateRate>, defaults to 0"); this->updateRate = 0; }       else this->updateRate = _sdf->GetElement("updateRate")->GetValueDouble;


 * 1) Unloading ###

Old plugin API:

////////////////////////////////////////////////////////////////////////////////       // Finalize the controller void GazeboRosLaser::FiniChild {         this->laser_queue_.clear; this->laser_queue_.disable; this->rosnode_->shutdown; sleep(1); #ifdef USE_CBQ this->callback_queue_thread_.join; #endif }

New plugin API: Move unloading calls into the destructor.

////////////////////////////////////////////////////////////////////////////////       // Destructor GazeboRosLaser::~GazeboRosLaser {         this->laser_queue_.clear; this->laser_queue_.disable; this->rosnode_->shutdown; this->callback_queue_thread_.join; delete this->rosnode_; }


 * 1) Plugin Registration Macros ###

How to register a sensor plugin. Old plugin API:

GZ_REGISTER_DYNAMIC_CONTROLLER("gazebo_ros_laser", GazeboRosLaser);

New plugin API:

GZ_REGISTER_SENSOR_PLUGIN(GazeboRosLaser)