laserscan_kinect.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  * Copyright (c) 2015, Michal Drwiega (drwiega.michal@gmail.com)
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions are met:
00009  *     1. Redistributions of source code must retain the above copyright
00010  *        notice, this list of conditions and the following disclaimer.
00011  *     2. Redistributions in binary form must reproduce the above copyright
00012  *        notice, this list of conditions and the following disclaimer in the
00013  *        documentation and/or other materials provided with the distribution.
00014  *     3. Neither the name of the copyright holder nor the names of its
00015  *        contributors may be used to endorse or promote products derived
00016  *        from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00021  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00022  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00023  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00024  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00025  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00026  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00027  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *****************************************************************************/
00037 #include <laserscan_kinect/laserscan_kinect_node.h>
00038 
00039 #define TIME_MEASUREMENT 0 /// Measurement of processing time
00040 
00041 #if TIME_MEASUREMENT
00042 #include <boost/chrono.hpp>
00043 #endif
00044 
00045 using namespace laserscan_kinect;
00046 
00047 //=================================================================================================
00048 // Public methods
00049 //=================================================================================================
00050 LaserScanKinect::LaserScanKinect():
00051   is_scan_msg_configurated_(false), scan_msg_(new sensor_msgs::LaserScan()) { }
00052 
00053 //=================================================================================================
00054 sensor_msgs::LaserScanPtr LaserScanKinect::prepareLaserScanMsg(
00055     const sensor_msgs::ImageConstPtr& depth_msg,
00056     const sensor_msgs::CameraInfoConstPtr& info_msg)
00057 {
00058 #if TIME_MEASUREMENT
00059   boost::chrono::high_resolution_clock::time_point start = boost::chrono::high_resolution_clock::now();
00060 #endif
00061   // Configure message if necessary
00062   if(!is_scan_msg_configurated_ || cam_model_update_)
00063   {
00064     camera_model_.fromCameraInfo(info_msg);
00065 
00066     const double cx = camera_model_.cx(), cy = camera_model_.cy();
00067     double min_angle, max_angle, vertical_fov;
00068 
00069     // Calculate vertical field of view angles
00070     fieldOfView(min_angle, max_angle, cx, 0, cx, cy, cx, depth_msg->height - 1);
00071     vertical_fov = max_angle - min_angle;
00072 
00073     // Calculate horizontal field of view angles
00074     fieldOfView(min_angle, max_angle, 0, cy, cx, cy, depth_msg->width - 1, cy);
00075 
00076     if ( ground_remove_enable_ )      // Remove floor from scan
00077       calcGroundDistancesForImgRows(vertical_fov);
00078 
00079     if ( tilt_compensation_enable_ )  // Sensor tilt compensation
00080       calcTiltCompensationFactorsForImgRows(vertical_fov);
00081 
00082     scan_msg_->angle_min = min_angle;
00083     scan_msg_->angle_max = max_angle;
00084     scan_msg_->angle_increment = (max_angle - min_angle) / (depth_msg->width - 1);
00085     scan_msg_->time_increment = 0.0;
00086     scan_msg_->scan_time = SCAN_TIME;
00087 
00088     // Set min and max range in preparing message
00089     if (tilt_compensation_enable_)
00090     {
00091       scan_msg_->range_min = range_min_ * *std::min_element( tilt_compensation_factor_.begin(),
00092                                                              tilt_compensation_factor_.end() );
00093       scan_msg_->range_max = range_max_ * *std::max_element( tilt_compensation_factor_.begin(),
00094                                                              tilt_compensation_factor_.end() );
00095     }
00096     else
00097     {
00098       scan_msg_->range_min = range_min_;
00099       scan_msg_->range_max = range_max_;
00100     }
00101 
00102     // Calculate scan message indexes for each depth image column
00103     calcScanMsgIndexForImgCols(depth_msg);
00104 
00105     // Check if scan_height is in image_height
00106     if(scan_height_ / 2 > cy || scan_height_ / 2 > depth_msg->height - cy)
00107     {
00108       std::stringstream ss;
00109       ss << "scan_height ( " << scan_height_ << " pixels) is too large for the image height.";
00110       throw std::runtime_error(ss.str());
00111     }
00112     is_scan_msg_configurated_ = true;
00113   }
00114 
00115   // Prepare laser scan message
00116   scan_msg_->header = depth_msg->header;
00117   if(output_frame_id_.length() > 0)
00118     scan_msg_->header.frame_id = output_frame_id_;
00119 
00120   scan_msg_->ranges.assign(depth_msg->width, std::numeric_limits<float>::quiet_NaN());
00121 
00122   // Check if image encoding is correctly
00123   if (depth_msg->encoding == sensor_msgs::image_encodings::TYPE_16UC1)
00124   {
00125     convertDepthToPolarCoords(depth_msg);
00126   }
00127   else
00128   {
00129     std::stringstream ss;
00130     ss << "Depth image has unsupported encoding: " << depth_msg->encoding;
00131     throw std::runtime_error(ss.str());
00132   }
00133 
00134 #if TIME_MEASUREMENT // End of time measurement
00135   boost::chrono::milliseconds ms = boost::chrono::duration_cast<boost::chrono::milliseconds>
00136       (boost::chrono::high_resolution_clock::now() - start);
00137   ROS_DEBUG_STREAM("\nProcessing takes " << ms.count() << " ms.");
00138 #endif
00139 
00140   return scan_msg_;
00141 }
00142 
00143 //=================================================================================================
00144 void LaserScanKinect::setRangeLimits(const float rmin, const float rmax)
00145 {
00146   if (rmin >= 0 && rmin < rmax)
00147     range_min_ = rmin;
00148   else
00149   {
00150     range_min_ = 0;
00151     ROS_ERROR("Incorrect value of range minimal parameter. Set default value: 0.");
00152   }
00153   if (rmax >= 0 && rmin < rmax)
00154     range_max_ = rmax;
00155   else
00156   {
00157     range_max_ = 10;
00158     ROS_ERROR("Incorrect value of range maximum parameter. Set default value: 10.");
00159   }
00160 }
00161 
00162 //=================================================================================================
00163 void LaserScanKinect::setScanHeight(const int scan_height)
00164 {
00165   if(scan_height > 0)
00166     scan_height_ = scan_height;
00167   else
00168   {
00169     scan_height_ = 10;
00170     ROS_ERROR("Incorrect value of scan height parameter. Set default value: 100.");
00171   }
00172 }
00173 
00174 //=================================================================================================
00175 void LaserScanKinect::setDepthImgRowStep(const int row_step)
00176 {
00177   if( row_step > 0 )
00178     depth_img_row_step_ = row_step;
00179   else
00180   {
00181     depth_img_row_step_ = 1;
00182     ROS_ERROR("Incorrect value depth imgage row step parameter. Set default value: 1.");
00183   }
00184 }
00185 
00186 //=================================================================================================
00187 void LaserScanKinect::setSensorMountHeight (const float height)
00188 {
00189   if( height > 0)
00190     sensor_mount_height_ = height;
00191   else
00192   {
00193     sensor_mount_height_ = 0;
00194     ROS_ERROR("Incorrect value of sensor mount height parameter. Set default value: 0.");
00195   }
00196 }
00197 
00198 //=================================================================================================
00199 void LaserScanKinect::setSensorTiltAngle (const float angle)
00200 {
00201   if( angle < 90 && angle > -90)
00202     sensor_tilt_angle_  = angle;
00203   else
00204   {
00205     sensor_tilt_angle_  = 0;
00206     ROS_ERROR("Incorrect value of sensor tilt angle parameter. Set default value: 0.");
00207   }
00208 }
00209 
00210 //=================================================================================================
00211 void LaserScanKinect::setGroundMargin (const float margin)
00212 {
00213   if( margin > 0)
00214     ground_margin_ = margin;
00215   else
00216   {
00217     ground_margin_ = 0;
00218     ROS_ERROR("Incorrect value of ground margin parameter. Set default value: 0.");
00219   }
00220 }
00221 
00222 //=================================================================================================
00223 // Private methods
00224 //=================================================================================================
00225 double LaserScanKinect::lengthOfVector(const cv::Point3d& vec) const
00226 {
00227   return sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
00228 }
00229 
00230 //=================================================================================================
00231 double LaserScanKinect::angleBetweenRays(const cv::Point3d& ray1, const cv::Point3d& ray2) const
00232 {
00233   double dot = ray1.x * ray2.x + ray1.y * ray2.y + ray1.z * ray2.z;
00234 
00235   return acos(dot / (lengthOfVector(ray1) * lengthOfVector(ray2)));
00236 }
00237 
00238 //=================================================================================================
00239 void LaserScanKinect::fieldOfView( double & min, double & max, double x1, double y1,
00240                                    double xc, double yc, double x2, double y2)
00241 {
00242   cv::Point2d raw_pixel_left(x1, y1);
00243   cv::Point2d rect_pixel_left = camera_model_.rectifyPoint(raw_pixel_left);
00244   cv::Point3d left_ray = camera_model_.projectPixelTo3dRay(rect_pixel_left);
00245 
00246   cv::Point2d raw_pixel_right(x2, y2);
00247   cv::Point2d rect_pixel_right = camera_model_.rectifyPoint(raw_pixel_right);
00248   cv::Point3d right_ray = camera_model_.projectPixelTo3dRay(rect_pixel_right);
00249 
00250   cv::Point2d raw_pixel_center(xc, yc);
00251   cv::Point2d rect_pixel_center = camera_model_.rectifyPoint(raw_pixel_center);
00252   cv::Point3d center_ray = camera_model_.projectPixelTo3dRay(rect_pixel_center);
00253 
00254   min = -angleBetweenRays(center_ray, right_ray);
00255   max = angleBetweenRays(left_ray, center_ray);
00256 
00257   ROS_ASSERT(min < 0 && max > 0);
00258 }
00259 
00260 //=================================================================================================
00261 void LaserScanKinect::calcGroundDistancesForImgRows(double vertical_fov)
00262 {
00263   const double alpha = sensor_tilt_angle_ * M_PI / 180.0; // Sensor tilt angle in radians
00264   const int img_height = camera_model_.fullResolution().height;
00265 
00266   ROS_ASSERT(img_height >= 0);
00267 
00268   dist_to_ground_.resize(img_height);
00269 
00270   for(int i = 0; i < img_height; i++) // Coefficients calculations for each row of image
00271   {
00272     // Angle between ray and optical center
00273     double delta = vertical_fov * (i - camera_model_.cy() - 0.5) / ((double)img_height - 1);
00274 
00275     if ((delta + alpha) > 0)
00276     {
00277       dist_to_ground_[i] = sensor_mount_height_ * sin(M_PI/2-delta) * 1000
00278           / cos(M_PI/2-delta-alpha);
00279       ROS_ASSERT(dist_to_ground_[i] > 0);
00280     }
00281     else
00282       dist_to_ground_[i] = 100 * 1000;
00283   }
00284 }
00285 
00286 //=================================================================================================
00287 void LaserScanKinect::calcTiltCompensationFactorsForImgRows(double vertical_fov)
00288 {
00289   const double alpha = sensor_tilt_angle_ * M_PI / 180.0; // Sensor tilt angle in radians
00290   const int img_height = camera_model_.fullResolution().height;
00291 
00292   ROS_ASSERT(img_height >= 0);
00293 
00294   tilt_compensation_factor_.resize(img_height);
00295 
00296   for(int i = 0; i < img_height; i++) // Process all rows
00297   {
00298     double delta = vertical_fov * (i - camera_model_.cy() - 0.5) / ((double)img_height - 1);
00299 
00300     tilt_compensation_factor_[i] = sin(M_PI/2 - delta - alpha) / sin(M_PI/2 - delta);
00301     ROS_ASSERT(tilt_compensation_factor_[i] > 0 && tilt_compensation_factor_[i] < 10);
00302   }
00303 }
00304 
00305 //=================================================================================================
00306 void LaserScanKinect::calcScanMsgIndexForImgCols(const sensor_msgs::ImageConstPtr& depth_msg)
00307 {
00308   scan_msg_index_.resize((int)depth_msg->width);
00309 
00310   for (int u = 0; u < (int)depth_msg->width; u++)
00311   {
00312     double th = -atan2((double)(u - camera_model_.cx()) * 0.001f / camera_model_.fx(), 0.001f);
00313     scan_msg_index_[u] = (th - scan_msg_->angle_min) / scan_msg_->angle_increment;
00314   }
00315 }
00316 
00317 //=================================================================================================
00318 void LaserScanKinect::convertDepthToPolarCoords(const sensor_msgs::ImageConstPtr &depth_msg)
00319 {
00320   const uint16_t* depth_row = reinterpret_cast<const uint16_t*>(&depth_msg->data[0]);
00321   const int offset = (int)(camera_model_.cy()- scan_height_ / 2);
00322   const int row_size = depth_msg->step / sizeof(uint16_t);
00323 
00324   const unsigned int range_min_mm = range_min_ * 1000;
00325   const unsigned int range_max_mm = range_max_ * 1000;
00326   const int ground_margin_mm = ground_margin_ * 1000;
00327 
00328   // Loop over each column in image
00329   for (int j = 0; j < (int)depth_msg->width; j++)
00330   {
00331     float depth_min = MAX_UINT16;
00332 
00333     // Loop over pixels in column. Calculate z_min in column
00334     for (int i = offset; i < offset + scan_height_; i += depth_img_row_step_)
00335     {
00336       uint16_t depth_raw = depth_row[row_size * i + j];
00337       float depth;
00338 
00339       // Check if tilt compensation is enabled
00340       if ( tilt_compensation_enable_ )
00341         depth = depth_raw * tilt_compensation_factor_[i] / 1000.0f;
00342       else
00343         depth = depth_raw / 1000.0f;
00344 
00345       // Enabled remove floor from scan feature
00346       if ( ground_remove_enable_ )
00347       {
00348         // Find min values in columns
00349         if( depth_raw >= range_min_mm && depth_raw <= range_max_mm &&
00350             depth < depth_min && depth_raw < (dist_to_ground_[i] - ground_margin_mm) )
00351         {
00352           depth_min = depth;
00353         }
00354       }
00355       else // Disabled remove floor from scan feature
00356       {
00357         // Find min values in columns
00358         if( depth_raw >= range_min_mm && depth_raw <= range_max_mm && depth < depth_min )
00359         {
00360           depth_min = depth;
00361         }
00362       }
00363     }
00364     // When the smallest distance in column found then conversion to polar coords
00365     if (depth_min != MAX_UINT16)
00366     {
00367       // Calculate x in XZ ( z = depth )
00368       float x = (j - camera_model_.cx()) * depth_min  / camera_model_.fx();
00369 
00370       // Calculate distance in polar coords
00371       scan_msg_->ranges[ scan_msg_index_[j] ] = sqrt(x * x + depth_min * depth_min);
00372     }
00373     else // No information about distances in j column
00374       scan_msg_->ranges[scan_msg_index_[j]] = NAN;
00375   }
00376 }


laserscan_kinect
Author(s): Michal Drwiega
autogenerated on Thu Jun 6 2019 22:10:52