sicklms.cpp
Go to the documentation of this file.
00001 
00002 // this program just uses sicktoolbox to get laser scans, and then publishes
00003 // them as ROS messages
00004 //
00005 // Copyright (C) 2008, Morgan Quigley
00006 //
00007 // I am distributing this code under the BSD license:
00008 //
00009 // Redistribution and use in source and binary forms, with or without 
00010 // modification, are permitted provided that the following conditions are met:
00011 //   * Redistributions of source code must retain the above copyright notice, 
00012 //     this list of conditions and the following disclaimer.
00013 //   * Redistributions in binary form must reproduce the above copyright 
00014 //     notice, this list of conditions and the following disclaimer in the 
00015 //     documentation and/or other materials provided with the distribution.
00016 //   * Neither the name of Stanford University nor the names of its 
00017 //     contributors may be used to endorse or promote products derived from 
00018 //     this software without specific prior written permission.
00019 //   
00020 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
00021 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
00022 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
00023 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
00024 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
00025 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
00026 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
00027 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
00028 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
00029 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
00030 // POSSIBILITY OF SUCH DAMAGE.
00031 
00032 #include <csignal>
00033 #include <cstdio>
00034 #include <math.h>
00035 #include <limits>
00036 #include <sicktoolbox/SickLMS2xx.hh>
00037 #include "ros/ros.h"
00038 #include "sensor_msgs/LaserScan.h"
00039 #include <diagnostic_updater/diagnostic_updater.h> // Publishing over the diagnostics channels.
00040 #include <diagnostic_updater/publisher.h>
00041 using namespace SickToolbox;
00042 using namespace std;
00043 
00044 void publish_scan(diagnostic_updater::DiagnosedPublisher<sensor_msgs::LaserScan> *pub, uint32_t *range_values,
00045                   uint32_t n_range_values, uint32_t *intensity_values,
00046                   uint32_t n_intensity_values, double scale, ros::Time start,
00047                   double scan_time, bool inverted, float angle_min,
00048                   float angle_max, std::string frame_id)
00049 {
00050   static int scan_count = 0;
00051   sensor_msgs::LaserScan scan_msg;
00052   scan_msg.header.frame_id = frame_id;
00053   scan_count++;
00054   if (inverted) {
00055     scan_msg.angle_min = angle_max;
00056     scan_msg.angle_max = angle_min;
00057   } else {
00058     scan_msg.angle_min = angle_min;
00059     scan_msg.angle_max = angle_max;
00060   }
00061   scan_msg.angle_increment = (scan_msg.angle_max - scan_msg.angle_min) / (double)(n_range_values-1);
00062   scan_msg.scan_time = scan_time;
00063   scan_msg.time_increment = scan_time / (2*M_PI) * scan_msg.angle_increment;
00064   scan_msg.range_min = 0;
00065   if (scale == 0.01) {
00066     scan_msg.range_max = 81;
00067   }
00068   else if (scale == 0.001) {
00069     scan_msg.range_max = 8.1;
00070   }
00071   scan_msg.ranges.resize(n_range_values);
00072   scan_msg.header.stamp = start;
00073   for (size_t i = 0; i < n_range_values; i++) {
00074     // Check for overflow values, see pg 124 of the Sick LMS telegram listing
00075     switch (range_values[i])
00076     {
00077     // 8m or 80m operation
00078     case 8191: // Measurement not valid
00079       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00080       break;
00081     case 8190: // Dazzling
00082       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00083       break;
00084     case 8189: // Operation Overflow
00085       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00086       break;
00087     case 8187: // Signal to Noise ratio too small
00088       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00089       break;
00090     case 8186: // Erorr when reading channel 1
00091       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00092       break;
00093     case 8183: // Measured value > maximum Value
00094       scan_msg.ranges[i] = numeric_limits<float>::infinity();
00095       break;
00097     /*// 16m operation
00098     case 16383: // Measurement not valid
00099       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00100       break;
00101     case 16382: // Dazzling
00102       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00103       break;
00104     case 16381: // Operation Overflow
00105       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00106       break;
00107     case 16379: // Signal to Noise ratio too small
00108       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00109       break;
00110     case 16378: // Erorr when reading channel 1
00111       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00112       break;
00113     case 16385: // Measured value > maximum Value
00114       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00115       break;
00116     // 32m operation
00117     case 32767: // Measurement not valid
00118       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00119       break;
00120     case 32766 // Dazzling
00121       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00122       break;
00123     case 32765: // Operation Overflow
00124       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00125       break;
00126     case 32763: // Signal to Noise ratio too small
00127       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00128       break;
00129     case 32762: // Erorr when reading channel 1
00130       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00131       break;
00132     case 32759: // Measured value > maximum Value
00133       scan_msg.ranges[i] = numeric_limits<float>::quiet_NaN();
00134       break;*/
00135     default:
00136       scan_msg.ranges[i] = (float)range_values[i] * (float)scale;
00137     }
00138   }
00139   scan_msg.intensities.resize(n_intensity_values);
00140   for (size_t i = 0; i < n_intensity_values; i++) {
00141     scan_msg.intensities[i] = (float)intensity_values[i];
00142   }
00143   
00144 
00145   pub->publish(scan_msg);
00146 }
00147 
00148 SickLMS2xx::sick_lms_2xx_measuring_units_t StringToLmsMeasuringUnits(string units)
00149 {
00150   if (units.compare("mm") == 0)
00151     return SickLMS2xx::SICK_MEASURING_UNITS_MM;
00152   else if (units.compare("cm") == 0)
00153     return SickLMS2xx::SICK_MEASURING_UNITS_CM;
00154   
00155   return SickLMS2xx::SICK_MEASURING_UNITS_UNKNOWN;
00156 }
00157 
00158 
00159 int main(int argc, char **argv)
00160 {
00161         ros::init(argc, argv, "sicklms");
00162         ros::NodeHandle nh;
00163         ros::NodeHandle nh_ns("~");
00164         string port;
00165         int baud;
00166         int delay;
00167         bool inverted;
00168         int angle;
00169         double resolution;
00170         std::string measuring_units;
00171         std::string frame_id;
00172         double scan_time = 0;
00173         double angle_increment = 0;
00174         float angle_min = 0.0;
00175         float angle_max = 0.0;
00176         
00177         // Diagnostic publisher parameters
00178         double desired_freq;
00179         nh_ns.param<double>("desired_frequency", desired_freq, 75.0);
00180         double min_freq;
00181         nh_ns.param<double>("min_frequency", min_freq, desired_freq);
00182         double max_freq;
00183         nh_ns.param<double>("max_frequency", max_freq, desired_freq);
00184         double freq_tolerance; // Tolerance before error, fractional percent of frequency.
00185         nh_ns.param<double>("frequency_tolerance", freq_tolerance, 0.3);
00186         int window_size; // Number of samples to consider in frequency
00187         nh_ns.param<int>("window_size", window_size, 30);
00188         double min_delay; // The minimum publishing delay (in seconds) before error.  Negative values mean future dated messages.
00189         nh_ns.param<double>("min_acceptable_delay", min_delay, 0.0);
00190         double max_delay; // The maximum publishing delay (in seconds) before error.
00191         nh_ns.param<double>("max_acceptable_delay", max_delay, 0.2);
00192         std::string hardware_id;
00193         nh_ns.param<std::string>("hardware_id", hardware_id, "SICK LMS");
00194         double time_offset_sec;
00195         nh_ns.param<double>("time_offset", time_offset_sec, 0.0);
00196         ros::Duration time_offset(time_offset_sec);
00197         
00198         // Set up diagnostics
00199         diagnostic_updater::Updater updater;
00200         updater.setHardwareID(hardware_id);
00201         diagnostic_updater::DiagnosedPublisher<sensor_msgs::LaserScan> scan_pub(nh.advertise<sensor_msgs::LaserScan>("scan", 10), updater, 
00202             diagnostic_updater::FrequencyStatusParam(&min_freq, &max_freq, freq_tolerance, window_size),
00203             diagnostic_updater::TimeStampStatusParam(min_delay, max_delay));
00204         
00205         nh_ns.param("port", port, string("/dev/lms200"));
00206         nh_ns.param("baud", baud, 38400);
00207         nh_ns.param("connect_delay", delay, 0);
00208         nh_ns.param("inverted", inverted, false);
00209         nh_ns.param("angle", angle, 0);
00210         nh_ns.param("resolution", resolution, 0.0);
00211         nh_ns.param("units", measuring_units, string());
00212         nh_ns.param<std::string>("frame_id", frame_id, "laser");
00213         
00214   // Check if use_rep_117 is set.
00215   std::string key;
00216   if (nh.searchParam("use_rep_117", key))
00217   {
00218     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");
00219   }
00220 
00221         SickLMS2xx::sick_lms_2xx_baud_t desired_baud = SickLMS2xx::IntToSickBaud(baud);
00222         if (desired_baud == SickLMS2xx::SICK_BAUD_UNKNOWN)
00223         {
00224                 ROS_ERROR("Baud rate must be in {9600, 19200, 38400, 500000}");
00225                 return 1;
00226         }
00227         uint32_t range_values[SickLMS2xx::SICK_MAX_NUM_MEASUREMENTS] = {0};
00228   uint32_t intensity_values[SickLMS2xx::SICK_MAX_NUM_MEASUREMENTS] = {0};
00229         uint32_t n_range_values = 0;
00230   uint32_t n_intensity_values = 0;
00231         SickLMS2xx sick_lms(port);
00232         double scale = 0;
00233   double angle_offset;
00234   uint32_t partial_scan_index;
00235 
00236         try
00237         {
00238                 uint32_t on_delay = 0;
00239                 if(delay > 0){
00240                   on_delay = delay;
00241                 }
00242                 sick_lms.Initialize(desired_baud, on_delay);
00243 
00244                 // Set the angle and resolution if possible (not an LMSFast) and
00245                 // the user specifies a setting.
00246                 int actual_angle = sick_lms.GetSickScanAngle();
00247                 double actual_resolution = sick_lms.GetSickScanResolution();
00248                 SickLMS2xx::sick_lms_2xx_measuring_units_t actual_units = sick_lms.GetSickMeasuringUnits();
00249 
00250                 // Attempt to set measurement angles and angular resolution
00251                 try {
00252                   if ((angle && actual_angle != angle) || (resolution && actual_resolution != resolution)) {
00253                      ROS_INFO("Setting variant to (%i, %f)", angle, resolution);
00254                      sick_lms.SetSickVariant(sick_lms.IntToSickScanAngle(angle),
00255                                              sick_lms.DoubleToSickScanResolution(resolution));
00256                   } else {
00257                     ROS_INFO("Variant setup not requested or identical to actual (%i, %f)", actual_angle,
00258                       actual_resolution);
00259                     angle = actual_angle;
00260                     resolution = actual_resolution;
00261                   }
00262                 } catch (SickConfigException e) {
00263                   actual_angle = sick_lms.GetSickScanAngle();
00264                   actual_resolution = sick_lms.GetSickScanResolution();
00265                   if (angle != actual_angle) {
00266                     ROS_WARN("Unable to set scan angle. Using %i instead of %i.", actual_angle, angle);
00267                     angle = actual_angle;
00268                   }
00269                   if (resolution != actual_resolution) {
00270                     ROS_WARN("Unable to set resolution. Using %e instead of %e.", actual_resolution, resolution);
00271                     resolution = actual_resolution;
00272                   }
00273                 }
00274                 
00275                 // Attempt to set measurement output mode to cm or mm
00276                 try {
00277                   if (!measuring_units.empty() && (actual_units != StringToLmsMeasuringUnits(measuring_units))) {
00278                     ROS_INFO("Setting measuring units to '%s'", measuring_units.c_str());
00279                     actual_units = StringToLmsMeasuringUnits(measuring_units);
00280                     sick_lms.SetSickMeasuringUnits(actual_units);
00281                   } else {
00282                     ROS_INFO("Measuring units setup not requested or identical to actual ('%s')",
00283                       sick_lms.SickMeasuringUnitsToString(actual_units).c_str());
00284                   }
00285                 }  catch (SickConfigException e) {
00286                   actual_units = sick_lms.GetSickMeasuringUnits();
00287                   if (StringToLmsMeasuringUnits(measuring_units) != actual_units) {
00288                     ROS_WARN("Unable to set measuring units. Using '%s' instead of '%s'.",
00289                       sick_lms.SickMeasuringUnitsToString(actual_units).c_str(), measuring_units.c_str());
00290                     measuring_units = sick_lms.SickMeasuringUnitsToString(actual_units);
00291                   }
00292                 }
00293 
00294                 if (actual_units == SickLMS2xx::SICK_MEASURING_UNITS_CM)
00295                         scale = 0.01;
00296                 else if (actual_units == SickLMS2xx::SICK_MEASURING_UNITS_MM)
00297                         scale = 0.001;
00298                 else
00299                 {
00300                         ROS_ERROR("Invalid measuring unit.");
00301                         return 1;
00302                 }
00303 
00304     // The scan time is always 1/75 because that's how long it takes
00305     // for the mirror to rotate. If we have a higher resolution, the
00306     // SICKs interleave the readings, so the net result is we just
00307     // shift the measurements.
00308     if (angle == 180 || sick_lms.IsSickLMS2xxFast()) {
00309       scan_time = 1.0 / 75;
00310     } 
00311     else {
00312       SickLMS2xx::sick_lms_2xx_scan_resolution_t scan_resolution =
00313         SickLMS2xx::DoubleToSickScanResolution(resolution);
00314       if ( scan_resolution == SickLMS2xx::SICK_SCAN_RESOLUTION_25) {
00315         // 0.25 degrees
00316         scan_time = 4.0 / 75;   // 53.33 ms
00317       }
00318       else if ( scan_resolution == SickLMS2xx::SICK_SCAN_RESOLUTION_50) {
00319         // 0.5 degrees
00320         scan_time = 2.0 / 75;   // 26.66 ms
00321       }
00322       else if ( scan_resolution == SickLMS2xx::SICK_SCAN_RESOLUTION_100) {
00323         // 1 degree
00324         scan_time = 1.0 / 75;   // 13.33 ms
00325       }
00326       else {
00327         ROS_ERROR("Bogus scan resolution.");
00328         return 1;
00329       }
00330       if ( scan_resolution != SickLMS2xx::SICK_SCAN_RESOLUTION_100) {
00331         ROS_WARN("You are using an angle smaller than 180 degrees and a "
00332                  "scan resolution less than 1 degree per scan. Thus, "
00333                  "you are in inteleaved mode and the returns will not "
00334                  "arrive sequentially how you read them. So, the "
00335                  "time_increment field will be misleading. If you need to "
00336                  "know when the measurement was made at a time resolution "
00337                  "better than the scan_time, use the whole 180 degree "
00338                  "field of view.");
00339       }
00340     }
00341 
00342     // The increment for the slower LMS is still 1.0 even if its set to
00343     // 0.5 or 0.25 degrees resolution because it is interleaved. So for
00344     // 0.5 degrees, two scans are needed, offset by 0.5 degrees. These
00345     // show up as two seperate LaserScan messages.
00346     angle_increment = sick_lms.IsSickLMS2xxFast() ? 0.5 : 1.0;
00347 
00348     angle_offset = (180.0-angle)/2;
00349         }
00350         catch (...)
00351         {
00352                 ROS_ERROR("Initialize failed! are you using the correct device path?");
00353     return 2;
00354         }
00355         try
00356         {
00357                 while (ros::ok())
00358                 {
00359       if (sick_lms.IsSickLMS2xxFast()) {
00360         // There's no inteleaving, but we can capture both the range
00361         // and intensity simultaneously
00362         sick_lms.GetSickScan(range_values, intensity_values,
00363                              n_range_values, n_intensity_values);
00364         angle_min = -M_PI/4;
00365         angle_max = M_PI/4;
00366       }
00367       else if (angle != 180) {
00368         // If the angle is not 180, we can't get partial scans as they
00369         // arrive. So, we have to wait for a full scan to get
00370         // there. 
00371         sick_lms.GetSickScan(range_values, n_range_values);
00372         angle_min = (-90.0 + angle_offset) * M_PI / 180.0;
00373         angle_max = (90.0 - angle_offset)  * M_PI / 180.0;
00374       }
00375       else {
00376         // We get scans that could be potentially interleaved
00377         // depending on the mode. We want to stream out the data as
00378         // soon as we get it otherwise the timing won't work right to
00379         // reconstruct the data if the sensor is moving.
00380         sick_lms.GetSickPartialScan(range_values, n_range_values,
00381                                     partial_scan_index);
00382         double partialScanOffset = 0.25 * partial_scan_index;
00383         angle_min = (-90.0 + angle_offset + partialScanOffset) * M_PI / 180.0;
00384         angle_max = (90.0 - angle_offset - fmod(1.0 - partialScanOffset, 1.0))
00385           * M_PI / 180.0;
00386       }
00387       // Figure out the time that the scan started. Since we just
00388       // fished receiving the data, we'll assume that the mirror is at
00389       // 180 degrees now, or half a scan time.
00390       // Add user provided time offset to handle constant latency.
00391       ros::Time end_of_scan = ros::Time::now();
00392       ros::Time start = end_of_scan - ros::Duration(scan_time / 2.0) + time_offset;
00393 
00394                         publish_scan(&scan_pub, range_values, n_range_values, intensity_values,
00395                    n_intensity_values, scale, start, scan_time, inverted,
00396                    angle_min, angle_max, frame_id);
00397                         ros::spinOnce();
00398                         // Update diagnostics
00399                         updater.update();
00400                 }
00401         }
00402         catch (...)
00403         {
00404                 ROS_ERROR("Unknown error.");
00405                 return 1;
00406         }
00407         try
00408         {
00409                 sick_lms.Uninitialize();
00410         }
00411         catch (...)
00412         {
00413                 ROS_ERROR("Error during uninitialize");
00414                 return 1;
00415         }
00416         ROS_INFO("Success.\n");
00417 
00418         return 0;
00419 }
00420 


sicktoolbox_wrapper
Author(s): Morgan Quigley
autogenerated on Thu Aug 27 2015 15:20:26