00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
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
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
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
00101
00102 if (!setBaseToLaserTf(scan_msg)) return;
00103
00104 initialized_ = true;
00105 last_update_time_ = scan_msg->header.stamp;
00106
00107 return;
00108 }
00109
00110
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
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
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
00147 if (!values.empty() && use_segmentation_){
00148 std::vector<double> values_seg;
00149 histogramSegmentation(values, values_seg);
00150 values = values_seg;
00151 }
00152
00153
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
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
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
00172 ROS_INFO("Height:%f",height_to_base);
00173
00174
00175
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
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;
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;
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
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
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
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
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
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
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
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
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 };