openni2_driver.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013, Willow Garage, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Author: Julius Kammerl (jkammerl@willowgarage.com)
30  */
31 
34 
37 
38 #include <boost/date_time/posix_time/posix_time.hpp>
39 #include <boost/thread/thread.hpp>
40 
41 namespace openni2_wrapper
42 {
43 
45  nh_(n),
46  pnh_(pnh),
47  device_manager_(OpenNI2DeviceManager::getSingelton()),
48  config_init_(false),
49  data_skip_ir_counter_(0),
50  data_skip_color_counter_(0),
51  data_skip_depth_counter_ (0),
52  ir_subscribers_(false),
53  color_subscribers_(false),
54  depth_subscribers_(false),
55  depth_raw_subscribers_(false),
56  enable_reconnect_(false)
57 {
58 
60 
62 
63  initDevice();
64 
65  // Initialize dynamic reconfigure
67  reconfigure_server_->setCallback(boost::bind(&OpenNI2Driver::configCb, this, _1, _2));
68 
69  while (!config_init_)
70  {
71  ROS_DEBUG("Waiting for dynamic reconfigure configuration.");
72  boost::this_thread::sleep(boost::posix_time::milliseconds(100));
73  }
74  ROS_DEBUG("Dynamic reconfigure configuration received.");
75 
77 
78  if( enable_reconnect_ )
79  {
80  ROS_WARN_STREAM("Reconnect has been enabled, only one camera "
81  << "should be plugged into each bus");
83  }
84  else
85  {
86  ROS_WARN_STREAM("Reconnect has been disabled");
87  }
88 }
89 
91 {
92 
93  // Allow remapping namespaces rgb, ir, depth, depth_registered
94  ros::NodeHandle color_nh(nh_, "rgb");
95  image_transport::ImageTransport color_it(color_nh);
96  ros::NodeHandle ir_nh(nh_, "ir");
98  ros::NodeHandle depth_nh(nh_, "depth");
99  image_transport::ImageTransport depth_it(depth_nh);
100  ros::NodeHandle depth_raw_nh(nh_, "depth");
101  image_transport::ImageTransport depth_raw_it(depth_raw_nh);
102  ros::NodeHandle projector_nh(nh_, "projector");
103  // Advertise all published topics
104 
105  // Prevent connection callbacks from executing until we've set all the publishers. Otherwise
106  // connectCb() can fire while we're advertising (say) "depth/image_raw", but before we actually
107  // assign to pub_depth_raw_. Then pub_depth_raw_.getNumSubscribers() returns 0, and we fail to start
108  // the depth generator.
109  boost::lock_guard<boost::mutex> lock(connect_mutex_);
110 
111  // Asus Xtion PRO does not have an RGB camera
112  if (device_->hasColorSensor())
113  {
116  pub_color_ = color_it.advertiseCamera("image", 1, itssc, itssc, rssc, rssc);
117  }
118 
119  if (device_->hasIRSensor())
120  {
123  pub_ir_ = ir_it.advertiseCamera("image", 1, itssc, itssc, rssc, rssc);
124  }
125 
126  if (device_->hasDepthSensor())
127  {
130  pub_depth_raw_ = depth_it.advertiseCamera("image_raw", 1, itssc, itssc, rssc, rssc);
131  pub_depth_ = depth_raw_it.advertiseCamera("image", 1, itssc, itssc, rssc, rssc);
132  pub_projector_info_ = projector_nh.advertise<sensor_msgs::CameraInfo>("camera_info", 1, rssc, rssc);
133  }
134 
136 
137  // Pixel offset between depth and IR images.
138  // By default assume offset of (5,4) from 9x7 correlation window.
139  // NOTE: These are now (temporarily?) dynamically reconfigurable, to allow tweaking.
140  //param_nh.param("depth_ir_offset_x", depth_ir_offset_x_, 5.0);
141  //param_nh.param("depth_ir_offset_y", depth_ir_offset_y_, 4.0);
142 
143  // The camera names are set to [rgb|depth]_[serial#], e.g. depth_B00367707227042B.
144  // camera_info_manager substitutes this for ${NAME} in the URL.
145  std::string serial_number = device_->getStringID();
146  std::string color_name, ir_name;
147 
148  color_name = "rgb_" + serial_number;
149  ir_name = "depth_" + serial_number;
150 
151  // Load the saved calibrations, if they exist
152  color_info_manager_ = boost::make_shared<camera_info_manager::CameraInfoManager>(color_nh, color_name, color_info_url_);
153  ir_info_manager_ = boost::make_shared<camera_info_manager::CameraInfoManager>(ir_nh, ir_name, ir_info_url_);
154 
156 
157 }
158 
159 bool OpenNI2Driver::getSerialCb(openni2_camera::GetSerialRequest& req, openni2_camera::GetSerialResponse& res) {
160  res.serial = device_manager_->getSerial(device_->getUri());
161  return true;
162 }
163 
164 void OpenNI2Driver::configCb(Config &config, uint32_t level)
165 {
166  bool stream_reset = false;
167 
168  depth_ir_offset_x_ = config.depth_ir_offset_x;
169  depth_ir_offset_y_ = config.depth_ir_offset_y;
170  z_offset_mm_ = config.z_offset_mm;
171  z_scaling_ = config.z_scaling;
172 
173  ir_time_offset_ = ros::Duration(config.ir_time_offset);
174  color_time_offset_ = ros::Duration(config.color_time_offset);
175  depth_time_offset_ = ros::Duration(config.depth_time_offset);
176 
177  if (lookupVideoModeFromDynConfig(config.ir_mode, ir_video_mode_)<0)
178  {
179  ROS_ERROR("Undefined IR video mode received from dynamic reconfigure");
180  exit(-1);
181  }
182 
183  if (lookupVideoModeFromDynConfig(config.color_mode, color_video_mode_)<0)
184  {
185  ROS_ERROR("Undefined color video mode received from dynamic reconfigure");
186  exit(-1);
187  }
188 
189  if (lookupVideoModeFromDynConfig(config.depth_mode, depth_video_mode_)<0)
190  {
191  ROS_ERROR("Undefined depth video mode received from dynamic reconfigure");
192  exit(-1);
193  }
194 
195  // assign pixel format
196 
200 
201  color_depth_synchronization_ = config.color_depth_synchronization;
202  depth_registration_ = config.depth_registration;
203 
204  auto_exposure_ = config.auto_exposure;
205  auto_white_balance_ = config.auto_white_balance;
206  exposure_ = config.exposure;
207 
208  use_device_time_ = config.use_device_time;
209 
210  data_skip_ = config.data_skip+1;
211 
213 
214  config_init_ = true;
215 
216  old_config_ = config;
217 }
218 
220 {
221  if (device_->isIRVideoModeSupported(ir_video_mode))
222  {
223  if (ir_video_mode != device_->getIRVideoMode())
224  {
225  device_->setIRVideoMode(ir_video_mode);
226  }
227 
228  }
229  else
230  {
231  ROS_ERROR_STREAM("Unsupported IR video mode - " << ir_video_mode);
232  }
233 }
235 {
236  if (device_->isColorVideoModeSupported(color_video_mode))
237  {
238  if (color_video_mode != device_->getColorVideoMode())
239  {
240  device_->setColorVideoMode(color_video_mode);
241  }
242  }
243  else
244  {
245  ROS_ERROR_STREAM("Unsupported color video mode - " << color_video_mode);
246  }
247 }
249 {
250  if (device_->isDepthVideoModeSupported(depth_video_mode))
251  {
252  if (depth_video_mode != device_->getDepthVideoMode())
253  {
254  device_->setDepthVideoMode(depth_video_mode);
255  }
256  }
257  else
258  {
259  ROS_ERROR_STREAM("Unsupported depth video mode - " << depth_video_mode);
260  }
261 }
262 
264 {
265 
269 
273 
274  if (device_->isImageRegistrationModeSupported())
275  {
276  try
277  {
278  if (!config_init_ || (old_config_.depth_registration != depth_registration_))
279  device_->setImageRegistrationMode(depth_registration_);
280  }
281  catch (const OpenNI2Exception& exception)
282  {
283  ROS_ERROR("Could not set image registration. Reason: %s", exception.what());
284  }
285  }
286 
287  try
288  {
289  if (!config_init_ || (old_config_.color_depth_synchronization != color_depth_synchronization_))
290  device_->setDepthColorSync(color_depth_synchronization_);
291  }
292  catch (const OpenNI2Exception& exception)
293  {
294  ROS_ERROR("Could not set color depth synchronization. Reason: %s", exception.what());
295  }
296 
297  try
298  {
299  if (!config_init_ || (old_config_.auto_exposure != auto_exposure_))
300  device_->setAutoExposure(auto_exposure_);
301  }
302  catch (const OpenNI2Exception& exception)
303  {
304  ROS_ERROR("Could not set auto exposure. Reason: %s", exception.what());
305  }
306 
307  try
308  {
309  if (!config_init_ || (old_config_.auto_white_balance != auto_white_balance_))
310  device_->setAutoWhiteBalance(auto_white_balance_);
311  }
312  catch (const OpenNI2Exception& exception)
313  {
314  ROS_ERROR("Could not set auto white balance. Reason: %s", exception.what());
315  }
316 
317 
318  // Workaound for https://github.com/ros-drivers/openni2_camera/issues/51
319  // This is only needed when any of the 3 setting change. For simplicity
320  // this check is always performed and exposure set.
321  if( (!auto_exposure_ && !auto_white_balance_) && exposure_ != 0 )
322  {
323  ROS_INFO_STREAM("Forcing exposure set, when auto exposure/white balance disabled");
325  }
326  else
327  {
328  // Setting the exposure the old way, although this should not have an effect
329  try
330  {
331  if (!config_init_ || (old_config_.exposure != exposure_))
332  device_->setExposure(exposure_);
333  }
334  catch (const OpenNI2Exception& exception)
335  {
336  ROS_ERROR("Could not set exposure. Reason: %s", exception.what());
337  }
338  }
339 
340  device_->setUseDeviceTimer(use_device_time_);
341 }
342 
343 
344 
346 {
347  int current_exposure_ = device_->getExposure();
348  try
349  {
350  if( current_exposure_ == exposure_ )
351  {
352  if( exposure_ < 254 )
353  {
354  device_->setExposure(exposure_ + 1);
355  }
356  else
357  {
358  device_->setExposure(exposure_ - 1);
359  }
360  }
361  device_->setExposure(exposure_);
362  }
363  catch (const OpenNI2Exception& exception)
364  {
365  ROS_ERROR("Could not set exposure. Reason: %s", exception.what());
366  }
367 }
368 
370 {
371  if( !device_ )
372  {
373  ROS_WARN_STREAM("Callback in " << __FUNCTION__ << "failed due to null device");
374  return;
375  }
376  boost::lock_guard<boost::mutex> lock(connect_mutex_);
377 
379 
380  if (color_subscribers_ && !device_->isColorStreamStarted())
381  {
382  // Can't stream IR and RGB at the same time. Give RGB preference.
383  if (device_->isIRStreamStarted())
384  {
385  ROS_ERROR("Cannot stream RGB and IR at the same time. Streaming RGB only.");
386  ROS_INFO("Stopping IR stream.");
387  device_->stopIRStream();
388  }
389 
390  device_->setColorFrameCallback(boost::bind(&OpenNI2Driver::newColorFrameCallback, this, _1));
391 
392  ROS_INFO("Starting color stream.");
393  device_->startColorStream();
394 
395  // Workaound for https://github.com/ros-drivers/openni2_camera/issues/51
396  if( exposure_ != 0 )
397  {
398  ROS_INFO_STREAM("Exposure is set to " << exposure_ << ", forcing on color stream start");
399  //delay for stream to start, before setting exposure
400  boost::this_thread::sleep(boost::posix_time::milliseconds(100));
402  }
403 
404  }
405  else if (!color_subscribers_ && device_->isColorStreamStarted())
406  {
407  ROS_INFO("Stopping color stream.");
408  device_->stopColorStream();
409 
410  // Start IR if it's been blocked on RGB subscribers
411  bool need_ir = pub_ir_.getNumSubscribers() > 0;
412  if (need_ir && !device_->isIRStreamStarted())
413  {
414  device_->setIRFrameCallback(boost::bind(&OpenNI2Driver::newIRFrameCallback, this, _1));
415 
416  ROS_INFO("Starting IR stream.");
417  device_->startIRStream();
418  }
419  }
420 }
421 
423 {
424  if( !device_ )
425  {
426  ROS_WARN_STREAM("Callback in " << __FUNCTION__ << "failed due to null device");
427  return;
428  }
429  boost::lock_guard<boost::mutex> lock(connect_mutex_);
430 
434 
435  bool need_depth = depth_subscribers_ || depth_raw_subscribers_;
436 
437  if (need_depth && !device_->isDepthStreamStarted())
438  {
439  device_->setDepthFrameCallback(boost::bind(&OpenNI2Driver::newDepthFrameCallback, this, _1));
440 
441  ROS_INFO("Starting depth stream.");
442  device_->startDepthStream();
443  }
444  else if (!need_depth && device_->isDepthStreamStarted())
445  {
446  ROS_INFO("Stopping depth stream.");
447  device_->stopDepthStream();
448  }
449 }
450 
452 {
453  if( !device_ )
454  {
455  ROS_WARN_STREAM("Callback in " << __FUNCTION__ << "failed due to null device");
456  return;
457  }
458  boost::lock_guard<boost::mutex> lock(connect_mutex_);
459 
461 
462  if (ir_subscribers_ && !device_->isIRStreamStarted())
463  {
464  // Can't stream IR and RGB at the same time
465  if (device_->isColorStreamStarted())
466  {
467  ROS_ERROR("Cannot stream RGB and IR at the same time. Streaming RGB only.");
468  }
469  else
470  {
471  device_->setIRFrameCallback(boost::bind(&OpenNI2Driver::newIRFrameCallback, this, _1));
472 
473  ROS_INFO("Starting IR stream.");
474  device_->startIRStream();
475  }
476  }
477  else if (!ir_subscribers_ && device_->isIRStreamStarted())
478  {
479  ROS_INFO("Stopping IR stream.");
480  device_->stopIRStream();
481  }
482 }
483 
484 void OpenNI2Driver::newIRFrameCallback(sensor_msgs::ImagePtr image)
485 {
487  {
489 
490  if (ir_subscribers_)
491  {
492  image->header.frame_id = ir_frame_id_;
493  image->header.stamp = image->header.stamp + ir_time_offset_;
494 
495  pub_ir_.publish(image, getIRCameraInfo(image->width, image->height, image->header.stamp));
496  }
497  }
498 }
499 
500 void OpenNI2Driver::newColorFrameCallback(sensor_msgs::ImagePtr image)
501 {
503  {
505 
506  if (color_subscribers_)
507  {
508  image->header.frame_id = color_frame_id_;
509  image->header.stamp = image->header.stamp + color_time_offset_;
510 
511  pub_color_.publish(image, getColorCameraInfo(image->width, image->height, image->header.stamp));
512  }
513  }
514 }
515 
516 void OpenNI2Driver::newDepthFrameCallback(sensor_msgs::ImagePtr image)
517 {
519  {
520 
522 
524  {
525  image->header.stamp = image->header.stamp + depth_time_offset_;
526 
527  if (z_offset_mm_ != 0)
528  {
529  uint16_t* data = reinterpret_cast<uint16_t*>(&image->data[0]);
530  for (unsigned int i = 0; i < image->width * image->height; ++i)
531  if (data[i] != 0)
532  data[i] += z_offset_mm_;
533  }
534 
535  if (fabs(z_scaling_ - 1.0) > 1e-6)
536  {
537  uint16_t* data = reinterpret_cast<uint16_t*>(&image->data[0]);
538  for (unsigned int i = 0; i < image->width * image->height; ++i)
539  if (data[i] != 0)
540  data[i] = static_cast<uint16_t>(data[i] * z_scaling_);
541  }
542 
543  sensor_msgs::CameraInfoPtr cam_info;
544 
546  {
547  image->header.frame_id = color_frame_id_;
548  cam_info = getColorCameraInfo(image->width,image->height, image->header.stamp);
549  } else
550  {
551  image->header.frame_id = depth_frame_id_;
552  cam_info = getDepthCameraInfo(image->width,image->height, image->header.stamp);
553  }
554 
556  {
557  pub_depth_raw_.publish(image, cam_info);
558  }
559 
560  if (depth_subscribers_ )
561  {
562  sensor_msgs::ImageConstPtr floating_point_image = rawToFloatingPointConversion(image);
563  pub_depth_.publish(floating_point_image, cam_info);
564  }
565 
566  // Projector "info" probably only useful for working with disparity images
568  {
569  pub_projector_info_.publish(getProjectorCameraInfo(image->width, image->height, image->header.stamp));
570  }
571  }
572  }
573 }
574 
575 // Methods to get calibration parameters for the various cameras
576 sensor_msgs::CameraInfoPtr OpenNI2Driver::getDefaultCameraInfo(int width, int height, double f) const
577 {
578  sensor_msgs::CameraInfoPtr info = boost::make_shared<sensor_msgs::CameraInfo>();
579 
580  info->width = width;
581  info->height = height;
582 
583  // No distortion
584  info->D.resize(5, 0.0);
585  info->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB;
586 
587  // Simple camera matrix: square pixels (fx = fy), principal point at center
588  info->K.assign(0.0);
589  info->K[0] = info->K[4] = f;
590  info->K[2] = (width / 2) - 0.5;
591  // Aspect ratio for the camera center on Kinect (and other devices?) is 4/3
592  // This formula keeps the principal point the same in VGA and SXGA modes
593  info->K[5] = (width * (3./8.)) - 0.5;
594  info->K[8] = 1.0;
595 
596  // No separate rectified image plane, so R = I
597  info->R.assign(0.0);
598  info->R[0] = info->R[4] = info->R[8] = 1.0;
599 
600  // Then P=K(I|0) = (K|0)
601  info->P.assign(0.0);
602  info->P[0] = info->P[5] = f; // fx, fy
603  info->P[2] = info->K[2]; // cx
604  info->P[6] = info->K[5]; // cy
605  info->P[10] = 1.0;
606 
607  return info;
608 }
609 
611 sensor_msgs::CameraInfoPtr OpenNI2Driver::getColorCameraInfo(int width, int height, ros::Time time) const
612 {
613  sensor_msgs::CameraInfoPtr info;
614 
615  if (color_info_manager_->isCalibrated())
616  {
617  info = boost::make_shared<sensor_msgs::CameraInfo>(color_info_manager_->getCameraInfo());
618  if ( info->width != width )
619  {
620  // Use uncalibrated values
621  ROS_WARN_ONCE("Image resolution doesn't match calibration of the RGB camera. Using default parameters.");
622  info = getDefaultCameraInfo(width, height, device_->getColorFocalLength(height));
623  }
624  }
625  else
626  {
627  // If uncalibrated, fill in default values
628  info = getDefaultCameraInfo(width, height, device_->getColorFocalLength(height));
629  }
630 
631  // Fill in header
632  info->header.stamp = time;
633  info->header.frame_id = color_frame_id_;
634 
635  return info;
636 }
637 
638 
639 sensor_msgs::CameraInfoPtr OpenNI2Driver::getIRCameraInfo(int width, int height, ros::Time time) const
640 {
641  sensor_msgs::CameraInfoPtr info;
642 
643  if (ir_info_manager_->isCalibrated())
644  {
645  info = boost::make_shared<sensor_msgs::CameraInfo>(ir_info_manager_->getCameraInfo());
646  if ( info->width != width )
647  {
648  // Use uncalibrated values
649  ROS_WARN_ONCE("Image resolution doesn't match calibration of the IR camera. Using default parameters.");
650  info = getDefaultCameraInfo(width, height, device_->getIRFocalLength(height));
651  }
652  }
653  else
654  {
655  // If uncalibrated, fill in default values
656  info = getDefaultCameraInfo(width, height, device_->getDepthFocalLength(height));
657  }
658 
659  // Fill in header
660  info->header.stamp = time;
661  info->header.frame_id = depth_frame_id_;
662 
663  return info;
664 }
665 
666 sensor_msgs::CameraInfoPtr OpenNI2Driver::getDepthCameraInfo(int width, int height, ros::Time time) const
667 {
668  // The depth image has essentially the same intrinsics as the IR image, BUT the
669  // principal point is offset by half the size of the hardware correlation window
670  // (probably 9x9 or 9x7 in 640x480 mode). See http://www.ros.org/wiki/kinect_calibration/technical
671 
672  double scaling = (double)width / 640;
673 
674  sensor_msgs::CameraInfoPtr info = getIRCameraInfo(width, height, time);
675  info->K[2] -= depth_ir_offset_x_*scaling; // cx
676  info->K[5] -= depth_ir_offset_y_*scaling; // cy
677  info->P[2] -= depth_ir_offset_x_*scaling; // cx
678  info->P[6] -= depth_ir_offset_y_*scaling; // cy
679 
681  return info;
682 }
683 
684 sensor_msgs::CameraInfoPtr OpenNI2Driver::getProjectorCameraInfo(int width, int height, ros::Time time) const
685 {
686  // The projector info is simply the depth info with the baseline encoded in the P matrix.
687  // It's only purpose is to be the "right" camera info to the depth camera's "left" for
688  // processing disparity images.
689  sensor_msgs::CameraInfoPtr info = getDepthCameraInfo(width, height, time);
690  // Tx = -baseline * fx
691  info->P[3] = -device_->getBaseline() * info->P[0];
692  return info;
693 }
694 
696 {
697  if (!pnh_.getParam("device_id", device_id_))
698  {
699  ROS_WARN ("~device_id is not set! Using first device.");
700  device_id_ = "#1";
701  }
702 
703  // Camera TF frames
704  pnh_.param("ir_frame_id", ir_frame_id_, std::string("/openni_ir_optical_frame"));
705  pnh_.param("rgb_frame_id", color_frame_id_, std::string("/openni_rgb_optical_frame"));
706  pnh_.param("depth_frame_id", depth_frame_id_, std::string("/openni_depth_optical_frame"));
707 
708  ROS_DEBUG("ir_frame_id = '%s' ", ir_frame_id_.c_str());
709  ROS_DEBUG("rgb_frame_id = '%s' ", color_frame_id_.c_str());
710  ROS_DEBUG("depth_frame_id = '%s' ", depth_frame_id_.c_str());
711 
712  pnh_.param("rgb_camera_info_url", color_info_url_, std::string());
713  pnh_.param("depth_camera_info_url", ir_info_url_, std::string());
714 
715  pnh_.param("enable_reconnect", enable_reconnect_, true);
716 
717 }
718 
719 std::string OpenNI2Driver::resolveDeviceURI(const std::string& device_id) throw(OpenNI2Exception)
720 {
721  // retrieve available device URIs, they look like this: "1d27/0601@1/5"
722  // which is <vendor ID>/<product ID>@<bus number>/<device number>
723  boost::shared_ptr<std::vector<std::string> > available_device_URIs =
724  device_manager_->getConnectedDeviceURIs();
725 
726  // look for '#<number>' format
727  if (device_id.size() > 1 && device_id[0] == '#')
728  {
729  std::istringstream device_number_str(device_id.substr(1));
730  int device_number;
731  device_number_str >> device_number;
732  int device_index = device_number - 1; // #1 refers to first device
733  if (device_index >= available_device_URIs->size() || device_index < 0)
734  {
736  "Invalid device number %i, there are %zu devices connected.",
737  device_number, available_device_URIs->size());
738  }
739  else
740  {
741  return available_device_URIs->at(device_index);
742  }
743  }
744  // look for '<bus>@<number>' format
745  // <bus> is usb bus id, typically start at 1
746  // <number> is the device number, for consistency with openni_camera, these start at 1
747  // although 0 specifies "any device on this bus"
748  else if (device_id.size() > 1 && device_id.find('@') != std::string::npos && device_id.find('/') == std::string::npos)
749  {
750  // get index of @ character
751  size_t index = device_id.find('@');
752  if (index <= 0)
753  {
755  "%s is not a valid device URI, you must give the bus number before the @.",
756  device_id.c_str());
757  }
758  if (index >= device_id.size() - 1)
759  {
761  "%s is not a valid device URI, you must give the device number after the @, specify 0 for any device on this bus",
762  device_id.c_str());
763  }
764 
765  // pull out device number on bus
766  std::istringstream device_number_str(device_id.substr(index+1));
767  int device_number;
768  device_number_str >> device_number;
769 
770  // reorder to @<bus>
771  std::string bus = device_id.substr(0, index);
772  bus.insert(0, "@");
773 
774  for (size_t i = 0; i < available_device_URIs->size(); ++i)
775  {
776  std::string s = (*available_device_URIs)[i];
777  if (s.find(bus) != std::string::npos)
778  {
779  // this matches our bus, check device number
780  std::ostringstream ss;
781  ss << bus << '/' << device_number;
782  if (device_number == 0 || s.find(ss.str()) != std::string::npos)
783  return s;
784  }
785  }
786 
787  THROW_OPENNI_EXCEPTION("Device not found %s", device_id.c_str());
788  }
789  else
790  {
791  // check if the device id given matches a serial number of a connected device
792  for(std::vector<std::string>::const_iterator it = available_device_URIs->begin();
793  it != available_device_URIs->end(); ++it)
794  {
795  try {
796  std::string serial = device_manager_->getSerial(*it);
797  if (serial.size() > 0 && device_id == serial)
798  return *it;
799  }
800  catch (const OpenNI2Exception& exception)
801  {
802  ROS_WARN("Could not query serial number of device \"%s\":", exception.what());
803  }
804  }
805 
806  // everything else is treated as part of the device_URI
807  bool match_found = false;
808  std::string matched_uri;
809  for (size_t i = 0; i < available_device_URIs->size(); ++i)
810  {
811  std::string s = (*available_device_URIs)[i];
812  if (s.find(device_id) != std::string::npos)
813  {
814  if (!match_found)
815  {
816  matched_uri = s;
817  match_found = true;
818  }
819  else
820  {
821  // more than one match
822  THROW_OPENNI_EXCEPTION("Two devices match the given device id '%s': %s and %s.", device_id.c_str(), matched_uri.c_str(), s.c_str());
823  }
824  }
825  }
826  if (match_found)
827  return matched_uri;
828  else
829  return "INVALID";
830  }
831 }
832 
834 {
835  while (ros::ok() && !device_)
836  {
837  try
838  {
839  std::string device_URI = resolveDeviceURI(device_id_);
840  device_ = device_manager_->getDevice(device_URI);
841  bus_id_ = extractBusID(device_->getUri() );
842  }
843  catch (const OpenNI2Exception& exception)
844  {
845  if (!device_)
846  {
847  ROS_INFO("No matching device found.... waiting for devices. Reason: %s", exception.what());
848  boost::this_thread::sleep(boost::posix_time::seconds(3));
849  continue;
850  }
851  else
852  {
853  ROS_ERROR("Could not retrieve device. Reason: %s", exception.what());
854  exit(-1);
855  }
856  }
857  }
858 
859  while (ros::ok() && !device_->isValid())
860  {
861  ROS_DEBUG("Waiting for device initialization..");
862  boost::this_thread::sleep(boost::posix_time::milliseconds(100));
863  }
864 
865 }
866 
867 
868 int OpenNI2Driver::extractBusID(const std::string& uri) const
869 {
870  // URI format is <vendor ID>/<product ID>@<bus number>/<device number>
871  unsigned first = uri.find('@');
872  unsigned last = uri.find('/', first);
873  std::string bus_id = uri.substr (first+1,last-first-1);
874  int rtn = atoi(bus_id.c_str());
875  return rtn;
876 }
877 
878 
880 {
881  // TODO: The current isConnected logic assumes that there is only one sensor
882  // on the bus of interest. In the future, we could compare serial numbers
883  // to make certain the same camera as been re-connected.
885  device_manager_->getConnectedDeviceURIs();
886  for (std::size_t i = 0; i != list->size(); ++i)
887  {
888  int uri_bus_id = extractBusID( list->at(i) );
889  if( uri_bus_id == bus_id_ )
890  {
891  return true;
892  }
893  }
894  return false;
895 }
896 
898 {
899  // If the connection is lost, clean up the device. If connected
900  // and the devices is not initialized, then initialize.
901  if( isConnected() )
902  {
903  if( !device_ )
904  {
905  ROS_INFO_STREAM("Detected re-connect...attempting reinit");
906  try
907  {
908  {
909  boost::lock_guard<boost::mutex> lock(connect_mutex_);
910  std::string device_URI = resolveDeviceURI(device_id_);
911  device_ = device_manager_->getDevice(device_URI);
912  bus_id_ = extractBusID(device_->getUri() );
913  while (ros::ok() && !device_->isValid())
914  {
915  ROS_INFO("Waiting for device initialization, before configuring and restarting publishers");
916  boost::this_thread::sleep(boost::posix_time::milliseconds(100));
917  }
918  }
919  ROS_INFO_STREAM("Re-applying configuration to camera on re-init");
920  config_init_ = false;
922 
923  // The color stream must be started in order to adjust the exposure white
924  // balance.
925  ROS_INFO_STREAM("Starting color stream to adjust camera");
926  colorConnectCb();
927 
928  // If auto exposure/white balance is disabled, then the rbg image won't
929  // be adjusted properly. This is a work around for now, but the final
930  // implimentation should only allow reconnection when auto exposure and
931  // white balance are disabled, and FIXED exposure is used instead.
932  if((!auto_exposure_ && !auto_white_balance_ ) && exposure_ == 0)
933  {
934  ROS_WARN_STREAM("Reconnection should not be enabled if auto expousre"
935  << "/white balance are disabled. Temporarily working"
936  << " around this issue");
937  ROS_WARN_STREAM("Toggling exposure and white balance to auto on re-connect"
938  << ", otherwise image will be very dark");
939  device_->setAutoExposure(true);
940  device_->setAutoWhiteBalance(true);
941  ROS_INFO_STREAM("Waiting for color camera to come up and adjust");
942  // It takes about 2.5 seconds for the camera to adjust
943  boost::this_thread::sleep(boost::posix_time::milliseconds(2500));
944  ROS_WARN_STREAM("Resetting auto exposure and white balance to previous values");
945  device_->setAutoExposure(auto_exposure_);
946  device_->setAutoWhiteBalance(auto_white_balance_);
947  }
948 
949  ROS_INFO_STREAM("Restarting publishers, if needed");
950  irConnectCb();
951  depthConnectCb();
952  ROS_INFO_STREAM("Done re-initializing cameras");
953  }
954 
955  catch (const OpenNI2Exception& exception)
956  {
957  if (!device_)
958  {
959  ROS_INFO_STREAM("Failed to re-initialize device on bus: " << bus_id_
960  << ", reason: " << exception.what());
961  }
962  }
963  }
964  }
965  else if( device_ )
966  {
967  ROS_WARN_STREAM("Detected loss of connection. Stopping all streams and resetting device");
968  device_->stopAllStreams();
969  device_.reset();
970  }
971 }
972 
973 
975 {
976  /*
977  * # Video modes defined by dynamic reconfigure:
978 output_mode_enum = gen.enum([ gen.const( "SXGA_30Hz", int_t, 1, "1280x1024@30Hz"),
979  gen.const( "SXGA_15Hz", int_t, 2, "1280x1024@15Hz"),
980  gen.const( "XGA_30Hz", int_t, 3, "1280x720@30Hz"),
981  gen.const( "XGA_15Hz", int_t, 4, "1280x720@15Hz"),
982  gen.const( "VGA_30Hz", int_t, 5, "640x480@30Hz"),
983  gen.const( "VGA_25Hz", int_t, 6, "640x480@25Hz"),
984  gen.const( "QVGA_25Hz", int_t, 7, "320x240@25Hz"),
985  gen.const( "QVGA_30Hz", int_t, 8, "320x240@30Hz"),
986  gen.const( "QVGA_60Hz", int_t, 9, "320x240@60Hz"),
987  gen.const( "QQVGA_25Hz", int_t, 10, "160x120@25Hz"),
988  gen.const( "QQVGA_30Hz", int_t, 11, "160x120@30Hz"),
989  gen.const( "QQVGA_60Hz", int_t, 12, "160x120@60Hz")],
990  "output mode")
991  */
992 
993  video_modes_lookup_.clear();
994 
995  OpenNI2VideoMode video_mode;
996 
997  // SXGA_30Hz
998  video_mode.x_resolution_ = 1280;
999  video_mode.y_resolution_ = 1024;
1000  video_mode.frame_rate_ = 30;
1001 
1002  video_modes_lookup_[1] = video_mode;
1003 
1004  // SXGA_15Hz
1005  video_mode.x_resolution_ = 1280;
1006  video_mode.y_resolution_ = 1024;
1007  video_mode.frame_rate_ = 15;
1008 
1009  video_modes_lookup_[2] = video_mode;
1010 
1011  // XGA_30Hz
1012  video_mode.x_resolution_ = 1280;
1013  video_mode.y_resolution_ = 720;
1014  video_mode.frame_rate_ = 30;
1015 
1016  video_modes_lookup_[3] = video_mode;
1017 
1018  // XGA_15Hz
1019  video_mode.x_resolution_ = 1280;
1020  video_mode.y_resolution_ = 720;
1021  video_mode.frame_rate_ = 15;
1022 
1023  video_modes_lookup_[4] = video_mode;
1024 
1025  // VGA_30Hz
1026  video_mode.x_resolution_ = 640;
1027  video_mode.y_resolution_ = 480;
1028  video_mode.frame_rate_ = 30;
1029 
1030  video_modes_lookup_[5] = video_mode;
1031 
1032  // VGA_25Hz
1033  video_mode.x_resolution_ = 640;
1034  video_mode.y_resolution_ = 480;
1035  video_mode.frame_rate_ = 25;
1036 
1037  video_modes_lookup_[6] = video_mode;
1038 
1039  // QVGA_25Hz
1040  video_mode.x_resolution_ = 320;
1041  video_mode.y_resolution_ = 240;
1042  video_mode.frame_rate_ = 25;
1043 
1044  video_modes_lookup_[7] = video_mode;
1045 
1046  // QVGA_30Hz
1047  video_mode.x_resolution_ = 320;
1048  video_mode.y_resolution_ = 240;
1049  video_mode.frame_rate_ = 30;
1050 
1051  video_modes_lookup_[8] = video_mode;
1052 
1053  // QVGA_60Hz
1054  video_mode.x_resolution_ = 320;
1055  video_mode.y_resolution_ = 240;
1056  video_mode.frame_rate_ = 60;
1057 
1058  video_modes_lookup_[9] = video_mode;
1059 
1060  // QQVGA_25Hz
1061  video_mode.x_resolution_ = 160;
1062  video_mode.y_resolution_ = 120;
1063  video_mode.frame_rate_ = 25;
1064 
1065  video_modes_lookup_[10] = video_mode;
1066 
1067  // QQVGA_30Hz
1068  video_mode.x_resolution_ = 160;
1069  video_mode.y_resolution_ = 120;
1070  video_mode.frame_rate_ = 30;
1071 
1072  video_modes_lookup_[11] = video_mode;
1073 
1074  // QQVGA_60Hz
1075  video_mode.x_resolution_ = 160;
1076  video_mode.y_resolution_ = 120;
1077  video_mode.frame_rate_ = 60;
1078 
1079  video_modes_lookup_[12] = video_mode;
1080 
1081 }
1082 
1084 {
1085  int ret = -1;
1086 
1087  std::map<int, OpenNI2VideoMode>::const_iterator it;
1088 
1089  it = video_modes_lookup_.find(mode_nr);
1090 
1091  if (it!=video_modes_lookup_.end())
1092  {
1093  video_mode = it->second;
1094  ret = 0;
1095  }
1096 
1097  return ret;
1098 }
1099 
1100 sensor_msgs::ImageConstPtr OpenNI2Driver::rawToFloatingPointConversion(sensor_msgs::ImageConstPtr raw_image)
1101 {
1102  static const float bad_point = std::numeric_limits<float>::quiet_NaN ();
1103 
1104  sensor_msgs::ImagePtr new_image = boost::make_shared<sensor_msgs::Image>();
1105 
1106  new_image->header = raw_image->header;
1107  new_image->width = raw_image->width;
1108  new_image->height = raw_image->height;
1109  new_image->is_bigendian = 0;
1110  new_image->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
1111  new_image->step = sizeof(float)*raw_image->width;
1112 
1113  std::size_t data_size = new_image->width*new_image->height;
1114  new_image->data.resize(data_size*sizeof(float));
1115 
1116  const unsigned short* in_ptr = reinterpret_cast<const unsigned short*>(&raw_image->data[0]);
1117  float* out_ptr = reinterpret_cast<float*>(&new_image->data[0]);
1118 
1119  for (std::size_t i = 0; i<data_size; ++i, ++in_ptr, ++out_ptr)
1120  {
1121  if (*in_ptr==0 || *in_ptr==0x7FF)
1122  {
1123  *out_ptr = bad_point;
1124  } else
1125  {
1126  *out_ptr = static_cast<float>(*in_ptr)/1000.0f;
1127  }
1128  }
1129 
1130  return new_image;
1131 }
1132 
1133 }
boost::shared_ptr< ReconfigureServer > reconfigure_server_
reconfigure server
sensor_msgs::CameraInfoPtr getDefaultCameraInfo(int width, int height, double f) const
boost::shared_ptr< camera_info_manager::CameraInfoManager > ir_info_manager_
void publish(const sensor_msgs::Image &image, const sensor_msgs::CameraInfo &info) const
bool enable_reconnect_
indicates if reconnect logic is enabled.
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
std::string resolveDeviceURI(const std::string &device_id)
void publish(const boost::shared_ptr< M > &message) const
ros::ServiceServer get_serial_server
get_serial server
uint32_t getNumSubscribers() const
boost::shared_ptr< OpenNI2Device > device_
XmlRpcServer s
bool getSerialCb(openni2_camera::GetSerialRequest &req, openni2_camera::GetSerialResponse &res)
sensor_msgs::ImageConstPtr rawToFloatingPointConversion(sensor_msgs::ImageConstPtr raw_image)
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
openni2_camera::OpenNI2Config Config
void setColorVideoMode(const OpenNI2VideoMode &color_video_mode)
#define THROW_OPENNI_EXCEPTION(format,...)
void setDepthVideoMode(const OpenNI2VideoMode &depth_video_mode)
image_transport::CameraPublisher pub_depth_
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
#define ROS_WARN(...)
boost::shared_ptr< OpenNI2DeviceManager > device_manager_
int lookupVideoModeFromDynConfig(int mode_nr, OpenNI2VideoMode &video_mode)
image_transport::CameraPublisher pub_ir_
CameraPublisher advertiseCamera(const std::string &base_topic, uint32_t queue_size, bool latch=false)
boost::shared_ptr< camera_info_manager::CameraInfoManager > color_info_manager_
Camera info manager objects.
void newColorFrameCallback(sensor_msgs::ImagePtr image)
sensor_msgs::CameraInfoPtr getProjectorCameraInfo(int width, int height, ros::Time time) const
#define ROS_WARN_ONCE(...)
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
const std::string TYPE_32FC1
General exception class.
sensor_msgs::CameraInfoPtr getDepthCameraInfo(int width, int height, ros::Time time) const
ROSCPP_DECL bool ok()
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
image_transport::CameraPublisher pub_depth_raw_
std::map< int, OpenNI2VideoMode > video_modes_lookup_
image_transport::CameraPublisher pub_color_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
sensor_msgs::CameraInfoPtr getColorCameraInfo(int width, int height, ros::Time time) const
OpenNI2Driver(ros::NodeHandle &n, ros::NodeHandle &pnh)
#define ROS_WARN_STREAM(args)
void monitorConnection(const ros::TimerEvent &event)
virtual const char * what() const
uint32_t getNumSubscribers() const
#define ROS_INFO_STREAM(args)
void newIRFrameCallback(sensor_msgs::ImagePtr image)
bool getParam(const std::string &key, std::string &s) const
dynamic_reconfigure::Server< Config > ReconfigureServer
sensor_msgs::CameraInfoPtr getIRCameraInfo(int width, int height, ros::Time time) const
int extractBusID(const std::string &uri) const
#define ROS_ERROR_STREAM(args)
ros::Timer timer_
timer for connection monitoring thread
#define ROS_ERROR(...)
void setIRVideoMode(const OpenNI2VideoMode &ir_video_mode)
OpenNI2VideoMode depth_video_mode_
OpenNI2VideoMode color_video_mode_
void newDepthFrameCallback(sensor_msgs::ImagePtr image)
#define ROS_DEBUG(...)
void configCb(Config &config, uint32_t level)


openni2_camera
Author(s): Julius Kammerl
autogenerated on Fri Jun 7 2019 22:05:43