Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
SickLDMRS-Process.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Sick LD-MRS laser driver.
33  *
34 *****************************************************************************/
35 
57 #include <visp3/core/vpConfig.h>
58 #include <visp3/core/vpDebug.h>
59 #include <visp3/core/vpDisplay.h>
60 #include <visp3/core/vpImage.h>
61 #include <visp3/core/vpImagePoint.h>
62 #include <visp3/io/vpImageIo.h>
63 #include <visp3/sensor/vpSickLDMRS.h>
64 #ifdef VISP_HAVE_MODULE_GUI
65 #include <visp3/gui/vpDisplayGTK.h>
66 #include <visp3/gui/vpDisplayX.h>
67 #endif
68 #include <visp3/core/vpIoTools.h>
69 #include <visp3/io/vpParseArgv.h>
70 #include <visp3/sensor/vp1394TwoGrabber.h>
71 
72 #if (!defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))) && \
73  (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK)) && defined(VISP_HAVE_THREADS)
74 
75 #include <thread>
76 #include <mutex>
77 
78 #ifdef ENABLE_VISP_NAMESPACE
79 using namespace VISP_NAMESPACE_NAME;
80 #endif
81 
82 static int save = 0;
83 static int layerToDisplay = 0xF; // 0xF = 1111 => all the layers are selected
84 static vpLaserScan shm_laserscan[4];
85 double time_offset = 0;
86 std::mutex shm_mutex;
87 std::string output_path;
88 
89 void laser_display_and_save_loop()
90 {
91  vpImage<unsigned char> map(700, 300);
92  map = 0;
93  unsigned int width = map.getWidth();
94  unsigned int height = map.getHeight();
95  vpImagePoint O; // Beam origin
96  O.set_i(height);
97  O.set_j(width / 2.);
98  vpScanPoint p;
99  vpColor color[4]; // one color per layer
100  char filename[FILENAME_MAX];
101  std::ofstream fdscan;
102  vpLaserScan laserscan[4];
103 
104  for (int layer = 0; layer < 4; layer++) {
105  switch (layer) {
106  case 0:
107  color[layer] = vpColor::red;
108  break;
109  case 1:
110  color[layer] = vpColor::green;
111  break;
112  case 2:
113  color[layer] = vpColor::blue;
114  break;
115  case 3:
116  color[layer] = vpColor::yellow;
117  break;
118  }
119  }
120 
121  vpDisplay *display = nullptr;
122 #ifdef VISP_HAVE_MODULE_GUI
123 #if defined(VISP_HAVE_X11)
124  display = new vpDisplayX;
125 #elif defined(VISP_HAVE_GTK)
126  display = new vpDisplayGTK;
127 #endif
128  display->init(map, 10, 10, "Laser scan");
129 #endif
130 
131  unsigned int iter = 0;
132  for (;;) {
133 
134 #if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK))
135  vpDisplay::display(map);
136 #endif
137 
138  shm_mutex.lock();
139  for (int layer = 0; layer < 4; layer++) {
140  laserscan[layer] = shm_laserscan[layer];
141  }
142  shm_mutex.unlock();
143 
144  // Parse the four layers
145  for (int layer = 0; layer < 4; layer++) {
146  if (!((0x1 << layer) & layerToDisplay)) {
147  std::cout << "Layer " << layer + 1 << " is not displayed" << std::endl;
148  continue;
149  }
150 
151  std::vector<vpScanPoint> pointsLayer = laserscan[layer].getScanPoints();
152 
153  if (save) {
154  // Set the scan data filename to store the measures
155  snprintf(filename, FILENAME_MAX, "%s/scan%04u-layer%d.txt", output_path.c_str(), iter, layer + 1);
156  fdscan.open(filename);
157 
158  // Write the file header
159  fdscan << "# Scan layer [1 to 4] : " << layer + 1 << std::endl
160  << "# Start timestamp (s) : " << laserscan[layer].getStartTimestamp() - time_offset << std::endl
161  << "# End timestamp (s) : " << laserscan[layer].getEndTimestamp() - time_offset << std::endl
162  << "# Data : \"radial distance (m)\" \"horizontal angle "
163  "(rad)\" \"vertical angle (rad)\" \"X (m)\" \"Y (m)\" \"Z "
164  "(m)\""
165  << std::endl;
166  }
167 
168  vpImagePoint E; // Beam echo
169  double resolution = 5; // 100 pixels = 1 meter - increase this value to
170  // see better near info
171  for (unsigned int i = 0; i < pointsLayer.size(); i++) {
172  p = pointsLayer[i];
173  E.set_i(height - resolution * p.getRadialDist() * cos(p.getHAngle()));
174  E.set_j(width / 2. - resolution * p.getRadialDist() * sin(p.getHAngle()));
175 // std::cout << "E: " << E << std::endl;
176 #if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK))
177  vpDisplay::displayLine(map, O, E, color[layer]);
178 #endif
179  if (save) {
180  // Save the measures in the file
181  fdscan << p << std::endl;
182  }
183  }
184  if (save) {
185  fdscan.close();
186  }
187  }
188 #if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK))
189  vpDisplay::flush(map);
190 #endif
191  iter++;
192  // std::cout << "display time: " << vpTime::measureTimeMs() - t1 <<
193  // std::endl;
194  }
195  delete display;
196 }
197 
198 void laser_acq_loop()
199 {
200  std::string ip = "131.254.12.119";
201 
202  vpSickLDMRS laser;
203  laser.setIpAddress(ip);
204  laser.setup();
205  vpLaserScan laserscan[4];
206 
207  for (;;) {
208  double t1 = vpTime::measureTimeMs();
209  if (laser.measure(laserscan) == false)
210  continue;
211 
212  shm_mutex.lock();
213  for (int layer = 0; layer < 4; layer++) {
214  shm_laserscan[layer] = laserscan[layer];
215  }
216  shm_mutex.unlock();
217 
218  std::cout << "laser acq time: " << vpTime::measureTimeMs() - t1 << std::endl;
219  }
220 }
221 
222 void camera_acq_and_display_loop()
223 {
224 #ifdef VISP_HAVE_DC1394
225  try {
226  // Initialize the firewire framegrabber
227  vp1394TwoGrabber g; // Create a grabber based on libdc1394-2.x third party lib
228 
229  // If no camera found return
230  if (g.getNumCameras() == 0) {
231  return;
232  }
233 
234  // g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8);
235  // g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_60);
236 
237  vpImage<unsigned char> I; // Create a gray level image container
238  vpImage<unsigned char> Q; // Create a quarter size gray level image container
239  g.acquire(I); // Acquire an image
240  I.quarterSizeImage(Q);
241 
242  vpDisplay *display = nullptr;
243 #ifdef VISP_HAVE_MODULE_GUI
244 #if defined(VISP_HAVE_X11)
245  display = new vpDisplayX;
246 #elif defined(VISP_HAVE_GTK)
247  display = new vpDisplayGTK;
248 #endif
249  display->init(Q, 320, 10, "Camera");
250 #endif
251 
252  // Create a file with image time stamps
253  std::ofstream fdimage_ts;
254  if (save) {
255  std::string filename = output_path + "/image_timestamp.txt";
256  fdimage_ts.open(filename.c_str());
257  fdimage_ts << "# [image name] [time stamp in second]" << std::endl;
258  }
259  unsigned iter = 0;
260  char filename[FILENAME_MAX];
261  uint64_t timestamp;
262  uint32_t id;
263  for (;;) {
264  dc1394video_frame_t *frame = g.dequeue(I, timestamp, id); // Acquire an image
265  I.quarterSizeImage(Q);
266  double image_timestamp = timestamp / 1000000. - time_offset;
267  std::cout << "camera timestamp: " << image_timestamp << " s " << std::endl;
268  if (save) {
269  // Set the image filename
270  snprintf(filename, FILENAME_MAX, "%s/image%04u.png", output_path.c_str(), iter);
271  vpImageIo::write(Q, filename);
272  fdimage_ts << filename << " " << image_timestamp << std::endl;
273  }
274 #if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK))
276  vpDisplay::flush(Q);
277 #endif
278  g.enqueue(frame);
279 
280  iter++;
281  }
282  delete display;
283  if (save) {
284  fdimage_ts.close();
285  }
286  }
287  catch (...) {
288  }
289 #endif
290 }
291 
292 int main(int argc, const char **argv)
293 {
294  try {
295  output_path = "data";
296  // Test if the output path directory exist. If no try to create it
297  if (vpIoTools::checkDirectory(output_path) == false) {
298  try {
299  // Create a directory with name "username"
300  vpIoTools::makeDirectory(output_path);
301  }
302  catch (...) {
303  std::cout << "Cannot create " << output_path << " directory" << std::endl;
304  return EXIT_FAILURE;
305  }
306  }
307 
308  // Parse the command line to set the variables
309  vpParseArgv::vpArgvInfo argTable[] = {
310  {"-layer", vpParseArgv::ARGV_INT, (char *)nullptr, (char *)&layerToDisplay,
311  "The layer to display:\n"
312  "\t\t. 0x1 for layer 1.\n"
313  "\t\t. 0x2 for layer 2.\n"
314  "\t\t. 0x4 for layer 3.\n"
315  "\t\t. 0x8 for layer 4.\n"
316  "\t\tTo display all the layers you should set 0xF value."},
317  {"-save", vpParseArgv::ARGV_INT, (char *)nullptr, (char *)&save, "Turn to 1 in order to save data."},
318  {"-h", vpParseArgv::ARGV_HELP, (char *)nullptr, (char *)nullptr,
319  "Display one or more measured layers form a Sick LD-MRS laser "
320  "scanner."},
321  {(char *)nullptr, vpParseArgv::ARGV_END, (char *)nullptr, (char *)nullptr, (char *)nullptr} };
322 
323  // Read the command line options
324  if (vpParseArgv::parse(&argc, argv, argTable,
327  return (EXIT_FAILURE);
328  }
329  time_offset = vpTime::measureTimeSecond();
330 
331  std::thread thread_camera_acq(&camera_acq_and_display_loop);
332  std::thread thread_laser_acq(&laser_acq_loop);
333  std::thread thread_laser_display(&laser_display_and_save_loop);
334  thread_camera_acq.join();
335  thread_laser_acq.join();
336  thread_laser_display.join();
337 
338  return EXIT_SUCCESS;
339  }
340  catch (const vpException &e) {
341  std::cout << "Catch an exception: " << e << std::endl;
342  return EXIT_FAILURE;
343  }
344 }
345 
346 #elif !(defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK))
347 int main()
348 {
349  std::cout << "You do not have X11, or GTK functionalities to display images..." << std::endl;
350  std::cout << "Tip if you are on a unix-like system:" << std::endl;
351  std::cout << "- Install X11, configure again ViSP using cmake and build again this example" << std::endl;
352  std::cout << "Tip if you are on a windows-like system:" << std::endl;
353  std::cout << "- Install GTK, configure again ViSP using cmake and build again this example" << std::endl;
354  return EXIT_SUCCESS;
355 }
356 #else // #ifdef UNIX and display
357 
358 int main()
359 {
360  std::cout << "This example is only working on unix-like platforms \n"
361  << "since the Sick LD-MRS driver was not ported to Windows." << std::endl;
362  return EXIT_SUCCESS;
363  }
364 
365 #endif // #ifdef UNIX
Class for firewire ieee1394 video devices using libdc1394-2.x api.
void acquire(vpImage< unsigned char > &I)
void enqueue(dc1394video_frame_t *frame)
dc1394video_frame_t * dequeue()
void getNumCameras(unsigned int &ncameras) const
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static const vpColor red
Definition: vpColor.h:217
static const vpColor blue
Definition: vpColor.h:223
static const vpColor yellow
Definition: vpColor.h:225
static const vpColor green
Definition: vpColor.h:220
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:133
Class that defines generic functionalities for display.
Definition: vpDisplay.h:178
static void display(const vpImage< unsigned char > &I)
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void flush(const vpImage< unsigned char > &I)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:291
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
void set_j(double jj)
Definition: vpImagePoint.h:309
void set_i(double ii)
Definition: vpImagePoint.h:298
void quarterSizeImage(vpImage< Type > &res) const
Definition: vpImage.h:791
static bool checkDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:396
static void makeDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:550
Implements a laser scan data structure that contains especially the list of scanned points that have ...
Definition: vpLaserScan.h:65
double getStartTimestamp()
Definition: vpLaserScan.h:114
std::vector< vpScanPoint > getScanPoints()
Definition: vpLaserScan.h:93
double getEndTimestamp()
Definition: vpLaserScan.h:116
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:70
@ ARGV_NO_DEFAULTS
No default options like -help.
Definition: vpParseArgv.h:172
@ ARGV_NO_LEFTOVERS
Print an error message if an option is not in the argument list.
Definition: vpParseArgv.h:173
@ ARGV_INT
Argument is associated to an int.
Definition: vpParseArgv.h:155
@ ARGV_END
End of the argument list.
Definition: vpParseArgv.h:164
@ ARGV_HELP
Argument is for help displaying.
Definition: vpParseArgv.h:163
Class that defines a single laser scanner point.
Definition: vpScanPoint.h:76
double getRadialDist() const
Definition: vpScanPoint.h:116
double getHAngle() const
Definition: vpScanPoint.h:124
VISP_EXPORT double measureTimeSecond()
VISP_EXPORT double measureTimeMs()