Main MRPT website > C++ reference for MRPT 1.9.9
test.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 /*
11  Example : kinect-online-offline-demo
12  Web page :
13  http://www.mrpt.org/Switching_between_reading_live_Kinect_RGBD_dataset_for_debugging
14 
15  Purpose : Demonstrate how to programmatically switch between online Kinect
16  grabbing and offline parsing a Rawlog dataset. Refer to the launch
17  of the grabbing thread in Test_KinectOnlineOffline()
18 */
19 
20 #include <mrpt/hwdrivers/CKinect.h>
21 #include <mrpt/gui.h>
25 #include <mrpt/obs/CRawlog.h>
28 #include <mrpt/opengl/CFrustum.h>
31 #include <mrpt/system/CTicTac.h>
32 #include <mrpt/img/TColor.h>
34 #include <memory>
35 #include <iostream>
36 
37 // Demonstrate MRPT RGB+D --> PCL point cloud conversion:
38 #if MRPT_HAS_PCL
39 #include <mrpt/maps/PCL_adapters.h>
40 #endif
41 
42 using namespace mrpt;
43 using namespace mrpt::hwdrivers;
44 using namespace mrpt::gui;
45 using namespace mrpt::obs;
46 using namespace mrpt::system;
47 using namespace mrpt::img;
48 using namespace mrpt::serialization;
49 using namespace mrpt::io;
50 using namespace std;
51 
52 // Thread for online/offline capturing: This should be done in another thread
53 // specially in the online mode, but also in offline to emulate the
54 // possibility
55 // of losing frames if our program doesn't process them quickly.
56 struct TThreadParam
57 {
59  bool _is_online, const string& _rawlog_file = string(),
60  bool _generate_3D_pointcloud_in_this_thread = false)
61  : is_online(_is_online),
62  rawlog_file(_rawlog_file),
63  generate_3D_pointcloud_in_this_thread(
64  _generate_3D_pointcloud_in_this_thread),
65  quit(false),
66  Hz(0)
67  {
68  }
69 
70  /** true: live grabbing from the sensor, false: from rawlog */
71  const bool is_online;
72  /** Only when is_online==false */
73  const string rawlog_file;
74  /** true: populate the 3D point fields in the output observation; false:
75  * only RGB and Depth images. */
76  const bool generate_3D_pointcloud_in_this_thread;
77 
78  /** In/Out variable: Forces the thread to exit or indicates an error in the
79  * thread that caused it to end. */
80  volatile bool quit;
81  /** Out variable: Approx. capturing rate from the thread. */
82  volatile double Hz;
83 
84  /** RGB+D (+ optionally, 3D point cloud) */
86 };
87 
88 // Only for offline operation:
89 // Uncoment to force the simulated sensor to run at this given rate.
90 // If commented out, the simulated sensor will reproduce the real rate
91 // as indicated by timestamps in the dataset.
92 //#define FAKE_KINECT_FPS_RATE 10 // In Hz
93 
94 // ----------------------------------------------------
95 // The online/offline grabbing thread
96 // ----------------------------------------------------
98 {
99  try
100  {
101  typedef std::unique_ptr<CKinect>
102  CKinectPtr; // This assures automatic destruction
103 
104  // Only one of these will be actually used:
105  CKinectPtr kinect;
106  CFileGZInputStream dataset;
107 
108  mrpt::system::TTimeStamp dataset_prev_tim = INVALID_TIMESTAMP;
109  mrpt::system::TTimeStamp my_last_read_obs_tim = INVALID_TIMESTAMP;
110 
111  if (p.is_online)
112  {
113  // Online:
114  // ---------------------
115  kinect.reset(new CKinect());
116 
117  // Set params:
118  kinect->enableGrabRGB(true);
119  kinect->enableGrabDepth(true);
120  kinect->enableGrabAccelerometers(false);
121  kinect->enableGrab3DPoints(p.generate_3D_pointcloud_in_this_thread);
122 
123  // Open:
124  cout << "Calling CKinect::initialize()...";
125  kinect->initialize();
126  cout << "OK\n";
127  }
128  else
129  {
130  // Offline:
131  // ---------------------
132  if (!dataset.open(p.rawlog_file))
133  throw std::runtime_error(
134  "Couldn't open rawlog dataset file for input...");
135 
136  // Set external images directory:
137  CImage::setImagesPathBase(
138  CRawlog::detectImagesDirectory(p.rawlog_file));
139  }
140 
141  CTicTac tictac;
142  int nImgs = 0;
143 
144  while (!p.quit)
145  {
146  if (p.is_online)
147  {
148  // Grab new observation from the camera:
149  bool there_is_obs = true, hard_error = false;
150 
152  CObservation3DRangeScan>(); // Smart pointers to
153  // observations. Memory pooling
154  // is used internally
155  kinect->getNextObservation(*obs, there_is_obs, hard_error);
156 
157  if (hard_error)
158  throw std::runtime_error(
159  "Sensor returned 'hardware error'");
160  else if (there_is_obs)
161  {
162  // Send object to the main thread:
163  std::atomic_store(&p.new_obs, obs);
164  }
165  }
166  else
167  {
168  // Offline:
169  CSerializable::Ptr obs;
170  do
171  {
172  try
173  {
174  archiveFrom(dataset) >> obs;
175  }
176  catch (std::exception& e)
177  {
178  throw std::runtime_error(
179  string(
180  "\nError reading from dataset file (EOF?):\n") +
181  string(e.what()));
182  }
183  ASSERT_(obs);
184  } while (!IS_CLASS(obs, CObservation3DRangeScan));
185 
186  // We have one observation:
188  std::dynamic_pointer_cast<CObservation3DRangeScan>(obs);
189  obs3D->load(); // *Important* This is needed to load the range
190  // image if stored as a separate file.
191 
192  // Do we have to wait to emulate real-time behavior?
193  const mrpt::system::TTimeStamp cur_tim = obs3D->timestamp;
195 
196  if (dataset_prev_tim != INVALID_TIMESTAMP &&
197  my_last_read_obs_tim != INVALID_TIMESTAMP)
198  {
199 #ifndef FAKE_KINECT_FPS_RATE
200  const double At_dataset =
201  mrpt::system::timeDifference(dataset_prev_tim, cur_tim);
202 #else
203  const double At_dataset = 1.0 / FAKE_KINECT_FPS_RATE;
204 #endif
205  const double At_actual = mrpt::system::timeDifference(
206  my_last_read_obs_tim, now_tim);
207 
208  const double need_to_wait_ms =
209  1000. * (At_dataset - At_actual);
210  // cout << "[Kinect grab thread] Need to wait (ms): " <<
211  // need_to_wait_ms << endl;
212  if (need_to_wait_ms > 0)
213  std::this_thread::sleep_for(
214  std::chrono::duration<double, std::milli>(
215  need_to_wait_ms));
216  }
217 
218  // Send observation to main thread:
219  std::atomic_store(&p.new_obs, obs3D);
220 
221  dataset_prev_tim = cur_tim;
222  my_last_read_obs_tim = mrpt::system::now();
223  }
224 
225  // Update Hz rate estimate
226  nImgs++;
227  if (nImgs > 10)
228  {
229  p.Hz = nImgs / tictac.Tac();
230  nImgs = 0;
231  tictac.Tic();
232  }
233  }
234  }
235  catch (std::exception& e)
236  {
237  cout << "Exception in Kinect thread: " << e.what() << endl;
238  p.quit = true;
239  }
240 }
241 
242 // ------------------------------------------------------
243 // Test_KinectOnlineOffline
244 // ------------------------------------------------------
246  bool is_online, const string& rawlog_file = string())
247 {
248  // Launch grabbing thread:
249  // --------------------------------------------------------
250  TThreadParam thrPar(
251  is_online, rawlog_file,
252  false // generate_3D_pointcloud_in_this_thread -> Don't, we'll do it in
253  // this main thread.
254  );
255 
256  std::thread thHandle(thread_grabbing, std::ref(thrPar));
257 
258  // Wait until data stream starts so we can say for sure the sensor has been
259  // initialized OK:
260  cout << "Waiting for sensor initialization...\n";
261  do
262  {
263  CObservation3DRangeScan::Ptr newObs = std::atomic_load(&thrPar.new_obs);
264  if (newObs && newObs->timestamp != INVALID_TIMESTAMP)
265  break;
266  else
267  std::this_thread::sleep_for(10ms);
268  } while (!thrPar.quit);
269 
270  // Check error condition:
271  if (thrPar.quit) return;
272  cout << "OK! Sensor started to emit observations.\n";
273 
274  // Create window and prepare OpenGL object in the scene:
275  // --------------------------------------------------------
276  mrpt::gui::CDisplayWindow3D win3D("Kinect 3D view", 800, 600);
277 
278  win3D.setCameraAzimuthDeg(140);
279  win3D.setCameraElevationDeg(20);
280  win3D.setCameraZoom(8.0);
281  win3D.setFOV(90);
282  win3D.setCameraPointingToPoint(2.5, 0, 0);
283 
285  mrpt::make_aligned_shared<mrpt::opengl::CPointCloudColoured>();
286  gl_points->setPointSize(2.5);
287 
289  viewInt; // Extra viewports for the RGB images.
290  {
291  mrpt::opengl::COpenGLScene::Ptr& scene = win3D.get3DSceneAndLock();
292 
293  // Create the Opengl object for the point cloud:
294  scene->insert(gl_points);
295  scene->insert(mrpt::make_aligned_shared<mrpt::opengl::CGridPlaneXY>());
297 
298  const double aspect_ratio = 480.0 / 640.0;
299  const int VW_WIDTH =
300  400; // Size of the viewport into the window, in pixel units.
301  const int VW_HEIGHT = aspect_ratio * VW_WIDTH;
302 
303  // Create an extra opengl viewport for the RGB image:
304  viewInt = scene->createViewport("view2d_int");
305  viewInt->setViewportPosition(5, 30, VW_WIDTH, VW_HEIGHT);
306  win3D.addTextMessage(
307  10, 30 + VW_HEIGHT + 10, "Intensity data", TColorf(1, 1, 1), 2,
309 
310  win3D.addTextMessage(
311  5, 5, format("'o'/'i'-zoom out/in, ESC: quit"), TColorf(0, 0, 1),
313 
314  win3D.unlockAccess3DScene();
315  win3D.repaint();
316  }
317 
319 
320  while (win3D.isOpen() && !thrPar.quit)
321  {
322  CObservation3DRangeScan::Ptr newObs = std::atomic_load(&thrPar.new_obs);
323  if (newObs && newObs->timestamp != INVALID_TIMESTAMP &&
324  newObs->timestamp != last_obs_tim)
325  {
326  // It IS a new observation:
327  last_obs_tim = newObs->timestamp;
328 
329  // Update visualization ---------------------------------------
330 
331  win3D.get3DSceneAndLock();
332 
333  // Estimated grabbing rate:
334  win3D.addTextMessage(
335  -350, -13,
336  format(
337  "Timestamp: %s",
338  mrpt::system::dateTimeLocalToString(last_obs_tim).c_str()),
339  TColorf(0.6, 0.6, 0.6), "mono", 10, mrpt::opengl::FILL, 100);
340  win3D.addTextMessage(
341  -100, -30, format("%.02f Hz", thrPar.Hz), TColorf(1, 1, 1),
342  "mono", 10, mrpt::opengl::FILL, 101);
343 
344  // Show intensity image:
345  if (newObs->hasIntensityImage)
346  {
347  viewInt->setImageView(newObs->intensityImage); // This is not
348  // "_fast" since
349  // the intensity
350  // image may be
351  // needed later
352  // on.
353  }
354  win3D.unlockAccess3DScene();
355 
356  // -------------------------------------------------------
357  // Create 3D points from RGB+D data
358  //
359  // There are several methods to do this.
360  // Switch the #if's to select among the options:
361  // See also:
362  // http://www.mrpt.org/Generating_3D_point_clouds_from_RGB_D_observations
363  // -------------------------------------------------------
364  if (newObs->hasRangeImage)
365  {
366  static mrpt::system::CTimeLogger logger;
367  logger.enter("RGBD->3D");
368 
369 // Pathway: RGB+D --> PCL <PointXYZ> --> XYZ opengl
370 #if 0
371  static pcl::PointCloud<pcl::PointXYZ> cloud;
372  logger.enter("RGBD->3D.projectInto");
373  newObs->project3DPointsFromDepthImageInto(cloud, false /* without obs.sensorPose */);
374  logger.leave("RGBD->3D.projectInto");
375 
376  win3D.get3DSceneAndLock();
377  logger.enter("RGBD->3D.load in OpenGL");
378  gl_points->loadFromPointsMap(&cloud);
379  logger.leave("RGBD->3D.load in OpenGL");
380  win3D.unlockAccess3DScene();
381 #endif
382 
383 // Pathway: RGB+D --> PCL <PointXYZRGB> --> XYZ+RGB opengl
384 #if 0
385  static pcl::PointCloud<pcl::PointXYZRGB> cloud;
386  logger.enter("RGBD->3D.projectInto");
387  newObs->project3DPointsFromDepthImageInto(cloud, false /* without obs.sensorPose */);
388  logger.leave("RGBD->3D.projectInto");
389 
390  win3D.get3DSceneAndLock();
391  logger.enter("RGBD->3D.load in OpenGL");
392  gl_points->loadFromPointsMap(&cloud);
393  logger.leave("RGBD->3D.load in OpenGL");
394  win3D.unlockAccess3DScene();
395 #endif
396 
397 // Pathway: RGB+D --> XYZ+RGB opengl
398 #if 1
399  win3D.get3DSceneAndLock();
400  logger.enter("RGBD->3D.projectInto");
403 
404  newObs->project3DPointsFromDepthImageInto(*gl_points, pp);
405 
406  logger.leave("RGBD->3D.projectInto");
407  win3D.unlockAccess3DScene();
408 #endif
409 
410 // Pathway: RGB+D --> XYZ+RGB opengl (With a 6D global pose of the robot)
411 #if 0
412  const CPose3D globalPose(1,2,3,DEG2RAD(10),DEG2RAD(20),DEG2RAD(30));
413  win3D.get3DSceneAndLock();
414  logger.enter("RGBD->3D.projectInto");
415  newObs->project3DPointsFromDepthImageInto(*gl_points, false /* without obs.sensorPose */, &globalPose);
416  logger.leave("RGBD->3D.projectInto");
417  win3D.unlockAccess3DScene();
418 #endif
419 
420 // Pathway: RGB+D --> internal local XYZ pointcloud --> XYZ+RGB point cloud map
421 // --> XYZ+RGB opengl
422 #if 0
423  // Project 3D points:
424  if (!newObs->hasPoints3D)
425  {
426  logger.enter("RGBD->3D.projectInto");
427  newObs->project3DPointsFromDepthImage();
428  logger.leave("RGBD->3D.projectInto");
429  }
430 
431  CColouredPointsMap pntsMap;
432  pntsMap.colorScheme.scheme = CColouredPointsMap::cmFromIntensityImage;
433  pntsMap.loadFromRangeScan(*newObs);
434 
435  win3D.get3DSceneAndLock();
436  logger.enter("RGBD->3D.load in OpenGL");
437  gl_points->loadFromPointsMap(&pntsMap);
438  logger.leave("RGBD->3D.load in OpenGL");
439  win3D.unlockAccess3DScene();
440 #endif
441 
442  logger.leave("RGBD->3D");
443  }
444 
445  win3D.repaint();
446  } // end update visualization:
447 
448  // Process possible keyboard commands:
449  // --------------------------------------
450  if (win3D.keyHit())
451  {
452  const int key = tolower(win3D.getPushedKey());
453 
454  switch (key)
455  {
456  // Some of the keys are processed in this thread:
457  case 'o':
458  win3D.setCameraZoom(win3D.getCameraZoom() * 1.2);
459  win3D.repaint();
460  break;
461  case 'i':
462  win3D.setCameraZoom(win3D.getCameraZoom() / 1.2);
463  win3D.repaint();
464  break;
465  case 27: // ESC
466  thrPar.quit = true;
467  break;
468  default:
469  break;
470  };
471  }
472 
473  std::this_thread::sleep_for(1ms);
474  }
475 
476  cout << "Waiting for grabbing thread to exit...\n";
477  thrPar.quit = true;
478  thHandle.join();
479  cout << "Bye!\n";
480 }
481 
482 int main(int argc, char** argv)
483 {
484  try
485  {
486  // Determine online/offline operation:
487  if (argc != 1 && argc != 2)
488  {
489  cerr << "Usage:\n"
490  << argv[0] << " --> Online grab from sensor\n"
491  << argv[0] << " [DATASET.rawlog] --> Offline from dataset\n";
492  return 1;
493  }
494 
495  if (argc == 1)
496  {
497  // Online
498  cout << "Using online operation" << endl;
500  }
501  else
502  {
503  // Offline:
504  cout << "Using offline operation with: " << argv[1] << endl;
505  Test_KinectOnlineOffline(false, string(argv[1]));
506  }
507 
508  std::this_thread::sleep_for(50ms);
509  return 0;
510  }
511  catch (std::exception& e)
512  {
513  std::cout << "EXCEPCION: " << e.what() << std::endl;
514  return -1;
515  }
516  catch (...)
517  {
518  printf("Another exception!!");
519  return -1;
520  }
521 }
mrpt::system::timeDifference
double timeDifference(const mrpt::system::TTimeStamp t_first, const mrpt::system::TTimeStamp t_later)
Returns the time difference from t1 to t2 (positive if t2 is posterior to t1), in seconds.
Definition: datetime.cpp:209
mrpt::system::dateTimeLocalToString
std::string dateTimeLocalToString(const mrpt::system::TTimeStamp t)
Convert a timestamp into this textual form (in local time): YEAR/MONTH/DAY,HH:MM:SS....
Definition: datetime.cpp:270
mrpt::io
Definition: img/CImage.h:22
mrpt::obs::T3DPointsProjectionParams
Used in CObservation3DRangeScan::project3DPointsFromDepthImageInto()
Definition: CObservation3DRangeScan.h:30
mrpt::serialization::CSerializable::Ptr
std::shared_ptr< CSerializable > Ptr
Definition: CSerializable.h:37
mrpt::system::CTicTac
A high-performance stopwatch, with typical resolution of nanoseconds.
Definition: system/CTicTac.h:19
mrpt::system::CTimeLogger
A versatile "profiler" that logs the time spent within each pair of calls to enter(X)-leave(X),...
Definition: system/CTimeLogger.h:43
INVALID_TIMESTAMP
#define INVALID_TIMESTAMP
Represents an invalid timestamp, where applicable.
Definition: datetime.h:15
CPointCloudColoured.h
mrpt::system::now
mrpt::system::TTimeStamp now()
A shortcut for system::getCurrentTime.
Definition: datetime.h:75
mrpt::make_aligned_shared
std::shared_ptr< T > make_aligned_shared(Args &&... args)
Creates a shared_ptr with aligned memory via aligned_allocator_cpp11<>.
Definition: aligned_allocator.h:78
mrpt::hwdrivers
Contains classes for various device interfaces.
Definition: C2DRangeFinderAbstract.h:22
mrpt
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Definition: CKalmanFilterCapable.h:30
ASSERT_
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
stock_objects.h
p
GLfloat GLfloat p
Definition: glext.h:6305
mrpt::opengl::CPointCloudColoured::Ptr
std::shared_ptr< CPointCloudColoured > Ptr
Definition: CPointCloudColoured.h:49
TThreadParam
Definition: vision_stereo_rectify/test.cpp:56
mrpt::opengl::MRPT_GLUT_BITMAP_HELVETICA_12
@ MRPT_GLUT_BITMAP_HELVETICA_12
Definition: opengl_fonts.h:31
mrpt::system::CTimeLogger::enter
void enter(const char *func_name)
Start of a named section.
Definition: system/CTimeLogger.h:116
mrpt::obs
This namespace contains representation of robot actions and observations.
Definition: CParticleFilter.h:17
mrpt::system::CTimeLogger::leave
double leave(const char *func_name)
End of a named section.
Definition: system/CTimeLogger.h:122
mrpt::opengl::MRPT_GLUT_BITMAP_HELVETICA_18
@ MRPT_GLUT_BITMAP_HELVETICA_18
Definition: opengl_fonts.h:32
mrpt::system::TTimeStamp
uint64_t TTimeStamp
A system independent time type, it holds the the number of 100-nanosecond intervals since January 1,...
Definition: datetime.h:31
mrpt::system::CTicTac::Tac
double Tac() noexcept
Stops the stopwatch.
Definition: CTicTac.cpp:90
main
int main()
Definition: vision_stereo_rectify/test.cpp:78
mrpt::io::CFileGZInputStream::open
bool open(const std::string &fileName)
Opens the file for read.
Definition: CFileGZInputStream.cpp:36
mrpt::img
Definition: CCanvas.h:17
mrpt::hwdrivers::CKinect
A class for grabing "range images", intensity images (either RGB or IR) and other information from an...
Definition: CKinect.h:268
mrpt::io::CFileGZInputStream
Transparently opens a compressed "gz" file and reads uncompressed data from it.
Definition: io/CFileGZInputStream.h:26
mrpt::obs::CObservation3DRangeScan
Declares a class derived from "CObservation" that encapsules a 3D range scan measurement,...
Definition: CObservation3DRangeScan.h:224
mrpt::format
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
mrpt::img::TColorf
A RGB color - floats in the range [0,1].
Definition: TColor.h:79
IS_CLASS
#define IS_CLASS(ptrObj, class_name)
Evaluates to true if the given pointer to an object (derived from mrpt::rtti::CObject) is of the give...
Definition: CObject.h:103
Test_KinectOnlineOffline
void Test_KinectOnlineOffline(bool is_online, const string &rawlog_file=string())
Definition: vision_stereo_rectify/test.cpp:245
mrpt::system::CTicTac::Tic
void Tic() noexcept
Starts the stopwatch.
Definition: CTicTac.cpp:79
mrpt::serialization
Definition: aligned_serialization.h:14
mrpt::gui
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:16
mrpt::opengl::FILL
@ FILL
renders glyphs as filled polygons
Definition: opengl_fonts.h:38
mrpt::serialization::archiveFrom
CArchiveStreamBase< STREAM > archiveFrom(STREAM &s)
Helper function to create a templatized wrapper CArchive object for a: MRPT's CStream,...
Definition: CArchive.h:561
mrpt::opengl::stock_objects::CornerXYZ
CSetOfObjects::Ptr CornerXYZ(float scale=1.0)
Returns three arrows representing a X,Y,Z 3D corner.
Definition: StockObjects.cpp:209
CTicTac.h
CTimeLogger.h
CKinect.h
thread_grabbing
void thread_grabbing(TThreadParam &p)
Definition: vision_stereo_rectify/test.cpp:97
mrpt::obs::CObservation3DRangeScan::Ptr
std::shared_ptr< CObservation3DRangeScan > Ptr
Definition: CObservation3DRangeScan.h:226
CFileGZInputStream.h
CFrustum.h
gui.h
ref
GLenum GLint ref
Definition: glext.h:4050
CColouredPointsMap.h
mrpt::opengl::COpenGLScene::Ptr
std::shared_ptr< COpenGLScene > Ptr
Definition: COpenGLScene.h:61
CPlanarLaserScan.h
CArchive.h
mrpt::obs::T3DPointsProjectionParams::takeIntoAccountSensorPoseOnRobot
bool takeIntoAccountSensorPoseOnRobot
(Default: false) If false, local (sensor-centric) coordinates of points are generated.
Definition: CObservation3DRangeScan.h:36
CRawlog.h
PCL_adapters.h
CGridPlaneXY.h
mrpt::opengl::COpenGLViewport::Ptr
std::shared_ptr< COpenGLViewport > Ptr
Definition: COpenGLViewport.h:63
mrpt::gui::CDisplayWindow3D
A graphical user interface (GUI) for efficiently rendering 3D scenes in real-time.
Definition: CDisplayWindow3D.h:117
TColor.h
mrpt::system
This namespace provides a OS-independent interface to many useful functions: filenames manipulation,...
Definition: math_frwds.h:25
mrpt::DEG2RAD
double DEG2RAD(const double x)
Degrees to radians.
Definition: core/include/mrpt/core/bits_math.h:42



Page generated by Doxygen 1.8.17 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at miƩ 12 jul 2023 10:03:34 CEST