sicklms.cpp
Go to the documentation of this file.
1 // this program just uses sicktoolbox to get laser scans, and then publishes
3 // them as ROS messages
4 //
5 // Copyright (C) 2008, Morgan Quigley
6 //
7 // I am distributing this code under the BSD license:
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 // * Redistributions of source code must retain the above copyright notice,
12 // this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 // * Neither the name of Stanford University nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 // POSSIBILITY OF SUCH DAMAGE.
31 
32 #include <csignal>
33 #include <cstdio>
34 #include <math.h>
35 #include <limits>
37 #include "ros/ros.h"
38 #include "sensor_msgs/LaserScan.h"
39 #include <diagnostic_updater/diagnostic_updater.h> // Publishing over the diagnostics channels.
41 using namespace SickToolbox;
42 using namespace std;
43 
45  uint32_t n_range_values, uint32_t *intensity_values,
46  uint32_t n_intensity_values, double scale, ros::Time start,
47  double scan_time, bool inverted, float angle_min,
48  float angle_max, std::string frame_id)
49 {
50  static int scan_count = 0;
51  sensor_msgs::LaserScan scan_msg;
52  scan_msg.header.frame_id = frame_id;
53  scan_count++;
54  if (inverted) {
55  scan_msg.angle_min = angle_max;
56  scan_msg.angle_max = angle_min;
57  } else {
58  scan_msg.angle_min = angle_min;
59  scan_msg.angle_max = angle_max;
60  }
61  scan_msg.angle_increment = (scan_msg.angle_max - scan_msg.angle_min) / (double)(n_range_values-1);
62  scan_msg.scan_time = scan_time;
63  scan_msg.time_increment = scan_time / (2*M_PI) * scan_msg.angle_increment;
64  scan_msg.range_min = 0;
65  if (scale == 0.01) {
66  scan_msg.range_max = 81;
67  }
68  else if (scale == 0.001) {
69  scan_msg.range_max = 8.1;
70  }
71  scan_msg.ranges.resize(n_range_values);
72  scan_msg.header.stamp = start;
73  for (size_t i = 0; i < n_range_values; i++) {
74  // Check for overflow values, see pg 124 of the Sick LMS telegram listing
75  switch (range_values[i])
76  {
77  // 8m or 80m operation
78  case 8191: // Measurement not valid
79  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
80  break;
81  case 8190: // Dazzling
82  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
83  break;
84  case 8189: // Operation Overflow
85  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
86  break;
87  case 8187: // Signal to Noise ratio too small
88  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
89  break;
90  case 8186: // Erorr when reading channel 1
91  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
92  break;
93  case 8183: // Measured value > maximum Value
94  scan_msg.ranges[i] = numeric_limits<float>::infinity();
95  break;
97  /*// 16m operation
98  case 16383: // Measurement not valid
99  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
100  break;
101  case 16382: // Dazzling
102  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
103  break;
104  case 16381: // Operation Overflow
105  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
106  break;
107  case 16379: // Signal to Noise ratio too small
108  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
109  break;
110  case 16378: // Erorr when reading channel 1
111  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
112  break;
113  case 16385: // Measured value > maximum Value
114  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
115  break;
116  // 32m operation
117  case 32767: // Measurement not valid
118  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
119  break;
120  case 32766 // Dazzling
121  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
122  break;
123  case 32765: // Operation Overflow
124  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
125  break;
126  case 32763: // Signal to Noise ratio too small
127  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
128  break;
129  case 32762: // Erorr when reading channel 1
130  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
131  break;
132  case 32759: // Measured value > maximum Value
133  scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
134  break;*/
135  default:
136  scan_msg.ranges[i] = (float)range_values[i] * (float)scale;
137  }
138  }
139  scan_msg.intensities.resize(n_intensity_values);
140  for (size_t i = 0; i < n_intensity_values; i++) {
141  scan_msg.intensities[i] = (float)intensity_values[i];
142  }
143 
144 
145  pub->publish(scan_msg);
146 }
147 
149 {
150  if (units.compare("mm") == 0)
152  else if (units.compare("cm") == 0)
154 
156 }
157 
158 
159 int main(int argc, char **argv)
160 {
161  ros::init(argc, argv, "sicklms");
162  ros::NodeHandle nh;
163  ros::NodeHandle nh_ns("~");
164  string port;
165  int baud;
166  int delay;
167  bool inverted;
168  int angle;
169  double resolution;
170  std::string measuring_units;
171  std::string frame_id;
172  double scan_time = 0;
173  double angle_increment = 0;
174  float angle_min = 0.0;
175  float angle_max = 0.0;
176 
177  // Diagnostic publisher parameters
178  double desired_freq;
179  nh_ns.param<double>("desired_frequency", desired_freq, 75.0);
180  double min_freq;
181  nh_ns.param<double>("min_frequency", min_freq, desired_freq);
182  double max_freq;
183  nh_ns.param<double>("max_frequency", max_freq, desired_freq);
184  double freq_tolerance; // Tolerance before error, fractional percent of frequency.
185  nh_ns.param<double>("frequency_tolerance", freq_tolerance, 0.3);
186  int window_size; // Number of samples to consider in frequency
187  nh_ns.param<int>("window_size", window_size, 30);
188  double min_delay; // The minimum publishing delay (in seconds) before error. Negative values mean future dated messages.
189  nh_ns.param<double>("min_acceptable_delay", min_delay, 0.0);
190  double max_delay; // The maximum publishing delay (in seconds) before error.
191  nh_ns.param<double>("max_acceptable_delay", max_delay, 0.2);
192  std::string hardware_id;
193  nh_ns.param<std::string>("hardware_id", hardware_id, "SICK LMS");
194  double time_offset_sec;
195  nh_ns.param<double>("time_offset", time_offset_sec, 0.0);
196  ros::Duration time_offset(time_offset_sec);
197 
198  // Set up diagnostics
200  updater.setHardwareID(hardware_id);
201  diagnostic_updater::DiagnosedPublisher<sensor_msgs::LaserScan> scan_pub(nh.advertise<sensor_msgs::LaserScan>("scan", 10), updater,
202  diagnostic_updater::FrequencyStatusParam(&min_freq, &max_freq, freq_tolerance, window_size),
203  diagnostic_updater::TimeStampStatusParam(min_delay, max_delay));
204 
205  nh_ns.param("port", port, string("/dev/lms200"));
206  nh_ns.param("baud", baud, 38400);
207  nh_ns.param("connect_delay", delay, 0);
208  nh_ns.param("inverted", inverted, false);
209  nh_ns.param("angle", angle, 0);
210  nh_ns.param("resolution", resolution, 0.0);
211  nh_ns.param("units", measuring_units, string());
212  nh_ns.param<std::string>("frame_id", frame_id, "laser");
213 
214  // Check if use_rep_117 is set.
215  std::string key;
216  if (nh.searchParam("use_rep_117", key))
217  {
218  ROS_ERROR("The use_rep_117 parameter has not been specified and is now ignored. This parameter was removed in Hydromedusa. Please see: http://ros.org/wiki/rep_117/migration");
219  }
220 
222  if (desired_baud == SickLMS2xx::SICK_BAUD_UNKNOWN)
223  {
224  ROS_ERROR("Baud rate must be in {9600, 19200, 38400, 500000}");
225  return 1;
226  }
227  uint32_t range_values[SickLMS2xx::SICK_MAX_NUM_MEASUREMENTS] = {0};
228  uint32_t intensity_values[SickLMS2xx::SICK_MAX_NUM_MEASUREMENTS] = {0};
229  uint32_t n_range_values = 0;
230  uint32_t n_intensity_values = 0;
231  SickLMS2xx sick_lms(port);
232  double scale = 0;
233  double angle_offset;
234  uint32_t partial_scan_index;
235 
236  try
237  {
238  uint32_t on_delay = 0;
239  if(delay > 0){
240  on_delay = delay;
241  }
242  sick_lms.Initialize(desired_baud, on_delay);
243 
244  // Set the angle and resolution if possible (not an LMSFast) and
245  // the user specifies a setting.
246  int actual_angle = sick_lms.GetSickScanAngle();
247  double actual_resolution = sick_lms.GetSickScanResolution();
249 
250  // Attempt to set measurement angles and angular resolution
251  try {
252  if ((angle && actual_angle != angle) || (resolution && actual_resolution != resolution)) {
253  ROS_INFO("Setting variant to (%i, %f)", angle, resolution);
254  sick_lms.SetSickVariant(sick_lms.IntToSickScanAngle(angle),
255  sick_lms.DoubleToSickScanResolution(resolution));
256  } else {
257  ROS_INFO("Variant setup not requested or identical to actual (%i, %f)", actual_angle,
258  actual_resolution);
259  angle = actual_angle;
260  resolution = actual_resolution;
261  }
262  } catch (SickConfigException e) {
263  actual_angle = sick_lms.GetSickScanAngle();
264  actual_resolution = sick_lms.GetSickScanResolution();
265  if (angle != actual_angle) {
266  ROS_WARN("Unable to set scan angle. Using %i instead of %i.", actual_angle, angle);
267  angle = actual_angle;
268  }
269  if (resolution != actual_resolution) {
270  ROS_WARN("Unable to set resolution. Using %e instead of %e.", actual_resolution, resolution);
271  resolution = actual_resolution;
272  }
273  }
274 
275  // Attempt to set measurement output mode to cm or mm
276  try {
277  if (!measuring_units.empty() && (actual_units != StringToLmsMeasuringUnits(measuring_units))) {
278  ROS_INFO("Setting measuring units to '%s'", measuring_units.c_str());
279  actual_units = StringToLmsMeasuringUnits(measuring_units);
280  sick_lms.SetSickMeasuringUnits(actual_units);
281  } else {
282  ROS_INFO("Measuring units setup not requested or identical to actual ('%s')",
283  sick_lms.SickMeasuringUnitsToString(actual_units).c_str());
284  }
285  } catch (SickConfigException e) {
286  actual_units = sick_lms.GetSickMeasuringUnits();
287  if (StringToLmsMeasuringUnits(measuring_units) != actual_units) {
288  ROS_WARN("Unable to set measuring units. Using '%s' instead of '%s'.",
289  sick_lms.SickMeasuringUnitsToString(actual_units).c_str(), measuring_units.c_str());
290  measuring_units = sick_lms.SickMeasuringUnitsToString(actual_units);
291  }
292  }
293 
294  if (actual_units == SickLMS2xx::SICK_MEASURING_UNITS_CM)
295  scale = 0.01;
296  else if (actual_units == SickLMS2xx::SICK_MEASURING_UNITS_MM)
297  scale = 0.001;
298  else
299  {
300  ROS_ERROR("Invalid measuring unit.");
301  return 1;
302  }
303 
304  // The scan time is always 1/75 because that's how long it takes
305  // for the mirror to rotate. If we have a higher resolution, the
306  // SICKs interleave the readings, so the net result is we just
307  // shift the measurements.
308  if (angle == 180 || sick_lms.IsSickLMS2xxFast()) {
309  scan_time = 1.0 / 75;
310  }
311  else {
314  if ( scan_resolution == SickLMS2xx::SICK_SCAN_RESOLUTION_25) {
315  // 0.25 degrees
316  scan_time = 4.0 / 75; // 53.33 ms
317  }
318  else if ( scan_resolution == SickLMS2xx::SICK_SCAN_RESOLUTION_50) {
319  // 0.5 degrees
320  scan_time = 2.0 / 75; // 26.66 ms
321  }
322  else if ( scan_resolution == SickLMS2xx::SICK_SCAN_RESOLUTION_100) {
323  // 1 degree
324  scan_time = 1.0 / 75; // 13.33 ms
325  }
326  else {
327  ROS_ERROR("Bogus scan resolution.");
328  return 1;
329  }
330  if ( scan_resolution != SickLMS2xx::SICK_SCAN_RESOLUTION_100) {
331  ROS_WARN("You are using an angle smaller than 180 degrees and a "
332  "scan resolution less than 1 degree per scan. Thus, "
333  "you are in inteleaved mode and the returns will not "
334  "arrive sequentially how you read them. So, the "
335  "time_increment field will be misleading. If you need to "
336  "know when the measurement was made at a time resolution "
337  "better than the scan_time, use the whole 180 degree "
338  "field of view.");
339  }
340  }
341 
342  // The increment for the slower LMS is still 1.0 even if its set to
343  // 0.5 or 0.25 degrees resolution because it is interleaved. So for
344  // 0.5 degrees, two scans are needed, offset by 0.5 degrees. These
345  // show up as two seperate LaserScan messages.
346  angle_increment = sick_lms.IsSickLMS2xxFast() ? 0.5 : 1.0;
347 
348  angle_offset = (180.0-angle)/2;
349  }
350  catch (...)
351  {
352  ROS_ERROR("Initialize failed! are you using the correct device path?");
353  return 2;
354  }
355  try
356  {
357  while (ros::ok())
358  {
359  if (sick_lms.IsSickLMS2xxFast()) {
360  // There's no inteleaving, but we can capture both the range
361  // and intensity simultaneously
362  sick_lms.GetSickScan(range_values, intensity_values,
363  n_range_values, n_intensity_values);
364  angle_min = -M_PI/4;
365  angle_max = M_PI/4;
366  }
367  else if (angle != 180) {
368  // If the angle is not 180, we can't get partial scans as they
369  // arrive. So, we have to wait for a full scan to get
370  // there.
371  sick_lms.GetSickScan(range_values, n_range_values);
372  angle_min = (-90.0 + angle_offset) * M_PI / 180.0;
373  angle_max = (90.0 - angle_offset) * M_PI / 180.0;
374  }
375  else {
376  // We get scans that could be potentially interleaved
377  // depending on the mode. We want to stream out the data as
378  // soon as we get it otherwise the timing won't work right to
379  // reconstruct the data if the sensor is moving.
380  sick_lms.GetSickPartialScan(range_values, n_range_values,
381  partial_scan_index);
382  double partialScanOffset = 0.25 * partial_scan_index;
383  angle_min = (-90.0 + angle_offset + partialScanOffset) * M_PI / 180.0;
384  angle_max = (90.0 - angle_offset - fmod(1.0 - partialScanOffset, 1.0))
385  * M_PI / 180.0;
386  }
387  // Figure out the time that the scan started. Since we just
388  // fished receiving the data, we'll assume that the mirror is at
389  // 180 degrees now, or half a scan time.
390  // Add user provided time offset to handle constant latency.
391  ros::Time end_of_scan = ros::Time::now();
392  ros::Time start = end_of_scan - ros::Duration(scan_time / 2.0) + time_offset;
393 
394  publish_scan(&scan_pub, range_values, n_range_values, intensity_values,
395  n_intensity_values, scale, start, scan_time, inverted,
396  angle_min, angle_max, frame_id);
397  ros::spinOnce();
398  // Update diagnostics
399  updater.update();
400  }
401  }
402  catch (...)
403  {
404  ROS_ERROR("Unknown error.");
405  return 1;
406  }
407  try
408  {
409  sick_lms.Uninitialize();
410  }
411  catch (...)
412  {
413  ROS_ERROR("Error during uninitialize");
414  return 1;
415  }
416  ROS_INFO("Success.\n");
417 
418  return 0;
419 }
420 
ROSCPP_DECL void start()
void setHardwareID(const std::string &hwid)
void GetSickScan(unsigned int *const measurement_values, unsigned int &num_measurement_values, unsigned int *const sick_field_a_values=NULL, unsigned int *const sick_field_b_values=NULL, unsigned int *const sick_field_c_values=NULL, unsigned int *const sick_telegram_index=NULL, unsigned int *const sick_real_time_scan_index=NULL)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
void Initialize(const sick_lms_2xx_baud_t desired_baud_rate, const uint32_t delay=0)
virtual void publish(const boost::shared_ptr< T > &message)
static const uint16_t SICK_MAX_NUM_MEASUREMENTS
void SetSickMeasuringUnits(const sick_lms_2xx_measuring_units_t sick_units=SICK_MEASURING_UNITS_MM)
updater
#define ROS_WARN(...)
double GetSickScanResolution() const
int main(int argc, char **argv)
Definition: sicklms.cpp:159
void SetSickVariant(const sick_lms_2xx_scan_angle_t scan_angle, const sick_lms_2xx_scan_resolution_t scan_resolution)
void publish_scan(diagnostic_updater::DiagnosedPublisher< sensor_msgs::LaserScan > *pub, uint32_t *range_values, uint32_t n_range_values, uint32_t *intensity_values, uint32_t n_intensity_values, double scale, ros::Time start, double scan_time, bool inverted, float angle_min, float angle_max, std::string frame_id)
Definition: sicklms.cpp:44
#define ROS_INFO(...)
bool searchParam(const std::string &key, std::string &result) const
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool IsSickLMS2xxFast() const
ROSCPP_DECL bool ok()
static sick_lms_2xx_scan_angle_t IntToSickScanAngle(const int scan_angle_int)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
static sick_lms_2xx_scan_resolution_t DoubleToSickScanResolution(const double scan_resolution_double)
double GetSickScanAngle() const
void GetSickPartialScan(unsigned int *const measurement_values, unsigned int &num_measurement_values, unsigned int &partial_scan_index, unsigned int *const sick_field_a_values=NULL, unsigned int *const sick_field_b_values=NULL, unsigned int *const sick_field_c_values=NULL, unsigned int *const sick_telegram_index=NULL, unsigned int *const sick_real_time_scan_index=NULL)
SickLMS2xx::sick_lms_2xx_measuring_units_t GetSickMeasuringUnits() const
static Time now()
static sick_lms_2xx_baud_t IntToSickBaud(const int baud_int)
SickLMS2xx::sick_lms_2xx_measuring_units_t StringToLmsMeasuringUnits(string units)
Definition: sicklms.cpp:148
static std::string SickMeasuringUnitsToString(const sick_lms_2xx_measuring_units_t sick_units)
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)


sicktoolbox_wrapper
Author(s): Morgan Quigley
autogenerated on Mon Jun 10 2019 15:03:33