sick_mrs1000_parser.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2017, Osnabrück University
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of Osnabrück University nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  *
00029  *      Author: Sebastian Pütz <spuetz@uos.de>
00030  *
00031  */
00032 
00033 #include <sick_tim/sick_mrs1000_parser.h>
00034 
00035 #include <ros/ros.h>
00036 
00037 namespace sick_tim
00038 {
00039 
00040 SickMRS1000Parser::SickMRS1000Parser() :
00041     override_range_min_(0.2),
00042     override_range_max_(64.0),
00043     override_time_increment_(-1.0f),
00044     modifier_(cloud_),
00045     x_iter((modifier_.setPointCloud2FieldsByString(1, "xyz"), sensor_msgs::PointCloud2Iterator<float>(cloud_, "x"))),
00046     y_iter(cloud_, "y"), z_iter(cloud_, "z"), layer_count_(0)
00047 
00048 {
00049 }
00050 
00051 SickMRS1000Parser::~SickMRS1000Parser()
00052 {
00053 }
00054 
00055 int SickMRS1000Parser::parse_datagram(char* datagram, size_t datagram_length, SickTimConfig &config,
00056                                       sensor_msgs::LaserScan &scan, sensor_msgs::PointCloud2& cloud)
00057 {
00058   // Only allow config changes for a whole new cloud / scan and not for partial fragments of a cloud.
00059   if(layer_count_ == 0)
00060   {
00061     current_config_ = config;
00062   }
00063 
00064   static const size_t HEADER_FIELDS = 32;
00065   char* cur_field;
00066   size_t count;
00067 
00068   // Reserve sufficient space
00069   std::vector<char *> fields;
00070   fields.reserve(datagram_length / 2);
00071 
00072   // ----- only for debug output
00073   char datagram_copy[datagram_length + 1];
00074   strncpy(datagram_copy, datagram, datagram_length); // datagram will be changed by strtok
00075   datagram_copy[datagram_length] = 0;
00076 
00077   // ----- tokenize
00078   cur_field = strtok(datagram, " ");
00079 
00080   while (cur_field != NULL)
00081   {
00082     fields.push_back(cur_field);
00083     cur_field = strtok(NULL, " ");
00084   }
00085 
00086   count = fields.size();
00087 
00088   // Validate header. Total number of tokens is highly unreliable as this may
00089   // change when you change the scanning range or the device name using SOPAS ET
00090   // tool. The header remains stable, however.
00091   if (count < HEADER_FIELDS)
00092   {
00093     ROS_WARN(
00094         "received less fields than minimum fields (actual: %zu, minimum: %zu), ignoring scan", count, HEADER_FIELDS);
00095     ROS_WARN("are you using the correct node? (124 --> sick_tim310_1130000m01, > 33 --> sick_tim551_2050001, 580 --> sick_tim310s01, 592 --> sick_tim310)");
00096     // ROS_DEBUG("received message was: %s", datagram_copy);
00097     return ExitError;
00098   }
00099   if (strcmp(fields[20], "DIST1"))
00100   {
00101     ROS_WARN("Field 20 of received data is not equal to DIST1 (%s). Unexpected data, ignoring scan", fields[20]);
00102     return ExitError;
00103   }
00104 
00105   // More in depth checks: check data length and RSSI availability
00106   // 25: Number of data (<= 10F)
00107   unsigned short int number_of_data = 0;
00108   sscanf(fields[25], "%hx", &number_of_data);
00109 
00110   if (number_of_data < 1 || number_of_data > 1101)
00111   {
00112     ROS_WARN("Data length is outside acceptable range 1-1101 (%d). Ignoring scan", number_of_data);
00113     return ExitError;
00114   }
00115   if (count < HEADER_FIELDS + number_of_data)
00116   {
00117     ROS_WARN("Less fields than expected for %d data points (%zu). Ignoring scan", number_of_data, count);
00118     return ExitError;
00119   }
00120   ROS_DEBUG("Number of data: %d", number_of_data);
00121 
00122   // Calculate offset of field that contains indicator of whether or not RSSI data is included
00123   size_t rssi_idx = 26 + number_of_data;
00124   int tmp;
00125   sscanf(fields[rssi_idx], "%d", &tmp);
00126   bool rssi = tmp > 0;
00127   unsigned short int number_of_rssi_data = 0;
00128   if (rssi)
00129   {
00130     sscanf(fields[rssi_idx + 6], "%hx", &number_of_rssi_data);
00131 
00132     // Number of RSSI data should be equal to number of data
00133     if (number_of_rssi_data != number_of_data)
00134     {
00135       ROS_WARN("Number of RSSI data is lower than number of range data (%d vs %d", number_of_data, number_of_rssi_data);
00136       return ExitError;
00137     }
00138 
00139     // Check if the total length is still appropriate.
00140     // RSSI data size = number of RSSI readings + 6 fields describing the data
00141     if (count < HEADER_FIELDS + number_of_data + number_of_rssi_data + 6)
00142     {
00143       ROS_WARN("Less fields than expected for %d data points (%zu). Ignoring scan", number_of_data, count);
00144       return ExitError;
00145     }
00146 
00147     if (strcmp(fields[rssi_idx + 1], "RSSI1"))
00148     {
00149       ROS_WARN("Field %zu of received data is not equal to RSSI1 (%s). Unexpected data, ignoring scan", rssi_idx + 1, fields[rssi_idx + 1]);
00150     }
00151   }
00152 
00153   short layer = -1;
00154   sscanf(fields[15], "%hx", &layer);
00155   scan.header.seq = layer;
00156 
00157   // Only set the frame id for the layer 0, because only the points of that layer 0 lie in a plane.
00158   // If the frame_id is not set the caller will not and should not publish the scan.
00159   scan.header.frame_id = layer == 0 ? current_config_.frame_id.c_str() : "";
00160 
00161   // ----- read fields into scan
00162   ROS_DEBUG_STREAM("publishing with frame_id " << scan.header.frame_id);
00163 
00164   ros::Time start_time = ros::Time::now(); // will be adjusted in the end
00165 
00166   // 16: Scanning Frequency (5DC)
00167   unsigned short scanning_freq = -1;
00168   sscanf(fields[16], "%hx", &scanning_freq);
00169   scan.scan_time = 1.0 / (scanning_freq / 100.0);
00170   // ROS_DEBUG("hex: %s, scanning_freq: %d, scan_time: %f", fields[16], scanning_freq, scan.scan_time);
00171 
00172   // 17: Measurement Frequency (36)
00173   unsigned short measurement_freq = -1;
00174   sscanf(fields[17], "%hx", &measurement_freq);
00175   // Measurement Frequency = Inverse of the time between two measurement shots -> 275 * 4 / 20ms = 55kHz
00176   scan.time_increment = 1.0 / (4 * measurement_freq * 100.0);
00177   if (override_time_increment_ > 0.0)
00178   {
00179     // Some lasers may report incorrect measurement frequency
00180     scan.time_increment = override_time_increment_;
00181   }
00182 
00183   // 23: Starting angle (FFF92230)
00184   int starting_angle = -1;
00185   sscanf(fields[23], "%x", &starting_angle);
00186   scan.angle_min = (starting_angle / 10000.0) / 180.0 * M_PI - M_PI / 2;
00187   // ROS_DEBUG("starting_angle: %d, angle_min: %f", starting_angle, scan.angle_min);
00188 
00189   // 24: Angular step width (2710)
00190   unsigned short angular_step_width = -1;
00191   sscanf(fields[24], "%hx", &angular_step_width);
00192   scan.angle_increment = (angular_step_width / 10000.0) / 180.0 * M_PI;
00193   scan.angle_max = scan.angle_min + (number_of_data - 1) * scan.angle_increment;
00194 
00195   // 25: Number of data (<= 10F)
00196   // This is already determined above in number_of_data
00197 
00198   // adjust angle_min to min_ang config param
00199   int index_min = 0;
00200   while (scan.angle_min + scan.angle_increment < current_config_.min_ang)
00201   {
00202     scan.angle_min += scan.angle_increment;
00203     index_min++;
00204   }
00205 
00206   // adjust angle_max to max_ang config param
00207   int index_max = number_of_data - 1;
00208   while (scan.angle_max - scan.angle_increment > current_config_.max_ang)
00209   {
00210     scan.angle_max -= scan.angle_increment;
00211     index_max--;
00212   }
00213 
00214   ROS_DEBUG("index_min: %d, index_max: %d", index_min, index_max);
00215   // ROS_DEBUG("angular_step_width: %d, angle_increment: %f, angle_max: %f", angular_step_width, scan.angle_increment, scan.angle_max);
00216 
00217   double phi = scan.angle_min;
00218   // -250 -> 2.5 => x -> -x/100
00219   // radiant(-x/100) -> (-x / 100) * (pi / 180) -> -x * pi / 18000
00220   double alpha = -layer * M_PI / 18000;
00221 
00222   // order of layers: 2, 3, 1, 4
00223   // layer 2: +0.0 degree, layer ==  0
00224   // layer 3: +2.5 degree, layer == -250
00225   // layer 1: -2.5 degree, layer ==  250
00226   // layer 4: +5.0 degree, layer == -500
00227 
00228   // first layer
00229   if(layer == 0){
00230     modifier_.resize(4 * (index_max - index_min + 1));
00231     x_iter = sensor_msgs::PointCloud2Iterator<float>(cloud_, "x");
00232     y_iter = sensor_msgs::PointCloud2Iterator<float>(cloud_, "y");
00233     z_iter = sensor_msgs::PointCloud2Iterator<float>(cloud_, "z");
00234     // 26..26 + n - 1: Data_1 .. Data_n
00235     scan.ranges.resize(index_max - index_min + 1);
00236     // set time when first row is received.
00237     cloud_.header.stamp = start_time + ros::Duration().fromSec(current_config_.time_offset);  // FIXME: will throw runtime error if use_sim_time = true
00238   }
00239 
00240   // check for space, space was allocated if the layer was layer == 0 (Layer2) sometime before.
00241   if(modifier_.size() > 0){
00242     layer_count_++;
00243     for (int j = index_min; j <= index_max; ++j)
00244     {
00245 
00246       unsigned short range;
00247       sscanf(fields[j + 26], "%hx", &range);
00248       float range_meter = range / 1000.0;
00249 
00250       // only copy data to laser scan for layer 2 (layer == 0 degree)
00251       if(layer == 0)
00252       {
00253         if (range == 0)
00254           scan.ranges[j - index_min] = std::numeric_limits<float>::infinity();
00255         else
00256           scan.ranges[j - index_min] = range_meter;
00257       }
00258 
00259       /*
00260        * Transform point from spherical coordinates to Cartesian coordinates.
00261        * Alpha is measured from the xy-plane and not from the
00262        * upper z axis ---> use "pi/2 - alpha" for transformation
00263        * Simplified sin(pi/2 - alpha) to cos(alpha).
00264        * Simplified cos(pi/2 - alpha) to sin(alpha).
00265        */
00266       *x_iter = range_meter * cos(alpha) * cos(phi);
00267       *y_iter = range_meter * cos(alpha) * sin(phi);
00268       *z_iter = range_meter * sin(alpha);
00269 
00270       ++x_iter;
00271       ++y_iter;
00272       ++z_iter;
00273 
00274       phi += scan.angle_increment;
00275     }
00276 
00277     // last layer in the ordered list: 0, -250, 250, -500
00278     if(layer == -500){
00279       ROS_ASSERT_MSG(layer_count_ == 4, "Expected four layers and layer == -500 to be the last layer! Package loss in communication!");
00280       layer_count_ = 0;
00281       cloud = cloud_;
00282       cloud.header.frame_id = current_config_.frame_id.c_str();
00283     }
00284   }
00285 
00286   if (current_config_.intensity) {
00287     if (rssi)
00288     {
00289       // 26 + n: RSSI data included
00290 
00291       //   26 + n + 1 = RSSI Measured Data Contents (RSSI1)
00292       //   26 + n + 2 = RSSI scaling factor (3F80000)
00293       //   26 + n + 3 = RSSI Scaling offset (0000000)
00294       //   26 + n + 4 = RSSI starting angle (equal to Range starting angle)
00295       //   26 + n + 5 = RSSI angular step width (equal to Range angular step width)
00296       //   26 + n + 6 = RSSI number of data (equal to Range number of data)
00297       //   26 + n + 7 .. 26 + n + 7 + n - 1: RSSI_Data_1 .. RSSI_Data_n
00298       //   26 + n + 7 + n .. 26 + n + 7 + n + 2 = unknown (but seems to be [0, 1, B] always)
00299       //   26 + n + 7 + n + 2 .. count - 4 = device label
00300       //   count - 3 .. count - 1 = unknown (but seems to be 0 always)
00301       //   <ETX> (\x03)
00302       scan.intensities.resize(index_max - index_min + 1);
00303       size_t offset = 26 + number_of_data + 7;
00304       for (int j = index_min; j <= index_max; ++j)
00305       {
00306         unsigned short intensity;
00307         sscanf(fields[j + offset], "%hx", &intensity);
00308         scan.intensities[j - index_min] = intensity;
00309       }
00310     } else {
00311       ROS_WARN_ONCE("Intensity parameter is enabled, but the scanner is not configured to send RSSI values! "
00312                         "Please read the section 'Enabling intensity (RSSI) output' here: http://wiki.ros.org/sick_tim.");
00313     }
00314   }
00315 
00316   // 26 + n: RSSI data included
00317   // IF RSSI not included:
00318   //   26 + n + 1 .. 26 + n + 3 = unknown (but seems to be [0, 1, B] always)
00319   //   26 + n + 4 .. count - 4 = device label
00320   //   count - 3 .. count - 1 = unknown (but seems to be 0 always)
00321   //   <ETX> (\x03)
00322 
00323   scan.range_min = override_range_min_; // TODO
00324   scan.range_max = override_range_max_;
00325 
00326   // ----- adjust start time
00327   // - last scan point = now  ==>  first scan point = now - number_of_data * time increment
00328   double start_time_adjusted = start_time.toSec()
00329             - number_of_data * scan.time_increment  // shift backward to time of first scan point
00330             + index_min * scan.time_increment       // shift forward to time of first published scan point
00331             + current_config_.time_offset;          // add time offset (usually negative) to account for USB latency etc.
00332   if (start_time_adjusted >= 0.0)   // ensure that ros::Time is not negative (otherwise runtime error)
00333   {
00334     scan.header.stamp.fromSec(start_time_adjusted);
00335   } else {
00336     ROS_WARN("ROS time is 0! Did you set the parameter use_sim_time to true?");
00337   }
00338 
00339   // ----- consistency check
00340   float expected_time_increment = scan.scan_time * scan.angle_increment / (2.0 * M_PI);
00341   if (fabs(expected_time_increment - scan.time_increment) > 0.00001)
00342   {
00343     ROS_WARN_THROTTLE(60, "The time_increment, scan_time and angle_increment values reported by the scanner are inconsistent! "
00344         "Expected time_increment: %.9f, reported time_increment: %.9f. "
00345         "Perhaps you should set the parameter time_increment to the expected value. This message will print every 60 seconds.",
00346                       expected_time_increment, scan.time_increment);
00347   }
00348 
00349   return ExitSuccess;
00350 }
00351 
00352 void SickMRS1000Parser::set_range_min(float min)
00353 {
00354   override_range_min_ = min;
00355 }
00356 
00357 void SickMRS1000Parser::set_range_max(float max)
00358 {
00359   override_range_max_ = max;
00360 }
00361 
00362 void SickMRS1000Parser::set_time_increment(float time)
00363 {
00364   override_time_increment_ = time;
00365 }
00366 
00367 } /* namespace sick_tim */


sick_tim
Author(s): Jochen Sprickerhof , Martin Günther , Sebastian Pütz
autogenerated on Thu May 9 2019 02:32:02