laser_height.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  * Copyright (c) 2010,
00005  *  Ivan Dryanovski <ivan.dryanovski@gmail.com>
00006  *  William Morris <morris@ee.ccny.cuny.edu>
00007  *  Henrique Silva <henrique.rd.silva@gmail.com>
00008  *  All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted provided that the following conditions
00012  * are met:
00013  *
00014  *  * Redistributions of source code must retain the above copyright
00015  *    notice, this list of conditions and the following disclaimer.
00016  *  * Redistributions in binary form must reproduce the above
00017  *    copyright notice, this list of conditions and the following
00018  *    disclaimer in the documentation and/or other materials provided
00019  *    with the distribution.
00020  *  * Neither the name of CCNY Robotics Lab nor the names of its
00021  *    contributors may be used to endorse or promote products derived
00022  *    from this software without specific prior written permission.
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00025  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00026  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00027  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00028  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00029  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00030  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00031  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00032  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00033  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00034  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00035  * POSSIBILITY OF SUCH DAMAGE.
00036 */
00037 
00038 #include "laser_height/laser_height.h"
00039 
00040 namespace mav{
00041 
00042 LaserHeightEstimation::LaserHeightEstimation(ros::NodeHandle nh, ros::NodeHandle nh_private):
00043   nh_(nh),
00044   nh_private_(nh_private),
00045   first_time_(true)
00046 {
00047   ROS_INFO("Starting LaserHeight");
00048 
00049   initialized_  = false;
00050   prev_height_  = 0.0;
00051 
00052   world_to_base_.setIdentity();
00053 
00054   ros::NodeHandle nh_mav (nh_, "quad_height");
00055 
00056   // Parameters
00057   nh_private_.param<std::string>("global_frame", world_frame_, "map");
00058   nh_private_.param<std::string>("stabilized_frame", stabilized_frame_, "base_stabilized");
00059   nh_private_.param<std::string>("base_frame", base_frame_, "base_link");
00060 
00061   nh_private_.param<std::string>("imu_topic", imu_topic_, "/imu/data");
00062   nh_private_.param<std::string>("scan_topic", scan_topic_, "scan");
00063 
00064   nh_private_.param("min_values", min_values_, 5);
00065   nh_private_.param("max_stdev", max_stdev_, 0.1);
00066   nh_private_.param("max_height_jump", max_height_jump_, 0.25);
00067   nh_private_.param("bin_size", bin_size_, 0.2);
00068 
00069   nh_private_.param<bool>("use_imu", use_imu_, true);
00070   nh_private_.param<bool>("use_segmentation", use_segmentation_, true);
00071 
00072   // Publishers
00073   height_to_base_publisher_ = nh_mav.advertise<mav_msgs::Height>("height_to_base", 5);
00074   height_to_footprint_publisher_ = nh_mav.advertise<mav_msgs::Height>("height_to_footprint", 5);
00075 
00076   // Subscribers
00077   scan_subscriber_ = nh_.subscribe(scan_topic_, 5, &LaserHeightEstimation::scanCallback, this);
00078   if (use_imu_){
00079     imu_subscriber_ = nh_.subscribe(imu_topic_, 5, &LaserHeightEstimation::imuCallback, this);
00080   }
00081 }
00082 
00083 LaserHeightEstimation::~LaserHeightEstimation(){
00084   ROS_INFO("Destroying LaserHeightEstimation");
00085 }
00086 
00087 void LaserHeightEstimation::imuCallback (const sensor_msgs::ImuPtr& imu_msg){
00088 
00089   mutex_.lock();
00090 
00091   tf::Quaternion q;
00092   tf::quaternionMsgToTF(imu_msg->orientation, q);   
00093   world_to_base_.setRotation(q);
00094 
00095   mutex_.unlock();
00096 }
00097 
00098 void LaserHeightEstimation::scanCallback (const sensor_msgs::LaserScanPtr& scan_msg){
00099   if (!initialized_){
00100     // if this is the first scan, lookup the static base to laser tf
00101     // if tf is not available yet, skip this scan
00102     if (!setBaseToLaserTf(scan_msg)) return;
00103 
00104     initialized_ = true;
00105     last_update_time_ = scan_msg->header.stamp;
00106 
00107     return;
00108   }
00109 
00110   // Get required transforms
00111   if(!use_imu_){
00112     tf::StampedTransform world_to_base_tf;
00113 
00114     try{
00115       tf_listener_.waitForTransform (
00116         world_frame_, base_frame_, scan_msg->header.stamp, ros::Duration(0.5));
00117       tf_listener_.lookupTransform (
00118         world_frame_, base_frame_, scan_msg->header.stamp, world_to_base_tf);
00119     }
00120     catch (tf::TransformException ex){
00121       // transform unavailable - skip scan
00122       ROS_WARN ("TF unavailable, skipping scan (%s)", ex.what());
00123       return;
00124     }
00125     world_to_base_.setRotation( world_to_base_tf.getRotation());
00126   }
00127 
00128   mutex_.lock();
00129 
00130   tf::Transform rotated_laser     = world_to_base_ * base_to_laser_;
00131   tf::Transform rotated_footprint = world_to_base_ * base_to_footprint_;
00132 
00133   mutex_.unlock();
00134 
00135   // Get vector of height values
00136   std::vector<double> values;
00137   for(unsigned int i = 0; i < scan_msg->ranges.size(); i++){
00138     if (scan_msg->ranges[i] > scan_msg->range_min && scan_msg->ranges[i] < scan_msg->range_max){
00139       double angle = scan_msg->angle_min + i * scan_msg->angle_increment;
00140       tf::Vector3 v(cos(angle)*scan_msg->ranges[i], sin(angle)*scan_msg->ranges[i], 0.0);
00141       tf::Vector3 p = rotated_laser * v;
00142       values.push_back(p.getZ());
00143      }
00144   }
00145 
00146   // Segment if required
00147   if (!values.empty() && use_segmentation_){
00148     std::vector<double> values_seg;
00149     histogramSegmentation(values, values_seg);
00150     values = values_seg;
00151   }
00152 
00153   // Check if we have enough values present
00154   if ((int)values.size() < min_values_){
00155     ROS_WARN("Not enough valid values to determine height, skipping (%d collected, %d needed)",values.size(), min_values_);
00156     return;
00157   }
00158 
00159   // Get mean and standard dev
00160   double mean_value, stdev_value;
00161   getStats(values, mean_value, stdev_value);
00162 
00163   if (stdev_value > max_stdev_){
00164     ROS_WARN("Stdev of height readings too big to determine height, skipping (stdev is %f, max is %f)",stdev_value, max_stdev_);
00165     return;
00166   }
00167 
00168   // Estimate height (to base and to footprint)
00169   double height_to_base = 0.0 - 0.13 - 0.375 - mean_value;
00170   double height_to_footprint = rotated_footprint.getOrigin().getZ() - mean_value;
00171   //ROS_INFO("2Base:%f, 2Footprint:%f",height_to_base,height_to_footprint);
00172   ROS_INFO("Height:%f",height_to_base);
00173 
00174   // Check for discontinuity
00175   // Prevent first-time discontinuities
00176   if (first_time_){
00177     first_time_ = false;
00178     prev_height_ = height_to_base;
00179   }
00180 
00181   double height_jump = prev_height_ - height_to_base;
00182 
00183   if (fabs(height_jump) > max_height_jump_){
00184     ROS_WARN("Laser height estimation: floor discontinuity detected");
00185     height_to_base += height_jump;
00186     height_to_footprint += height_jump;
00187   }
00188 
00189   double dt = (scan_msg->header.stamp - last_update_time_).toSec();
00190 
00191   double climb = (height_to_base - prev_height_)/dt;
00192   prev_height_ = height_to_base;
00193   last_update_time_ = scan_msg->header.stamp;
00194 
00195   // Publish height message
00196   mav_msgs::HeightPtr height_to_base_msg;
00197   height_to_base_msg = boost::make_shared<mav_msgs::Height>();
00198   height_to_base_msg->height = height_to_base;
00199   height_to_base_msg->height_variance = stdev_value;
00200   height_to_base_msg->climb = climb;
00201   height_to_base_msg->header.stamp = scan_msg->header.stamp;
00202   height_to_base_msg->header.frame_id = scan_msg->header.frame_id;  //TODO:wrong frame
00203   height_to_base_publisher_.publish(height_to_base_msg);
00204 
00205   mav_msgs::HeightPtr height_to_footprint_msg;
00206   height_to_footprint_msg = boost::make_shared<mav_msgs::Height>();
00207   height_to_footprint_msg->height = height_to_footprint;
00208   height_to_footprint_msg->height_variance = stdev_value;
00209   height_to_footprint_msg->climb = climb;
00210   height_to_footprint_msg->header.stamp = scan_msg->header.stamp;
00211   height_to_footprint_msg->header.frame_id = scan_msg->header.frame_id; //TODO:wrong frame
00212   height_to_footprint_publisher_.publish(height_to_footprint_msg);
00213 
00214 }
00215 
00216 bool LaserHeightEstimation::setBaseToLaserTf(const sensor_msgs::LaserScanPtr& scan_msg){
00217   ros::Time time = scan_msg->header.stamp;
00218 
00219   // Get transform base to laser
00220   tf::StampedTransform base_to_laser_tf;
00221   try{
00222     tf_listener_.waitForTransform(
00223       base_frame_, scan_msg->header.frame_id, time, ros::Duration(3.0));
00224 
00225     tf_listener_.lookupTransform (
00226       base_frame_, scan_msg->header.frame_id, time, base_to_laser_tf);
00227   }
00228   catch (tf::TransformException ex){
00229     // transform unavailable - skip scan
00230     ROS_WARN("%s: Transform unavailable, skipping scan (%s)", ros::this_node::getName().c_str(), ex.what());
00231     return false;
00232   }
00233 
00234   base_to_laser_ = base_to_laser_tf;
00235 
00236   // Get transform base to base_footprint
00237   tf::StampedTransform base_to_footprint_tf;
00238   try{
00239     tf_listener_.waitForTransform(
00240       base_frame_, stabilized_frame_, time, ros::Duration(3.0));
00241 
00242     tf_listener_.lookupTransform (
00243       base_frame_, stabilized_frame_, time, base_to_footprint_tf);
00244   }
00245   catch (tf::TransformException ex){
00246     // transform unavailable - skip scan
00247     ROS_WARN("%s: Transform unavailable, skipping scan (%s)", ros::this_node::getName().c_str(), ex.what());
00248     return false;
00249   }
00250 
00251   base_to_footprint_ = base_to_footprint_tf;
00252 
00253   return true;
00254 }
00255 
00256 void LaserHeightEstimation::getStats(const std::vector<double> values, double& ave, double& stdev){
00257   double sum   = 0.0;
00258   double sumsq = 0.0;
00259 
00260   for (size_t i = 0; i < values.size(); ++i)
00261     sum += values[i];
00262 
00263   ave = sum/values.size();
00264 
00265   for (size_t i = 0; i < values.size(); ++i)
00266     sumsq += (values[i] - ave) * (values[i] - ave);
00267 
00268   stdev = sqrt(sumsq/values.size());
00269 }
00270 
00271 void LaserHeightEstimation::histogramSegmentation(const std::vector<double>& input,std::vector<double>& output){
00272   // Get min and max bounds
00273   double min_value = input[0];
00274   double max_value = input[0];
00275   for (unsigned int i = 0; i < input.size(); ++i){
00276     if (input[i] < min_value) min_value = input[i];
00277     if (input[i] > max_value) max_value = input[i];
00278   }  
00279 
00280   // Create histogram
00281   int n_bins = (max_value - min_value) / bin_size_ + 1;
00282   std::vector<int> bins;
00283   bins.resize(n_bins);
00284 
00285   for (unsigned int i = 0; i < bins.size(); ++i)
00286     bins[i] = 0; 
00287 
00288   for (unsigned int i = 0; i < input.size(); ++i)
00289   { 
00290     unsigned int bin_idx = (input[i] - min_value) / bin_size_;
00291     bins[bin_idx]++;
00292   } 
00293 
00294   // Get max of histogram
00295   unsigned int best_bin_idx = 0;
00296   int best_bin_count = 0;
00297   for (unsigned int i = 0; i < bins.size(); ++i){
00298     if (bins[i] > best_bin_count){
00299       best_bin_count = bins[i];
00300       best_bin_idx = i;
00301     }
00302   }
00303 
00304   // Find inliers
00305   output.clear();
00306   for (unsigned int i = 0; i < input.size(); ++i){
00307     unsigned int bin_idx = (input[i] - min_value) / bin_size_;
00308     if (bin_idx == best_bin_idx)
00309       output.push_back(input[i]);
00310   }
00311 }
00312 
00313 };// namespace mav


laser_height
Author(s): Created by Ivan Dryanovski, modified by Henrique Silva
autogenerated on Mon Jan 6 2014 11:47:43