sick_lms400.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009 Radu Bogdan Rusu <rusu -=- cs.tum.edu>
3  * Code based on the LGPL Player SICK LMS400 driver by Nico Blodow and Radu Bogdan Rusu
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $Id$
29  *
30  */
31 
32 #include <sick_lms400.h>
33 #include <angles/angles.h>
34 
35 const int CMD_BUFFER_SIZE = 255;
36 
37 
39 // Constructor.
40 asr_sick_lms_400::asr_sick_lms_400::asr_sick_lms_400 (const char* host, int port, int debug_mode)
41 {
42  portno_ = port;
43  hostname_ = host;
44  verbose_ = debug_mode;
45  memset (command_, 0, BUF_SIZE);
46  MeasurementQueue_ = new std::vector<MeasurementQueueElement_t >;
47 }
48 
50 // Connect to the LMS400 unit using hostname:portno
51 // Returns 0 if connection was successful, -1 otherwise
52 int
54 {
55  // Create a socket
56  sockfd_ = socket (AF_INET, SOCK_STREAM, 0);
57  if (sockfd_ < 0)
58  return (-1);
59 
60  // Get the network host entry
61  memset (&serv_addr_, 0, sizeof (serv_addr_));
62  serv_addr_.sin_port = htons (portno_);
63  serv_addr_.sin_family = AF_INET;
64 #if defined (HAVE_GETADDRINFO)
65  addr_ptr = NULL;
66  if (getaddrinfo (hostname_, NULL, NULL, &(addr_ptr)))
67  {
68  ROS_ERROR ("getaddrinfo() failed with error");
69  return (-1);
70  }
71  assert (addr_ptr);
72  assert (addr_ptr->ai_addr);
73  if ((addr_ptr->ai_addr->sa_family) != AF_INET)
74  {
75  ROS_ERROR ("unsupported internet address family");
76  return (-1);
77  }
78  serv_addr.sin_addr.s_addr = (reinterpret_cast<struct sockaddr_in*> (addr_ptr->ai_addr))->sin_addr.s_addr;
79  freeaddrinfo (addr_ptr);
80  addr_ptr = NULL;
81 #else
82  server_ = gethostbyname (hostname_);
83  if ((server_) == NULL)
84  return (-1);
85  memcpy (&(serv_addr_.sin_addr.s_addr), server_->h_addr, server_->h_length);
86 #endif
87 
88  // Attempt to connect
89  if (connect (sockfd_, reinterpret_cast<struct sockaddr*> (&serv_addr_), sizeof (serv_addr_)) < 0)
90  return (-1);
91 
92  return (0);
93 }
94 
96 // Disconnect from the LMS400 unit
97 // Returns 0 if connection was successful, -1 otherwise
98 int
100 {
101  return (close (sockfd_));
102 }
103 
105 // Enable/Disable extended RIS (Remission Information System) detectivity
106 int
108 {
109  char cmd[CMD_BUFFER_SIZE];
110  snprintf (cmd, CMD_BUFFER_SIZE, "sWN MDblex %i", onoff);
111  SendCommand (cmd);
112 
113  if (ReadAnswer () != 0)
114  return (-1);
115  ExtendedRIS_ = onoff;
116  return (0);
117 }
118 
120 // Set the mean filter parameters
121 int
123 {
124  char cmd[CMD_BUFFER_SIZE];
125  snprintf (cmd, CMD_BUFFER_SIZE, "sWN FLmean 0 %i", num_scans);
126  SendCommand (cmd);
127 
128  if (ReadAnswer () != 0)
129  return (-1);
130  MeanFilterNumScans_ = num_scans;
131  return (0);
132 }
133 
135 // Set the range filter parameters
136 int
138 {
139  char cmd[CMD_BUFFER_SIZE];
140  snprintf (cmd, CMD_BUFFER_SIZE, "sWN FLrang %+f %+f", (float)range_min, (float)range_max);
141  SendCommand (cmd);
142 
143  if (ReadAnswer () != 0)
144  return (-1);
145  RangeFilterBottomLimit_ = range_min;
146  RangeFilterTopLimit_ = range_max;
147  return (0);
148 }
149 
151 // Enable filters using a filter mask
152 int
154 {
155  char cmd[CMD_BUFFER_SIZE];
156  snprintf (cmd, CMD_BUFFER_SIZE, "sWN FLsel %+i", filter_mask);
157  SendCommand (cmd);
158 
159  if (ReadAnswer () != 0)
160  return (-1);
161  FilterMask_ = filter_mask;
162  return (0);
163 }
164 
166 // Takes a string containing an ip adress and returns an array of 4 u_chars
167 unsigned char*
169 {
170  char* tmp = (char*) malloc (strlen (ip) + 1);
171  unsigned char* _ip = (unsigned char*) malloc (4);
172 
173  strcpy (tmp, ip);
174  _ip[0] = atoi (strtok (tmp, "."));
175  for (int i = 1; i < 4; i++)
176  _ip[i] = atoi (strtok (NULL, "."));
177 
178  free (tmp);
179  return (_ip);
180 }
181 
183 // Set the desired userlevel by logging in with the appropriate password
184 int
185  asr_sick_lms_400::asr_sick_lms_400::SetUserLevel (int8_t userlevel, const char* password)
186 {
187  char cmd[CMD_BUFFER_SIZE];
188  snprintf (cmd, CMD_BUFFER_SIZE, "sMN SetAccessMode %d %s", userlevel, password);
189  SendCommand (cmd);
190  return (ReadConfirmationAndAnswer ());
191 }
192 
194 // Fills string pointed to by macadress with the MAC adress read from the sensor
195 int
197 {
198  char *mac = (char*) malloc (20);
199  int index = 0;
200  char* tmp;
201 
202  SendCommand ("sRN EImac ");
203  if (ReadAnswer () != 0)
204  return (-1);
205 
206  strtok ((char*) buffer_, " ");
207  strtok (NULL, " ");
208 
209  for (int i = 0; i < 6; i++)
210  {
211  tmp = strtok (NULL, "-");
212  strncpy (&mac[index], tmp, 2);
213  index += 2;
214  mac[index++] = ':';
215  }
216 
217  mac[--index] = 0;
218  *macaddress = mac;
219  return (0);
220 }
221 
223 // Set the IP address of the LMS400
224 int
226 {
227  unsigned char* ip_str;
228  ip_str = ParseIP (ip);
229  char cmd[80];
230 
231  snprintf (cmd, 80, "sWN EIip %X %X %X %X", ip_str[0], ip_str[1], ip_str[2], ip_str[3]);
232  free (ip_str);
233  SendCommand (cmd);
234 
235  return (ReadAnswer ());
236 }
237 
239 // Set the gateway address for the Ethernet interface
240 int
242 {
243  unsigned char* gw_str;
244  gw_str = ParseIP (gw);
245  char cmd[CMD_BUFFER_SIZE];
246 
247  snprintf (cmd, CMD_BUFFER_SIZE, "sWN EIgate %X %X %X %X", gw_str[0], gw_str[1], gw_str[2], gw_str[3]);
248  free (gw_str);
249  SendCommand (cmd);
250 
251  return (ReadAnswer ());
252 }
253 
255 // Set the subnet mask for the Ethernet interface
256 int
258 {
259  unsigned char* mask_str;
260  mask_str = ParseIP (mask);
261  char cmd[CMD_BUFFER_SIZE];
262 
263  snprintf (cmd, CMD_BUFFER_SIZE, "sWN EImask %X %X %X %X", mask_str[0], mask_str[1], mask_str[2], mask_str[3]);
264  free (mask_str);
265  SendCommand (cmd);
266 
267  return (ReadAnswer ());
268 }
269 
271 // Set port for TCP/IP communication
272 int
274 {
275  char cmd[CMD_BUFFER_SIZE];
276 
277  snprintf (cmd,CMD_BUFFER_SIZE, "sWN EIport %04X", port);
278  SendCommand (cmd);
279 
280  return (ReadAnswer ());
281 }
282 
284 // Reset the LMS400 unit
285 int
287 {
288  const char* cmd = "sMN mDCreset ";
289  SendCommand (cmd);
290 
291  return (ReadAnswer ());
292 }
293 
295 // Terminate configuration and change back to userlevel 0
296 int
298 {
299  const char* cmd = "sMN Run";
300  SendCommand (cmd);
301 
302  return (ReadConfirmationAndAnswer ());
303 }
304 
306 // Set the laser angular resolution. Requires userlevel 2. Unused for now.
307 int
308  asr_sick_lms_400::asr_sick_lms_400::SetAngularResolution (const char* password, float ang_res,
309  float angle_start, float angle_range)
310 {
311  char cmd[CMD_BUFFER_SIZE];
312  snprintf (cmd, CMD_BUFFER_SIZE, "sMN mSCconfigbyang 04 %s %+f 01 %+f %+f",
313  password, ang_res, angle_start, angle_range);
314  SendCommand (cmd);
315 
316  return (ReadConfirmationAndAnswer ());
317 }
318 
320 // Set the laser scanning frequency. Requires userlevel 2. Unused for now.
321 int
323  float angle_start, float angle_range)
324 {
325  char cmd[CMD_BUFFER_SIZE];
326  snprintf (cmd, CMD_BUFFER_SIZE, "sMN mSCconfigbyfreq 04 %s %+f 01 %+f %+f",
327  password, freq, angle_start, angle_range);
328  SendCommand (cmd);
329 
330  return (ReadConfirmationAndAnswer ());
331 }
332 
334 // Set both resolution and frequency without going to a higher user level (?)
335 int
337  float angle_start, float angle_range)
338 {
339  char cmd[CMD_BUFFER_SIZE];
340  snprintf (cmd, CMD_BUFFER_SIZE, "sMN mSCsetscanconfig %+.2f %+.2f %+.2f %+.2f",
341  freq, ang_res, angle_start, angle_range);
342  SendCommand (cmd);
343 
344  int error = ReadConfirmationAndAnswer ();
345 
346  // If no error, parse the results
347  if (error == 0)
348  {
349  strtok ((char*)buffer_, " "); strtok (NULL, " ");
350  int ErrorCode = strtol (strtok (NULL, " "), NULL, 16);
351  long int sf = strtol (strtok (NULL, " "), NULL, 16);
352  long int re = strtol (strtok (NULL, " "), NULL, 16);
353 
354  if ((ErrorCode != 0) && (verbose_ > 0))
355  printf (">> Warning: got an error code %d\n", ErrorCode);
356 
357  scanning_frequency_ = sf;
358  resolution_ = re;
359 
360  if (verbose_ > 0)
361  printf (">> Measured value quality is: %ld [5-10]\n",
362  strtol (strtok (NULL, " "), NULL, 16));
363  }
364 
365  return (error);
366 }
367 
369 // Start a measurement for both distance and intensity or just distance.
370 int
372 {
373  char cmd[CMD_BUFFER_SIZE];
374  if (intensity)
375  snprintf (cmd, CMD_BUFFER_SIZE, "sMN mLRreqdata %x", 0x20);
376  else
377  snprintf (cmd, CMD_BUFFER_SIZE, "sMN mLRreqdata %x", 0x21);
378 
379  SendCommand (cmd);
380 
381  return (ReadConfirmationAndAnswer ());
382 }
383 
385 // Read a measurement
386 sensor_msgs::LaserScan
388 {
389  sensor_msgs::LaserScan scan;
390 
391  char cs_read = 0, cs_calc = 0;
392  int length = 0;
393  int current = 0;
394 
395  memset (buffer_, 0, 256);
396  if (!MeasurementQueue_->empty ())
397  {
398  if (verbose_ > 0)
399  ROS_DEBUG (">>> Reading from queue...\n");
400  memcpy (buffer_, (char*) MeasurementQueue_->front ().string, MeasurementQueue_->front ().length + 1);
401  free (MeasurementQueue_->front ().string);
402  MeasurementQueue_->erase (MeasurementQueue_->begin ());
403  }
404  else
405  {
406  if (verbose_ == 2)
407  ROS_DEBUG (">>> Queue empty. Reading from socket...\n");
408  n_ = read (sockfd_, buffer_, 8);
409  if (n_ < 0)
410  {
411  if (verbose_ > 0)
412  ROS_DEBUG (">>> E: error reading from socket!\n");
413  return (scan);
414  }
415  if (buffer_[0] != 0x02 || buffer_[1] != 0x02 || buffer_[2] != 0x02 || buffer_[3] != 0x02)
416  {
417  if (verbose_ > 0)
418  ROS_DEBUG (">>> E: error expected 4 bytes STX's!\n");
419  n_ = read (sockfd_, buffer_, 255);
420  return (scan);
421  }
422 
423  // find message length
424  length = ( (buffer_[4] << 24) | (buffer_[5] << 16) | (buffer_[6] << 8) | (buffer_[7]) );
425  do
426  {
427  n_ = read (sockfd_, &buffer_[current], length-current);
428  current += n_;
429  } while (current < length);
430 
431  // read checksum:
432  int ret = read (sockfd_, &cs_read, 1);
433  if (ret < 1)
434  {
435  ROS_ERROR ("LMS400 didnt get any data in read %d",ret);
436  return (scan);
437  }
438 
439  for (int i = 0; i < length; i++)
440  cs_calc ^= buffer_[i];
441 
442  if (cs_calc != cs_read)
443  {
444  if (verbose_ > 0)
445  ROS_WARN (">>> E: checksums do not match!\n");
446  return (scan);
447  }
448  }
449 
450  // parse measurements header and fill in the configuration parameters
451  MeasurementHeader_t meas_header;
452  memcpy (&meas_header, (void *)buffer_, sizeof (MeasurementHeader_t));
453 
454  float min_angle = meas_header.StartingAngle / 10000.0;
455  float resolution = meas_header.AngularStepWidth / 10000.0;
456  float max_angle = ((float) meas_header.NumberMeasuredValues) * resolution + min_angle;
457  //float scanning_frequency = meas_header.ScanningFrequency;
458 
459  if (verbose_ == 2)
460  ROS_DEBUG (">>> Reading %d values from %f to %f", meas_header.NumberMeasuredValues, meas_header.StartingAngle / 10000.0,
461  ((float) meas_header.NumberMeasuredValues) * resolution + min_angle);
462 
463  uint16_t distance = 0;
464  uint8_t remission = 0;
465  int index = sizeof (MeasurementHeader_t);
466 
467  // Fill in the appropriate values
468  scan.angle_min = angles::from_degrees (min_angle);
469  scan.angle_max = angles::from_degrees (max_angle);
470  scan.angle_increment = angles::from_degrees (resolution);
471  scan.range_min = 0.7;
472  scan.range_max = 3.6;
473  scan.ranges.resize (meas_header.NumberMeasuredValues);
474  scan.intensities.resize (meas_header.NumberMeasuredValues);
475 
476  memcpy (&scan.scan_time, &buffer_[sizeof(MeasurementHeader_t) + meas_header.NumberMeasuredValues * 3 + 14], 2);
477 
478  // Parse the read buffer and copy values into our distance/intensity buffer
479  for (int i = 0; i < meas_header.NumberMeasuredValues; i++)
480  {
481  if (meas_header.Format == 0x20 || meas_header.Format == 0x21)
482  {
483  memcpy (&distance, (void *)&buffer_[index], sizeof (uint16_t) );
484  index += sizeof (uint16_t);
485  }
486  if (meas_header.Format == 0x20 || meas_header.Format == 0x22)
487  {
488  memcpy (&remission, (void *)&buffer_[index], sizeof (uint8_t) );
489  index += sizeof (uint8_t);
490  }
491  scan.ranges[i] = distance * meas_header.DistanceScaling / 1000.0;
492  scan.intensities[i] = remission * meas_header.RemissionScaling;
493 
494  if (verbose_ == 2)
495  ROS_DEBUG (" >>> [%i] dist: %i\t remission: %i", i, distance * meas_header.DistanceScaling, remission * meas_header.RemissionScaling);
496  }
497 
498  scan.header.frame_id = "lms400_tilt_laser";
499  scan.header.stamp = ros::Time::now ();
500 
501  return (scan);
502 }
503 
505 // Stop a measurement
506 int
508 {
509  char cmd[CMD_BUFFER_SIZE];
510  snprintf (cmd, CMD_BUFFER_SIZE, "sMN mLRstopdata");
511  SendCommand (cmd);
512 
513  return (ReadConfirmationAndAnswer ());
514 }
515 
517 // Send a command to the laser unit. Returns -1 on error.
518 int
520 {
521  if (verbose_ > 0)
522  ROS_DEBUG (">> Sent: \"%s\"\n", cmd);
523  AssembleCommand ((unsigned char *) cmd, strlen (cmd));
524 
525  n_ = write (sockfd_, command_, commandlength_);
526  if (n_ < 0)
527  return (-1);
528 
529  return (0);
530 }
531 
533 // Read a result from the laser unit.
534 int
536 {
537  memset (buffer_, 0, 256);
538  n_ = read (sockfd_, buffer_, 8);
539  if (n_ < 0)
540  return (-1);
541 
542  if (buffer_[0] != 0x02 || buffer_[1] != 0x02 || buffer_[2] != 0x02 || buffer_[3] != 0x02)
543  {
544  if (verbose_ > 0)
545  ROS_WARN ("> E: expected 4 bytes STX's!");
546  n_ = read (sockfd_, buffer_, 255);
547  return (-1);
548  }
549 
550  // Find message length
551  int length = ( (buffer_[4] << 24) | (buffer_[5] << 16) | (buffer_[6] << 8) | (buffer_[7]) );
552  int current = 0;
553  do
554  {
555  n_ = read (sockfd_, &buffer_[current], length-current);
556  current += n_;
557  } while (current < length);
558 
559  bufferlength_ = length;
560  if ((verbose_ > 0) && (buffer_[0] != 0x20))
561  ROS_DEBUG (">> Received: \"%s\"\n", buffer_);
562 
563  // Check for error
564  if (strncmp ((const char*)buffer_, "sFA", 3) == 0)
565  {
566  strtok ((char*)buffer_, " ");
567  ROS_DEBUG (">> E: Got an error message with code 0x%s\n", strtok (NULL, " "));
568  }
569 
570  // Read checksum:
571  char cs_read = 0;
572  int ret = read (sockfd_, &cs_read, 1);
573  if (ret < 1)
574  {
575  ROS_ERROR ("LMS400 didnt get any data in read %d",ret);
576  return (-1);
577  }
578 
579  if (buffer_[0] == 's')
580  return (0);
581  else if (buffer_[0] == 0x20)
582  return (ReadResult ());
583  else if (bufferlength_ > sizeof (MeasurementHeader_t))
584  {
585  if (verbose_ > 0)
586  ROS_DEBUG (">>>> ReadResult: probably found a data packet!\n>>>> %s\n", buffer_);
587  // Don't throw away our precious measurement, queue it for later use :)
588  unsigned char* tmp = (unsigned char*) malloc (bufferlength_ + 1);
589  memcpy (tmp, buffer_, bufferlength_ + 1);
591  q.string = tmp;
592  q.length = bufferlength_;
593  MeasurementQueue_->push_back (q);
594  // and then, try to read what we actually wanted to read...
595  return (ReadResult ());
596  }
597 
598  return (0);
599 }
600 
602 // Read an answer from the laser unit
603 int
605 {
606  return (ReadResult ());
607 }
608 
610 // Read a confirmation and an answer from the laser unit
611 int
613 {
614  ReadResult ();
615  if (buffer_[0] == 's' && buffer_[1] == 'F' && buffer_[2] == 'A')
616  return (-1);
617  else
618  return (ReadResult ());
619 }
620 
622 // adds a header and the checksum to the command to be sent
623 int
625 {
626  unsigned char checksum = 0;
627  int index = 0;
628 
629  command_[0] = 0x02; // Messages start with 4 STX's
630  command_[1] = 0x02;
631  command_[2] = 0x02;
632  command_[3] = 0x02;
633  command_[4] = (len >> 24) & 0xff; // then message length
634  command_[5] = (len >> 16) & 0xff;
635  command_[6] = (len >> 8) & 0xff;
636  command_[7] = (len ) & 0xff;
637 
638  for (index = 0; index < len; index++)
639  {
640  command_[index + 8] = cmd[index];
641  checksum ^= cmd[index];
642  }
643  command_[8 + len] = checksum;
644  command_[9 + len] = 0x00;
645 
646  commandlength_ = 9 + len;
647  return (0);
648 }
int AssembleCommand(unsigned char *command, int len)
int SetScanningFrequency(const char *password, float freq, float angle_start, float angle_range)
int SendCommand(const char *cmd)
const int CMD_BUFFER_SIZE
Definition: sick_lms400.cpp:35
int SetResolutionAndFrequency(float freq, float ang_res, float angle_start, float angle_range)
int SetMeanFilterParameters(int num_scans)
unsigned char * ParseIP(char *ip)
#define ROS_WARN(...)
sensor_msgs::LaserScan ReadMeasurement()
#define BUF_SIZE
Definition: sick_lms400.h:37
int GetMACAddress(char **macadress)
int StartMeasurement(bool intensity=true)
static Time now()
int EnableFilters(int filter_mask)
int SetAngularResolution(const char *password, float ang_res, float angle_start, float angle_range)
#define ROS_ERROR(...)
int SetRangeFilterParameters(float range_min, float range_max)
int SetUserLevel(int8_t userlevel, const char *password)
#define ROS_DEBUG(...)


asr_sick_lms_400
Author(s): Aumann Florian, Krehl Yann, Meißner Pascal
autogenerated on Mon Jun 10 2019 12:41:54