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 {
00046 ROS_INFO("%s: Starting LaserHeightEstimation", ros::this_node::getName().c_str());
00047
00048 initialized_ = false;
00049 floor_height_ = 0.0;
00050 prev_height_ = 0.0;
00051
00052 ros::NodeHandle nh_mav (nh_, mav::ROS_NAMESPACE);
00053
00054
00055 if (!nh_private_.getParam ("fixed_frame", world_frame_))
00056 world_frame_ = "/world";
00057 if (!nh_private_.getParam ("base_frame", base_frame_))
00058 base_frame_ = "base_link";
00059 if (!nh_private_.getParam ("footprint_frame", footprint_frame_))
00060 footprint_frame_ = "base_footprint";
00061 if (!nh_private_.getParam ("min_values", min_values_))
00062 min_values_ = 5;
00063 if (!nh_private_.getParam ("max_stdev", max_stdev_))
00064 max_stdev_ = 0.10;
00065 if (!nh_private_.getParam ("max_height_jump", max_height_jump_))
00066 max_height_jump_ = 0.25;
00067 if (!nh_private_.getParam ("use_imu", use_imu_))
00068 use_imu_ = true;
00069
00070
00071
00072 scan_subscriber_ = nh_.subscribe(
00073 scan_topic_, 5, &LaserHeightEstimation::scanCallback, this);
00074 if (use_imu_)
00075 {
00076 imu_subscriber_ = nh_.subscribe(
00077 mav::IMU_TOPIC, 5, &LaserHeightEstimation::imuCallback, this);
00078 }
00079
00080
00081
00082 height_to_base_publisher_ = nh_mav.advertise<mav_msgs::Height>(
00083 mav::HEIGHT_TO_BASE_TOPIC, 5);
00084
00085 height_to_footprint_publisher_ = nh_mav.advertise<mav_msgs::Height>(
00086 mav::HEIGHT_TO_FOOTPRINT_TOPIC, 5);
00087 }
00088
00089 LaserHeightEstimation::~LaserHeightEstimation()
00090 {
00091 ROS_INFO("%s: Destroying LaserHeightEstimation", ros::this_node::getName().c_str());
00092 }
00093
00094 void LaserHeightEstimation::imuCallback (const sensor_msgs::ImuPtr& imu_msg)
00095 {
00096 latest_imu_msg_ = *imu_msg;
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108 }
00109
00110 void LaserHeightEstimation::scanCallback (const sensor_msgs::LaserScanPtr& scan_msg)
00111 {
00112 if (!initialized_)
00113 {
00114
00115
00116 if (!setBaseToLaserTf(scan_msg)) return;
00117 initialized_ = true;
00118
00119 last_update_time_ = scan_msg->header.stamp;
00120 return;
00121 }
00122
00123
00124
00125 if(use_imu_)
00126 {
00127 world_to_base_.setIdentity();
00128 btQuaternion q;
00129 tf::quaternionMsgToTF(latest_imu_msg_.orientation, q);
00130 world_to_base_.setRotation(q);
00131 }
00132 else
00133 {
00134 tf::StampedTransform world_to_base_tf;
00135
00136 try
00137 {
00138 tf_listener_.waitForTransform (
00139 world_frame_, base_frame_, scan_msg->header.stamp, ros::Duration(0.5));
00140 tf_listener_.lookupTransform (
00141 world_frame_, base_frame_, scan_msg->header.stamp, world_to_base_tf);
00142 }
00143 catch (tf::TransformException ex)
00144 {
00145
00146 ROS_WARN ("%s: Skipping scan (%s)", ros::this_node::getName().c_str(), ex.what ());
00147 return;
00148 }
00149 world_to_base_.setRotation( world_to_base_tf.getRotation());
00150 }
00151
00152 btTransform rotated_laser = world_to_base_ * base_to_laser_;
00153 btTransform rotated_footprint = world_to_base_ * base_to_footprint_;
00154
00155
00156
00157 std::vector<double> values;
00158 for(unsigned int i = 0; i < scan_msg->ranges.size(); i++)
00159 {
00160 if (scan_msg->ranges[i] > scan_msg->range_min && scan_msg->ranges[i] < scan_msg->range_max)
00161 {
00162 double angle = scan_msg->angle_min + i * scan_msg->angle_increment;
00163 btVector3 v(cos(angle)*scan_msg->ranges[i], sin(angle)*scan_msg->ranges[i], 0.0);
00164 btVector3 p = rotated_laser * v;
00165
00166 values.push_back(p.getZ());
00167 }
00168 }
00169
00170 if (values.size() < min_values_)
00171 {
00172 ROS_WARN("%s: Not enough valid values to determine height, skipping (%d collected, %d needed)",
00173 ros::this_node::getName().c_str(), values.size(), min_values_);
00174 return;
00175 }
00176
00177
00178
00179 double mean_value, stdev_value;
00180 getStats(values, mean_value, stdev_value);
00181
00182 if (stdev_value > max_stdev_)
00183 {
00184 ROS_WARN("%s: Stdev of height readings too big to determine height, skipping (stdev is %f, max is %f)",
00185 ros::this_node::getName().c_str(), stdev_value, max_stdev_);
00186 return;
00187 }
00188
00189
00190
00191 double height_to_base = 0.0 - mean_value + floor_height_;
00192 double height_to_footprint = rotated_footprint.getOrigin().getZ() - mean_value + floor_height_;
00193
00194
00195
00196 double height_jump = prev_height_ - height_to_base;
00197
00198 if (fabs(height_jump) > max_height_jump_)
00199 {
00200 ROS_WARN("%s: Laser Height Estimation: Floor Discontinuity detected", ros::this_node::getName().c_str());
00201 floor_height_ += height_jump;
00202 height_to_base += height_jump;
00203 height_to_footprint += height_jump;
00204 }
00205
00206 double dt = (scan_msg->header.stamp - last_update_time_).toSec();
00207
00208 double climb = (height_to_base - prev_height_)/dt;
00209 prev_height_ = height_to_base;
00210 last_update_time_ = scan_msg->header.stamp;
00211
00212
00213
00214 mav_msgs::HeightPtr height_to_base_msg;
00215 height_to_base_msg = boost::make_shared<mav_msgs::Height>();
00216 height_to_base_msg->height = height_to_base;
00217 height_to_base_msg->height_variance = stdev_value;
00218 height_to_base_msg->climb = climb;
00219 height_to_base_msg->header.stamp = scan_msg->header.stamp;
00220 height_to_base_msg->header.frame_id = scan_msg->header.frame_id;
00221
00222 mav_msgs::HeightPtr height_to_footprint_msg;
00223 height_to_footprint_msg = boost::make_shared<mav_msgs::Height>();
00224 height_to_footprint_msg->height = height_to_footprint;
00225 height_to_footprint_msg->height_variance = stdev_value;
00226 height_to_footprint_msg->climb = climb;
00227 height_to_footprint_msg->header.stamp = scan_msg->header.stamp;
00228 height_to_footprint_msg->header.frame_id = scan_msg->header.frame_id;
00229
00230 height_to_base_publisher_.publish(height_to_base_msg);
00231 height_to_footprint_publisher_.publish(height_to_footprint_msg);
00232 }
00233
00234 bool LaserHeightEstimation::setBaseToLaserTf(const sensor_msgs::LaserScanPtr& scan_msg)
00235 {
00236 ros::Time time = scan_msg->header.stamp;
00237
00238
00239
00240 tf::StampedTransform base_to_laser_tf;
00241 try
00242 {
00243 tf_listener_.waitForTransform(
00244 base_frame_, scan_msg->header.frame_id, time, ros::Duration(3.0));
00245
00246 tf_listener_.lookupTransform (
00247 base_frame_, scan_msg->header.frame_id, time, base_to_laser_tf);
00248 }
00249 catch (tf::TransformException ex)
00250 {
00251
00252 ROS_WARN("%s: Transform unavailable, skipping scan (%s)", ros::this_node::getName().c_str(), ex.what());
00253 return false;
00254 }
00255
00256 base_to_laser_ = base_to_laser_tf;
00257
00258
00259
00260 tf::StampedTransform base_to_footprint_tf;
00261 try
00262 {
00263 tf_listener_.waitForTransform(
00264 base_frame_, footprint_frame_, time, ros::Duration(3.0));
00265
00266 tf_listener_.lookupTransform (
00267 base_frame_, footprint_frame_, time, base_to_footprint_tf);
00268 }
00269 catch (tf::TransformException ex)
00270 {
00271
00272 ROS_WARN("%s: Transform unavailable, skipping scan (%s)", ros::this_node::getName().c_str(), ex.what());
00273 return false;
00274 }
00275
00276 base_to_footprint_ = base_to_footprint_tf;
00277
00278 return true;
00279 }
00280
00281 void LaserHeightEstimation::getStats(const std::vector<double> values, double& ave, double& stdev)
00282 {
00283 double sum = 0.0;
00284 double sumsq = 0.0;
00285
00286 for (size_t i = 0; i < values.size(); ++i)
00287 sum += values[i];
00288
00289 ave = sum/values.size();
00290
00291 for (size_t i = 0; i < values.size(); ++i)
00292 sumsq += (values[i] - ave) * (values[i] - ave);
00293
00294 stdev = sqrt(sumsq/values.size());
00295 }
00296 };