gazebo_ros_depth_camera.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2013 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 /*
18  Desc: GazeboRosDepthCamera plugin for simulating cameras in Gazebo
19  Author: John Hsu
20  Date: 24 Sept 2008
21  */
22 
23 #include <algorithm>
24 #include <assert.h>
25 #include <boost/thread/thread.hpp>
26 #include <boost/bind.hpp>
27 
29 
30 #include <gazebo/sensors/Sensor.hh>
31 #include <sdf/sdf.hh>
32 #include <gazebo/sensors/SensorTypes.hh>
33 
35 
36 #include <tf/tf.h>
37 
38 namespace gazebo
39 {
40 // Register this plugin with the simulator
41 GZ_REGISTER_SENSOR_PLUGIN(GazeboRosDepthCamera)
42 
43 // Constructor
46 {
47  this->point_cloud_connect_count_ = 0;
48  this->depth_image_connect_count_ = 0;
49  this->depth_info_connect_count_ = 0;
50  this->last_depth_image_camera_info_update_time_ = common::Time(0);
51 }
52 
54 // Destructor
56 {
57 }
58 
60 // Load the controller
61 void GazeboRosDepthCamera::Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
62 {
63  DepthCameraPlugin::Load(_parent, _sdf);
64 
65  // Make sure the ROS node for Gazebo has already been initialized
66  if (!ros::isInitialized())
67  {
68  ROS_FATAL_STREAM_NAMED("depth_camera", "A ROS node for Gazebo has not been initialized, unable to load plugin. "
69  << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
70  return;
71  }
72 
73  // copying from DepthCameraPlugin into GazeboRosCameraUtils
74  this->parentSensor_ = this->parentSensor;
75  this->width_ = this->width;
76  this->height_ = this->height;
77  this->depth_ = this->depth;
78  this->format_ = this->format;
79  this->camera_ = this->depthCamera;
80 
81  // using a different default
82  if (!_sdf->HasElement("imageTopicName"))
83  this->image_topic_name_ = "ir/image_raw";
84  if (!_sdf->HasElement("cameraInfoTopicName"))
85  this->camera_info_topic_name_ = "ir/camera_info";
86 
87  // point cloud stuff
88  if (!_sdf->HasElement("pointCloudTopicName"))
89  this->point_cloud_topic_name_ = "points";
90  else
91  this->point_cloud_topic_name_ = _sdf->GetElement("pointCloudTopicName")->Get<std::string>();
92 
93  // depth image stuff
94  if (!_sdf->HasElement("depthImageTopicName"))
95  this->depth_image_topic_name_ = "depth/image_raw";
96  else
97  this->depth_image_topic_name_ = _sdf->GetElement("depthImageTopicName")->Get<std::string>();
98 
99  if (!_sdf->HasElement("depthImageCameraInfoTopicName"))
100  this->depth_image_camera_info_topic_name_ = "depth/camera_info";
101  else
102  this->depth_image_camera_info_topic_name_ = _sdf->GetElement("depthImageCameraInfoTopicName")->Get<std::string>();
103 
104  if (!_sdf->HasElement("pointCloudCutoff"))
105  this->point_cloud_cutoff_ = 0.4;
106  else
107  this->point_cloud_cutoff_ = _sdf->GetElement("pointCloudCutoff")->Get<double>();
108 
110  GazeboRosCameraUtils::Load(_parent, _sdf);
111 }
112 
114 {
115  ros::AdvertiseOptions point_cloud_ao =
116  ros::AdvertiseOptions::create<sensor_msgs::PointCloud2 >(
117  this->point_cloud_topic_name_,1,
118  boost::bind( &GazeboRosDepthCamera::PointCloudConnect,this),
119  boost::bind( &GazeboRosDepthCamera::PointCloudDisconnect,this),
120  ros::VoidPtr(), &this->camera_queue_);
121  this->point_cloud_pub_ = this->rosnode_->advertise(point_cloud_ao);
122 
123  ros::AdvertiseOptions depth_image_ao =
124  ros::AdvertiseOptions::create< sensor_msgs::Image >(
125  this->depth_image_topic_name_,1,
126  boost::bind( &GazeboRosDepthCamera::DepthImageConnect,this),
127  boost::bind( &GazeboRosDepthCamera::DepthImageDisconnect,this),
128  ros::VoidPtr(), &this->camera_queue_);
129  this->depth_image_pub_ = this->rosnode_->advertise(depth_image_ao);
130 
131  ros::AdvertiseOptions depth_image_camera_info_ao =
132  ros::AdvertiseOptions::create<sensor_msgs::CameraInfo>(
134  boost::bind( &GazeboRosDepthCamera::DepthInfoConnect,this),
135  boost::bind( &GazeboRosDepthCamera::DepthInfoDisconnect,this),
136  ros::VoidPtr(), &this->camera_queue_);
137  this->depth_image_camera_info_pub_ = this->rosnode_->advertise(depth_image_camera_info_ao);
138 }
139 
140 
142 // Increment count
144 {
146  (*this->image_connect_count_)++;
147  this->parentSensor->SetActive(true);
148 }
150 // Decrement count
152 {
154  (*this->image_connect_count_)--;
155  if (this->point_cloud_connect_count_ <= 0)
156  this->parentSensor->SetActive(false);
157 }
158 
160 // Increment count
162 {
164  this->parentSensor->SetActive(true);
165 }
167 // Decrement count
169 {
171 }
172 
174 // Increment count
176 {
178 }
180 // Decrement count
182 {
184 }
185 
187 // Update the controller
188 void GazeboRosDepthCamera::OnNewDepthFrame(const float *_image,
189  unsigned int _width, unsigned int _height, unsigned int _depth,
190  const std::string &_format)
191 {
192  if (!this->initialized_ || this->height_ <=0 || this->width_ <=0)
193  return;
194 
195  this->depth_sensor_update_time_ = this->parentSensor->LastMeasurementTime();
196 
197  if (this->parentSensor->IsActive())
198  {
199  if (this->point_cloud_connect_count_ <= 0 &&
200  this->depth_image_connect_count_ <= 0 &&
201  (*this->image_connect_count_) <= 0)
202  {
203  this->parentSensor->SetActive(false);
204  }
205  else
206  {
207  if (this->point_cloud_connect_count_ > 0)
208  this->FillPointdCloud(_image);
209 
210  if (this->depth_image_connect_count_ > 0)
211  this->FillDepthImage(_image);
212  }
213  }
214  else
215  {
216  if (this->point_cloud_connect_count_ > 0 ||
217  this->depth_image_connect_count_ <= 0)
218  // do this first so there's chance for sensor to run 1 frame after activate
219  this->parentSensor->SetActive(true);
220  }
222 }
223 
225 // Update the controller
227  unsigned int _width, unsigned int _height, unsigned int _depth,
228  const std::string &_format)
229 {
230  if (!this->initialized_ || this->height_ <=0 || this->width_ <=0)
231  return;
232 
233  this->depth_sensor_update_time_ = this->parentSensor->LastMeasurementTime();
234 
235  if (!this->parentSensor->IsActive())
236  {
237  if (this->point_cloud_connect_count_ > 0)
238  // do this first so there's chance for sensor to run 1 frame after activate
239  this->parentSensor->SetActive(true);
240  }
241  else
242  {
243  if (this->point_cloud_connect_count_ > 0)
244  {
245  this->lock_.lock();
246  this->point_cloud_msg_.header.frame_id = this->frame_name_;
247  this->point_cloud_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
248  this->point_cloud_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
249  this->point_cloud_msg_.width = this->width;
250  this->point_cloud_msg_.height = this->height;
251  this->point_cloud_msg_.row_step = this->point_cloud_msg_.point_step * this->width;
252 
254  pcd_modifier.setPointCloud2FieldsByString(2, "xyz", "rgb");
255  pcd_modifier.resize(_width*_height);
256 
257  point_cloud_msg_.is_dense = true;
258 
263 
264  for (unsigned int i = 0; i < _width; i++)
265  {
266  for (unsigned int j = 0; j < _height; j++, ++iter_x, ++iter_y, ++iter_z, ++iter_rgb)
267  {
268  unsigned int index = (j * _width) + i;
269  *iter_x = _pcd[4 * index];
270  *iter_y = _pcd[4 * index + 1];
271  *iter_z = _pcd[4 * index + 2];
272  *iter_rgb = _pcd[4 * index + 3];
273  if (i == _width /2 && j == _height / 2)
274  {
275  uint32_t rgb = *reinterpret_cast<int*>(&(*iter_rgb));
276  uint8_t r = (rgb >> 16) & 0x0000ff;
277  uint8_t g = (rgb >> 8) & 0x0000ff;
278  uint8_t b = (rgb) & 0x0000ff;
279  std::cerr << (int)r << " " << (int)g << " " << (int)b << "\n";
280  }
281  }
282  }
283 
285  this->lock_.unlock();
286  }
287  }
288 }
289 
291 // Update the controller
292 void GazeboRosDepthCamera::OnNewImageFrame(const unsigned char *_image,
293  unsigned int _width, unsigned int _height, unsigned int _depth,
294  const std::string &_format)
295 {
296  if (!this->initialized_ || this->height_ <=0 || this->width_ <=0)
297  return;
298 
299  //ROS_ERROR_NAMED("depth_camera", "camera_ new frame %s %s",this->parentSensor_->GetName().c_str(),this->frame_name_.c_str());
300  this->sensor_update_time_ = this->parentSensor->LastMeasurementTime();
301 
302  if (!this->parentSensor->IsActive())
303  {
304  if ((*this->image_connect_count_) > 0)
305  // do this first so there's chance for sensor to run 1 frame after activate
306  this->parentSensor->SetActive(true);
307  }
308  else
309  {
310  if ((*this->image_connect_count_) > 0)
311  {
312  this->PutCameraData(_image);
313  // TODO(lucasw) publish camera info with depth image
314  // this->PublishCameraInfo(sensor_update_time);
315  }
316  }
317 }
318 
320 // Put camera data to the interface
322 {
323  this->lock_.lock();
324 
325  this->point_cloud_msg_.header.frame_id = this->frame_name_;
326  this->point_cloud_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
327  this->point_cloud_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
328  this->point_cloud_msg_.width = this->width;
329  this->point_cloud_msg_.height = this->height;
330  this->point_cloud_msg_.row_step = this->point_cloud_msg_.point_step * this->width;
331 
334  this->height,
335  this->width,
336  this->skip_,
337  (void*)_src );
338 
340 
341  this->lock_.unlock();
342 }
343 
345 // Put depth image data to the interface
347 {
348  this->lock_.lock();
349  // copy data into image
350  this->depth_image_msg_.header.frame_id = this->frame_name_;
351  this->depth_image_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
352  this->depth_image_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
353 
356  this->height,
357  this->width,
358  this->skip_,
359  (void*)_src );
360 
362 
363  this->lock_.unlock();
364 }
365 
366 
367 // Fill depth information
369  sensor_msgs::PointCloud2 &point_cloud_msg,
370  uint32_t rows_arg, uint32_t cols_arg,
371  uint32_t step_arg, void* data_arg)
372 {
373  sensor_msgs::PointCloud2Modifier pcd_modifier(point_cloud_msg);
374  pcd_modifier.setPointCloud2FieldsByString(2, "xyz", "rgb");
375  pcd_modifier.resize(rows_arg*cols_arg);
376 
381 
382  point_cloud_msg.is_dense = true;
383 
384  float* toCopyFrom = (float*)data_arg;
385  int index = 0;
386 
387  double hfov = this->parentSensor->DepthCamera()->HFOV().Radian();
388  double fl = ((double)this->width) / (2.0 *tan(hfov/2.0));
389 
390  // convert depth to point cloud
391  for (uint32_t j=0; j<rows_arg; j++)
392  {
393  double pAngle;
394  if (rows_arg>1) pAngle = atan2( (double)j - 0.5*(double)(rows_arg-1), fl);
395  else pAngle = 0.0;
396 
397  for (uint32_t i=0; i<cols_arg; i++, ++iter_x, ++iter_y, ++iter_z, ++iter_rgb)
398  {
399  double yAngle;
400  if (cols_arg>1) yAngle = atan2( (double)i - 0.5*(double)(cols_arg-1), fl);
401  else yAngle = 0.0;
402 
403  double depth = toCopyFrom[index++];
404 
405  // in optical frame
406  // hardcoded rotation rpy(-M_PI/2, 0, -M_PI/2) is built-in
407  // to urdf, where the *_optical_frame should have above relative
408  // rotation from the physical camera *_frame
409  *iter_x = depth * tan(yAngle);
410  *iter_y = depth * tan(pAngle);
411  if(depth > this->point_cloud_cutoff_)
412  {
413  *iter_z = depth;
414  }
415  else //point in the unseeable range
416  {
417  *iter_x = *iter_y = *iter_z = std::numeric_limits<float>::quiet_NaN ();
418  point_cloud_msg.is_dense = false;
419  }
420 
421  // put image color data for each point
422  uint8_t* image_src = (uint8_t*)(&(this->image_msg_.data[0]));
423  if (this->image_msg_.data.size() == rows_arg*cols_arg*3)
424  {
425  // color
426  iter_rgb[0] = image_src[i*3+j*cols_arg*3+0];
427  iter_rgb[1] = image_src[i*3+j*cols_arg*3+1];
428  iter_rgb[2] = image_src[i*3+j*cols_arg*3+2];
429  }
430  else if (this->image_msg_.data.size() == rows_arg*cols_arg)
431  {
432  // mono (or bayer? @todo; fix for bayer)
433  iter_rgb[0] = image_src[i+j*cols_arg];
434  iter_rgb[1] = image_src[i+j*cols_arg];
435  iter_rgb[2] = image_src[i+j*cols_arg];
436  }
437  else
438  {
439  // no image
440  iter_rgb[0] = 0;
441  iter_rgb[1] = 0;
442  iter_rgb[2] = 0;
443  }
444  }
445  }
446 
447  return true;
448 }
449 
450 // Fill depth information
452  sensor_msgs::Image& image_msg,
453  uint32_t rows_arg, uint32_t cols_arg,
454  uint32_t step_arg, void* data_arg)
455 {
456  image_msg.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
457  image_msg.height = rows_arg;
458  image_msg.width = cols_arg;
459  image_msg.step = sizeof(float) * cols_arg;
460  image_msg.data.resize(rows_arg * cols_arg * sizeof(float));
461  image_msg.is_bigendian = 0;
462 
463  const float bad_point = std::numeric_limits<float>::quiet_NaN();
464 
465  float* dest = (float*)(&(image_msg.data[0]));
466  float* toCopyFrom = (float*)data_arg;
467  int index = 0;
468 
469  // convert depth to point cloud
470  for (uint32_t j = 0; j < rows_arg; j++)
471  {
472  for (uint32_t i = 0; i < cols_arg; i++)
473  {
474  float depth = toCopyFrom[index++];
475 
476  if (depth > this->point_cloud_cutoff_)
477  {
478  dest[i + j * cols_arg] = depth;
479  }
480  else //point in the unseeable range
481  {
482  dest[i + j * cols_arg] = bad_point;
483  }
484  }
485  }
486  return true;
487 }
488 
490 {
491  ROS_DEBUG_NAMED("depth_camera", "publishing default camera info, then depth camera info");
493 
494  if (this->depth_info_connect_count_ > 0)
495  {
496  common::Time sensor_update_time = this->parentSensor_->LastMeasurementTime();
497 
498  this->sensor_update_time_ = sensor_update_time;
499  if (sensor_update_time - this->last_depth_image_camera_info_update_time_ >= this->update_period_)
500  {
501  this->PublishCameraInfo(this->depth_image_camera_info_pub_); // , sensor_update_time);
502  this->last_depth_image_camera_info_update_time_ = sensor_update_time;
503  }
504  }
505 }
506 
507 //@todo: publish disparity similar to openni_camera_deprecated/src/nodelets/openni_nodelet.cpp.
508 /*
509 #include <stereo_msgs/DisparityImage.h>
510 pub_disparity_ = comm_nh.advertise<stereo_msgs::DisparityImage > ("depth/disparity", 5, subscriberChanged2, subscriberChanged2);
511 
512 void GazeboRosDepthCamera::PublishDisparityImage(const DepthImage& depth, ros::Time time)
513 {
514  stereo_msgs::DisparityImagePtr disp_msg = boost::make_shared<stereo_msgs::DisparityImage > ();
515  disp_msg->header.stamp = time;
516  disp_msg->header.frame_id = device_->isDepthRegistered () ? rgb_frame_id_ : depth_frame_id_;
517  disp_msg->image.header = disp_msg->header;
518  disp_msg->image.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
519  disp_msg->image.height = depth_height_;
520  disp_msg->image.width = depth_width_;
521  disp_msg->image.step = disp_msg->image.width * sizeof (float);
522  disp_msg->image.data.resize (disp_msg->image.height * disp_msg->image.step);
523  disp_msg->T = depth.getBaseline ();
524  disp_msg->f = depth.getFocalLength () * depth_width_ / depth.getWidth ();
525 
527  disp_msg->min_disparity = 0.0;
528  disp_msg->max_disparity = disp_msg->T * disp_msg->f / 0.3;
529  disp_msg->delta_d = 0.125;
530 
531  depth.fillDisparityImage (depth_width_, depth_height_, reinterpret_cast<float*>(&disp_msg->image.data[0]), disp_msg->image.step);
532 
533  pub_disparity_.publish (disp_msg);
534 }
535 */
536 
537 
538 }
virtual void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
Load the plugin.
std::string image_topic_name_
ROS image topic name.
void publish(const boost::shared_ptr< M > &message) const
boost::mutex lock_
A mutex to lock access to fields that are used in ROS message callbacks.
std::string depth_image_topic_name_
image where each pixel contains the depth information
ROSCPP_DECL bool isInitialized()
std::string point_cloud_topic_name_
ROS image topic name.
void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf, const std::string &_camera_name_suffix="")
Load the plugin.
boost::shared_ptr< int > image_connect_count_
Keep track of number of image connections.
sensor_msgs::Image image_msg_
ROS image message.
#define ROS_DEBUG_NAMED(name,...)
event::ConnectionPtr OnLoad(const boost::function< void()> &)
#define ROS_FATAL_STREAM_NAMED(name, args)
const std::string TYPE_32FC1
bool initialized_
True if camera util is initialized.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::string frame_name_
ROS frame transform name to use in the image message header. This should typically match the link nam...
bool FillDepthImageHelper(sensor_msgs::Image &image_msg, uint32_t rows_arg, uint32_t cols_arg, uint32_t step_arg, void *data_arg)
sensor_msgs::PointCloud2 point_cloud_msg_
PointCloud2 point cloud message.
ros::Publisher point_cloud_pub_
A pointer to the ROS node. A node will be instantiated if it does not exist.
void PutCameraData(const unsigned char *_src)
Put camera data to the ROS topic.
int point_cloud_connect_count_
Keep track of number of connctions for point clouds.
virtual void OnNewDepthFrame(const float *_image, unsigned int _width, unsigned int _height, unsigned int _depth, const std::string &_format)
Update the controller.
ros::NodeHandle * rosnode_
A pointer to the ROS node. A node will be instantiated if it does not exist.
void FillPointdCloud(const float *_src)
Put camera data to the ROS topic.
int depth_image_connect_count_
Keep track of number of connctions for point clouds.
void FillDepthImage(const float *_src)
push depth image data into ros topic
virtual void Advertise()
Advertise point cloud and depth image.
void setPointCloud2FieldsByString(int n_fields,...)
virtual void OnNewRGBPointCloud(const float *_pcd, unsigned int _width, unsigned int _height, unsigned int _depth, const std::string &_format)
Update the controller.
boost::shared_ptr< void > VoidPtr
virtual void OnNewImageFrame(const unsigned char *_image, unsigned int _width, unsigned int _height, unsigned int _depth, const std::string &_format)
Update the controller.
bool FillPointCloudHelper(sensor_msgs::PointCloud2 &point_cloud_msg, uint32_t rows_arg, uint32_t cols_arg, uint32_t step_arg, void *data_arg)


gazebo_plugins
Author(s): John Hsu
autogenerated on Tue Apr 6 2021 02:19:39