GazeboRosVelodyneLaser.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2015-2017, Dataspeed Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of Dataspeed Inc. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include <velodyne_gazebo_plugins/GazeboRosVelodyneLaser.h>
00036 
00037 #include <algorithm>
00038 #include <assert.h>
00039 
00040 #include <gazebo/physics/World.hh>
00041 #include <gazebo/sensors/Sensor.hh>
00042 #include <sdf/sdf.hh>
00043 #include <sdf/Param.hh>
00044 #include <gazebo/common/Exception.hh>
00045 #include <gazebo/sensors/RaySensor.hh>
00046 #include <gazebo/sensors/SensorTypes.hh>
00047 #include <gazebo/transport/Node.hh>
00048 
00049 #include <sensor_msgs/PointCloud2.h>
00050 
00051 #include <tf/tf.h>
00052 
00053 namespace gazebo
00054 {
00055 // Register this plugin with the simulator
00056 GZ_REGISTER_SENSOR_PLUGIN(GazeboRosVelodyneLaser)
00057 
00058 
00059 // Constructor
00060 GazeboRosVelodyneLaser::GazeboRosVelodyneLaser() : nh_(NULL), gaussian_noise_(0), min_range_(0), max_range_(0)
00061 {
00062 }
00063 
00065 // Destructor
00066 GazeboRosVelodyneLaser::~GazeboRosVelodyneLaser()
00067 {
00069   // Finalize the controller / Custom Callback Queue
00070   laser_queue_.clear();
00071   laser_queue_.disable();
00072   if (nh_) {
00073     nh_->shutdown();
00074     delete nh_;
00075     nh_ = NULL;
00076   }
00077   callback_laser_queue_thread_.join();
00078 }
00079 
00081 // Load the controller
00082 void GazeboRosVelodyneLaser::Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
00083 {
00084   // Load plugin
00085   RayPlugin::Load(_parent, _sdf);
00086 
00087   // Initialize Gazebo node
00088   gazebo_node_ = gazebo::transport::NodePtr(new gazebo::transport::Node());
00089   gazebo_node_->Init();
00090 
00091   // Get the parent ray sensor
00092 #if GAZEBO_MAJOR_VERSION >= 7
00093   parent_ray_sensor_ = std::dynamic_pointer_cast<sensors::RaySensor>(_parent);
00094 #else
00095   parent_ray_sensor_ = boost::dynamic_pointer_cast<sensors::RaySensor>(_parent);
00096 #endif
00097   if (!parent_ray_sensor_) {
00098     gzthrow("GazeboRosVelodyneLaser controller requires a Ray Sensor as its parent");
00099   }
00100 
00101   robot_namespace_ = "/";
00102   if (_sdf->HasElement("robotNamespace")) {
00103     robot_namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>();
00104   }
00105 
00106   if (!_sdf->HasElement("frameName")) {
00107     ROS_INFO("Velodyne laser plugin missing <frameName>, defaults to /world");
00108     frame_name_ = "/world";
00109   } else {
00110     frame_name_ = _sdf->GetElement("frameName")->Get<std::string>();
00111   }
00112 
00113   if (!_sdf->HasElement("min_range")) {
00114     ROS_INFO("Velodyne laser plugin missing <min_range>, defaults to 0");
00115     min_range_ = 0;
00116   } else {
00117     min_range_ = _sdf->GetElement("min_range")->Get<double>();
00118   }
00119 
00120   if (!_sdf->HasElement("max_range")) {
00121     ROS_INFO("Velodyne laser plugin missing <max_range>, defaults to infinity");
00122     max_range_ = INFINITY;
00123   } else {
00124     max_range_ = _sdf->GetElement("max_range")->Get<double>();
00125   }
00126 
00127   if (!_sdf->HasElement("topicName")) {
00128     ROS_INFO("Velodyne laser plugin missing <topicName>, defaults to /points");
00129     topic_name_ = "/points";
00130   } else {
00131     topic_name_ = _sdf->GetElement("topicName")->Get<std::string>();
00132   }
00133 
00134   if (!_sdf->HasElement("gaussianNoise")) {
00135     ROS_INFO("Velodyne laser plugin missing <gaussianNoise>, defaults to 0.0");
00136     gaussian_noise_ = 0;
00137   } else {
00138     gaussian_noise_ = _sdf->GetElement("gaussianNoise")->Get<double>();
00139   }
00140 
00141   // Make sure the ROS node for Gazebo has already been initialized
00142   if (!ros::isInitialized()) {
00143     ROS_FATAL_STREAM("A ROS node for Gazebo has not been initialized, unable to load plugin. "
00144       << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
00145     return;
00146   }
00147 
00148   // Create node handle
00149   nh_ = new ros::NodeHandle(robot_namespace_);
00150 
00151   // Resolve tf prefix
00152   std::string prefix;
00153   nh_->getParam(std::string("tf_prefix"), prefix);
00154   if (robot_namespace_ == "/") {
00155     frame_name_ = tf::resolve(prefix, frame_name_);
00156   } else {
00157     frame_name_ = tf::resolve(robot_namespace_, frame_name_);
00158   }
00159 
00160   // Advertise publisher with a custom callback queue
00161   if (topic_name_ != "") {
00162     ros::AdvertiseOptions ao = ros::AdvertiseOptions::create<sensor_msgs::PointCloud2>(
00163         topic_name_, 1,
00164         boost::bind(&GazeboRosVelodyneLaser::ConnectCb, this),
00165         boost::bind(&GazeboRosVelodyneLaser::ConnectCb, this),
00166         ros::VoidPtr(), &laser_queue_);
00167     pub_ = nh_->advertise(ao);
00168   }
00169 
00170   // Sensor generation off by default
00171   parent_ray_sensor_->SetActive(false);
00172 
00173   // Start custom queue for laser
00174   callback_laser_queue_thread_ = boost::thread( boost::bind( &GazeboRosVelodyneLaser::laserQueueThread,this ) );
00175 
00176 #if GAZEBO_MAJOR_VERSION >= 7
00177   ROS_INFO("Velodyne laser plugin ready, %i lasers", parent_ray_sensor_->VerticalRangeCount());
00178 #else
00179   ROS_INFO("Velodyne laser plugin ready, %i lasers", parent_ray_sensor_->GetVerticalRangeCount());
00180 #endif
00181 }
00182 
00184 // Subscribe on-demand
00185 void GazeboRosVelodyneLaser::ConnectCb()
00186 {
00187   boost::lock_guard<boost::mutex> lock(lock_);
00188   if (pub_.getNumSubscribers()) {
00189     if (!sub_) {
00190 #if GAZEBO_MAJOR_VERSION >= 7
00191       sub_ = gazebo_node_->Subscribe(this->parent_ray_sensor_->Topic(), &GazeboRosVelodyneLaser::OnScan, this);
00192 #else
00193       sub_ = gazebo_node_->Subscribe(this->parent_ray_sensor_->GetTopic(), &GazeboRosVelodyneLaser::OnScan, this);
00194 #endif
00195     }
00196     parent_ray_sensor_->SetActive(true);
00197   } else {
00198 #if GAZEBO_MAJOR_VERSION >= 7
00199     if (sub_) {
00200       sub_->Unsubscribe();
00201       sub_.reset();
00202     }
00203 #endif
00204     parent_ray_sensor_->SetActive(false);
00205   }
00206 }
00207 
00208 void GazeboRosVelodyneLaser::OnScan(ConstLaserScanStampedPtr& _msg)
00209 {
00210 #if GAZEBO_MAJOR_VERSION >= 7
00211   const math::Angle maxAngle = parent_ray_sensor_->AngleMax();
00212   const math::Angle minAngle = parent_ray_sensor_->AngleMin();
00213 
00214   const double maxRange = parent_ray_sensor_->RangeMax();
00215   const double minRange = parent_ray_sensor_->RangeMin();
00216 
00217   const int rayCount = parent_ray_sensor_->RayCount();
00218   const int rangeCount = parent_ray_sensor_->RangeCount();
00219   assert(rayCount == rangeCount);
00220 
00221   const int verticalRayCount = parent_ray_sensor_->VerticalRayCount();
00222   const int verticalRangeCount = parent_ray_sensor_->VerticalRangeCount();
00223   assert(verticalRayCount == verticalRangeCount);
00224 
00225   const math::Angle verticalMaxAngle = parent_ray_sensor_->VerticalAngleMax();
00226   const math::Angle verticalMinAngle = parent_ray_sensor_->VerticalAngleMin();
00227 #else
00228   math::Angle maxAngle = parent_ray_sensor_->GetAngleMax();
00229   math::Angle minAngle = parent_ray_sensor_->GetAngleMin();
00230 
00231   const double maxRange = parent_ray_sensor_->GetRangeMax();
00232   const double minRange = parent_ray_sensor_->GetRangeMin();
00233 
00234   const int rayCount = parent_ray_sensor_->GetRayCount();
00235   const int rangeCount = parent_ray_sensor_->GetRangeCount();
00236   assert(rayCount == rangeCount);
00237 
00238   const int verticalRayCount = parent_ray_sensor_->GetVerticalRayCount();
00239   const int verticalRangeCount = parent_ray_sensor_->GetVerticalRangeCount();
00240   assert(verticalRayCount == verticalRangeCount);
00241 
00242   const math::Angle verticalMaxAngle = parent_ray_sensor_->GetVerticalAngleMax();
00243   const math::Angle verticalMinAngle = parent_ray_sensor_->GetVerticalAngleMin();
00244 #endif
00245 
00246   const double yDiff = maxAngle.Radian() - minAngle.Radian();
00247   const double pDiff = verticalMaxAngle.Radian() - verticalMinAngle.Radian();
00248 
00249   const double MIN_RANGE = std::max(min_range_, minRange);
00250   const double MAX_RANGE = std::min(max_range_, maxRange - minRange - 0.01);
00251 
00252   // Populate message fields
00253   const uint32_t POINT_STEP = 32;
00254   sensor_msgs::PointCloud2 msg;
00255   msg.header.frame_id = frame_name_;
00256   msg.header.stamp = ros::Time(_msg->time().sec(), _msg->time().nsec());
00257   msg.fields.resize(5);
00258   msg.fields[0].name = "x";
00259   msg.fields[0].offset = 0;
00260   msg.fields[0].datatype = sensor_msgs::PointField::FLOAT32;
00261   msg.fields[0].count = 1;
00262   msg.fields[1].name = "y";
00263   msg.fields[1].offset = 4;
00264   msg.fields[1].datatype = sensor_msgs::PointField::FLOAT32;
00265   msg.fields[1].count = 1;
00266   msg.fields[2].name = "z";
00267   msg.fields[2].offset = 8;
00268   msg.fields[2].datatype = sensor_msgs::PointField::FLOAT32;
00269   msg.fields[2].count = 1;
00270   msg.fields[3].name = "intensity";
00271   msg.fields[3].offset = 16;
00272   msg.fields[3].datatype = sensor_msgs::PointField::FLOAT32;
00273   msg.fields[3].count = 1;
00274   msg.fields[4].name = "ring";
00275   msg.fields[4].offset = 20;
00276   msg.fields[4].datatype = sensor_msgs::PointField::UINT16;
00277   msg.fields[4].count = 1;
00278   msg.data.resize(verticalRangeCount * rangeCount * POINT_STEP);
00279 
00280   int i, j;
00281   uint8_t *ptr = msg.data.data();
00282   for (j = 0; j < verticalRangeCount; j++) {
00283     for (i = 0; i < rangeCount; i++) {
00284 
00285       // Range and noise
00286       double r = std::min(_msg->scan().ranges(i + j * rangeCount), maxRange-minRange);
00287       if (gaussian_noise_ != 0.0) {
00288         r += gaussianKernel(0,gaussian_noise_);
00289       }
00290 
00291       // Intensity
00292       double intensity = _msg->scan().intensities(i + j * rangeCount);
00293 
00294       // Get angles of ray to get xyz for point
00295       double yAngle = i * yDiff / (rayCount -1) + minAngle.Radian();
00296       double pAngle = j * pDiff / (verticalRayCount -1) + verticalMinAngle.Radian();
00297 
00298       // pAngle is rotated by yAngle:
00299       if ((MIN_RANGE < r) && (r < MAX_RANGE)) {
00300         *((float*)(ptr + 0)) = r * cos(pAngle) * cos(yAngle);
00301         *((float*)(ptr + 4)) = r * cos(pAngle) * sin(yAngle);
00302 #if GAZEBO_MAJOR_VERSION > 2
00303         *((float*)(ptr + 8)) = r * sin(pAngle);
00304 #else
00305         *((float*)(ptr + 8)) = -r * sin(pAngle);
00306 #endif
00307         *((float*)(ptr + 16)) = intensity;
00308 #if GAZEBO_MAJOR_VERSION > 2
00309         *((uint16_t*)(ptr + 20)) = j; // ring
00310 #else
00311         *((uint16_t*)(ptr + 20)) = verticalRangeCount - 1 - j; // ring
00312 #endif
00313         ptr += POINT_STEP;
00314       }
00315     }
00316   }
00317 
00318   // Populate message with number of valid points
00319   msg.point_step = POINT_STEP;
00320   msg.row_step = ptr - msg.data.data();
00321   msg.height = 1;
00322   msg.width = msg.row_step / POINT_STEP;
00323   msg.is_bigendian = false;
00324   msg.is_dense = true;
00325   msg.data.resize(msg.row_step); // Shrink to actual size
00326 
00327   // Publish output
00328   pub_.publish(msg);
00329 }
00330 
00331 // Custom Callback Queue
00333 // Custom callback queue thread
00334 void GazeboRosVelodyneLaser::laserQueueThread()
00335 {
00336   while (nh_->ok()) {
00337     laser_queue_.callAvailable(ros::WallDuration(0.01));
00338   }
00339 }
00340 
00341 } // namespace gazebo
00342 


velodyne_gazebo_plugins
Author(s): Kevin Hallenbeck
autogenerated on Thu Jun 6 2019 19:00:24