driver.cpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2011 Willow Garage, Inc.
5  * Suat Gedikli <gedikli@willowgarage.com>
6  * Patrick Michelich <michelich@willowgarage.com>
7  * Radu Bogdan Rusu <rusu@willowgarage.com>
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above
18  * copyright notice, this list of conditions and the following
19  * disclaimer in the documentation and/or other materials provided
20  * with the distribution.
21  * * Neither the name of Willow Garage, Inc. nor the names of its
22  * contributors may be used to endorse or promote products derived
23  * from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  */
39 #include "driver.h"
42 #include <boost/algorithm/string/replace.hpp>
43 #include <log4cxx/logger.h>
44 
45 using namespace std;
46 namespace freenect_camera {
47 
48 DriverNodelet::~DriverNodelet ()
49 {
50  // If we're still stuck in initialization (e.g. can't connect to device), break out
51  init_thread_.interrupt();
52  init_thread_.join();
53 
54  // Interrupt and close diagnostics thread
55  close_diagnostics_ = true;
56  diagnostics_thread_.join();
57 
58  FreenectDriver& driver = FreenectDriver::getInstance ();
59  driver.shutdown();
60 
63 }
64 
65 void DriverNodelet::onInit ()
66 {
67  // Setting up device can take awhile but onInit shouldn't block, so we spawn a
68  // new thread to do all the initialization
69  init_thread_ = boost::thread(boost::bind(&DriverNodelet::onInitImpl, this));
70 }
71 
72 void DriverNodelet::onInitImpl ()
73 {
74  ros::NodeHandle& nh = getNodeHandle(); // topics
75  ros::NodeHandle& param_nh = getPrivateNodeHandle(); // parameters
76 
77  // Allow remapping namespaces rgb, ir, depth, depth_registered
79  ros::NodeHandle rgb_nh(nh, "rgb");
80  image_transport::ImageTransport rgb_it(rgb_nh);
81  ros::NodeHandle ir_nh(nh, "ir");
83  ros::NodeHandle depth_nh(nh, "depth");
84  image_transport::ImageTransport depth_it(depth_nh);
85  ros::NodeHandle depth_registered_nh(nh, "depth_registered");
86  image_transport::ImageTransport depth_registered_it(depth_registered_nh);
87  ros::NodeHandle projector_nh(nh, "projector");
88 
89  rgb_frame_counter_ = depth_frame_counter_ = ir_frame_counter_ = 0;
90  publish_rgb_ = publish_ir_ = publish_depth_ = true;
91 
92  // Check to see if we should enable debugging messages in libfreenect
93  // libfreenect_debug_ should be set before calling setupDevice
94  param_nh.param("debug" , libfreenect_debug_, false);
95 
96  // Initialize the sensor, but don't start any streams yet. That happens in the connection callbacks.
97  updateModeMaps();
98  setupDevice();
99 
100  // Initialize dynamic reconfigure
101  reconfigure_server_.reset( new ReconfigureServer(param_nh) );
102  reconfigure_server_->setCallback(boost::bind(&DriverNodelet::configCb, this, _1, _2));
103 
104  // Camera TF frames
105  param_nh.param("rgb_frame_id", rgb_frame_id_, string("/openni_rgb_optical_frame"));
106  param_nh.param("depth_frame_id", depth_frame_id_, string("/openni_depth_optical_frame"));
107  NODELET_INFO("rgb_frame_id = '%s' ", rgb_frame_id_.c_str());
108  NODELET_INFO("depth_frame_id = '%s' ", depth_frame_id_.c_str());
109 
110  // Pixel offset between depth and IR images.
111  // By default assume offset of (5,4) from 9x7 correlation window.
112  // NOTE: These are now (temporarily?) dynamically reconfigurable, to allow tweaking.
113  //param_nh.param("depth_ir_offset_x", depth_ir_offset_x_, 5.0);
114  //param_nh.param("depth_ir_offset_y", depth_ir_offset_y_, 4.0);
115 
116  // The camera names are set to [rgb|depth]_[serial#], e.g. depth_B00367707227042B.
117  // camera_info_manager substitutes this for ${NAME} in the URL.
118  std::string serial_number = device_->getSerialNumber();
119  std::string rgb_name, ir_name;
120  if (serial_number.empty())
121  {
122  rgb_name = "rgb";
123  ir_name = "depth";
124  }
125  else
126  {
127  rgb_name = "rgb_" + serial_number;
128  ir_name = "depth_" + serial_number;
129  }
130 
131  std::string rgb_info_url, ir_info_url;
132  param_nh.param("rgb_camera_info_url", rgb_info_url, std::string());
133  param_nh.param("depth_camera_info_url", ir_info_url, std::string());
134 
135  double diagnostics_max_frequency, diagnostics_min_frequency;
136  double diagnostics_tolerance, diagnostics_window_time;
137  param_nh.param("enable_rgb_diagnostics", enable_rgb_diagnostics_, false);
138  param_nh.param("enable_ir_diagnostics", enable_ir_diagnostics_, false);
139  param_nh.param("enable_depth_diagnostics", enable_depth_diagnostics_, false);
140  param_nh.param("diagnostics_max_frequency", diagnostics_max_frequency, 30.0);
141  param_nh.param("diagnostics_min_frequency", diagnostics_min_frequency, 30.0);
142  param_nh.param("diagnostics_tolerance", diagnostics_tolerance, 0.05);
143  param_nh.param("diagnostics_window_time", diagnostics_window_time, 5.0);
144 
145  // Suppress some of the output from loading camera calibrations (kinda hacky)
146  log4cxx::LoggerPtr logger_ccp = log4cxx::Logger::getLogger("ros.camera_calibration_parsers");
147  log4cxx::LoggerPtr logger_cim = log4cxx::Logger::getLogger("ros.camera_info_manager");
148  logger_ccp->setLevel(log4cxx::Level::getFatal());
149  logger_cim->setLevel(log4cxx::Level::getWarn());
150  // Also suppress sync warnings from image_transport::CameraSubscriber. When subscribing to
151  // depth_registered/foo with Freenect registration disabled, the rectify nodelet for depth_registered/
152  // will complain because it receives depth_registered/camera_info (from the register nodelet), but
153  // the driver is not publishing depth_registered/image_raw.
154  log4cxx::LoggerPtr logger_its = log4cxx::Logger::getLogger("ros.image_transport.sync");
155  logger_its->setLevel(log4cxx::Level::getError());
157 
158  // Load the saved calibrations, if they exist
159  rgb_info_manager_ = boost::make_shared<camera_info_manager::CameraInfoManager>(rgb_nh, rgb_name, rgb_info_url);
160  ir_info_manager_ = boost::make_shared<camera_info_manager::CameraInfoManager>(ir_nh, ir_name, ir_info_url);
161 
162  if (!rgb_info_manager_->isCalibrated())
163  NODELET_WARN("Using default parameters for RGB camera calibration.");
164  if (!ir_info_manager_->isCalibrated())
165  NODELET_WARN("Using default parameters for IR camera calibration.");
166 
167  // Advertise all published topics
168  {
169  // Prevent connection callbacks from executing until we've set all the publishers. Otherwise
170  // connectCb() can fire while we're advertising (say) "depth/image_raw", but before we actually
171  // assign to pub_depth_. Then pub_depth_.getNumSubscribers() returns 0, and we fail to start
172  // the depth generator.
173  boost::lock_guard<boost::mutex> lock(connect_mutex_);
174 
175  // Instantiate the diagnostic updater
176  pub_freq_max_ = diagnostics_max_frequency;
177  pub_freq_min_ = diagnostics_min_frequency;
178  diagnostic_updater_.reset(new diagnostic_updater::Updater);
179 
180  // Set hardware id
181  std::string hardware_id = std::string(device_->getProductName()) + "-" +
182  std::string(device_->getSerialNumber());
183  diagnostic_updater_->setHardwareID(hardware_id);
184 
185  // Asus Xtion PRO does not have an RGB camera
186  if (device_->hasImageStream())
187  {
188  image_transport::SubscriberStatusCallback itssc = boost::bind(&DriverNodelet::rgbConnectCb, this);
189  ros::SubscriberStatusCallback rssc = boost::bind(&DriverNodelet::rgbConnectCb, this);
190  pub_rgb_ = rgb_it.advertiseCamera("image_raw", 1, itssc, itssc, rssc, rssc);
191  if (enable_rgb_diagnostics_) {
192  pub_rgb_freq_.reset(new TopicDiagnostic("RGB Image", *diagnostic_updater_,
193  FrequencyStatusParam(&pub_freq_min_, &pub_freq_max_,
194  diagnostics_tolerance, diagnostics_window_time)));
195  }
196  }
197 
198  if (device_->hasIRStream())
199  {
200  image_transport::SubscriberStatusCallback itssc = boost::bind(&DriverNodelet::irConnectCb, this);
201  ros::SubscriberStatusCallback rssc = boost::bind(&DriverNodelet::irConnectCb, this);
202  pub_ir_ = ir_it.advertiseCamera("image_raw", 1, itssc, itssc, rssc, rssc);
203  if (enable_ir_diagnostics_) {
204  pub_ir_freq_.reset(new TopicDiagnostic("IR Image", *diagnostic_updater_,
205  FrequencyStatusParam(&pub_freq_min_, &pub_freq_max_,
206  diagnostics_tolerance, diagnostics_window_time)));
207  }
208  }
209 
210  if (device_->hasDepthStream())
211  {
212  image_transport::SubscriberStatusCallback itssc = boost::bind(&DriverNodelet::depthConnectCb, this);
213  ros::SubscriberStatusCallback rssc = boost::bind(&DriverNodelet::depthConnectCb, this);
214  pub_depth_ = depth_it.advertiseCamera("image_raw", 1, itssc, itssc, rssc, rssc);
215  if (enable_depth_diagnostics_) {
216  pub_depth_freq_.reset(new TopicDiagnostic("Depth Image", *diagnostic_updater_,
217  FrequencyStatusParam(&pub_freq_min_, &pub_freq_max_,
218  diagnostics_tolerance, diagnostics_window_time)));
219  }
220 
221  pub_projector_info_ = projector_nh.advertise<sensor_msgs::CameraInfo>("camera_info", 1, rssc, rssc);
222 
223  if (device_->isDepthRegistrationSupported()) {
224  pub_depth_registered_ = depth_registered_it.advertiseCamera("image_raw", 1, itssc, itssc, rssc, rssc);
225  }
226  }
227  }
228 
229  // Create separate diagnostics thread
230  close_diagnostics_ = false;
231  diagnostics_thread_ = boost::thread(boost::bind(&DriverNodelet::updateDiagnostics, this));
232 
233  // Create watch dog timer callback
234  param_nh.param<double>("time_out", time_out_, 5.0);
235  if (time_out_ > 0.0)
236  {
237  watch_dog_timer_ = nh.createTimer(ros::Duration(time_out_), &DriverNodelet::watchDog, this);
238  }
239 
240  device_->publishersAreReady();
241 }
242 
243 void DriverNodelet::updateDiagnostics() {
244  while (!close_diagnostics_) {
245  diagnostic_updater_->update();
246  boost::this_thread::sleep(boost::posix_time::milliseconds(10));
247  }
248 }
249 
250 void DriverNodelet::setupDevice ()
251 {
252  // Initialize the openni device
253  FreenectDriver& driver = FreenectDriver::getInstance();
254 
255  // Enable debugging in libfreenect if requested
256  if (libfreenect_debug_)
257  driver.enableDebug();
258 
259  do {
260  driver.updateDeviceList ();
261 
262  if (driver.getNumberDevices () == 0)
263  {
264  NODELET_INFO ("No devices connected.... waiting for devices to be connected");
265  boost::this_thread::sleep(boost::posix_time::seconds(3));
266  continue;
267  }
268 
269  NODELET_INFO ("Number devices connected: %d", driver.getNumberDevices ());
270  for (unsigned deviceIdx = 0; deviceIdx < driver.getNumberDevices (); ++deviceIdx)
271  {
272  NODELET_INFO("%u. device on bus %03u:%02u is a %s (%03x) from %s (%03x) with serial id \'%s\'",
273  deviceIdx + 1, driver.getBus(deviceIdx), driver.getAddress(deviceIdx),
274  driver.getProductName(deviceIdx), driver.getProductID(deviceIdx),
275  driver.getVendorName(deviceIdx), driver.getVendorID(deviceIdx),
276  driver.getSerialNumber(deviceIdx));
277  }
278 
279  try {
280  string device_id;
281  if (!getPrivateNodeHandle().getParam("device_id", device_id))
282  {
283  NODELET_WARN ("~device_id is not set! Using first device.");
284  device_ = driver.getDeviceByIndex(0);
285  }
286  else if (device_id.find ('@') != string::npos)
287  {
288  size_t pos = device_id.find ('@');
289  unsigned bus = atoi (device_id.substr (0, pos).c_str ());
290  unsigned address = atoi (device_id.substr (pos + 1, device_id.length () - pos - 1).c_str ());
291  NODELET_INFO ("Searching for device with bus@address = %d@%d", bus, address);
292  device_ = driver.getDeviceByAddress (bus, address);
293  }
294  else if (device_id[0] == '#')
295  {
296  unsigned index = atoi (device_id.c_str() + 1);
297  NODELET_INFO ("Searching for device with index = %d", index);
298  device_ = driver.getDeviceByIndex (index - 1);
299  }
300  else
301  {
302  NODELET_INFO ("Searching for device with serial number = '%s'", device_id.c_str ());
303  device_ = driver.getDeviceBySerialNumber (device_id);
304  }
305  }
306  catch (exception& e)
307  {
308  if (!device_)
309  {
310  NODELET_INFO ("No matching device found.... waiting for devices. Reason: %s", e.what ());
311  boost::this_thread::sleep(boost::posix_time::seconds(3));
312  continue;
313  }
314  else
315  {
316  NODELET_FATAL ("Could not retrieve device. Reason: %s", e.what ());
317  exit (-1);
318  }
319  }
320  } while (!device_);
321 
322  NODELET_INFO ("Opened '%s' on bus %d:%d with serial number '%s'", device_->getProductName (),
323  device_->getBus (), device_->getAddress (), device_->getSerialNumber ());
324 
325  device_->registerImageCallback(&DriverNodelet::rgbCb, *this);
326  device_->registerDepthCallback(&DriverNodelet::depthCb, *this);
327  device_->registerIRCallback (&DriverNodelet::irCb, *this);
328 }
329 
330 void DriverNodelet::rgbConnectCb()
331 {
332  //std::cout << "rgb connect cb called";
333  boost::lock_guard<boost::mutex> lock(connect_mutex_);
334  //std::cout << "..." << std::endl;
335  bool need_rgb = pub_rgb_.getNumSubscribers() > 0;
336  //std::cout << " need_rgb: " << need_rgb << std::endl;
337 
338  if (need_rgb && !device_->isImageStreamRunning())
339  {
340  // Can't stream IR and RGB at the same time. Give RGB preference.
341  if (device_->isIRStreamRunning())
342  {
343  NODELET_ERROR("Cannot stream RGB and IR at the same time. Streaming RGB only.");
344  device_->stopIRStream();
345  }
346 
347  device_->startImageStream();
348  startSynchronization();
349  rgb_time_stamp_ = ros::Time::now(); // update stamp for watchdog
350  }
351  else if (!need_rgb && device_->isImageStreamRunning())
352  {
353  stopSynchronization();
354  device_->stopImageStream();
355 
356  // Start IR if it's been blocked on RGB subscribers
357  bool need_ir = pub_ir_.getNumSubscribers() > 0;
358  if (need_ir && !device_->isIRStreamRunning())
359  {
360  device_->startIRStream();
361  ir_time_stamp_ = ros::Time::now(); // update stamp for watchdog
362  }
363  }
364  //std::cout << "rgb connect cb end..." << std::endl;
365 }
366 
367 void DriverNodelet::depthConnectCb()
368 {
369  //std::cout << "depth connect cb called";
370  boost::lock_guard<boost::mutex> lock(connect_mutex_);
371  //std::cout << "..." << std::endl;
373  bool need_depth =
374  device_->isDepthRegistered() ? pub_depth_registered_.getNumSubscribers() > 0 : pub_depth_.getNumSubscribers() > 0;
376  //std::cout << " need_depth: " << need_depth << std::endl;
377 
378  if (need_depth && !device_->isDepthStreamRunning())
379  {
380  device_->startDepthStream();
381  startSynchronization();
382  depth_time_stamp_ = ros::Time::now(); // update stamp for watchdog
383 
384  }
385  else if (!need_depth && device_->isDepthStreamRunning())
386  {
387  stopSynchronization();
388  device_->stopDepthStream();
389  }
390  //std::cout << "depth connect cb end..." << std::endl;
391 }
392 
393 void DriverNodelet::irConnectCb()
394 {
395  boost::lock_guard<boost::mutex> lock(connect_mutex_);
396  bool need_ir = pub_ir_.getNumSubscribers() > 0;
397 
398  if (need_ir && !device_->isIRStreamRunning())
399  {
400  // Can't stream IR and RGB at the same time
401  if (device_->isImageStreamRunning())
402  {
403  NODELET_ERROR("Cannot stream RGB and IR at the same time. Streaming RGB only.");
404  }
405  else
406  {
407  device_->startIRStream();
408  ir_time_stamp_ = ros::Time::now(); // update stamp for watchdog
409  }
410  }
411  else if (!need_ir)
412  {
413  device_->stopIRStream();
414  }
415 }
416 
417 // If any stream is ready, publish next available image from all streams
418 // This publishes all available data with a maximum time offset of one frame between any two sources
419 // Need to have lock to call this, since callbacks can be in different threads
420 void DriverNodelet::checkFrameCounters()
421 {
422  if (max(rgb_frame_counter_, max(depth_frame_counter_, ir_frame_counter_)) > config_.data_skip) {
423  // Reset all counters after we trigger publish
424  rgb_frame_counter_ = 0;
425  depth_frame_counter_ = 0;
426  ir_frame_counter_ = 0;
427 
428  // Trigger publish on all topics
429  publish_rgb_ = true;
430  publish_depth_ = true;
431  publish_ir_ = true;
432  }
433 }
434 
435 void DriverNodelet::rgbCb(const ImageBuffer& image, void* cookie)
436 {
437  ros::Time time = ros::Time::now () + ros::Duration(config_.image_time_offset);
438  rgb_time_stamp_ = time; // for watchdog
439 
440  bool publish = false;
441  {
442  boost::unique_lock<boost::mutex> counter_lock(counter_mutex_);
443  rgb_frame_counter_++;
444  checkFrameCounters();
445  publish = publish_rgb_;
446 
447  if (publish)
448  rgb_frame_counter_ = 0; // Reset counter if we publish this message to avoid under-throttling
449  }
450 
451  if (publish)
452  publishRgbImage(image, time);
453 
454  publish_rgb_ = false;
455 }
456 
457 void DriverNodelet::depthCb(const ImageBuffer& depth_image, void* cookie)
458 {
459  ros::Time time = ros::Time::now () + ros::Duration(config_.depth_time_offset);
460  depth_time_stamp_ = time; // for watchdog
461 
462  bool publish = false;
463  {
464  boost::unique_lock<boost::mutex> counter_lock(counter_mutex_);
465  depth_frame_counter_++;
466  checkFrameCounters();
467  publish = publish_depth_;
468 
469  if (publish)
470  depth_frame_counter_ = 0; // Reset counter if we publish this message to avoid under-throttling
471  }
472 
473  if (publish)
474  publishDepthImage(depth_image, time);
475 
476  publish_depth_ = false;
477 }
478 
479 void DriverNodelet::irCb(const ImageBuffer& ir_image, void* cookie)
480 {
481  ros::Time time = ros::Time::now() + ros::Duration(config_.depth_time_offset);
482  ir_time_stamp_ = time; // for watchdog
483 
484  bool publish = false;
485  {
486  boost::unique_lock<boost::mutex> counter_lock(counter_mutex_);
487  ir_frame_counter_++;
488  checkFrameCounters();
489  publish = publish_ir_;
490 
491  if (publish)
492  ir_frame_counter_ = 0; // Reset counter if we publish this message to avoid under-throttling
493  }
494 
495  if (publish)
496  publishIrImage(ir_image, time);
497  publish_ir_ = false;
498 }
499 
500 void DriverNodelet::publishRgbImage(const ImageBuffer& image, ros::Time time) const
501 {
502  //NODELET_INFO_THROTTLE(1.0, "rgb image callback called");
503  sensor_msgs::ImagePtr rgb_msg = boost::make_shared<sensor_msgs::Image >();
504  rgb_msg->header.stamp = time;
505  rgb_msg->header.frame_id = rgb_frame_id_;
506  rgb_msg->height = image.metadata.height;
507  rgb_msg->width = image.metadata.width;
508  switch(image.metadata.video_format) {
509  case FREENECT_VIDEO_RGB:
510  rgb_msg->encoding = sensor_msgs::image_encodings::RGB8;
511  rgb_msg->step = rgb_msg->width * 3;
512  break;
513  case FREENECT_VIDEO_BAYER:
514  rgb_msg->encoding = sensor_msgs::image_encodings::BAYER_GRBG8;
515  rgb_msg->step = rgb_msg->width;
516  break;
517  case FREENECT_VIDEO_YUV_RGB:
518  rgb_msg->encoding = sensor_msgs::image_encodings::YUV422;
519  rgb_msg->step = rgb_msg->width * 2;
520  break;
521  default:
522  NODELET_ERROR("Unknown RGB image format received from libfreenect");
523  // Unknown encoding -- don't publish
524  return;
525  }
526  rgb_msg->data.resize(rgb_msg->height * rgb_msg->step);
527  fillImage(image, reinterpret_cast<void*>(&rgb_msg->data[0]));
528 
529  pub_rgb_.publish(rgb_msg, getRgbCameraInfo(image, time));
530  if (enable_rgb_diagnostics_)
531  pub_rgb_freq_->tick();
532 }
533 
534 void DriverNodelet::publishDepthImage(const ImageBuffer& depth, ros::Time time) const
535 {
536  //NODELET_INFO_THROTTLE(1.0, "depth image callback called");
537  bool registered = depth.is_registered;
538 
539  sensor_msgs::ImagePtr depth_msg = boost::make_shared<sensor_msgs::Image>();
540  depth_msg->header.stamp = time;
541  depth_msg->encoding = sensor_msgs::image_encodings::TYPE_16UC1;
542  depth_msg->height = depth.metadata.height;
543  depth_msg->width = depth.metadata.width;
544  depth_msg->step = depth_msg->width * sizeof(short);
545  depth_msg->data.resize(depth_msg->height * depth_msg->step);
546 
547  fillImage(depth, reinterpret_cast<void*>(&depth_msg->data[0]));
548 
549  if (z_offset_mm_ != 0)
550  {
551  uint16_t* data = reinterpret_cast<uint16_t*>(&depth_msg->data[0]);
552  for (unsigned int i = 0; i < depth_msg->width * depth_msg->height; ++i)
553  if (data[i] != 0)
554  data[i] += z_offset_mm_;
555  }
556 
557  if (registered)
558  {
559  // Publish RGB camera info and raw depth image to depth_registered/ ns
560  depth_msg->header.frame_id = rgb_frame_id_;
561  pub_depth_registered_.publish(depth_msg, getRgbCameraInfo(depth, time));
562  }
563  else
564  {
565  // Publish depth camera info and raw depth image to depth/ ns
566  depth_msg->header.frame_id = depth_frame_id_;
567  pub_depth_.publish(depth_msg, getDepthCameraInfo(depth, time));
568  }
569  if (enable_depth_diagnostics_)
570  pub_depth_freq_->tick();
571 
572  // Projector "info" probably only useful for working with disparity images
573  if (pub_projector_info_.getNumSubscribers() > 0)
574  {
575  pub_projector_info_.publish(getProjectorCameraInfo(depth, time));
576  }
577 }
578 
579 void DriverNodelet::publishIrImage(const ImageBuffer& ir, ros::Time time) const
580 {
581  sensor_msgs::ImagePtr ir_msg = boost::make_shared<sensor_msgs::Image>();
582  ir_msg->header.stamp = time;
583  ir_msg->header.frame_id = depth_frame_id_;
584  ir_msg->encoding = sensor_msgs::image_encodings::MONO16;
585  ir_msg->height = ir.metadata.height;
586  ir_msg->width = ir.metadata.width;
587  ir_msg->step = ir_msg->width * sizeof(uint16_t);
588  ir_msg->data.resize(ir_msg->height * ir_msg->step);
589 
590  fillImage(ir, reinterpret_cast<void*>(&ir_msg->data[0]));
591 
592  pub_ir_.publish(ir_msg, getIrCameraInfo(ir, time));
593 
594  if (enable_ir_diagnostics_)
595  pub_ir_freq_->tick();
596 }
597 
598 sensor_msgs::CameraInfoPtr DriverNodelet::getDefaultCameraInfo(int width, int height, double f) const
599 {
600  sensor_msgs::CameraInfoPtr info = boost::make_shared<sensor_msgs::CameraInfo>();
601 
602  info->width = width;
603  info->height = height;
604 
605  // No distortion
606  info->D.resize(5, 0.0);
607  info->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB;
608 
609  // Simple camera matrix: square pixels (fx = fy), principal point at center
610  info->K.assign(0.0);
611  info->K[0] = info->K[4] = f;
612  info->K[2] = (width / 2) - 0.5;
613  // Aspect ratio for the camera center on Kinect (and other devices?) is 4/3
614  // This formula keeps the principal point the same in VGA and SXGA modes
615  info->K[5] = (width * (3./8.)) - 0.5;
616  info->K[8] = 1.0;
617 
618  // No separate rectified image plane, so R = I
619  info->R.assign(0.0);
620  info->R[0] = info->R[4] = info->R[8] = 1.0;
621 
622  // Then P=K(I|0) = (K|0)
623  info->P.assign(0.0);
624  info->P[0] = info->P[5] = f; // fx, fy
625  info->P[2] = info->K[2]; // cx
626  info->P[6] = info->K[5]; // cy
627  info->P[10] = 1.0;
628 
629  return info;
630 }
631 
633 sensor_msgs::CameraInfoPtr DriverNodelet::getRgbCameraInfo(const ImageBuffer& image, ros::Time time) const
634 {
635  sensor_msgs::CameraInfoPtr info;
636 
637  if (rgb_info_manager_->isCalibrated())
638  {
639  info = boost::make_shared<sensor_msgs::CameraInfo>(rgb_info_manager_->getCameraInfo());
640  }
641  else
642  {
643  // If uncalibrated, fill in default values
644  info = getDefaultCameraInfo(image.metadata.width, image.metadata.height, image.focal_length);
645  }
646 
647  // Fill in header
648  info->header.stamp = time;
649  info->header.frame_id = rgb_frame_id_;
650 
651  return info;
652 }
653 
654 sensor_msgs::CameraInfoPtr DriverNodelet::getIrCameraInfo(
655  const ImageBuffer& image, ros::Time time) const {
656  sensor_msgs::CameraInfoPtr info;
657 
658  if (ir_info_manager_->isCalibrated())
659  {
660  info = boost::make_shared<sensor_msgs::CameraInfo>(ir_info_manager_->getCameraInfo());
661  }
662  else
663  {
664  // If uncalibrated, fill in default values
665  info = getDefaultCameraInfo(image.metadata.width, image.metadata.height, image.focal_length);
666  }
667 
668  // Fill in header
669  info->header.stamp = time;
670  info->header.frame_id = depth_frame_id_;
671 
672  return info;
673 }
674 
675 sensor_msgs::CameraInfoPtr DriverNodelet::getDepthCameraInfo(
676  const ImageBuffer& image, ros::Time time) const {
677  // The depth image has essentially the same intrinsics as the IR image, BUT the
678  // principal point is offset by half the size of the hardware correlation window
679  // (probably 9x9 or 9x7). See http://www.ros.org/wiki/kinect_calibration/technical
680 
681  sensor_msgs::CameraInfoPtr info = getIrCameraInfo(image, time);
682  info->K[2] -= depth_ir_offset_x_; // cx
683  info->K[5] -= depth_ir_offset_y_; // cy
684  info->P[2] -= depth_ir_offset_x_; // cx
685  info->P[6] -= depth_ir_offset_y_; // cy
686 
688  return info;
689 }
690 
691 sensor_msgs::CameraInfoPtr DriverNodelet::getProjectorCameraInfo(
692  const ImageBuffer& image, ros::Time time) const {
693  // The projector info is simply the depth info with the baseline encoded in the P matrix.
694  // It's only purpose is to be the "right" camera info to the depth camera's "left" for
695  // processing disparity images.
696  sensor_msgs::CameraInfoPtr info = getDepthCameraInfo(image, time);
697  // Tx = -baseline * fx
698  info->P[3] = -device_->getBaseline() * info->P[0];
699  return info;
700 }
701 
702 void DriverNodelet::configCb(Config &config, uint32_t level)
703 {
704  depth_ir_offset_x_ = config.depth_ir_offset_x;
705  depth_ir_offset_y_ = config.depth_ir_offset_y;
706  z_offset_mm_ = config.z_offset_mm;
707 
708  // We need this for the ASUS Xtion Pro
709  OutputMode old_image_mode, image_mode, compatible_image_mode;
710  if (device_->hasImageStream ())
711  {
712  old_image_mode = device_->getImageOutputMode ();
713 
714  // does the device support the new image mode?
715  image_mode = mapConfigMode2OutputMode (config.image_mode);
716 
717  if (!device_->findCompatibleImageMode (image_mode, compatible_image_mode))
718  {
719  OutputMode default_mode = device_->getDefaultImageMode();
720  NODELET_WARN("Could not find any compatible image output mode for %d. "
721  "Falling back to default image output mode %d.",
722  image_mode,
723  default_mode);
724 
725  config.image_mode = mapMode2ConfigMode(default_mode);
726  image_mode = compatible_image_mode = default_mode;
727  }
728  }
729 
730  OutputMode old_depth_mode, depth_mode, compatible_depth_mode;
731  old_depth_mode = device_->getDepthOutputMode();
732  depth_mode = mapConfigMode2OutputMode (config.depth_mode);
733  if (!device_->findCompatibleDepthMode (depth_mode, compatible_depth_mode))
734  {
735  OutputMode default_mode = device_->getDefaultDepthMode();
736  NODELET_WARN("Could not find any compatible depth output mode for %d. "
737  "Falling back to default depth output mode %d.",
738  depth_mode,
739  default_mode);
740 
741  config.depth_mode = mapMode2ConfigMode(default_mode);
742  depth_mode = compatible_depth_mode = default_mode;
743  }
744 
745  // here everything is fine. Now make the changes
746  if ( (device_->hasImageStream () && compatible_image_mode != old_image_mode) ||
747  compatible_depth_mode != old_depth_mode)
748  {
749  // streams need to be reset!
750  stopSynchronization();
751 
752  if (device_->hasImageStream () && compatible_image_mode != old_image_mode)
753  device_->setImageOutputMode (compatible_image_mode);
754 
755  if (compatible_depth_mode != old_depth_mode)
756  device_->setDepthOutputMode (compatible_depth_mode);
757 
758  startSynchronization ();
759  }
760 
761  // @todo Run connectCb if registration setting changes
762  if (device_->isDepthRegistered () && !config.depth_registration)
763  {
764  device_->setDepthRegistration (false);
765  }
766  else if (!device_->isDepthRegistered () && config.depth_registration)
767  {
768  device_->setDepthRegistration (true);
769  }
770 
771  // now we can copy
772  config_ = config;
773 }
774 
775 void DriverNodelet::startSynchronization()
776 {
777  if (device_->isSynchronizationSupported() &&
778  !device_->isSynchronized() &&
779  device_->isImageStreamRunning() &&
780  device_->isDepthStreamRunning() )
781  {
782  device_->setSynchronization(true);
783  }
784 }
785 
786 void DriverNodelet::stopSynchronization()
787 {
788  if (device_->isSynchronizationSupported() &&
789  device_->isSynchronized())
790  {
791  device_->setSynchronization(false);
792  }
793 }
794 
795 void DriverNodelet::updateModeMaps ()
796 {
797  OutputMode output_mode;
798 
799  output_mode = FREENECT_RESOLUTION_HIGH;
800  mode2config_map_[output_mode] = Freenect_SXGA;
801  config2mode_map_[Freenect_SXGA] = output_mode;
802 
803  output_mode = FREENECT_RESOLUTION_MEDIUM;
804  mode2config_map_[output_mode] = Freenect_VGA;
805  config2mode_map_[Freenect_VGA] = output_mode;
806 }
807 
808 int DriverNodelet::mapMode2ConfigMode (const OutputMode& output_mode) const
809 {
810  std::map<OutputMode, int>::const_iterator it = mode2config_map_.find (output_mode);
811 
812  if (it == mode2config_map_.end ())
813  {
814  NODELET_ERROR ("mode not be found");
815  exit (-1);
816  }
817  else
818  return it->second;
819 }
820 
821 OutputMode DriverNodelet::mapConfigMode2OutputMode (int mode) const
822 {
823  std::map<int, OutputMode>::const_iterator it = config2mode_map_.find (mode);
824  if (it == config2mode_map_.end ())
825  {
826  NODELET_ERROR ("mode %d could not be found", mode);
827  exit (-1);
828  }
829  else
830  return it->second;
831 }
832 
833 void DriverNodelet::watchDog (const ros::TimerEvent& event)
834 {
835  bool timed_out = false;
836  if (!rgb_time_stamp_.isZero() && device_->isImageStreamRunning()) {
837  ros::Duration duration = ros::Time::now() - rgb_time_stamp_;
838  timed_out = timed_out || duration.toSec() > time_out_;
839  }
840  if (!depth_time_stamp_.isZero() && device_->isDepthStreamRunning()) {
841  ros::Duration duration = ros::Time::now() - depth_time_stamp_;
842  timed_out = timed_out || duration.toSec() > time_out_;
843  }
844  if (!ir_time_stamp_.isZero() && device_->isIRStreamRunning()) {
845  ros::Duration duration = ros::Time::now() - ir_time_stamp_;
846  timed_out = timed_out || duration.toSec() > time_out_;
847  }
848 
849  if (timed_out) {
850  ROS_INFO("Device timed out. Flushing device.");
851  device_->flushDeviceStreams();
852  }
853 
854 }
855 
856 }
857 
858 // Register as nodelet
const std::string BAYER_GRBG8
unsigned getAddress(unsigned device_idx)
void fillImage(const ImageBuffer &buffer, void *data)
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
#define NODELET_ERROR(...)
boost::shared_ptr< FreenectDevice > getDeviceByIndex(unsigned device_idx)
ROSCONSOLE_DECL void notifyLoggerLevelsChanged()
#define NODELET_WARN(...)
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
data
boost::shared_ptr< FreenectDevice > getDeviceByAddress(unsigned bus, unsigned address)
freenect_resolution OutputMode
unsigned getBus(unsigned device_idx)
CameraPublisher advertiseCamera(const std::string &base_topic, uint32_t queue_size, bool latch=false)
Holds an image buffer with all the metadata required to transmit the image over ROS channels...
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
dynamic_reconfigure::Server< Config > ReconfigureServer
Definition: driver.h:69
unsigned getVendorID(unsigned device_idx)
const std::string TYPE_16UC1
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
unsigned getProductID(unsigned device_idx)
boost::shared_ptr< FreenectDevice > getDeviceBySerialNumber(std::string serial)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
const std::string MONO16
const char * getSerialNumber(unsigned device_idx)
const char * getVendorName(unsigned device_idx)
#define NODELET_INFO(...)
const char * getProductName(unsigned device_idx)
const std::string YUV422
static Time now()
#define NODELET_FATAL(...)
FreenectConfig Config
Definition: driver.h:68
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
freenect_frame_mode metadata


freenect_camera
Author(s): Patrick Mihelich, Suat Gedikli, Radu Bogdan Rusu (original openni_camera driver)., Piyush Khandelwal (libfreenect port).
autogenerated on Fri Mar 20 2020 03:22:26