GazeboRosVelodyneLaser.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Modification Copyright (c) 2016 - 2017, Jorge Beltran de la Cita (Line 372)
5  * Copyright (c) 2015-2016, Dataspeed Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Dataspeed Inc. nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 #include <GazeboRosVelodyneLaser.h>
37 
38 #include <algorithm>
39 #include <assert.h>
40 
41 #include <gazebo/physics/World.hh>
42 #include <gazebo/sensors/Sensor.hh>
43 #include <sdf/sdf.hh>
44 #include <sdf/Param.hh>
45 #include <gazebo/common/Exception.hh>
46 #include <gazebo/sensors/RaySensor.hh>
47 #include <gazebo/sensors/SensorTypes.hh>
48 #include <gazebo/transport/Node.hh>
49 
50 #include <sensor_msgs/PointCloud2.h>
51 
52 #include <tf/tf.h>
53 
54 namespace gazebo
55 {
56 // Register this plugin with the simulator
57 GZ_REGISTER_SENSOR_PLUGIN(GazeboRosVelodyneLaser)
58 
59 // Constructor
61 GazeboRosVelodyneLaser::GazeboRosVelodyneLaser() : rosnode_(NULL), laser_connect_count_(0)
62 {
63 }
64 
66 // Destructor
68 {
70  // Finalize the controller / Custom Callback Queue
73  if (rosnode_) {
74  rosnode_->shutdown();
75  delete rosnode_;
76  rosnode_ = NULL;
77  }
79 }
80 
82 // Load the controller
83 void GazeboRosVelodyneLaser::Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
84 {
85  // load plugin
86  RayPlugin::Load(_parent, _sdf);
87 
88  // Get then name of the parent sensor
89  parent_sensor_ = _parent;
90 
91  // Get the world name.
92 #if GAZEBO_MAJOR_VERSION >= 7
93  std::string worldName = _parent->WorldName();
94 #else
95  std::string worldName = _parent->GetWorldName();
96 #endif
97  world_ = physics::get_world(worldName);
98 
99  last_update_time_ = world_->GetSimTime();
100 
101  node_ = transport::NodePtr(new transport::Node());
102  node_->Init(worldName);
103 
104 #if GAZEBO_MAJOR_VERSION >= 7
105  parent_ray_sensor_ = std::dynamic_pointer_cast<sensors::RaySensor>(parent_sensor_);
106 #else
107  parent_ray_sensor_ = boost::dynamic_pointer_cast<sensors::RaySensor>(parent_sensor_);
108 #endif
109 
110  if (!parent_ray_sensor_) {
111  gzthrow("GazeboRosVelodyneLaser controller requires a Ray Sensor as its parent");
112  }
113 
114  robot_namespace_ = "";
115  if (_sdf->HasElement("robotNamespace")) {
116  robot_namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>() + "/";
117  }
118 
119  if (!_sdf->HasElement("frameName")) {
120  ROS_INFO("Velodyne laser plugin missing <frameName>, defaults to /world");
121  frame_name_ = "/world";
122  } else {
123  frame_name_ = _sdf->GetElement("frameName")->Get<std::string>();
124  }
125 
126  if (!_sdf->HasElement("min_range")) {
127  ROS_INFO("Velodyne laser plugin missing <min_range>, defaults to 0");
128  min_range_ = 0;
129  } else {
130  min_range_ = _sdf->GetElement("min_range")->Get<double>();
131  }
132 
133  if (!_sdf->HasElement("max_range")) {
134  ROS_INFO("Velodyne laser plugin missing <max_range>, defaults to infinity");
135  max_range_ = INFINITY;
136  } else {
137  max_range_ = _sdf->GetElement("max_range")->Get<double>();
138  }
139 
140  if (!_sdf->HasElement("topicName")) {
141  ROS_INFO("Velodyne laser plugin missing <topicName>, defaults to /world");
142  topic_name_ = "/world";
143  } else {
144  topic_name_ = _sdf->GetElement("topicName")->Get<std::string>();
145  }
146 
147  if (!_sdf->HasElement("gaussianNoise")) {
148  ROS_INFO("Velodyne laser plugin missing <gaussianNoise>, defaults to 0.0");
149  gaussian_noise_ = 0;
150  } else {
151  gaussian_noise_ = _sdf->GetElement("gaussianNoise")->Get<double>();
152  }
153 
154  // Make sure the ROS node for Gazebo has already been initialized
155  if (!ros::isInitialized()) {
156  ROS_FATAL_STREAM("A ROS node for Gazebo has not been initialized, unable to load plugin. "
157  << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
158  return;
159  }
160 
162 
163  // resolve tf prefix
164  std::string prefix;
165  rosnode_->getParam(std::string("tf_prefix"), prefix);
167 
168  if (topic_name_ != "") {
169  // Custom Callback Queue
170  ros::AdvertiseOptions ao = ros::AdvertiseOptions::create<sensor_msgs::PointCloud2>(
171  topic_name_,1,
172  boost::bind( &GazeboRosVelodyneLaser::laserConnect,this),
174  pub_ = rosnode_->advertise(ao);
175  }
176 
177  // sensor generation off by default
178  parent_ray_sensor_->SetActive(false);
179  // start custom queue for laser
180  callback_laser_queue_thread_ = boost::thread( boost::bind( &GazeboRosVelodyneLaser::laserQueueThread,this ) );
181 
182 #if GAZEBO_MAJOR_VERSION >= 7
183  ROS_INFO("Velodyne laser plugin ready, %i lasers", parent_ray_sensor_->VerticalRangeCount());
184 #else
185  ROS_INFO("Velodyne laser plugin ready, %i lasers", parent_ray_sensor_->GetVerticalRangeCount());
186 #endif
187 }
188 
190 // Increment count
192 {
194  parent_ray_sensor_->SetActive(true);
195 }
197 // Decrement count
199 {
201  if (laser_connect_count_ == 0) {
202  parent_ray_sensor_->SetActive(false);
203  }
204 }
205 
207 // Update the controller
209 {
210  if (topic_name_ != "") {
211 #if GAZEBO_MAJOR_VERSION >= 7
212  common::Time sensor_update_time = parent_sensor_->LastUpdateTime();
213 #else
214  common::Time sensor_update_time = parent_sensor_->GetLastUpdateTime();
215 #endif
216  if (last_update_time_ < sensor_update_time) {
217  putLaserData(sensor_update_time);
218  last_update_time_ = sensor_update_time;
219  }
220  } else {
221  ROS_INFO("gazebo_ros_velodyne_laser topic name not set");
222  }
223 }
224 
226 // Put laser data to the interface
227 void GazeboRosVelodyneLaser::putLaserData(common::Time &_updateTime)
228 {
229  int i, hja, hjb;
230  int j, vja, vjb;
231  double vb, hb;
232  int j1, j2, j3, j4; // four corners indices
233  double r1, r2, r3, r4, r; // four corner values + interpolated range
234  double intensity;
235 
236  parent_ray_sensor_->SetActive(false);
237 
238 #if GAZEBO_MAJOR_VERSION >= 7
239  math::Angle maxAngle = parent_ray_sensor_->AngleMax();
240  math::Angle minAngle = parent_ray_sensor_->AngleMin();
241 
242  double maxRange = parent_ray_sensor_->RangeMax();
243  double minRange = parent_ray_sensor_->RangeMin();
244 
245  int rayCount = parent_ray_sensor_->RayCount();
246  int rangeCount = parent_ray_sensor_->RangeCount();
247 
248  int verticalRayCount = parent_ray_sensor_->VerticalRayCount();
249  int verticalRangeCount = parent_ray_sensor_->VerticalRangeCount();
250  math::Angle verticalMaxAngle = parent_ray_sensor_->VerticalAngleMax();
251  math::Angle verticalMinAngle = parent_ray_sensor_->VerticalAngleMin();
252 #else
253  math::Angle maxAngle = parent_ray_sensor_->GetAngleMax();
254  math::Angle minAngle = parent_ray_sensor_->GetAngleMin();
255 
256  double maxRange = parent_ray_sensor_->GetRangeMax();
257  double minRange = parent_ray_sensor_->GetRangeMin();
258 
259  int rayCount = parent_ray_sensor_->GetRayCount();
260  int rangeCount = parent_ray_sensor_->GetRangeCount();
261 
262  int verticalRayCount = parent_ray_sensor_->GetVerticalRayCount();
263  int verticalRangeCount = parent_ray_sensor_->GetVerticalRangeCount();
264  math::Angle verticalMaxAngle = parent_ray_sensor_->GetVerticalAngleMax();
265  math::Angle verticalMinAngle = parent_ray_sensor_->GetVerticalAngleMin();
266 #endif
267 
268  double yDiff = maxAngle.Radian() - minAngle.Radian();
269  double pDiff = verticalMaxAngle.Radian() - verticalMinAngle.Radian();
270 
271  /***************************************************************/
272  /* */
273  /* point scan from laser */
274  /* */
275  /***************************************************************/
276  boost::mutex::scoped_lock lock(lock_);
277 
278  // Populate message fields
279  const uint32_t POINT_STEP = 32;
280  sensor_msgs::PointCloud2 msg;
281  msg.header.frame_id = frame_name_;
282  msg.header.stamp.sec = _updateTime.sec;
283  msg.header.stamp.nsec = _updateTime.nsec;
284  msg.fields.resize(5);
285  msg.fields[0].name = "x";
286  msg.fields[0].offset = 0;
287  msg.fields[0].datatype = sensor_msgs::PointField::FLOAT32;
288  msg.fields[0].count = 1;
289  msg.fields[1].name = "y";
290  msg.fields[1].offset = 4;
291  msg.fields[1].datatype = sensor_msgs::PointField::FLOAT32;
292  msg.fields[1].count = 1;
293  msg.fields[2].name = "z";
294  msg.fields[2].offset = 8;
295  msg.fields[2].datatype = sensor_msgs::PointField::FLOAT32;
296  msg.fields[2].count = 1;
297  msg.fields[3].name = "intensity";
298  msg.fields[3].offset = 16;
299  msg.fields[3].datatype = sensor_msgs::PointField::FLOAT32;
300  msg.fields[3].count = 1;
301  msg.fields[4].name = "ring";
302  msg.fields[4].offset = 20;
303  msg.fields[4].datatype = sensor_msgs::PointField::UINT16;
304  msg.fields[4].count = 1;
305  msg.data.resize(verticalRangeCount * rangeCount * POINT_STEP);
306 
307  const double MIN_RANGE = std::max(min_range_, minRange);
308  const double MAX_RANGE = std::min(max_range_, maxRange - minRange - 0.01);
309 
310  uint8_t *ptr = msg.data.data();
311  for (j = 0; j<verticalRangeCount; j++) {
312  // interpolating in vertical direction
313  vb = (verticalRangeCount == 1) ? 0 : (double) j * (verticalRayCount - 1) / (verticalRangeCount - 1);
314  vja = (int) floor(vb);
315  vjb = std::min(vja + 1, verticalRayCount - 1);
316  vb = vb - floor(vb); // fraction from min
317 
318  assert(vja >= 0 && vja < verticalRayCount);
319  assert(vjb >= 0 && vjb < verticalRayCount);
320 
321  for (i = 0; i<rangeCount; i++) {
322  // Interpolate the range readings from the rays in horizontal direction
323  hb = (rangeCount == 1)? 0 : (double) i * (rayCount - 1) / (rangeCount - 1);
324  hja = (int) floor(hb);
325  hjb = std::min(hja + 1, rayCount - 1);
326  hb = hb - floor(hb); // fraction from min
327 
328  assert(hja >= 0 && hja < rayCount);
329  assert(hjb >= 0 && hjb < rayCount);
330 
331  // indices of 4 corners
332  j1 = hja + vja * rayCount;
333  j2 = hjb + vja * rayCount;
334  j3 = hja + vjb * rayCount;
335  j4 = hjb + vjb * rayCount;
336  // range readings of 4 corners
337 #if GAZEBO_MAJOR_VERSION >= 7
338  r1 = std::min(parent_ray_sensor_->LaserShape()->GetRange(j1) , maxRange-minRange);
339  r2 = std::min(parent_ray_sensor_->LaserShape()->GetRange(j2) , maxRange-minRange);
340  r3 = std::min(parent_ray_sensor_->LaserShape()->GetRange(j3) , maxRange-minRange);
341  r4 = std::min(parent_ray_sensor_->LaserShape()->GetRange(j4) , maxRange-minRange);
342 #else
343  r1 = std::min(parent_ray_sensor_->GetLaserShape()->GetRange(j1) , maxRange-minRange);
344  r2 = std::min(parent_ray_sensor_->GetLaserShape()->GetRange(j2) , maxRange-minRange);
345  r3 = std::min(parent_ray_sensor_->GetLaserShape()->GetRange(j3) , maxRange-minRange);
346  r4 = std::min(parent_ray_sensor_->GetLaserShape()->GetRange(j4) , maxRange-minRange);
347 #endif
348 
349  // Range is linear interpolation if values are close,
350  // and min if they are very different
351  r = (1 - vb) * ((1 - hb) * r1 + hb * r2)
352  + vb * ((1 - hb) * r3 + hb * r4);
353  if (gaussian_noise_ != 0.0) {
355  }
356 
357  // Intensity is averaged
358 #if GAZEBO_MAJOR_VERSION >= 7
359  intensity = 0.25*(parent_ray_sensor_->LaserShape()->GetRetro(j1) +
360  parent_ray_sensor_->LaserShape()->GetRetro(j2) +
361  parent_ray_sensor_->LaserShape()->GetRetro(j3) +
362  parent_ray_sensor_->LaserShape()->GetRetro(j4));
363 #else
364  intensity = 0.25*(parent_ray_sensor_->GetLaserShape()->GetRetro(j1) +
365  parent_ray_sensor_->GetLaserShape()->GetRetro(j2) +
366  parent_ray_sensor_->GetLaserShape()->GetRetro(j3) +
367  parent_ray_sensor_->GetLaserShape()->GetRetro(j4));
368 #endif
369 
370  // get angles of ray to get xyz for point
371  double yAngle = 0.5*(hja+hjb) * yDiff / (rayCount -1) + minAngle.Radian();
372  double pAngle = j * pDiff / (verticalRayCount -1) + verticalMinAngle.Radian(); // 0.5*(vja+vjb)
373 
374  //pAngle is rotated by yAngle:
375  if ((MIN_RANGE < r) && (r < MAX_RANGE)) {
376  *((float*)(ptr + 0)) = r * cos(pAngle) * cos(yAngle);
377  *((float*)(ptr + 4)) = r * cos(pAngle) * sin(yAngle);
378 #if GAZEBO_MAJOR_VERSION > 2
379  *((float*)(ptr + 8)) = r * sin(pAngle);
380 #else
381  *((float*)(ptr + 8)) = -r * sin(pAngle);
382 #endif
383  *((float*)(ptr + 16)) = intensity;
384 #if GAZEBO_MAJOR_VERSION > 2
385  *((uint16_t*)(ptr + 20)) = j; // ring
386 #else
387  *((uint16_t*)(ptr + 20)) = verticalRangeCount - 1 - j; // ring
388 #endif
389  ptr += POINT_STEP;
390  }
391  }
392  }
393  parent_ray_sensor_->SetActive(true);
394 
395  // Populate message with number of valid points
396  msg.point_step = POINT_STEP;
397  msg.row_step = ptr - msg.data.data();
398  msg.height = 1;
399  msg.width = msg.row_step / POINT_STEP;
400  msg.is_bigendian = false;
401  msg.is_dense = true;
402  msg.data.resize(msg.row_step); // Shrink to actual size
403 
404  // Publish output
405  pub_.publish(msg);
406 }
407 
408 // Custom Callback Queue
410 // custom callback queue thread
412 {
413  static const double TIMEOUT = 0.01;
414 
415  while (rosnode_->ok()) {
417  }
418 }
419 
421 {
422  sim_time_ = msgs::Convert( _msg->sim_time() );
423 
424  math::Pose pose;
425  pose.pos.x = 0.5*sin(0.01*sim_time_.Double());
426  gzdbg << "plugin simTime [" << sim_time_.Double() << "] update pose [" << pose.pos.x << "]\n";
427 }
428 
429 }
msg
void publish(const boost::shared_ptr< M > &message) const
ros::NodeHandle * rosnode_
pointer to ros node
int laser_connect_count_
Keep track of number of connections.
sensors::RaySensorPtr parent_ray_sensor_
ROSCPP_DECL bool isInitialized()
static double gaussianKernel(double mu, double sigma)
Gaussian noise generator.
void putLaserData(common::Time &_updateTime)
Put laser data to the ROS topic.
void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
Load the plugin.
std::string resolve(const std::string &prefix, const std::string &frame_name)
#define ROS_FATAL_STREAM(args)
sensors::SensorPtr parent_sensor_
The parent sensor.
#define ROS_INFO(...)
double gaussian_noise_
Gaussian noise.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
double min_range_
Minimum range to publish.
std::string robot_namespace_
for setting ROS name space
bool getParam(const std::string &key, std::string &s) const
double max_range_
Maximum range to publish.
void onStats(const boost::shared_ptr< msgs::WorldStatistics const > &_msg)
std::string frame_name_
frame transform name, should match link name
bool ok() const
boost::mutex lock_
A mutex to lock access to fields that are used in message callbacks.
boost::shared_ptr< void > VoidPtr
virtual void OnNewLaserScans()
Update the controller.


velodyne_gazebo_plugin
Author(s): Kevin Hallenbeck
autogenerated on Fri Jan 15 2021 03:26:48