camera_nodelet.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2018 ifm electronic, gmbh
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 distribted 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 #include <ifm3d/camera_nodelet.h>
18 
19 #include <cstdint>
20 #include <memory>
21 #include <mutex>
22 #include <sstream>
23 #include <stdexcept>
24 #include <string>
25 #include <vector>
26 
27 #include <cv_bridge/cv_bridge.h>
29 #include <nodelet/nodelet.h>
30 #include <opencv2/opencv.hpp>
32 #include <pcl_ros/point_cloud.h>
34 #include <ros/ros.h>
35 #include <sensor_msgs/Image.h>
37 
38 #include <ifm3d/camera.h>
39 #include <ifm3d/fg.h>
40 #include <ifm3d/image.h>
41 #include <ifm3d/Config.h>
42 #include <ifm3d/Dump.h>
43 #include <ifm3d/Extrinsics.h>
44 #include <ifm3d/SoftOff.h>
45 #include <ifm3d/SoftOn.h>
46 #include <ifm3d/SyncClocks.h>
47 #include <ifm3d/Trigger.h>
48 
50 
51 void
53 {
54  std::string nn = this->getName();
55  NODELET_INFO_STREAM("onInit(): " << nn);
56 
57  this->np_ = getMTPrivateNodeHandle();
58  this->it_.reset(new image_transport::ImageTransport(this->np_));
59 
60  //
61  // parse data out of the parameter server
62  //
63  // NOTE: AFAIK, there is no way to get an unsigned int type out of the ROS
64  // parameter server.
65  //
66  int schema_mask;
67  int xmlrpc_port;
68  std::string frame_id_base;
69 
70  if ((nn.size() > 0) && (nn.at(0) == '/'))
71  {
72  frame_id_base = nn.substr(1);
73  }
74  else
75  {
76  frame_id_base = nn;
77  }
78 
79  this->np_.param("ip", this->camera_ip_, ifm3d::DEFAULT_IP);
80  this->np_.param("xmlrpc_port", xmlrpc_port, (int) ifm3d::DEFAULT_XMLRPC_PORT);
81  this->np_.param("password", this->password_, ifm3d::DEFAULT_PASSWORD);
82  this->np_.param("schema_mask", schema_mask, (int) ifm3d::DEFAULT_SCHEMA_MASK);
83  this->np_.param("timeout_millis", this->timeout_millis_, 500);
84  this->np_.param("timeout_tolerance_secs", this->timeout_tolerance_secs_, 5.0);
85  this->np_.param("assume_sw_triggered", this->assume_sw_triggered_, false);
86  this->np_.param("soft_on_timeout_millis", this->soft_on_timeout_millis_, 500);
87  this->np_.param("soft_on_timeout_tolerance_secs",
89  this->np_.param("soft_off_timeout_millis",
90  this->soft_off_timeout_millis_, 500);
91  this->np_.param("soft_off_timeout_tolerance_secs",
93  this->np_.param("sync_clocks", this->sync_clocks_, false);
94  this->np_.param("frame_latency_thresh", this->frame_latency_thresh_, 60.0f);
95  this->np_.param("frame_id_base", frame_id_base, frame_id_base);
96 
97  this->xmlrpc_port_ = static_cast<std::uint16_t>(xmlrpc_port);
98  this->schema_mask_ = static_cast<std::uint16_t>(schema_mask);
99 
100  this->frame_id_ = frame_id_base + "_link";
101  this->optical_frame_id_ = frame_id_base + "_optical_link";
102 
103  //-------------------
104  // Published topics
105  //-------------------
106  this->cloud_pub_ =
107  this->np_.advertise<pcl::PointCloud<ifm3d::PointT> >("cloud", 1);
108  this->distance_pub_ = this->it_->advertise("distance", 1);
109  this->amplitude_pub_ = this->it_->advertise("amplitude", 1);
110  this->raw_amplitude_pub_ = this->it_->advertise("raw_amplitude", 1);
111  this->conf_pub_ = this->it_->advertise("confidence", 1);
112  this->good_bad_pub_ = this->it_->advertise("good_bad_pixels", 1);
113  this->xyz_image_pub_ = this->it_->advertise("xyz_image", 1);
114 
115  // we latch the unit vectors
116  this->uvec_pub_ =
117  this->np_.advertise<sensor_msgs::Image>("unit_vectors", 1, true);
118 
119  this->extrinsics_pub_ =
120  this->np_.advertise<ifm3d::Extrinsics>("extrinsics", 1);
121 
122  //---------------------
123  // Advertised Services
124  //---------------------
125  this->dump_srv_ =
126  this->np_.advertiseService<ifm3d::Dump::Request, ifm3d::Dump::Response>
127  ("Dump", std::bind(&CameraNodelet::Dump, this,
128  std::placeholders::_1,
129  std::placeholders::_2));
130 
131  this->config_srv_ =
132  this->np_.advertiseService<ifm3d::Config::Request, ifm3d::Config::Response>
133  ("Config", std::bind(&CameraNodelet::Config, this,
134  std::placeholders::_1,
135  std::placeholders::_2));
136 
137  this->trigger_srv_ =
138  this->np_.advertiseService<ifm3d::Trigger::Request,
139  ifm3d::Trigger::Response>
140  ("Trigger", std::bind(&CameraNodelet::Trigger, this,
141  std::placeholders::_1,
142  std::placeholders::_2));
143 
144  this->soft_off_srv_ =
145  this->np_.advertiseService<ifm3d::SoftOff::Request,
146  ifm3d::SoftOff::Response>
147  ("SoftOff", std::bind(&CameraNodelet::SoftOff, this,
148  std::placeholders::_1,
149  std::placeholders::_2));
150 
151  this->soft_on_srv_ =
152  this->np_.advertiseService<ifm3d::SoftOn::Request,
153  ifm3d::SoftOn::Response>
154  ("SoftOn", std::bind(&CameraNodelet::SoftOn, this,
155  std::placeholders::_1,
156  std::placeholders::_2));
157 
158  this->sync_clocks_srv_ =
159  this->np_.advertiseService<ifm3d::SyncClocks::Request,
160  ifm3d::SyncClocks::Response>
161  ("SyncClocks", std::bind(&CameraNodelet::SyncClocks, this,
162  std::placeholders::_1,
163  std::placeholders::_2));
164 
165  //----------------------------------
166  // Fire off our main publishing loop
167  //----------------------------------
168  this->publoop_timer_ =
169  this->np_.createTimer(ros::Duration(.001),
170  [this](const ros::TimerEvent& t)
171  { this->Run(); },
172  true); // oneshot timer
173 }
174 
175 bool
176 ifm3d_ros::CameraNodelet::Dump(ifm3d::Dump::Request& req,
177  ifm3d::Dump::Response& res)
178 {
179  std::lock_guard<std::mutex> lock(this->mutex_);
180  res.status = 0;
181 
182  try
183  {
184  res.config = this->cam_->ToJSONStr();
185  }
186  catch (const ifm3d::error_t& ex)
187  {
188  res.status = ex.code();
189  NODELET_WARN_STREAM(ex.what());
190  }
191  catch (const std::exception& std_ex)
192  {
193  res.status = -1;
194  NODELET_WARN_STREAM(std_ex.what());
195  }
196  catch (...)
197  {
198  res.status = -2;
199  }
200 
201  if (res.status != 0)
202  {
203  NODELET_WARN_STREAM("Dump: " << res.status);
204  }
205 
206  return true;
207 }
208 
209 bool
210 ifm3d_ros::CameraNodelet::Config(ifm3d::Config::Request& req,
211  ifm3d::Config::Response& res)
212 {
213  std::lock_guard<std::mutex> lock(this->mutex_);
214  res.status = 0;
215  res.msg = "OK";
216 
217  try
218  {
219  this->cam_->FromJSONStr(req.json);
220  }
221  catch (const ifm3d::error_t& ex)
222  {
223  res.status = ex.code();
224  res.msg = ex.what();
225  }
226  catch (const std::exception& std_ex)
227  {
228  res.status = -1;
229  res.msg = std_ex.what();
230  }
231  catch (...)
232  {
233  res.status = -2;
234  res.msg = "Unknown error in `Config'";
235  }
236 
237  if (res.status != 0)
238  {
239  NODELET_WARN_STREAM("Config: " << res.status << " - " << res.msg);
240  }
241 
242  return true;
243 }
244 
245 bool
246 ifm3d_ros::CameraNodelet::Trigger(ifm3d::Trigger::Request& req,
247  ifm3d::Trigger::Response& res)
248 {
249  std::lock_guard<std::mutex> lock(this->mutex_);
250  res.status = 0;
251 
252  try
253  {
254  this->fg_->SWTrigger();
255  }
256  catch (const ifm3d::error_t& ex)
257  {
258  res.status = ex.code();
259  }
260 
261  return true;
262 }
263 
264 bool
265 ifm3d_ros::CameraNodelet::SyncClocks(ifm3d::SyncClocks::Request& req,
266  ifm3d::SyncClocks::Response& res)
267 {
268  std::lock_guard<std::mutex> lock(this->mutex_);
269  res.status = 0;
270  res.msg = "OK";
271 
272  NODELET_INFO_STREAM("Syncing camera clock to system...");
273  try
274  {
275  this->cam_->SetCurrentTime(-1);
276  }
277  catch (const ifm3d::error_t& ex)
278  {
279  res.status = ex.code();
280  res.msg = ex.what();
281  NODELET_WARN_STREAM(res.status << ": " << res.msg);
282  return false;
283  }
284 
285  return true;
286 }
287 
288 bool
289 ifm3d_ros::CameraNodelet::SoftOff(ifm3d::SoftOff::Request& req,
290  ifm3d::SoftOff::Response& res)
291 {
292  std::lock_guard<std::mutex> lock(this->mutex_);
293  res.status = 0;
294  res.msg = "OK";
295 
296  int active_application = 0;
297 
298  try
299  {
300  active_application = this->cam_->ActiveApplication();
301  if (active_application > 0)
302  {
303  json dict =
304  {
305  {"Apps",
306  {{{"Index", std::to_string(active_application)},
307  {"TriggerMode",
308  std::to_string(
309  static_cast<int>(ifm3d::Camera::trigger_mode::SW))}}}
310  }
311  };
312 
313  this->cam_->FromJSON(dict);
314 
315  this->assume_sw_triggered_ = true;
319  }
320  }
321  catch (const ifm3d::error_t& ex)
322  {
323  res.status = ex.code();
324  res.msg = ex.what();
325  return false;
326  }
327 
328  return true;
329 }
330 
331 bool
332 ifm3d_ros::CameraNodelet::SoftOn(ifm3d::SoftOn::Request& req,
333  ifm3d::SoftOn::Response& res)
334 {
335  std::lock_guard<std::mutex> lock(this->mutex_);
336  res.status = 0;
337  res.msg = "OK";
338 
339  int active_application = 0;
340 
341  try
342  {
343  active_application = this->cam_->ActiveApplication();
344  if (active_application > 0)
345  {
346  json dict =
347  {
348  {"Apps",
349  {{{"Index", std::to_string(active_application)},
350  {"TriggerMode",
351  std::to_string(
352  static_cast<int>(ifm3d::Camera::trigger_mode::FREE_RUN))}}}
353  }
354  };
355 
356  this->cam_->FromJSON(dict);
357 
358  this->assume_sw_triggered_ = false;
362  }
363  }
364  catch (const ifm3d::error_t& ex)
365  {
366  res.status = ex.code();
367  res.msg = ex.what();
368  return false;
369  }
370 
371  return true;
372 }
373 
374 bool
376 {
377  std::lock_guard<std::mutex> lock(this->mutex_);
378  bool retval = false;
379 
380  try
381  {
382  NODELET_INFO_STREAM("Running dtors...");
383  this->im_.reset();
384  this->fg_.reset();
385  this->cam_.reset();
386 
387  NODELET_INFO_STREAM("Initializing camera...");
388  this->cam_ = ifm3d::Camera::MakeShared(this->camera_ip_,
389  this->xmlrpc_port_,
390  this->password_);
391  ros::Duration(1.0).sleep();
392 
393  NODELET_INFO_STREAM("Initializing framegrabber...");
394  this->fg_ = std::make_shared<ifm3d::FrameGrabber>(this->cam_, mask);
395 
396  NODELET_INFO_STREAM("Initializing image buffer...");
397  this->im_ = std::make_shared<ifm3d::ImageBuffer>();
398 
399  retval = true;
400  }
401  catch (const ifm3d::error_t& ex)
402  {
403  NODELET_WARN_STREAM(ex.code() << ": " << ex.what());
404  this->im_.reset();
405  this->fg_.reset();
406  this->cam_.reset();
407  retval = false;
408  }
409 
410  return retval;
411 }
412 
413 bool
415 {
416  std::lock_guard<std::mutex> lock(this->mutex_);
417  bool retval = false;
418 
419  try
420  {
421  retval = this->fg_->WaitForFrame(this->im_.get(), this->timeout_millis_);
422  }
423  catch (const ifm3d::error_t& ex)
424  {
425  NODELET_WARN_STREAM(ex.code() << ": " << ex.what());
426  retval = false;
427  }
428 
429  return retval;
430 }
431 
432 void
434 {
435  std::unique_lock<std::mutex> lock(this->mutex_, std::defer_lock);
436 
437  //
438  // Sync camera clock with system clock if necessary
439  //
440  if (this->sync_clocks_)
441  {
442  NODELET_INFO_STREAM("Syncing camera clock to system...");
443  try
444  {
445  this->cam_ = ifm3d::Camera::MakeShared(this->camera_ip_,
446  this->xmlrpc_port_,
447  this->password_);
448  this->cam_->SetCurrentTime(-1);
449  }
450  catch (const ifm3d::error_t& ex)
451  {
452  NODELET_WARN_STREAM("Failed to sync clocks!");
453  NODELET_WARN_STREAM(ex.code() << ": " << ex.what());
454  }
455  }
456  else
457  {
458  NODELET_INFO_STREAM("Camera clock will not be sync'd to system clock");
459  }
460 
461  //
462  // We need to account for the case of when the nodelet is being started prior
463  // to the camera being plugged in.
464  //
465  while (ros::ok() && (! this->InitStructures(ifm3d::IMG_UVEC)))
466  {
467  NODELET_WARN_STREAM("Could not initialize pixel stream!");
468  ros::Duration(1.0).sleep();
469  }
470 
471  pcl::PointCloud<ifm3d::PointT>::Ptr
472  cloud(new pcl::PointCloud<ifm3d::PointT>());
473 
474  cv::Mat confidence_img;
475  cv::Mat distance_img;
476  cv::Mat amplitude_img;
477  cv::Mat xyz_img;
478  cv::Mat raw_amplitude_img;
479  cv::Mat good_bad_pixels_img;
480 
481  std::vector<float> extrinsics(6);
482 
483  // XXX: need to implement a nice strategy for getting the actual times
484  // from the camera which are registered to the frame data in the image
485  // buffer.
486  ros::Time last_frame = ros::Time::now();
487  bool got_uvec = false;
488 
489  while (ros::ok())
490  {
491  if (! this->AcquireFrame())
492  {
493  if (! this->assume_sw_triggered_)
494  {
495  NODELET_WARN_STREAM("Timeout waiting for camera!");
496  }
497  else
498  {
499  ros::Duration(.001).sleep();
500  }
501 
502  if ((ros::Time::now() - last_frame).toSec() >
504  {
505  NODELET_WARN_STREAM("Attempting to restart framegrabber...");
506  while (! this->InitStructures(got_uvec
507  ? this->schema_mask_
508  : ifm3d::IMG_UVEC))
509  {
510  NODELET_WARN_STREAM("Could not re-initialize pixel stream!");
511  ros::Duration(1.0).sleep();
512  }
513 
514  last_frame = ros::Time::now();
515  }
516 
517  continue;
518  }
519 
520  last_frame = ros::Time::now();
521 
522  std_msgs::Header head = std_msgs::Header();
523  head.frame_id = this->frame_id_;
524  head.stamp = ros::Time(
525  std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>
526  (this->im_->TimeStamp().time_since_epoch()).count());
527  if ((ros::Time::now() - head.stamp) >
529  {
530  ROS_WARN_ONCE("Camera's time is not up to date, therefore header's "
531  "timestamps will be the reception time and not capture time. "
532  "Please update the camera's time if you need the capture time.");
533  head.stamp = ros::Time::now();
534  }
535 
536  std_msgs::Header optical_head = std_msgs::Header();
537  optical_head.stamp = head.stamp;
538  optical_head.frame_id = this->optical_frame_id_;
539 
540  // publish unit vectors once on a latched topic, then re-initialize the
541  // framegrabber with the user's requested schema mask
542  if (! got_uvec)
543  {
544  lock.lock();
545  sensor_msgs::ImagePtr uvec_msg =
546  cv_bridge::CvImage(optical_head,
547  enc::TYPE_32FC3,
548  this->im_->UnitVectors()).toImageMsg();
549  lock.unlock();
550  this->uvec_pub_.publish(uvec_msg);
551  got_uvec = true;
552  ROS_INFO("Got unit vectors, restarting framegrabber with mask: %d",
553  (int) this->schema_mask_);
554 
555  while (! this->InitStructures(this->schema_mask_))
556  {
557  ROS_WARN("Could not re-initialize pixel stream!");
558  ros::Duration(1.0).sleep();
559  }
560 
561  // should solve the problem of first image being (0,0)
562  // see: https://github.com/lovepark/ifm3d/issues/12
563  continue;
564  }
565 
566  //
567  // Pull out all the wrapped images so that we can release the "GIL"
568  // while publishing
569  //
570  lock.lock();
571 
572  // boost::shared_ptr vs std::shared_ptr forces this copy
573  pcl::copyPointCloud(*(this->im_->Cloud().get()), *cloud);
574  xyz_img = this->im_->XYZImage();
575  confidence_img = this->im_->ConfidenceImage();
576  distance_img = this->im_->DistanceImage();
577  amplitude_img = this->im_->AmplitudeImage();
578  raw_amplitude_img = this->im_->RawAmplitudeImage();
579  extrinsics = this->im_->Extrinsics();
580 
581  lock.unlock();
582 
583  //
584  // Now, do the publishing
585  //
586 
587  // Confidence image is invariant - no need to check the mask
588  sensor_msgs::ImagePtr confidence_msg =
589  cv_bridge::CvImage(optical_head,
590  "mono8",
591  confidence_img).toImageMsg();
592  this->conf_pub_.publish(confidence_msg);
593 
594  if ((this->schema_mask_ & ifm3d::IMG_CART) == ifm3d::IMG_CART)
595  {
596  cloud->header = pcl_conversions::toPCL(head);
597  this->cloud_pub_.publish(cloud);
598 
599  sensor_msgs::ImagePtr xyz_image_msg =
600  cv_bridge::CvImage(head,
601  xyz_img.type() == CV_32FC3 ?
602  enc::TYPE_32FC3 : enc::TYPE_16SC3,
603  xyz_img).toImageMsg();
604  this->xyz_image_pub_.publish(xyz_image_msg);
605  }
606 
607  if ((this->schema_mask_ & ifm3d::IMG_RDIS) == ifm3d::IMG_RDIS)
608  {
609  sensor_msgs::ImagePtr distance_msg =
610  cv_bridge::CvImage(optical_head,
611  distance_img.type() == CV_32FC1 ?
612  enc::TYPE_32FC1 : enc::TYPE_16UC1,
613  distance_img).toImageMsg();
614  this->distance_pub_.publish(distance_msg);
615  }
616 
617  if ((this->schema_mask_ & ifm3d::IMG_AMP) == ifm3d::IMG_AMP)
618  {
619  sensor_msgs::ImagePtr amplitude_msg =
620  cv_bridge::CvImage(optical_head,
621  amplitude_img.type() == CV_32FC1 ?
622  enc::TYPE_32FC1 : enc::TYPE_16UC1,
623  amplitude_img).toImageMsg();
624  this->amplitude_pub_.publish(amplitude_msg);
625  }
626 
627  if ((this->schema_mask_ & ifm3d::IMG_RAMP) == ifm3d::IMG_RAMP)
628  {
629  sensor_msgs::ImagePtr raw_amplitude_msg =
630  cv_bridge::CvImage(optical_head,
631  raw_amplitude_img.type() == CV_32FC1 ?
632  enc::TYPE_32FC1 : enc::TYPE_16UC1,
633  raw_amplitude_img).toImageMsg();
634  this->raw_amplitude_pub_.publish(raw_amplitude_msg);
635  }
636 
637  //
638  // XXX: Need to publish ambient light / gray image
639  // ... however, as of now (3/26/2017) there is no
640  // imager mode on the O3X that actually supports it
641  //
642  // Update: as of 5/10/2018 still not supported.
643  //
644 
645  good_bad_pixels_img = cv::Mat::ones(confidence_img.rows,
646  confidence_img.cols,
647  CV_8UC1);
648  cv::bitwise_and(confidence_img, good_bad_pixels_img,
649  good_bad_pixels_img);
650  sensor_msgs::ImagePtr good_bad_msg =
651  cv_bridge::CvImage(optical_head,
652  "mono8",
653  (good_bad_pixels_img == 0)).toImageMsg();
654  this->good_bad_pub_.publish(good_bad_msg);
655 
656  //
657  // publish extrinsics
658  //
659  ifm3d::Extrinsics extrinsics_msg;
660  extrinsics_msg.header = optical_head;
661  try
662  {
663  extrinsics_msg.tx = extrinsics.at(0);
664  extrinsics_msg.ty = extrinsics.at(1);
665  extrinsics_msg.tz = extrinsics.at(2);
666  extrinsics_msg.rot_x = extrinsics.at(3);
667  extrinsics_msg.rot_y = extrinsics.at(4);
668  extrinsics_msg.rot_z = extrinsics.at(5);
669  }
670  catch (const std::out_of_range& ex)
671  {
672  ROS_WARN("out-of-range error fetching extrinsics");
673  }
674  this->extrinsics_pub_.publish(extrinsics_msg);
675 
676  } // end: while (ros::ok()) { ... }
677 } // end: Run()
678 
679 
#define NODELET_INFO_STREAM(...)
bool Config(ifm3d::Config::Request &req, ifm3d::Config::Response &res)
void publish(const boost::shared_ptr< M > &message) const
f
ros::ServiceServer soft_off_srv_
image_transport::Publisher conf_pub_
std::uint16_t schema_mask_
ifm3d::Camera::Ptr cam_
bool sleep() const
ifm3d::ImageBuffer::Ptr im_
ros::ServiceServer dump_srv_
PLUGINLIB_EXPORT_CLASS(BAGReader, nodelet::Nodelet)
const std::string & getName() const
#define NODELET_WARN_STREAM(...)
image_transport::Publisher good_bad_pub_
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
image_transport::Publisher raw_amplitude_pub_
#define ROS_WARN(...)
ros::ServiceServer sync_clocks_srv_
image_transport::Publisher amplitude_pub_
ros::NodeHandle & getMTPrivateNodeHandle() const
ifm3d::FrameGrabber::Ptr fg_
bool InitStructures(std::uint16_t mask)
ros::Publisher extrinsics_pub_
image_transport::Publisher xyz_image_pub_
void publish(const sensor_msgs::Image &message) const
image_transport::Publisher distance_pub_
#define ROS_WARN_ONCE(...)
#define ROS_INFO(...)
ros::ServiceServer trigger_srv_
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool Trigger(ifm3d::Trigger::Request &req, ifm3d::Trigger::Response &res)
ROSCPP_DECL bool ok()
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
ros::ServiceServer soft_on_srv_
bool Dump(ifm3d::Dump::Request &req, ifm3d::Dump::Response &res)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool SoftOff(ifm3d::SoftOff::Request &req, ifm3d::SoftOff::Response &res)
std::uint16_t xmlrpc_port_
virtual void onInit() override
bool SoftOn(ifm3d::SoftOn::Request &req, ifm3d::SoftOn::Response &res)
bool SyncClocks(ifm3d::SyncClocks::Request &req, ifm3d::SyncClocks::Response &res)
static Time now()
ros::ServiceServer config_srv_
std::unique_ptr< image_transport::ImageTransport > it_
sensor_msgs::ImagePtr toImageMsg() const
void toPCL(const ros::Time &stamp, pcl::uint64_t &pcl_stamp)


ifm3d
Author(s): Tom Panzarella
autogenerated on Thu Feb 4 2021 03:23:54