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 #include "laser_height_estimation/laser_height_estimation.h"
00038
00039 namespace mav
00040 {
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 LaserHeightEstimation");
00048
00049 initialized_ = false;
00050 floor_height_ = 0.0;
00051 prev_height_ = 0.0;
00052
00053 world_to_base_.setIdentity();
00054
00055 ros::NodeHandle nh_mav (nh_, "mav");
00056
00057
00058
00059 if (!nh_private_.getParam ("fixed_frame", world_frame_))
00060 world_frame_ = "/world";
00061 if (!nh_private_.getParam ("base_frame", base_frame_))
00062 base_frame_ = "base_link";
00063 if (!nh_private_.getParam ("footprint_frame", footprint_frame_))
00064 footprint_frame_ = "base_footprint";
00065 if (!nh_private_.getParam ("min_values", min_values_))
00066 min_values_ = 5;
00067 if (!nh_private_.getParam ("max_stdev", max_stdev_))
00068 max_stdev_ = 0.10;
00069 if (!nh_private_.getParam ("max_height_jump", max_height_jump_))
00070 max_height_jump_ = 0.25;
00071 if (!nh_private_.getParam ("use_imu", use_imu_))
00072 use_imu_ = true;
00073 if (!nh_private_.getParam ("use_segmentation", use_segmentation_))
00074 use_segmentation_ = true;
00075 if (!nh_private_.getParam ("bin_size", bin_size_))
00076 bin_size_ = 0.02;
00077
00078
00079
00080 height_to_base_publisher_ = nh_mav.advertise<mav_msgs::Height>(
00081 "height_to_base", 5);
00082 height_to_footprint_publisher_ = nh_mav.advertise<mav_msgs::Height>(
00083 "height_to_footprint", 5);
00084 floor_publisher_ = nh_mav.advertise<mav_msgs::Height>(
00085 "floor_height", 5);
00086
00087
00088
00089 scan_subscriber_ = nh_.subscribe(
00090 "scan", 5, &LaserHeightEstimation::scanCallback, this);
00091 if (use_imu_)
00092 {
00093 imu_subscriber_ = nh_.subscribe(
00094 "imu/data", 5, &LaserHeightEstimation::imuCallback, this);
00095 }
00096 }
00097
00098 LaserHeightEstimation::~LaserHeightEstimation()
00099 {
00100 ROS_INFO("Destroying LaserHeightEstimation");
00101 }
00102
00103 void LaserHeightEstimation::imuCallback (const sensor_msgs::ImuPtr& imu_msg)
00104 {
00105 mutex_.lock();
00106
00107 tf::Quaternion q;
00108 tf::quaternionMsgToTF(imu_msg->orientation, q);
00109 world_to_base_.setRotation(q);
00110
00111 mutex_.unlock();
00112 }
00113
00114 void LaserHeightEstimation::scanCallback (const sensor_msgs::LaserScanPtr& scan_msg)
00115 {
00116 if (!initialized_)
00117 {
00118
00119
00120 if (!setBaseToLaserTf(scan_msg)) return;
00121
00122 initialized_ = true;
00123 last_update_time_ = scan_msg->header.stamp;
00124
00125 return;
00126 }
00127
00128
00129
00130 if(!use_imu_)
00131 {
00132 tf::StampedTransform world_to_base_tf;
00133
00134 try
00135 {
00136 tf_listener_.waitForTransform (
00137 world_frame_, base_frame_, scan_msg->header.stamp, ros::Duration(0.5));
00138 tf_listener_.lookupTransform (
00139 world_frame_, base_frame_, scan_msg->header.stamp, world_to_base_tf);
00140 }
00141 catch (tf::TransformException ex)
00142 {
00143
00144 ROS_WARN ("TF unavailable, skipping scan (%s)", ex.what());
00145 return;
00146 }
00147 world_to_base_.setRotation( world_to_base_tf.getRotation());
00148 }
00149
00150 mutex_.lock();
00151
00152 tf::Transform rotated_laser = world_to_base_ * base_to_laser_;
00153 tf::Transform rotated_footprint = world_to_base_ * base_to_footprint_;
00154
00155 mutex_.unlock();
00156
00157
00158
00159 std::vector<double> values;
00160 for(unsigned int i = 0; i < scan_msg->ranges.size(); i++)
00161 {
00162 if (scan_msg->ranges[i] > scan_msg->range_min && scan_msg->ranges[i] < scan_msg->range_max)
00163 {
00164 double angle = scan_msg->angle_min + i * scan_msg->angle_increment;
00165 tf::Vector3 v(cos(angle)*scan_msg->ranges[i], sin(angle)*scan_msg->ranges[i], 0.0);
00166 tf::Vector3 p = rotated_laser * v;
00167 values.push_back(p.getZ());
00168 }
00169 }
00170
00171
00172
00173 if (!values.empty() && use_segmentation_)
00174 {
00175 std::vector<double> values_seg;
00176
00177 histogramSegmentation(values, values_seg);
00178 values = values_seg;
00179 }
00180
00181
00182
00183 if ((int)values.size() < min_values_)
00184 {
00185 ROS_WARN("Not enough valid values to determine height, skipping (%d collected, %d needed)",
00186 values.size(), min_values_);
00187 return;
00188 }
00189
00190
00191
00192 double mean_value, stdev_value;
00193 getStats(values, mean_value, stdev_value);
00194
00195 if (stdev_value > max_stdev_)
00196 {
00197 ROS_WARN("Stdev of height readings too big to determine height, skipping (stdev is %f, max is %f)",
00198 stdev_value, max_stdev_);
00199 return;
00200 }
00201
00202
00203
00204 double height_to_base = 0.0 - mean_value + floor_height_;
00205 double height_to_footprint = rotated_footprint.getOrigin().getZ() - mean_value + floor_height_;
00206
00207
00208
00209
00210 if (first_time_)
00211 {
00212 first_time_ = false;
00213 prev_height_ = height_to_base;
00214 }
00215
00216 double height_jump = prev_height_ - height_to_base;
00217
00218 if (fabs(height_jump) > max_height_jump_)
00219 {
00220 ROS_WARN("Laser height estimation: floor discontinuity detected");
00221 floor_height_ += height_jump;
00222 height_to_base += height_jump;
00223 height_to_footprint += height_jump;
00224 }
00225
00226 double dt = (scan_msg->header.stamp - last_update_time_).toSec();
00227
00228 double climb = (height_to_base - prev_height_)/dt;
00229 prev_height_ = height_to_base;
00230 last_update_time_ = scan_msg->header.stamp;
00231
00232
00233
00234 mav_msgs::HeightPtr height_to_base_msg;
00235 height_to_base_msg = boost::make_shared<mav_msgs::Height>();
00236 height_to_base_msg->height = height_to_base;
00237 height_to_base_msg->height_variance = stdev_value;
00238 height_to_base_msg->climb = climb;
00239 height_to_base_msg->header.stamp = scan_msg->header.stamp;
00240 height_to_base_msg->header.frame_id = scan_msg->header.frame_id;
00241 height_to_base_publisher_.publish(height_to_base_msg);
00242
00243 mav_msgs::HeightPtr height_to_footprint_msg;
00244 height_to_footprint_msg = boost::make_shared<mav_msgs::Height>();
00245 height_to_footprint_msg->height = height_to_footprint;
00246 height_to_footprint_msg->height_variance = stdev_value;
00247 height_to_footprint_msg->climb = climb;
00248 height_to_footprint_msg->header.stamp = scan_msg->header.stamp;
00249 height_to_footprint_msg->header.frame_id = scan_msg->header.frame_id;
00250 height_to_footprint_publisher_.publish(height_to_footprint_msg);
00251
00252 mav_msgs::HeightPtr floor_msg;
00253 floor_msg = boost::make_shared<mav_msgs::Height>();
00254 floor_msg->height = floor_height_;
00255 floor_msg->header.stamp = scan_msg->header.stamp;
00256 floor_msg->header.frame_id = scan_msg->header.frame_id;
00257 floor_publisher_.publish(floor_msg);
00258 }
00259
00260 bool LaserHeightEstimation::setBaseToLaserTf(const sensor_msgs::LaserScanPtr& scan_msg)
00261 {
00262 ros::Time time = scan_msg->header.stamp;
00263
00264
00265
00266 tf::StampedTransform base_to_laser_tf;
00267 try
00268 {
00269 tf_listener_.waitForTransform(
00270 base_frame_, scan_msg->header.frame_id, time, ros::Duration(3.0));
00271
00272 tf_listener_.lookupTransform (
00273 base_frame_, scan_msg->header.frame_id, time, base_to_laser_tf);
00274 }
00275 catch (tf::TransformException ex)
00276 {
00277
00278 ROS_WARN("%s: Transform unavailable, skipping scan (%s)", ros::this_node::getName().c_str(), ex.what());
00279 return false;
00280 }
00281
00282 base_to_laser_ = base_to_laser_tf;
00283
00284
00285
00286 tf::StampedTransform base_to_footprint_tf;
00287 try
00288 {
00289 tf_listener_.waitForTransform(
00290 base_frame_, footprint_frame_, time, ros::Duration(3.0));
00291
00292 tf_listener_.lookupTransform (
00293 base_frame_, footprint_frame_, time, base_to_footprint_tf);
00294 }
00295 catch (tf::TransformException ex)
00296 {
00297
00298 ROS_WARN("%s: Transform unavailable, skipping scan (%s)", ros::this_node::getName().c_str(), ex.what());
00299 return false;
00300 }
00301
00302 base_to_footprint_ = base_to_footprint_tf;
00303
00304 return true;
00305 }
00306
00307 void LaserHeightEstimation::getStats(const std::vector<double> values, double& ave, double& stdev)
00308 {
00309 double sum = 0.0;
00310 double sumsq = 0.0;
00311
00312 for (size_t i = 0; i < values.size(); ++i)
00313 sum += values[i];
00314
00315 ave = sum/values.size();
00316
00317 for (size_t i = 0; i < values.size(); ++i)
00318 sumsq += (values[i] - ave) * (values[i] - ave);
00319
00320 stdev = sqrt(sumsq/values.size());
00321 }
00322
00323 void LaserHeightEstimation::histogramSegmentation(
00324 const std::vector<double>& input,
00325 std::vector<double>& output)
00326 {
00327
00328
00329 double min_value = input[0];
00330 double max_value = input[0];
00331 for (unsigned int i = 0; i < input.size(); ++i)
00332 {
00333 if (input[i] < min_value) min_value = input[i];
00334 if (input[i] > max_value) max_value = input[i];
00335 }
00336
00337
00338
00339 int n_bins = (max_value - min_value) / bin_size_ + 1;
00340 std::vector<int> bins;
00341 bins.resize(n_bins);
00342
00343 for (unsigned int i = 0; i < bins.size(); ++i)
00344 bins[i] = 0;
00345
00346 for (unsigned int i = 0; i < input.size(); ++i)
00347 {
00348 unsigned int bin_idx = (input[i] - min_value) / bin_size_;
00349 bins[bin_idx]++;
00350 }
00351
00352
00353
00354 unsigned int best_bin_idx = 0;
00355 int best_bin_count = 0;
00356 for (unsigned int i = 0; i < bins.size(); ++i)
00357 {
00358 if (bins[i] > best_bin_count)
00359 {
00360 best_bin_count = bins[i];
00361 best_bin_idx = i;
00362 }
00363 }
00364
00365
00366
00367 output.clear();
00368 for (unsigned int i = 0; i < input.size(); ++i)
00369 {
00370 unsigned int bin_idx = (input[i] - min_value) / bin_size_;
00371 if (bin_idx == best_bin_idx)
00372 output.push_back(input[i]);
00373 }
00374 }
00375
00376 void LaserHeightEstimation::kMeansSegmentation(
00377 const std::vector<double>& input,
00378 std::vector<double>& output)
00379 {
00380
00381
00382 double mean_a = input[0];
00383 double mean_b = input[input.size() - 1];
00384 int count_a, count_b;
00385
00386 std::vector<bool> mask;
00387 mask.resize(input.size());
00388 for(unsigned int i = 0; i < mask.size()-1; ++i)
00389 mask[i] = true;
00390 mask[mask.size()-1] = false;
00391
00392
00393
00394 bool converged = false;
00395 while (!converged)
00396 {
00397 converged = kMeansSegmentation(
00398 input, mask, mean_a, mean_b, count_a, count_b);
00399 }
00400
00401 if (std::abs(mean_a - mean_b) > max_height_jump_)
00402 ROS_WARN("Floor discontinuity in clusters");
00403
00404
00405
00406 if (count_a >= count_b)
00407 {
00408 for (unsigned int i = 0; i < input.size(); ++i)
00409 if (mask[i]) output.push_back(input[i]);
00410 }
00411 else
00412 {
00413 for (unsigned int i = 0; i < input.size(); ++i)
00414 if (!mask[i]) output.push_back(input[i]);
00415 }
00416 }
00417
00418 bool LaserHeightEstimation::kMeansSegmentation(
00419 const std::vector<double>& input,
00420 std::vector<bool>& mask,
00421 double& mean_a, double& mean_b,
00422 int& count_a, int& count_b)
00423 {
00424
00425
00426
00427 bool converged = true;
00428 double sum_a = 0.0;
00429 double sum_b = 0.0;
00430 count_a = 0;
00431 count_b = 0;
00432
00433 for (unsigned int i = 0; i < input.size(); ++i)
00434 {
00435 double da2 = (input[i] - mean_a) * (input[i] - mean_a);
00436 double db2 = (input[i] - mean_b) * (input[i] - mean_b);
00437
00438 if (da2 <= db2)
00439 {
00440
00441 if (!mask[i]) converged = false;
00442 mask[i] = true;
00443 sum_a += input[i];
00444 ++count_a;
00445 }
00446 else
00447 {
00448
00449 if (mask[i]) converged = false;
00450 mask[i] = false;
00451 sum_b += input[i];
00452 ++count_b;
00453 }
00454 }
00455
00456
00457
00458 if (count_a == 0 || count_b == 0) return true;
00459
00460 mean_a = sum_a / (double)count_a;
00461 mean_b = sum_b / (double)count_b;
00462
00463 return converged;
00464 }
00465
00466
00467 };