camera.cpp
Go to the documentation of this file.
2 
3 #include <cmath>
4 #include <fstream>
5 #include <set>
6 #include <string>
7 #include <vector>
8 
9 #include <diagnostic_msgs/DiagnosticArray.h>
12 #include "pcl_ros/point_cloud.h"
13 
14 #include "ensenso_camera/helper.h"
19 
24 double const STATUS_INTERVAL = 3.0; // Seconds.
25 
29 double const TRANSFORMATION_REQUEST_TIMEOUT = 0.1; // Seconds.
30 
35 std::string const DEFAULT_PARAMETER_SET = "default";
36 
40 std::string const TARGET_FRAME_LINK = "Workspace";
41 
42 // The ROS node gives back error codes from the NxLib. Additionally, we use the
43 // following error codes to indicate errors from the ROS node itself.
45 int const ERROR_CODE_TF = 101;
46 
47 #define LOG_NXLIB_EXCEPTION(EXCEPTION) \
48  try \
49  { \
50  if (EXCEPTION.getErrorCode() == NxLibExecutionFailed) \
51  { \
52  NxLibItem executionNode(EXCEPTION.getItemPath()); \
53  ROS_ERROR("%s: %s", executionNode[itmResult][itmErrorSymbol].asString().c_str(), \
54  executionNode[itmResult][itmErrorText].asString().c_str()); \
55  } /* NOLINT */ \
56  } /* NOLINT */ \
57  catch (...) \
58  { \
59  } /* NOLINT */ \
60  ROS_DEBUG("Current NxLib tree: %s", NxLibItem().asJson(true).c_str());
61 
62 // The following macros are called at the beginning and end of each action
63 // handler that uses the NxLib. In case of an NxLib exception they
64 // automatically abort the action and return the corresponding error code and
65 // message.
66 // This assumes that all of our actions have the property error that represents
67 // an NxLibException.
68 
69 #define START_NXLIB_ACTION(ACTION_NAME, ACTION_SERVER) \
70  ROS_DEBUG("Received a " #ACTION_NAME " request."); \
71  auto& server = ACTION_SERVER; \
72  if (server->isPreemptRequested()) \
73  { \
74  server->setPreempted(); \
75  return; \
76  } /* NOLINT */ \
77  std::lock_guard<std::mutex> lock(nxLibMutex); \
78  try \
79  {
80 #define FINISH_NXLIB_ACTION(ACTION_NAME) \
81  } /* NOLINT */ \
82  catch (NxLibException & e) \
83  { \
84  ROS_ERROR("NxLibException %d (%s) for item %s", e.getErrorCode(), e.getErrorText().c_str(), \
85  e.getItemPath().c_str()); \
86  LOG_NXLIB_EXCEPTION(e) \
87  ensenso_camera_msgs::ACTION_NAME##Result result; \
88  result.error.code = e.getErrorCode(); \
89  result.error.message = e.getErrorText(); \
90  server->setAborted(result); \
91  return; \
92  } /* NOLINT */ \
93  catch (tf::TransformException & e) \
94  { \
95  ROS_ERROR("TF Exception: %s", e.what()); \
96  ensenso_camera_msgs::ACTION_NAME##Result result; \
97  result.error.code = ERROR_CODE_TF; \
98  result.error.message = e.what(); \
99  server->setAborted(result); \
100  return; \
101  } /* NOLINT */ \
102  catch (std::exception & e) \
103  { \
104  ROS_ERROR("Unknown Exception: %s", e.what()); \
105  ensenso_camera_msgs::ACTION_NAME##Result result; \
106  result.error.code = ERROR_CODE_UNKNOWN_EXCEPTION; \
107  result.error.message = e.what(); \
108  server->setAborted(result); \
109  return; \
110  }
111 
112 #define PREEMPT_ACTION_IF_REQUESTED \
113  if (server->isPreemptRequested()) \
114  { \
115  server->setPreempted(); \
116  return; \
117  }
118 
122 bool checkNxLibVersion(int major, int minor)
123 {
124  int nxLibMajor = NxLibItem()[itmVersion][itmMajor].asInt();
125  int nxLibMinor = NxLibItem()[itmVersion][itmMinor].asInt();
126  return (nxLibMajor > major) || (nxLibMajor == major && nxLibMinor >= minor);
127 }
128 
129 ParameterSet::ParameterSet(const std::string& name, const NxLibItem& defaultParameters)
130 {
131  // Create a new NxLib node where we will store the parameters for this set and
132  // overwrite it with the default settings.
133  node = NxLibItem()["rosParameterSets"][name];
134  node << defaultParameters;
135 }
136 
137 Camera::Camera(ros::NodeHandle nh, std::string const& serial, std::string const& fileCameraPath, bool fixed,
138  std::string const& cameraFrame, std::string const& targetFrame, std::string const& robotFrame,
139  std::string const& wristFrame)
140  : serial(serial)
141  , fileCameraPath(fileCameraPath)
142  , fixed(fixed)
143  , cameraFrame(cameraFrame)
144  , targetFrame(targetFrame)
145  , robotFrame(robotFrame)
146  , wristFrame(wristFrame)
147 {
148  isFileCamera = !fileCameraPath.empty();
149 
150  leftCameraInfo = boost::make_shared<sensor_msgs::CameraInfo>();
151  rightCameraInfo = boost::make_shared<sensor_msgs::CameraInfo>();
152  leftRectifiedCameraInfo = boost::make_shared<sensor_msgs::CameraInfo>();
153  rightRectifiedCameraInfo = boost::make_shared<sensor_msgs::CameraInfo>();
154 
155  accessTreeServer = make_unique<AccessTreeServer>(nh, "access_tree", boost::bind(&Camera::onAccessTree, this, _1));
157  make_unique<ExecuteCommandServer>(nh, "execute_command", boost::bind(&Camera::onExecuteCommand, this, _1));
158 
160  make_unique<FitPrimitiveServer>(nh, "fit_primitive", boost::bind(&Camera::onFitPrimitive, this, _1));
161 
163  make_unique<GetParameterServer>(nh, "get_parameter", boost::bind(&Camera::onGetParameter, this, _1));
165  make_unique<SetParameterServer>(nh, "set_parameter", boost::bind(&Camera::onSetParameter, this, _1));
166 
167  requestDataServer = make_unique<RequestDataServer>(nh, "request_data", boost::bind(&Camera::onRequestData, this, _1));
169  make_unique<LocatePatternServer>(nh, "locate_pattern", boost::bind(&Camera::onLocatePattern, this, _1));
171  make_unique<ProjectPatternServer>(nh, "project_pattern", boost::bind(&Camera::onProjectPattern, this, _1));
173  make_unique<CalibrateHandEyeServer>(nh, "calibrate_hand_eye", boost::bind(&Camera::onCalibrateHandEye, this, _1));
174  calibrateWorkspaceServer = make_unique<CalibrateWorkspaceServer>(
175  nh, "calibrate_workspace", boost::bind(&Camera::onCalibrateWorkspace, this, _1));
176 
177  image_transport::ImageTransport imageTransport(nh);
178 
179  leftRawImagePublisher = imageTransport.advertiseCamera("raw/left/image", 1);
180  rightRawImagePublisher = imageTransport.advertiseCamera("raw/right/image", 1);
181  leftRectifiedImagePublisher = imageTransport.advertiseCamera("rectified/left/image", 1);
182  rightRectifiedImagePublisher = imageTransport.advertiseCamera("rectified/right/image", 1);
183  disparityMapPublisher = imageTransport.advertise("disparity_map", 1);
184  depthImagePublisher = imageTransport.advertiseCamera("depth/image", 1);
185 
186  pointCloudPublisher = nh.advertise<pcl::PointCloud<pcl::PointXYZ>>("point_cloud", 1);
187 
188  statusPublisher = nh.advertise<diagnostic_msgs::DiagnosticArray>("/diagnostics", 1);
190 
191  cameraNode = NxLibItem()[itmCameras][itmBySerialNo][serial];
192 
193  defaultParameters = NxLibItem()["rosDefaultParameters"];
194 }
195 
197 {
198  if (isFileCamera && !cameraNode.exists())
199  {
200  try
201  {
202  if (serial.size() > 15)
203  {
204  ROS_ERROR("The serial '%s' is too long!", serial.c_str());
205  return false;
206  }
207 
208  NxLibCommand createCamera(cmdCreateCamera);
209  createCamera.parameters()[itmSerialNumber] = serial;
210  createCamera.parameters()[itmFolderPath] = fileCameraPath;
211  createCamera.execute();
212 
213  createdFileCamera = true;
214  }
215  catch (NxLibException& e)
216  {
217  ROS_ERROR("Failed to create the file camera!");
219  return false;
220  }
221  }
222 
223  if (!cameraNode.exists())
224  {
225  ROS_ERROR("The camera '%s' could not be found", serial.c_str());
226  return false;
227  }
228  if (!cameraIsAvailable())
229  {
230  ROS_ERROR("The camera '%s' is already in use", serial.c_str());
231  return false;
232  }
233 
234  try
235  {
236  NxLibCommand open(cmdOpen);
237  open.parameters()[itmCameras] = serial;
238  open.execute();
239  }
240  catch (NxLibException& e)
241  {
242  ROS_ERROR("Error while opening the camera '%s'!", serial.c_str());
244  return false;
245  }
246 
247  ROS_INFO("Opened camera with serial number '%s'.", serial.c_str());
248 
251 
252  return true;
253 }
254 
256 {
257  std::lock_guard<std::mutex> lock(nxLibMutex);
258 
259  try
260  {
261  NxLibCommand close(cmdClose);
262  close.parameters()[itmCameras] = serial;
263  close.execute();
264 
266  {
267  NxLibCommand deleteCmd(cmdDeleteCamera);
268  deleteCmd.parameters()[itmCameras] = serial;
269  deleteCmd.execute();
270  }
271  }
272  catch (NxLibException&)
273  {
274  }
275 }
276 
278 {
279  accessTreeServer->start();
280  executeCommandServer->start();
281 
282  fitPrimitiveServer->start();
283 
284  getParameterServer->start();
285  setParameterServer->start();
286 
287  requestDataServer->start();
288  locatePatternServer->start();
289  projectPatternServer->start();
290  calibrateHandEyeServer->start();
291  calibrateWorkspaceServer->start();
292 }
293 
294 bool Camera::loadSettings(const std::string& jsonFile, bool saveAsDefaultParameters)
295 {
296  if (jsonFile.empty())
297  return true;
298 
299  std::ifstream file(jsonFile);
300  if (file.is_open() && file.rdbuf())
301  {
302  std::stringstream buffer;
303  buffer << file.rdbuf();
304  std::string const& jsonSettings = buffer.str();
305 
306  NxLibItem tmpParameters = NxLibItem()["rosTemporaryParameters"];
307  try
308  {
309  tmpParameters.setJson(jsonSettings);
310  }
311  catch (NxLibException&)
312  {
313  ROS_ERROR("The file '%s' does not contain valid JSON", jsonFile.c_str());
314  return false;
315  }
316 
317  try
318  {
319  if (tmpParameters[itmParameters].exists())
320  {
321  // The file contains the entire camera node.
322  cameraNode[itmParameters] << tmpParameters[itmParameters];
323  }
324  else
325  {
326  // The file contains only the parameter node.
327  cameraNode[itmParameters].setJson(jsonSettings, true);
328  }
329  tmpParameters.erase();
330 
331  NxLibCommand synchronize(cmdSynchronize);
332  synchronize.parameters()[itmCameras] = serial;
333  synchronize.execute();
334 
336  if (saveAsDefaultParameters)
338  }
339  catch (NxLibException& e)
340  {
342  return false;
343  }
344  }
345  else
346  {
347  ROS_ERROR("Could not open the file '%s'", jsonFile.c_str());
348  return false;
349  }
350 
351  return true;
352 }
353 
354 void Camera::onAccessTree(const ensenso_camera_msgs::AccessTreeGoalConstPtr& goal)
355 {
357 
358  loadParameterSet(goal->parameter_set);
359 
360  NxLibItem item(goal->path);
361 
362  bool treeChanged = false;
363  if (goal->erase)
364  {
365  item.erase();
366  treeChanged = true;
367  }
368  else if (goal->set_null)
369  {
370  item.setNull();
371  treeChanged = true;
372  }
373  else if (!goal->json_value.empty())
374  {
375  item.setJson(goal->json_value);
376  treeChanged = true;
377  }
378 
379  if (treeChanged)
380  {
381  saveParameterSet(goal->parameter_set);
382  }
383 
384  ensenso_camera_msgs::AccessTreeResult result;
385 
386  result.exists = false;
387  if (item.exists())
388  {
389  result.exists = true;
390 
391  result.json_value = item.asJson();
392  try
393  {
394  // This could load any image that does not belong to the node's camera,
395  // so we do not know its frame.
396  result.binary_data = *imageFromNxLibNode(item, "");
397  }
398  catch (NxLibException&)
399  {
400  } // The item was not binary.
401  }
402 
403  accessTreeServer->setSucceeded(result);
404 
405  FINISH_NXLIB_ACTION(AccessTree)
406 }
407 
408 void Camera::onExecuteCommand(const ensenso_camera_msgs::ExecuteCommandGoalConstPtr& goal)
409 {
411 
412  loadParameterSet(goal->parameter_set);
413 
414  NxLibCommand command(goal->command);
415  command.parameters().setJson(goal->parameters);
416  command.execute();
417 
418  ensenso_camera_msgs::ExecuteCommandResult result;
419  result.result = command.result().asJson();
420 
421  executeCommandServer->setSucceeded(result);
422 
423  FINISH_NXLIB_ACTION(ExecuteCommand)
424 }
425 
426 void Camera::onFitPrimitive(ensenso_camera_msgs::FitPrimitiveGoalConstPtr const& goal)
427 {
429 
430  NxLibCommand fitPrimitives(cmdFitPrimitive);
431  NxLibItem const& primitives = fitPrimitives.parameters()[itmPrimitive];
432 
433  int primitivesCount = 0;
434  for (auto const& primitive : goal->primitives)
435  {
436  if (primitive.type == ensenso_camera_msgs::Primitive::SPHERE)
437  {
438  primitives[primitivesCount][itmRadius][itmMin] = primitive.min_radius * ensenso_conversion::conversionFactor;
439  primitives[primitivesCount][itmRadius][itmMax] = primitive.max_radius * ensenso_conversion::conversionFactor;
440  primitives[primitivesCount][itmCount] = primitive.count;
441  }
442  else if (primitive.type == ensenso_camera_msgs::Primitive::CYLINDER)
443  {
444  primitives[primitivesCount][itmRadius][itmMin] = primitive.min_radius * ensenso_conversion::conversionFactor;
445  primitives[primitivesCount][itmRadius][itmMax] = primitive.min_radius * ensenso_conversion::conversionFactor;
446  }
447  else if (primitive.type == ensenso_camera_msgs::Primitive::PLANE)
448  {
449  primitives[primitivesCount][itmCount] = primitive.count;
450  }
451  primitives[primitivesCount][itmType] = primitive.type;
452  primitives[primitivesCount][itmInlierThreshold] = primitive.inlier_threshold * ensenso_conversion::conversionFactor;
453  primitives[primitivesCount][itmInlierFraction] = primitive.inlier_fraction_in;
454 
455  primitivesCount++;
456  }
457 
458  NxLibItem const& commandParameters = fitPrimitives.parameters();
459  if (goal->normal_radius)
460  {
461  commandParameters[itmNormal][itmRadius] = goal->normal_radius;
462  }
463 
464  float const zeroThreshold = 10e-6;
465  if (!(std::abs(goal->region_of_interest.lower.x) < zeroThreshold &&
466  std::abs(goal->region_of_interest.lower.y) < zeroThreshold &&
467  std::abs(goal->region_of_interest.lower.z) < zeroThreshold))
468  {
469  commandParameters[itmBoundingBox][itmMin] << ensenso_conversion::toEnsensoPoint(goal->region_of_interest.lower);
470  }
471  if (!(std::abs(goal->region_of_interest.upper.x) < zeroThreshold &&
472  std::abs(goal->region_of_interest.upper.y) < zeroThreshold &&
473  std::abs(goal->region_of_interest.upper.z) < zeroThreshold))
474  {
475  commandParameters[itmBoundingBox][itmMax] << ensenso_conversion::toEnsensoPoint(goal->region_of_interest.upper);
476  }
477 
478  if (goal->failure_probability != 0)
479  {
480  commandParameters[itmFailureProbability] = goal->failure_probability;
481  }
482 
483  if (std::abs(goal->inlier_threshold) > zeroThreshold)
484  {
485  commandParameters[itmInlierThreshold] = goal->inlier_threshold * ensenso_conversion::conversionFactor;
486  }
487  if (std::abs(goal->inlier_fraction) > zeroThreshold)
488  {
489  commandParameters[itmInlierFraction] = goal->inlier_fraction;
490  }
491  if (std::abs(goal->scaling) > zeroThreshold)
492  {
493  commandParameters[itmScaling] = goal->scaling;
494  }
495  if (goal->ransac_iterations != 0)
496  {
497  commandParameters[itmScaling] = goal->ransac_iterations;
498  }
499 
500  fitPrimitives.execute();
501 
502  ensenso_camera_msgs::FitPrimitiveResult result;
503 
504  NxLibItem const primitiveResults = fitPrimitives.result()[itmPrimitive];
505  if (primitiveResults.isArray())
506  {
507  result.primitives.reserve(primitiveResults.count());
508  for (int primitiveCount = 0; primitiveCount < primitiveResults.count(); primitiveCount++)
509  {
510  NxLibItem const& currentPrimitive = primitiveResults[primitiveCount];
511  ensenso_camera_msgs::Primitive primitive;
512 
513  primitive.type = currentPrimitive[itmType].asString();
514  primitive.ransac_iterations = currentPrimitive[itmIterations].asInt();
515  primitive.inlier_count = currentPrimitive[itmInlierCount].asInt();
516  primitive.inlier_fraction_out = currentPrimitive[itmInlierFraction].asDouble();
517  primitive.score = currentPrimitive[itmScore].asDouble();
518  primitive.center = ensenso_conversion::toRosPoint(currentPrimitive[itmCenter]);
519 
520  if (primitive.type == ensenso_camera_msgs::Primitive::PLANE)
521  {
522  primitive.axes.push_back(ensenso_conversion::toRosPoint(currentPrimitive[itmAxis][0]));
523  primitive.axes.push_back(ensenso_conversion::toRosPoint(currentPrimitive[itmAxis][1]));
524  primitive.normal = ensenso_conversion::toRosPoint(currentPrimitive[itmNormal], false);
525  }
526  else if (primitive.type == ensenso_camera_msgs::Primitive::SPHERE)
527  {
528  primitive.radius = currentPrimitive[itmRadius].asDouble() / ensenso_conversion::conversionFactor;
529  }
530  else if (primitive.type == ensenso_camera_msgs::Primitive::CYLINDER)
531  {
532  primitive.axis = ensenso_conversion::toRosPoint(currentPrimitive[itmAxis]);
533  }
534  result.primitives.emplace_back(primitive);
535  }
536  }
537 
538  fitPrimitiveServer->setSucceeded(result);
539 
540  FINISH_NXLIB_ACTION(FitPrimitive)
541 }
542 
543 void Camera::onGetParameter(ensenso_camera_msgs::GetParameterGoalConstPtr const& goal)
544 {
546 
547  ensenso_camera_msgs::GetParameterResult result;
548 
549  loadParameterSet(goal->parameter_set);
550 
551  result.stamp = ros::Time::now();
552  for (auto const& key : goal->keys)
553  {
554  result.results.push_back(*readParameter(key));
555  }
556 
557  getParameterServer->setSucceeded(result);
558 
559  FINISH_NXLIB_ACTION(GetParameter)
560 }
561 
562 void Camera::onSetParameter(ensenso_camera_msgs::SetParameterGoalConstPtr const& goal)
563 {
565 
566  ensenso_camera_msgs::SetParameterResult result;
567 
568  loadParameterSet(goal->parameter_set);
569 
570  result.parameter_file_applied = false;
571  if (!goal->parameter_file.empty())
572  {
573  result.parameter_file_applied = loadSettings(goal->parameter_file);
574  if (!result.parameter_file_applied)
575  {
576  server->setAborted(result);
577  return;
578  }
579  }
580 
581  bool projectorChanged = false;
582  for (auto const& parameter : goal->parameters)
583  {
584  writeParameter(parameter);
585 
586  if (parameter.key == parameter.PROJECTOR || parameter.key == parameter.FRONT_LIGHT)
587  {
588  projectorChanged = true;
589  }
590  }
591 
592  // Synchronize to make sure that we read back the correct values.
593  NxLibCommand synchronize(cmdSynchronize);
594  synchronize.parameters()[itmCameras] = serial;
595  synchronize.execute();
596 
597  saveParameterSet(goal->parameter_set, projectorChanged);
598 
599  // The new parameters might change the camera calibration.
601 
602  // Read back the actual values.
603  for (auto const& parameter : goal->parameters)
604  {
605  result.results.push_back(*readParameter(parameter.key));
606  }
607 
608  setParameterServer->setSucceeded(result);
609 
610  FINISH_NXLIB_ACTION(SetParameter)
611 }
612 
613 void Camera::onRequestData(ensenso_camera_msgs::RequestDataGoalConstPtr const& goal)
614 {
616 
617  ensenso_camera_msgs::RequestDataResult result;
618  ensenso_camera_msgs::RequestDataFeedback feedback;
619 
620  bool publishResults = goal->publish_results;
621  if (!goal->publish_results && !goal->include_results_in_response)
622  {
623  publishResults = true;
624  }
625 
626  bool requestPointCloud = goal->request_point_cloud || goal->request_depth_image;
627  if (!goal->request_raw_images && !goal->request_rectified_images && !goal->request_point_cloud &&
628  !goal->request_normals && !goal->request_depth_image)
629  {
630  requestPointCloud = true;
631  }
632 
633  bool computePointCloud = requestPointCloud || goal->request_normals;
634  bool computeDisparityMap = goal->request_disparity_map || computePointCloud;
635 
636  loadParameterSet(goal->parameter_set, computeDisparityMap ? projectorOn : projectorOff);
637  ros::Time imageTimestamp = capture();
638 
640 
641  feedback.images_acquired = true;
642  requestDataServer->publishFeedback(feedback);
643 
644  if (goal->request_raw_images)
645  {
646  auto rawImages = imagesFromNxLibNode(cameraNode[itmImages][itmRaw], cameraFrame);
647 
648  leftCameraInfo->header.stamp = rawImages[0].first->header.stamp;
649  rightCameraInfo->header.stamp = leftCameraInfo->header.stamp;
650 
651  if (goal->include_results_in_response)
652  {
653  for (auto const& imagePair : rawImages)
654  {
655  result.left_raw_images.push_back(*imagePair.first);
656  result.right_raw_images.push_back(*imagePair.second);
657  }
658  result.left_camera_info = *leftCameraInfo;
659  result.right_camera_info = *rightCameraInfo;
660  }
661  if (publishResults)
662  {
663  // We only publish one of the images on the topic, even if FlexView is
664  // enabled.
665  leftRawImagePublisher.publish(rawImages[0].first, leftCameraInfo);
666  rightRawImagePublisher.publish(rawImages[0].second, rightCameraInfo);
667  }
668  }
669 
671 
672  // If we need the disparity map, we do the rectification implicitly in
673  // cmdComputeDisparityMap. This is more efficient when using CUDA.
674  if (goal->request_rectified_images && !computeDisparityMap)
675  {
676  NxLibCommand rectify(cmdRectifyImages);
677  rectify.parameters()[itmCameras] = serial;
678  rectify.execute();
679  }
680  else if (computeDisparityMap)
681  {
682  NxLibCommand computeDisparityMap(cmdComputeDisparityMap);
683  computeDisparityMap.parameters()[itmCameras] = serial;
684  computeDisparityMap.execute();
685  }
686 
687  if (goal->request_rectified_images)
688  {
689  auto rectifiedImages = imagesFromNxLibNode(cameraNode[itmImages][itmRectified], cameraFrame);
690 
691  leftRectifiedCameraInfo->header.stamp = rectifiedImages[0].first->header.stamp;
692  rightRectifiedCameraInfo->header.stamp = leftRectifiedCameraInfo->header.stamp;
693 
694  if (goal->include_results_in_response)
695  {
696  for (auto const& imagePair : rectifiedImages)
697  {
698  result.left_rectified_images.push_back(*imagePair.first);
699  result.right_rectified_images.push_back(*imagePair.second);
700  }
701  result.left_rectified_camera_info = *leftRectifiedCameraInfo;
702  result.right_rectified_camera_info = *rightRectifiedCameraInfo;
703  }
704  if (publishResults)
705  {
706  // We only publish one of the images on the topic, even if FlexView is
707  // enabled.
710  }
711  }
712 
713  if (goal->request_disparity_map)
714  {
715  auto disparityMap = imageFromNxLibNode(cameraNode[itmImages][itmDisparityMap], cameraFrame);
716 
717  if (goal->include_results_in_response)
718  {
719  result.disparity_map = *disparityMap;
720  }
721  if (publishResults)
722  {
723  disparityMapPublisher.publish(disparityMap);
724  }
725  }
726 
728 
729  PointCloudROI const* pointCloudROI = 0;
730  if (parameterSets.at(currentParameterSet).useROI)
731  {
732  pointCloudROI = &parameterSets.at(currentParameterSet).roi;
733  }
734 
735  if (computePointCloud)
736  {
737  updateTransformations(imageTimestamp, "", goal->use_cached_transformation);
738 
739  NxLibCommand computePointMap(cmdComputePointMap);
740  computePointMap.parameters()[itmCameras] = serial;
741  computePointMap.execute();
742 
743  if (requestPointCloud && !goal->request_normals)
744  {
745  auto pointCloud = pointCloudFromNxLib(cameraNode[itmImages][itmPointMap], targetFrame, pointCloudROI);
746 
747  if (goal->include_results_in_response)
748  {
749  pcl::toROSMsg(*pointCloud, result.point_cloud);
750  }
751  if (publishResults)
752  {
753  pointCloudPublisher.publish(pointCloud);
754  }
755  }
756 
757  if (goal->request_depth_image)
758  {
759  auto depthImage = depthImageFromNxLibNode(cameraNode[itmImages][itmPointMap], cameraFrame);
760 
761  if (goal->include_results_in_response)
762  {
763  result.depth_image = *depthImage;
764  }
765 
766  if (publishResults)
767  {
769  }
770  }
771  }
772 
774 
775  if (goal->request_normals)
776  {
777  NxLibCommand(cmdComputeNormals).execute();
778 
779  auto pointCloud = pointCloudWithNormalsFromNxLib(cameraNode[itmImages][itmPointMap],
780  cameraNode[itmImages][itmNormals], targetFrame, pointCloudROI);
781 
782  if (goal->include_results_in_response)
783  {
784  pcl::toROSMsg(*pointCloud, result.point_cloud);
785  }
786  if (publishResults)
787  {
788  pointCloudPublisher.publish(pointCloud);
789  }
790  }
791 
792  requestDataServer->setSucceeded(result);
793 
794  FINISH_NXLIB_ACTION(RequestData)
795 }
796 
797 void Camera::onLocatePattern(ensenso_camera_msgs::LocatePatternGoalConstPtr const& goal)
798 {
800 
801  ensenso_camera_msgs::LocatePatternResult result;
802  ensenso_camera_msgs::LocatePatternFeedback feedback;
803 
804  loadParameterSet(goal->parameter_set, projectorOff);
805 
806  int numberOfShots = goal->number_of_shots;
807  if (numberOfShots < 1)
808  {
809  numberOfShots = 1;
810  }
811 
812  std::vector<CalibrationPattern> patterns;
813  ros::Time imageTimestamp;
814  for (int i = 0; i < numberOfShots; i++)
815  {
817 
818  ros::Time timestamp = capture();
819  if (i == 0)
820  imageTimestamp = timestamp;
821 
823 
824  if (i == (numberOfShots - 1))
825  {
826  // The last image has been captured.
827  feedback.images_acquired = true;
828  locatePatternServer->publishFeedback(feedback);
829  }
830 
831  bool clearBuffer = (i == 0);
832  patterns = collectPattern(clearBuffer);
833  if (patterns.empty())
834  {
835  result.found_pattern = false;
836  locatePatternServer->setSucceeded(result);
837  return;
838  }
839 
840  if (patterns.size() > 1)
841  {
842  // Cannot average multiple shots of multiple patterns. We will cancel the
843  // capturing and estimate the pose of each pattern individually.
844  break;
845  }
846  }
847 
848  result.found_pattern = true;
849  result.patterns.resize(patterns.size());
850  for (size_t i = 0; i < patterns.size(); i++)
851  {
852  patterns[i].writeToMessage(&result.patterns[i]);
853  }
854 
856 
857  std::string patternFrame = targetFrame;
858  if (!goal->target_frame.empty())
859  {
860  patternFrame = goal->target_frame;
861  }
862 
863  result.frame = patternFrame;
864 
865  if (patterns.size() > 1)
866  {
867  // Estimate the pose of all the patterns we found.
868  auto patternPoses = estimatePatternPoses(imageTimestamp, patternFrame);
869 
870  result.pattern_poses.resize(patternPoses.size());
871  for (size_t i = 0; i < patternPoses.size(); i++)
872  {
873  tf::poseStampedTFToMsg(patternPoses[i], result.pattern_poses[i]);
874  }
875  }
876  else
877  {
878  // Estimate the pose of a single pattern, averaging over the different
879  // shots.
880  tf::Stamped<tf::Pose> patternPose = estimatePatternPose(imageTimestamp, patternFrame);
881 
882  result.pattern_poses.resize(1);
883  tf::poseStampedTFToMsg(patternPose, result.pattern_poses[0]);
884  }
885 
886  if (!goal->tf_frame.empty())
887  {
888  if (patterns.size() == 1)
889  {
890  tf::StampedTransform transform = transformFromPose(result.pattern_poses[0], goal->tf_frame);
892  }
893  else
894  {
895  ROS_WARN("Cannot publish the pattern pose in TF, because there are "
896  "multiple patterns!");
897  }
898  }
899 
900  locatePatternServer->setSucceeded(result);
901 
902  FINISH_NXLIB_ACTION(LocatePattern)
903 }
904 
905 void Camera::onProjectPattern(ensenso_camera_msgs::ProjectPatternGoalConstPtr const& goal)
906 {
908 
909  ensenso_camera_msgs::ProjectPatternResult result;
910 
911  tf::Pose targetFrameTransformation;
912  tf::poseMsgToTF(goal->target_frame_transformation, targetFrameTransformation);
913  if (poseIsValid(targetFrameTransformation))
914  {
915  updateTransformations(targetFrameTransformation);
916  }
917  else
918  {
920  }
921 
923 
924  if (!checkNxLibVersion(2, 1))
925  {
926  // In old NxLib versions, the project pattern command does not take the grid
927  // spacing as a parameter.
928  NxLibItem()[itmParameters][itmPattern][itmGridSpacing] = goal->grid_spacing * 1000;
929  }
930 
931  tf::Pose patternPose;
932  tf::poseMsgToTF(goal->pattern_pose, patternPose);
933 
934  NxLibCommand projectPattern(cmdProjectPattern);
935  projectPattern.parameters()[itmCameras] = serial;
936  projectPattern.parameters()[itmGridSpacing] = goal->grid_spacing * 1000;
937  projectPattern.parameters()[itmGridSize][0] = goal->grid_size_x;
938  projectPattern.parameters()[itmGridSize][1] = goal->grid_size_y;
939  writePoseToNxLib(patternPose, projectPattern.parameters()[itmPatternPose]);
940  projectPattern.execute();
941 
943 
944  int sensorWidth = cameraNode[itmSensor][itmSize][0].asInt();
945  int sensorHeight = cameraNode[itmSensor][itmSize][1].asInt();
946 
947  NxLibItem projectedPoints = projectPattern.result()[itmStereo][0][itmPoints];
948  NxLibItem leftPoints = projectedPoints[0];
949  NxLibItem rightPoints = projectedPoints[1];
950 
951  result.pattern_is_visible = true;
952  result.left_points.resize(leftPoints.count());
953  result.right_points.resize(leftPoints.count());
954  for (int i = 0; i < leftPoints.count(); i++)
955  {
956  double leftX = leftPoints[i][0].asDouble();
957  double leftY = leftPoints[i][1].asDouble();
958  double rightX = rightPoints[i][0].asDouble();
959  double rightY = rightPoints[i][1].asDouble();
960 
961  result.left_points[i].x = leftX;
962  result.left_points[i].y = leftY;
963  result.right_points[i].x = rightX;
964  result.right_points[i].y = rightY;
965 
966  if (leftX < 0 || leftX > sensorWidth || leftY < 0 || leftY > sensorHeight || rightX < 0 || rightX > sensorWidth ||
967  rightY < 0 || rightY > sensorHeight)
968  {
969  result.pattern_is_visible = false;
970  }
971  }
972 
973  projectPatternServer->setSucceeded(result);
974 
975  FINISH_NXLIB_ACTION(ProjectPattern)
976 }
977 
978 void Camera::onCalibrateHandEye(ensenso_camera_msgs::CalibrateHandEyeGoalConstPtr const& goal)
979 {
980  START_NXLIB_ACTION(CalibrateHandEye, calibrateHandEyeServer)
981 
982  ensenso_camera_msgs::CalibrateHandEyeResult result;
983  result.command = goal->command;
984 
985  if (goal->command == goal->RESET)
986  {
989  }
990  else if (goal->command == goal->CAPTURE_PATTERN)
991  {
992  if (robotFrame.empty() || wristFrame.empty())
993  {
994  result.error_message = "You need to specify a robot base and wrist frame "
995  "to do a hand eye calibration!";
996  ROS_ERROR("%s", result.error_message.c_str());
997  calibrateHandEyeServer->setAborted(result);
998  return;
999  }
1000 
1001  loadParameterSet(goal->parameter_set, projectorOff);
1002  ros::Time imageTimestamp = capture();
1003 
1005 
1006  // Load the pattern buffer that we remembered from the previous
1007  // calibration steps.
1008  if (!handEyeCalibrationPatternBuffer.empty())
1009  {
1010  NxLibCommand setPatternBuffer(cmdSetPatternBuffer);
1011  setPatternBuffer.parameters()[itmPatterns] << handEyeCalibrationPatternBuffer;
1012  setPatternBuffer.execute();
1013  }
1014  else
1015  {
1016  NxLibCommand(cmdDiscardPatterns).execute();
1017  }
1018 
1019  std::vector<CalibrationPattern> patterns = collectPattern();
1020  if (patterns.empty())
1021  {
1022  result.found_pattern = false;
1023  calibrateHandEyeServer->setSucceeded(result);
1024  return;
1025  }
1026  if (patterns.size() > 1)
1027  {
1028  result.error_message = "Detected multiple calibration patterns during a "
1029  "hand eye calibration!";
1030  ROS_ERROR("%s", result.error_message.c_str());
1031  calibrateHandEyeServer->setAborted(result);
1032  return;
1033  }
1034 
1036 
1037  result.found_pattern = true;
1038  patterns[0].writeToMessage(&result.pattern);
1039 
1040  tf::Pose patternPose = estimatePatternPose(imageTimestamp, cameraFrame, true);
1041  tf::poseTFToMsg(patternPose, result.pattern_pose);
1042 
1044 
1045  tf::StampedTransform robotPose;
1046  try
1047  {
1049  }
1050  catch (tf::TransformException& e)
1051  {
1052  result.error_message = std::string("Could not look up the robot pose due to the TF error: ") + e.what();
1053  ROS_ERROR("%s", result.error_message.c_str());
1054  calibrateHandEyeServer->setAborted(result);
1055  return;
1056  }
1057 
1058  // Remember the newly collected data for the next step.
1059  NxLibCommand getPatternBuffer(cmdGetPatternBuffer);
1060  getPatternBuffer.execute();
1061  handEyeCalibrationPatternBuffer = getPatternBuffer.result()[itmPatterns].asJson();
1062 
1063  handEyeCalibrationRobotPoses.push_back(robotPose);
1064 
1065  tf::poseTFToMsg(robotPose, result.robot_pose);
1066  }
1067  else if (goal->command == goal->START_CALIBRATION)
1068  {
1069  if (handEyeCalibrationRobotPoses.size() < 5)
1070  {
1071  result.error_message = "You need collect at least 5 patterns before "
1072  "starting a hand eye calibration!";
1073  ROS_ERROR("%s", result.error_message.c_str());
1074  calibrateHandEyeServer->setAborted(result);
1075  return;
1076  }
1077 
1078  // Load the pattern observations.
1079  size_t numberOfPatterns = 0;
1080  NxLibCommand setPatternBuffer(cmdSetPatternBuffer);
1081  if (goal->pattern_observations.size() > 0)
1082  {
1083  for (size_t i = 0; i < goal->pattern_observations.size(); i++)
1084  {
1085  CalibrationPattern pattern(goal->pattern_observations[i]);
1086 
1087  NxLibItem patternNode = setPatternBuffer.parameters()[itmPatterns][i][serial];
1088  pattern.writeToNxLib(patternNode[itmLeft][0]);
1089  pattern.writeToNxLib(patternNode[itmRight][0], true);
1090  }
1091  }
1092  else
1093  {
1094  setPatternBuffer.parameters()[itmPatterns] << handEyeCalibrationPatternBuffer;
1095  }
1096  numberOfPatterns = setPatternBuffer.parameters()[itmPatterns].count();
1097  setPatternBuffer.execute();
1098 
1099  // Load the corresponding robot poses.
1100  auto robotPoses = handEyeCalibrationRobotPoses;
1101  if (goal->robot_poses.size() > 0)
1102  {
1103  robotPoses.clear();
1104  for (auto const& pose : goal->robot_poses)
1105  {
1106  tf::Pose tfPose;
1107  tf::poseMsgToTF(pose, tfPose);
1108  robotPoses.push_back(tfPose);
1109  }
1110  }
1111 
1112  if (robotPoses.size() != numberOfPatterns)
1113  {
1114  result.error_message = "The number of pattern observations does not "
1115  "match the number of robot poses!";
1116  ROS_ERROR("%s", result.error_message.c_str());
1117  calibrateHandEyeServer->setAborted(result);
1118  return;
1119  }
1120 
1121  // Load the initial guesses from the action goal.
1122  tf::Pose link, patternPose;
1123  tf::poseMsgToTF(goal->link, link);
1124  tf::poseMsgToTF(goal->pattern_pose, patternPose);
1125 
1126  NxLibCommand calibrateHandEye(cmdCalibrateHandEye);
1127  calibrateHandEye.parameters()[itmSetup] = fixed ? valFixed : valMoving;
1128  // The target node will be reset anyway before we calculate data for the next time.
1129  calibrateHandEye.parameters()[itmTarget] = TARGET_FRAME_LINK;
1130  if (poseIsValid(link))
1131  {
1132  writePoseToNxLib(link.inverse(), calibrateHandEye.parameters()[itmLink]);
1133  }
1134  if (poseIsValid(patternPose))
1135  {
1136  writePoseToNxLib(patternPose, calibrateHandEye.parameters()[itmPatternPose]);
1137  }
1138  for (size_t i = 0; i < robotPoses.size(); i++)
1139  {
1140  writePoseToNxLib(robotPoses[i], calibrateHandEye.parameters()[itmTransformations][i]);
1141  }
1142 
1143  calibrateHandEye.execute(false);
1144 
1145  auto getCalibrationResidual = [](NxLibItem const& node) { // NOLINT
1146  if (node[itmResidual].exists())
1147  {
1148  return node[itmResidual].asDouble();
1149  }
1150  // Compatibility with the SDK 2.0.
1151  return node[itmReprojectionError].asDouble();
1152  };
1153 
1154  ros::Rate waitingRate(5);
1155  while (!calibrateHandEye.finished())
1156  {
1157  ensenso_camera_msgs::CalibrateHandEyeFeedback feedback;
1158  feedback.number_of_iterations = calibrateHandEye.result()[itmProgress][itmIterations].asInt();
1159  feedback.residual = getCalibrationResidual(calibrateHandEye.result()[itmProgress]);
1160  calibrateHandEyeServer->publishFeedback(feedback);
1161 
1162  if (calibrateHandEyeServer->isPreemptRequested())
1163  {
1164  NxLibCommand(cmdBreak).execute();
1165 
1166  calibrateHandEyeServer->setPreempted();
1167  return;
1168  }
1169 
1170  waitingRate.sleep();
1171  }
1172 
1173  result.calibration_time =
1174  calibrateHandEye.result()[itmTime].asDouble() / 1000; // NxLib time is in milliseconds, ROS
1175  // expects time to be in seconds.
1176  result.number_of_iterations = calibrateHandEye.result()[itmIterations].asInt();
1177  result.residual = getCalibrationResidual(calibrateHandEye.result());
1178 
1179  tf::poseTFToMsg(poseFromNxLib(cameraNode[itmLink]).inverse(), result.link);
1180  tf::poseTFToMsg(poseFromNxLib(calibrateHandEye.result()[itmPatternPose]), result.pattern_pose);
1181 
1182  if (goal->write_calibration_to_eeprom)
1183  {
1184  // Save the new calibration link to the camera's EEPROM.
1185  NxLibCommand storeCalibration(cmdStoreCalibration);
1186  storeCalibration.parameters()[itmCameras] = serial;
1187  storeCalibration.parameters()[itmLink] = true;
1188  storeCalibration.execute();
1189  }
1190  }
1191 
1192  calibrateHandEyeServer->setSucceeded(result);
1193 
1194  FINISH_NXLIB_ACTION(CalibrateHandEye)
1195 }
1196 
1197 void Camera::onCalibrateWorkspace(const ensenso_camera_msgs::CalibrateWorkspaceGoalConstPtr& goal)
1198 {
1199  START_NXLIB_ACTION(CalibrateWorkspace, calibrateWorkspaceServer)
1200 
1201  if (!fixed)
1202  {
1203  ROS_WARN("You are performing a workspace calibration for a moving camera. "
1204  "Are you sure that this is what you want to do?");
1205  }
1206 
1207  ensenso_camera_msgs::CalibrateWorkspaceResult result;
1208  result.successful = true;
1209 
1210  loadParameterSet(goal->parameter_set, projectorOff);
1211 
1212  int numberOfShots = goal->number_of_shots;
1213  if (numberOfShots < 1)
1214  {
1215  numberOfShots = 1;
1216  }
1217 
1218  ros::Time imageTimestamp;
1219  for (int i = 0; i < numberOfShots; i++)
1220  {
1222 
1223  ros::Time timestamp = capture();
1224  if (i == 0)
1225  imageTimestamp = timestamp;
1226 
1228 
1229  bool clearBuffer = (i == 0);
1230  std::vector<CalibrationPattern> patterns = collectPattern(clearBuffer);
1231  if (patterns.size() != 1)
1232  {
1233  result.successful = false;
1234  calibrateWorkspaceServer->setSucceeded(result);
1235  return;
1236  }
1237  }
1238 
1240 
1241  tf::Transform patternTransformation = estimatePatternPose(imageTimestamp);
1242 
1244 
1245  tf::Pose definedPatternPose;
1246  tf::poseMsgToTF(goal->defined_pattern_pose, definedPatternPose);
1247 
1248  NxLibCommand calibrateWorkspace(cmdCalibrateWorkspace);
1249  calibrateWorkspace.parameters()[itmCameras] = serial;
1250  writePoseToNxLib(patternTransformation, calibrateWorkspace.parameters()[itmPatternPose]);
1251  writePoseToNxLib(definedPatternPose, calibrateWorkspace.parameters()[itmDefinedPose]);
1252  calibrateWorkspace.parameters()[itmTarget] = TARGET_FRAME_LINK;
1253  calibrateWorkspace.execute();
1254 
1255  if (goal->write_calibration_to_eeprom)
1256  {
1257  // Save the new calibration link to the camera's EEPROM.
1258  NxLibCommand storeCalibration(cmdStoreCalibration);
1259  storeCalibration.parameters()[itmCameras] = serial;
1260  storeCalibration.parameters()[itmLink] = true;
1261  storeCalibration.execute();
1262  }
1263 
1264  calibrateWorkspaceServer->setSucceeded(result);
1265 
1266  FINISH_NXLIB_ACTION(CalibrateWorkspace)
1267 }
1268 
1270 {
1271  return cameraNode[itmStatus].exists() && cameraNode[itmStatus][itmAvailable].exists() &&
1272  cameraNode[itmStatus][itmAvailable].asBool();
1273 }
1274 
1276 {
1277  return cameraNode[itmStatus].exists() && cameraNode[itmStatus][itmOpen].exists() &&
1278  cameraNode[itmStatus][itmOpen].asBool();
1279 }
1280 
1281 void Camera::publishStatus(ros::TimerEvent const& event) const
1282 {
1283  std::lock_guard<std::mutex> lock(nxLibMutex);
1284 
1285  diagnostic_msgs::DiagnosticStatus cameraStatus;
1286  cameraStatus.name = "Camera";
1287  cameraStatus.hardware_id = serial;
1288  cameraStatus.level = diagnostic_msgs::DiagnosticStatus::OK;
1289  cameraStatus.message = "OK";
1290 
1291  if (!cameraIsOpen())
1292  {
1293  cameraStatus.level = diagnostic_msgs::DiagnosticStatus::ERROR;
1294  cameraStatus.message = "Camera is no longer open";
1295  }
1296 
1297  diagnostic_msgs::DiagnosticArray status;
1298  status.header.stamp = ros::Time::now();
1299  status.status.push_back(cameraStatus);
1300  statusPublisher.publish(status);
1301 }
1302 
1304 {
1305  defaultParameters << cameraNode[itmParameters];
1306 }
1307 
1308 void Camera::saveParameterSet(std::string name, bool projectorWasSet)
1309 {
1310  if (name.empty())
1311  {
1312  name = DEFAULT_PARAMETER_SET;
1313  }
1314 
1315  ParameterSet& parameterSet = parameterSets.at(name);
1316 
1317  parameterSet.node.erase();
1318  parameterSet.node << cameraNode[itmParameters];
1319 
1320  if (projectorWasSet)
1321  {
1322  parameterSet.autoProjector = false;
1323  }
1324 }
1325 
1326 void Camera::loadParameterSet(std::string name, ProjectorState projector)
1327 {
1328  if (name.empty())
1329  {
1330  name = DEFAULT_PARAMETER_SET;
1331  }
1332 
1333  ROS_DEBUG("Loading parameter set '%s'", name.c_str());
1334 
1335  if (parameterSets.count(name) == 0)
1336  {
1337  // The parameter set was never used before. Create it by copying the
1338  // default settings.
1339  parameterSets.insert(std::make_pair(name, ParameterSet(name, defaultParameters)));
1340  }
1341 
1342  ParameterSet const& parameterSet = parameterSets.at(name);
1343 
1344  bool changedParameters = false;
1345  if (name != currentParameterSet)
1346  {
1347  cameraNode[itmParameters] << parameterSet.node;
1348  changedParameters = true;
1349  }
1350 
1351  if (parameterSet.autoProjector && projector != projectorDontCare)
1352  {
1353  try
1354  {
1355  if (projector == projectorOn)
1356  {
1357  cameraNode[itmParameters][itmCapture][itmProjector] = true;
1358  cameraNode[itmParameters][itmCapture][itmFrontLight] = false;
1359  }
1360  else
1361  {
1362  cameraNode[itmParameters][itmCapture][itmProjector] = false;
1363  cameraNode[itmParameters][itmCapture][itmFrontLight] = true;
1364  }
1365  changedParameters = true;
1366  }
1367  catch (...)
1368  {
1369  // Setting the projector and front light fails on file cameras.
1370  }
1371  }
1372 
1373  if (changedParameters)
1374  {
1375  NxLibCommand synchronize(cmdSynchronize);
1376  synchronize.parameters()[itmCameras] = serial;
1377  synchronize.execute();
1378  }
1379 
1380  currentParameterSet = name;
1381 }
1382 
1384 {
1385  ROS_DEBUG("Capturing an image...");
1386 
1387  NxLibCommand capture(cmdCapture);
1388  capture.parameters()[itmCameras] = serial;
1389  capture.execute();
1390 
1391  NxLibItem imageNode = cameraNode[itmImages][itmRaw];
1392  if (imageNode.isArray())
1393  {
1394  imageNode = imageNode[0];
1395  }
1396  imageNode = imageNode[itmLeft];
1397 
1398  return timestampFromNxLibNode(imageNode);
1399 }
1400 
1401 std::vector<CalibrationPattern> Camera::collectPattern(bool clearBuffer) const
1402 {
1403  if (clearBuffer)
1404  {
1405  NxLibCommand(cmdDiscardPatterns).execute();
1406  }
1407 
1408  NxLibCommand collectPattern(cmdCollectPattern);
1409  collectPattern.parameters()[itmCameras] = serial;
1410  collectPattern.parameters()[itmDecodeData] = true;
1411  collectPattern.parameters()[itmFilter][itmCameras] = serial;
1412  collectPattern.parameters()[itmFilter][itmUseModel] = true;
1413  collectPattern.parameters()[itmFilter][itmType] = valStatic;
1414  collectPattern.parameters()[itmFilter][itmValue] = true;
1415  try
1416  {
1417  collectPattern.execute();
1418  }
1419  catch (NxLibException& e)
1420  {
1421  if (e.getErrorCode() == NxLibExecutionFailed)
1422  {
1423  if (collectPattern.result()[itmErrorSymbol] == errPatternNotFound ||
1424  collectPattern.result()[itmErrorSymbol] == errPatternNotDecodable)
1425  {
1426  return {};
1427  }
1428  }
1429  throw;
1430  }
1431 
1432  if (!collectPattern.result()[itmStereo].exists())
1433  {
1434  // We did find patterns, but only in one of the cameras.
1435  return {};
1436  }
1437 
1438  std::vector<CalibrationPattern> result;
1439 
1440  for (int i = 0; i < collectPattern.result()[itmStereo].count(); i++)
1441  {
1442  result.emplace_back(collectPattern.result()[itmStereo][i]);
1443  }
1444 
1445  // Extract the pattern's image points from the result.
1446 
1447  NxLibItem pattern = collectPattern.result()[itmPatterns][0][serial];
1448  if (pattern[itmLeft].count() > 1 || pattern[itmRight].count() > 1)
1449  {
1450  // We cannot tell which of the patterns in the two cameras belong together,
1451  // because that would need a comparison with the stereo model.
1452  return result;
1453  }
1454 
1455  for (size_t i = 0; i < result.size(); i++)
1456  {
1457  for (int j = 0; j < pattern[itmLeft][i][itmPoints].count(); j++)
1458  {
1459  NxLibItem pointNode = pattern[itmLeft][i][itmPoints][j];
1460 
1461  ensenso_camera_msgs::ImagePoint point;
1462  point.x = pointNode[0].asDouble();
1463  point.y = pointNode[1].asDouble();
1464  result[i].leftPoints.push_back(point);
1465  }
1466  for (int j = 0; j < pattern[itmRight][i][itmPoints].count(); j++)
1467  {
1468  NxLibItem pointNode = pattern[itmRight][i][itmPoints][j];
1469 
1470  ensenso_camera_msgs::ImagePoint point;
1471  point.x = pointNode[0].asDouble();
1472  point.y = pointNode[1].asDouble();
1473  result[i].rightPoints.push_back(point);
1474  }
1475  }
1476 
1477  return result;
1478 }
1479 
1481  bool latestPatternOnly) const
1482 {
1483  updateTransformations(imageTimestamp, targetFrame);
1484 
1485  NxLibCommand estimatePatternPose(cmdEstimatePatternPose);
1486  if (latestPatternOnly)
1487  {
1488  estimatePatternPose.parameters()[itmAverage] = false;
1489 
1490  int patternCount = NxLibItem()[itmParameters][itmPatternCount].asInt();
1491  estimatePatternPose.parameters()[itmFilter][itmOr][0][itmAnd][0][itmType] = valIndex;
1492  estimatePatternPose.parameters()[itmFilter][itmOr][0][itmAnd][0][itmValue] = patternCount - 1;
1493  }
1494  else
1495  {
1496  estimatePatternPose.parameters()[itmAverage] = true;
1497  }
1498  estimatePatternPose.execute();
1499 
1500  ROS_ASSERT(estimatePatternPose.result()[itmPatterns].count() == 1);
1501 
1502  return poseFromNxLib(estimatePatternPose.result()[itmPatterns][0][itmPatternPose], ros::Time::now(), targetFrame);
1503 }
1504 
1505 std::vector<tf::Stamped<tf::Pose>> Camera::estimatePatternPoses(ros::Time imageTimestamp,
1506  const std::string& targetFrame) const
1507 {
1508  updateTransformations(imageTimestamp, targetFrame);
1509 
1510  NxLibCommand estimatePatternPose(cmdEstimatePatternPose);
1511  estimatePatternPose.parameters()[itmAverage] = false;
1512  estimatePatternPose.parameters()[itmFilter][itmCameras] = serial;
1513  estimatePatternPose.parameters()[itmFilter][itmUseModel] = true;
1514  estimatePatternPose.parameters()[itmFilter][itmType] = valStatic;
1515  estimatePatternPose.parameters()[itmFilter][itmValue] = true;
1516  estimatePatternPose.execute();
1517 
1518  int numberOfPatterns = estimatePatternPose.result()[itmPatterns].count();
1519 
1520  std::vector<tf::Stamped<tf::Pose>> result;
1521  result.reserve(numberOfPatterns);
1522 
1523  for (int i = 0; i < numberOfPatterns; i++)
1524  {
1525  result.push_back(
1526  poseFromNxLib(estimatePatternPose.result()[itmPatterns][i][itmPatternPose], ros::Time::now(), targetFrame));
1527  }
1528 
1529  return result;
1530 }
1531 
1532 void Camera::updateTransformations(ros::Time time, std::string frame, bool useCachedTransformation) const
1533 {
1534  if (frame.empty())
1535  {
1536  frame = targetFrame;
1537  }
1538 
1539  // Transformation are represented in the NxLib as follows.
1540  // The camera's link node contains the calibration data from e.g. the hand
1541  // eye calibration. This is always used when it is present.
1542  // The transformation between the camera frame and the target frame (in
1543  // which the data is returned) is fetched from TF and written to a link node
1544  // of the NxLib. When the target frame is different from the camera's own
1545  // frame, we set the camera's link target to this link node.
1546 
1547  if (cameraFrame == frame)
1548  {
1549  // The camera frame is the target frame already. No need to transform
1550  // anything in the NxLib.
1551  cameraNode[itmLink][itmTarget] = "";
1552  return;
1553  }
1554 
1555  cameraNode[itmLink][itmTarget] = TARGET_FRAME_LINK;
1556 
1557  // Update the transformation to the target frame in the NxLib according to
1558  // the current information from TF.
1559  tf::StampedTransform transform;
1560  if (useCachedTransformation && transformationCache.count(frame) != 0)
1561  {
1562  transform = transformationCache[frame];
1563  }
1564  else
1565  {
1566  // TF exceptions will be caught outside and cancel the current action.
1568  transformListener.lookupTransform(cameraFrame, frame, time, transform);
1569  transformationCache[frame] = transform;
1570  }
1571 
1572  writePoseToNxLib(transform, NxLibItem()[itmLinks][TARGET_FRAME_LINK]);
1573 }
1574 
1575 void Camera::updateTransformations(tf::Pose const& targetFrameTransformation) const
1576 {
1577  cameraNode[itmLink][itmTarget] = TARGET_FRAME_LINK;
1578  writePoseToNxLib(targetFrameTransformation, NxLibItem()[itmLinks][TARGET_FRAME_LINK]);
1579 }
1580 
1581 void Camera::fillCameraInfoFromNxLib(sensor_msgs::CameraInfoPtr const& info, bool right, bool rectified) const
1582 {
1583  info->header.frame_id = cameraFrame;
1584 
1585  info->width = cameraNode[itmSensor][itmSize][0].asInt();
1586  info->height = cameraNode[itmSensor][itmSize][1].asInt();
1587 
1588  NxLibItem monoCalibrationNode = cameraNode[itmCalibration][itmMonocular][right ? itmRight : itmLeft];
1589  NxLibItem stereoCalibrationNode = cameraNode[itmCalibration][itmStereo][right ? itmRight : itmLeft];
1590 
1591  if (rectified)
1592  {
1593  // For the rectified images all transformations are the identity (because
1594  // all of the distortions were already removed), except for the stereo
1595  // camera matrix.
1596 
1597  info->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB;
1598  info->D.clear();
1599  info->D.resize(5, 0);
1600 
1601  info->K.fill(0);
1602  info->P.fill(0);
1603  info->R.fill(0);
1604  for (int row = 0; row < 3; row++)
1605  {
1606  for (int column = 0; column < 3; column++)
1607  {
1608  info->P[4 * row + column] = stereoCalibrationNode[itmCamera][column][row].asDouble();
1609 
1610  if (row == column)
1611  {
1612  info->K[3 * row + column] = 1;
1613  info->R[3 * row + column] = 1;
1614  }
1615  }
1616  }
1617  }
1618  else
1619  {
1620  info->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB;
1621  info->D.clear();
1622  for (int i = 0; i < 5; i++)
1623  {
1624  info->D.push_back(monoCalibrationNode[itmDistortion][i].asDouble());
1625  }
1626 
1627  info->K.fill(0); // The mono camera matrix.
1628  info->P.fill(0); // The stereo camera matrix.
1629  info->R.fill(0);
1630  for (int row = 0; row < 3; row++)
1631  {
1632  for (int column = 0; column < 3; column++)
1633  {
1634  info->K[3 * row + column] = monoCalibrationNode[itmCamera][column][row].asDouble();
1635  info->P[4 * row + column] = stereoCalibrationNode[itmCamera][column][row].asDouble();
1636 
1637  info->R[3 * row + column] = stereoCalibrationNode[itmRotation][column][row].asDouble();
1638  }
1639  }
1640  }
1641 
1642  if (right)
1643  {
1644  // Add the offset of the right camera relative to the left one to the
1645  // projection matrix.
1646  double fx = stereoCalibrationNode[itmCamera][0][0].asDouble();
1647  double baseline = cameraNode[itmCalibration][itmStereo][itmBaseline].asDouble() / 1000.0;
1648  info->P[3] = -fx * baseline;
1649  }
1650 
1651  info->binning_x = cameraNode[itmParameters][itmCapture][itmBinning].asInt();
1652  info->binning_y = info->binning_x;
1653 
1654  int leftTopX = cameraNode[itmParameters][itmDisparityMap][itmAreaOfInterest][itmLeftTop][0].asInt();
1655  int leftTopY = cameraNode[itmParameters][itmDisparityMap][itmAreaOfInterest][itmLeftTop][1].asInt();
1656  int rightBottomX = cameraNode[itmParameters][itmDisparityMap][itmAreaOfInterest][itmRightBottom][0].asInt();
1657  int rightBottomY = cameraNode[itmParameters][itmDisparityMap][itmAreaOfInterest][itmRightBottom][1].asInt();
1658  info->roi.x_offset = leftTopX;
1659  info->roi.y_offset = leftTopY;
1660  info->roi.width = rightBottomX - leftTopX;
1661  info->roi.height = rightBottomY - leftTopY;
1662  if (rectified)
1663  {
1664  info->roi.do_rectify = true;
1665  }
1666 }
1667 
1669 {
1674 }
1675 
1676 ensenso_camera_msgs::ParameterPtr Camera::readParameter(std::string const& key) const
1677 {
1678  auto message = boost::make_shared<ensenso_camera_msgs::Parameter>();
1679  message->key = key;
1680 
1681  if (key == ensenso_camera_msgs::Parameter::REGION_OF_INTEREST)
1682  {
1683  PointCloudROI const& roi = parameterSets.at(currentParameterSet).roi;
1684 
1685  message->region_of_interest_value.lower.x = roi.minX;
1686  message->region_of_interest_value.lower.y = roi.minY;
1687  message->region_of_interest_value.lower.z = roi.minZ;
1688  message->region_of_interest_value.upper.x = roi.maxX;
1689  message->region_of_interest_value.upper.y = roi.maxY;
1690  message->region_of_interest_value.upper.z = roi.maxZ;
1691  }
1692  else if (key == ensenso_camera_msgs::Parameter::FLEX_VIEW)
1693  {
1694  NxLibItem flexViewNode = cameraNode[itmParameters][itmCapture][itmFlexView];
1695 
1696  if (!flexViewNode.exists())
1697  {
1698  message->float_value = -1;
1699  }
1700  else if (flexViewNode.isNumber())
1701  {
1702  message->float_value = flexViewNode.asInt();
1703  }
1704  else
1705  {
1706  message->float_value = 0;
1707  }
1708  }
1709  else if (parameterExists(key))
1710  {
1711  // The parameter is mapped to an NxLib node.
1712 
1713  ParameterMapping parameterMapping = parameterInformation.at(key);
1714  NxLibItem node = parameterMapping.node(cameraNode);
1715 
1716  if (node.exists())
1717  {
1718  switch (parameterMapping.type)
1719  {
1720  case ParameterType::Bool:
1721  message->bool_value = node.asBool();
1722  break;
1723  case ParameterType::Number:
1724  message->float_value = node.asDouble();
1725  break;
1726  case ParameterType::String:
1727  message->string_value = node.asString();
1728  break;
1729  }
1730  }
1731  else
1732  {
1733  ROS_WARN("Reading the parameter %s, but the camera does not support it!", key.c_str());
1734  }
1735  }
1736  else
1737  {
1738  ROS_ERROR("Unknown parameter '%s'!", key.c_str());
1739  }
1740 
1741  return message;
1742 }
1743 
1744 void Camera::writeParameter(ensenso_camera_msgs::Parameter const& parameter)
1745 {
1746  if (parameter.key == ensenso_camera_msgs::Parameter::REGION_OF_INTEREST)
1747  {
1749 
1750  roi.minX = parameter.region_of_interest_value.lower.x;
1751  roi.minY = parameter.region_of_interest_value.lower.y;
1752  roi.minZ = parameter.region_of_interest_value.lower.z;
1753  roi.maxX = parameter.region_of_interest_value.upper.x;
1754  roi.maxY = parameter.region_of_interest_value.upper.y;
1755  roi.maxZ = parameter.region_of_interest_value.upper.z;
1756 
1757  parameterSets.at(currentParameterSet).useROI = !roi.isEmpty();
1758  }
1759  else if (parameter.key == ensenso_camera_msgs::Parameter::FLEX_VIEW)
1760  {
1761  NxLibItem flexViewNode = cameraNode[itmParameters][itmCapture][itmFlexView];
1762 
1763  if (!flexViewNode.exists())
1764  {
1765  ROS_WARN("Writing the parameter FlexView, but the camera does not "
1766  "support it!");
1767  return;
1768  }
1769 
1770  int n = static_cast<int>(std::round(parameter.float_value));
1771  if (n <= 1)
1772  {
1773  flexViewNode = false;
1774  }
1775  else
1776  {
1777  flexViewNode = n;
1778  }
1779  }
1780  else if (parameterExists(parameter.key))
1781  {
1782  // The parameter is mapped to an NxLib node.
1783 
1784  ParameterMapping parameterMapping = parameterInformation.at(parameter.key);
1785  NxLibItem node = parameterMapping.node(cameraNode);
1786 
1787  if (!node.exists())
1788  {
1789  ROS_WARN("Writing the parameter %s, but the camera does not support it!", parameter.key.c_str());
1790  return;
1791  }
1792 
1793  switch (parameterMapping.type)
1794  {
1795  case ParameterType::Bool:
1796  node.set<bool>(parameter.bool_value);
1797  break;
1798  case ParameterType::Number:
1799  node.set<double>(parameter.float_value);
1800  break;
1801  case ParameterType::String:
1802  node.set<std::string>(parameter.string_value);
1803  }
1804  }
1805  else
1806  {
1807  ROS_ERROR("Unknown parameter '%s'!", parameter.key.c_str());
1808  }
1809 }
sensor_msgs::ImagePtr imageFromNxLibNode(NxLibItem const &node, std::string const &frame)
std::vector< tf::Pose > handEyeCalibrationRobotPoses
Definition: camera.h:155
std::map< std::string, ParameterSet > parameterSets
Definition: camera.h:144
static void poseMsgToTF(const geometry_msgs::Pose &msg, Pose &bt)
NxLibItem node
Definition: camera.h:53
image_transport::CameraPublisher leftRectifiedImagePublisher
Definition: camera.h:131
void onFitPrimitive(ensenso_camera_msgs::FitPrimitiveGoalConstPtr const &goal)
Definition: camera.cpp:426
void publish(const sensor_msgs::Image &image, const sensor_msgs::CameraInfo &info) const
bool fixed
Definition: camera.h:100
std::unique_ptr< FitPrimitiveServer > fitPrimitiveServer
Definition: camera.h:127
std::string targetFrame
Definition: camera.h:103
std::vector< CalibrationPattern > collectPattern(bool clearBuffer=false) const
Definition: camera.cpp:1401
void publish(const boost::shared_ptr< M > &message) const
int const ERROR_CODE_TF
Definition: camera.cpp:45
NxLibItem toEnsensoPoint(geometry_msgs::Point32 const &point, bool convertUnits=true)
Definition: conversion.h:31
std::string const DEFAULT_PARAMETER_SET
Definition: camera.cpp:35
bool parameterExists(std::string const &key)
Definition: parameters.h:87
std::vector< std::pair< sensor_msgs::ImagePtr, sensor_msgs::ImagePtr > > imagesFromNxLibNode(NxLibItem const &node, std::string const &frame)
ros::Time timestampFromNxLibNode(NxLibItem const &node)
void onCalibrateHandEye(ensenso_camera_msgs::CalibrateHandEyeGoalConstPtr const &goal)
Definition: camera.cpp:978
double const TRANSFORMATION_REQUEST_TIMEOUT
Definition: camera.cpp:29
std::map< std::string, ParameterMapping > const parameterInformation
Definition: parameters.h:52
static void poseStampedTFToMsg(const Stamped< Pose > &bt, geometry_msgs::PoseStamped &msg)
image_transport::CameraPublisher leftRawImagePublisher
Definition: camera.h:129
tf::TransformBroadcaster transformBroadcaster
Definition: camera.h:116
std::vector< tf::Stamped< tf::Pose > > estimatePatternPoses(ros::Time imageTimestamp=ros::Time::now(), std::string const &targetFrame="") const
Definition: camera.cpp:1505
NxLibItem node(NxLibItem const &cameraNode)
Definition: parameters.h:33
void publishStatus(ros::TimerEvent const &event) const
Definition: camera.cpp:1281
bool autoProjector
Definition: camera.h:71
ros::Publisher statusPublisher
Definition: camera.h:138
bool open()
Definition: camera.cpp:196
std::string cameraFrame
Definition: camera.h:102
NxLibItem defaultParameters
Definition: camera.h:142
std::string robotFrame
Definition: camera.h:104
std::unique_ptr< RequestDataServer > requestDataServer
Definition: camera.h:122
bool cameraIsOpen() const
Definition: camera.cpp:1275
Camera(ros::NodeHandle nh, std::string const &serial, std::string const &fileCameraPath, bool fixed, std::string const &cameraFrame, std::string const &targetFrame, std::string const &robotFrame, std::string const &wristFrame)
Definition: camera.cpp:137
void fillCameraInfoFromNxLib(sensor_msgs::CameraInfoPtr const &info, bool right, bool rectified=false) const
Definition: camera.cpp:1581
#define START_NXLIB_ACTION(ACTION_NAME, ACTION_SERVER)
Definition: camera.cpp:69
image_transport::CameraPublisher rightRawImagePublisher
Definition: camera.h:130
#define ROS_WARN(...)
image_transport::Publisher disparityMapPublisher
Definition: camera.h:133
void writeToNxLib(NxLibItem const &node, bool right=false)
ros::Publisher pointCloudPublisher
Definition: camera.h:136
std::mutex nxLibMutex
Definition: camera.h:108
sensor_msgs::CameraInfoPtr leftCameraInfo
Definition: camera.h:110
ProjectorState
Definition: camera.h:80
geometry_msgs::Point32 toRosPoint(NxLibItem const &itemArray, bool convertUnits=true)
Definition: conversion.h:12
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
void writePoseToNxLib(tf::Pose const &pose, NxLibItem const &node)
void updateCameraInfo()
Definition: camera.cpp:1668
void onCalibrateWorkspace(ensenso_camera_msgs::CalibrateWorkspaceGoalConstPtr const &goal)
Definition: camera.cpp:1197
std::unique_ptr< SetParameterServer > setParameterServer
Definition: camera.h:121
std::string handEyeCalibrationPatternBuffer
Definition: camera.h:154
bool isEmpty() const
void loadParameterSet(std::string name, ProjectorState projector=projectorDontCare)
Definition: camera.cpp:1326
std::string serial
Definition: camera.h:90
std::unique_ptr< LocatePatternServer > locatePatternServer
Definition: camera.h:123
#define LOG_NXLIB_EXCEPTION(EXCEPTION)
Definition: camera.cpp:47
ROSLIB_DECL std::string command(const std::string &cmd)
void publish(const sensor_msgs::Image &message) const
void updateTransformations(ros::Time time=ros::Time::now(), std::string targetFrame="", bool useCachedTransformation=false) const
Definition: camera.cpp:1532
#define ROS_INFO(...)
int const ERROR_CODE_UNKNOWN_EXCEPTION
Definition: camera.cpp:44
void onRequestData(ensenso_camera_msgs::RequestDataGoalConstPtr const &goal)
Definition: camera.cpp:613
TFSIMD_FORCE_INLINE Quaternion inverse(const Quaternion &q)
void onGetParameter(ensenso_camera_msgs::GetParameterGoalConstPtr const &goal)
Definition: camera.cpp:543
void sendTransform(const StampedTransform &transform)
void onAccessTree(ensenso_camera_msgs::AccessTreeGoalConstPtr const &goal)
Definition: camera.cpp:354
pcl::PointCloud< pcl::PointXYZ >::Ptr pointCloudFromNxLib(NxLibItem const &node, std::string const &frame, PointCloudROI const *roi=0)
Transform inverse() const
void writeParameter(ensenso_camera_msgs::Parameter const &parameter)
Definition: camera.cpp:1744
#define PREEMPT_ACTION_IF_REQUESTED
Definition: camera.cpp:112
void saveParameterSet(std::string name, bool projectorWritten=false)
Definition: camera.cpp:1308
bool poseIsValid(tf::Pose const &transform)
void onSetParameter(ensenso_camera_msgs::SetParameterGoalConstPtr const &goal)
Definition: camera.cpp:562
image_transport::CameraPublisher rightRectifiedImagePublisher
Definition: camera.h:132
std::string fileCameraPath
Definition: camera.h:94
sensor_msgs::CameraInfoPtr rightCameraInfo
Definition: camera.h:111
std::string wristFrame
Definition: camera.h:105
std::unique_ptr< ExecuteCommandServer > executeCommandServer
Definition: camera.h:119
tf::StampedTransform transformFromPose(geometry_msgs::PoseStamped const &pose, std::string const &childFrame)
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
bool sleep()
std::unique_ptr< CalibrateWorkspaceServer > calibrateWorkspaceServer
Definition: camera.h:126
pcl::PointCloud< pcl::PointNormal >::Ptr pointCloudWithNormalsFromNxLib(NxLibItem const &pointMapNode, NxLibItem const &normalNode, std::string const &frame, PointCloudROI const *roi=0)
tf::Stamped< tf::Pose > estimatePatternPose(ros::Time imageTimestamp=ros::Time::now(), std::string const &targetFrame="", bool latestPatternOnly=false) const
Definition: camera.cpp:1480
bool isFileCamera
Definition: camera.h:93
NxLibItem cameraNode
Definition: camera.h:91
void startServers() const
Definition: camera.cpp:277
sensor_msgs::ImagePtr depthImageFromNxLibNode(NxLibItem const &node, std::string const &frame)
bool createdFileCamera
Definition: camera.h:97
bool cameraIsAvailable() const
Definition: camera.cpp:1269
static void poseTFToMsg(const Pose &bt, geometry_msgs::Pose &msg)
void onExecuteCommand(ensenso_camera_msgs::ExecuteCommandGoalConstPtr const &goal)
Definition: camera.cpp:408
tf::Pose poseFromNxLib(NxLibItem const &node)
void onProjectPattern(ensenso_camera_msgs::ProjectPatternGoalConstPtr const &goal)
Definition: camera.cpp:905
tf::TransformListener transformListener
Definition: camera.h:115
void onLocatePattern(ensenso_camera_msgs::LocatePatternGoalConstPtr const &goal)
Definition: camera.cpp:797
const int conversionFactor
Definition: conversion.h:10
ParameterSet(std::string const &name, NxLibItem const &defaultParameters)
Definition: camera.cpp:129
void saveDefaultParameterSet()
Definition: camera.cpp:1303
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
static Time now()
ros::Timer statusTimer
Definition: camera.h:139
std::unique_ptr< AccessTreeServer > accessTreeServer
Definition: camera.h:118
ROSCPP_DECL bool exists(const std::string &service_name, bool print_failure_reason)
std::unique_ptr< GetParameterServer > getParameterServer
Definition: camera.h:120
#define ROS_ASSERT(cond)
sensor_msgs::CameraInfoPtr leftRectifiedCameraInfo
Definition: camera.h:112
bool checkNxLibVersion(int major, int minor)
Definition: camera.cpp:122
std::map< std::string, tf::StampedTransform > transformationCache
Definition: camera.h:147
double const STATUS_INTERVAL
Definition: camera.cpp:24
ensenso_camera_msgs::ParameterPtr readParameter(std::string const &key) const
Definition: camera.cpp:1676
std::string currentParameterSet
Definition: camera.h:145
sensor_msgs::CameraInfoPtr rightRectifiedCameraInfo
Definition: camera.h:113
ParameterType type
Definition: parameters.h:25
#define ROS_ERROR(...)
std::unique_ptr< ProjectPatternServer > projectPatternServer
Definition: camera.h:124
std::unique_ptr< CalibrateHandEyeServer > calibrateHandEyeServer
Definition: camera.h:125
bool loadSettings(std::string const &jsonFile, bool saveAsDefaultParameters=false)
Definition: camera.cpp:294
#define FINISH_NXLIB_ACTION(ACTION_NAME)
Definition: camera.cpp:80
std::string const TARGET_FRAME_LINK
Definition: camera.cpp:40
#define ROS_DEBUG(...)
void close()
Definition: camera.cpp:255
ros::Time capture() const
Definition: camera.cpp:1383
image_transport::CameraPublisher depthImagePublisher
Definition: camera.h:134


ensenso_camera
Author(s): Ensenso
autogenerated on Thu May 16 2019 02:44:23