00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <humanoid_localization/HumanoidLocalization.h>
00022 #include <iostream>
00023 #include <pcl/keypoints/uniform_sampling.h>
00024
00025 #include <pcl_ros/transforms.h>
00026
00027 #if PCL_VERSION_COMPARE(>=,1,7,0)
00028 #include<pcl_conversions/pcl_conversions.h>
00029 #endif
00030
00031
00032 #define _BENCH_TIME 0
00033
00034 namespace humanoid_localization{
00035 HumanoidLocalization::HumanoidLocalization(unsigned randomSeed)
00036 :
00037 m_rngEngine(randomSeed),
00038 m_rngNormal(m_rngEngine, NormalDistributionT(0.0, 1.0)),
00039 m_rngUniform(m_rngEngine, UniformDistributionT(0.0, 1.0)),
00040 m_nh(),m_privateNh("~"),
00041 m_odomFrameId("odom"), m_targetFrameId("odom"), m_baseFrameId("torso"), m_baseFootprintId("base_footprint"), m_globalFrameId("map"),
00042 m_useRaycasting(true), m_initFromTruepose(false), m_numParticles(500),
00043 m_sensorSampleDist(0.2),
00044 m_nEffFactor(1.0), m_minParticleWeight(0.0),
00045 m_bestParticleIdx(-1), m_lastIMUMsgBuffer(5),
00046 m_bestParticleAsMean(true),
00047 m_receivedSensorData(false), m_initialized(false), m_initGlobal(false), m_paused(false),
00048 m_syncedTruepose(false),
00049 m_observationThresholdTrans(0.1), m_observationThresholdRot(M_PI/6),
00050 m_observationThresholdHeadYawRot(0.5), m_observationThresholdHeadPitchRot(0.3),
00051 m_temporalSamplingRange(0.1), m_transformTolerance(0.1),
00052 m_groundFilterPointCloud(true), m_groundFilterDistance(0.04),
00053 m_groundFilterAngle(0.15), m_groundFilterPlaneDistance(0.07),
00054 m_sensorSampleDistGroundFactor(3),
00055 m_headYawRotationLastScan(0.0), m_headPitchRotationLastScan(0.0),
00056 m_useIMU(false),
00057 m_constrainMotionZ (false), m_constrainMotionRP(false), m_useTimer(false), m_timerPeriod(0.1)
00058 {
00059
00060 m_latest_transform.setData (tf::Transform(tf::createIdentityQuaternion()) );
00061
00062 m_privateNh.param("use_raycasting", m_useRaycasting, m_useRaycasting);
00063
00064 m_privateNh.param("odom_frame_id", m_odomFrameId, m_odomFrameId);
00065 m_privateNh.param("target_frame_id", m_targetFrameId, m_targetFrameId);
00066 m_privateNh.param("base_frame_id", m_baseFrameId, m_baseFrameId);
00067 m_privateNh.param("base_footprint_id", m_baseFootprintId, m_baseFootprintId);
00068 m_privateNh.param("global_frame_id", m_globalFrameId, m_globalFrameId);
00069 m_privateNh.param("init_from_truepose", m_initFromTruepose, m_initFromTruepose);
00070 m_privateNh.param("init_global", m_initGlobal, m_initGlobal);
00071 m_privateNh.param("best_particle_as_mean", m_bestParticleAsMean, m_bestParticleAsMean);
00072 m_privateNh.param("num_particles", m_numParticles, m_numParticles);
00073 m_privateNh.param("neff_factor", m_nEffFactor, m_nEffFactor);
00074 m_privateNh.param("min_particle_weight", m_minParticleWeight, m_minParticleWeight);
00075
00076 m_privateNh.param("initial_pose/x", m_initPose(0), 0.0);
00077 m_privateNh.param("initial_pose/y", m_initPose(1), 0.0);
00078 m_privateNh.param("initial_pose/z", m_initPose(2), 0.32);
00079 m_privateNh.param("initial_pose/roll", m_initPose(3), 0.0);
00080 m_privateNh.param("initial_pose/pitch", m_initPose(4), 0.0);
00081 m_privateNh.param("initial_pose/yaw", m_initPose(5), 0.0);
00082 m_privateNh.param("initial_pose_real_zrp", m_initPoseRealZRP, false);
00083
00084 m_privateNh.param("initial_std/x", m_initNoiseStd(0), 0.1);
00085 m_privateNh.param("initial_std/y", m_initNoiseStd(1), 0.1);
00086 m_privateNh.param("initial_std/z", m_initNoiseStd(2), 0.02);
00087 m_privateNh.param("initial_std/roll", m_initNoiseStd(3), 0.04);
00088 m_privateNh.param("initial_std/pitch", m_initNoiseStd(4), 0.04);
00089 m_privateNh.param("initial_std_yaw", m_initNoiseStd(5), M_PI/12);
00090
00091 if (m_privateNh.hasParam("num_sensor_beams"))
00092 ROS_WARN("Parameter \"num_sensor_beams\" is no longer used, use \"sensor_sampling_dist\" instead");
00093
00094
00095 m_privateNh.param("sensor_sampling_dist", m_sensorSampleDist, m_sensorSampleDist);
00096 m_privateNh.param("max_range", m_filterMaxRange, 30.0);
00097 m_privateNh.param("min_range", m_filterMinRange, 0.05);
00098 ROS_DEBUG("Using a range filter of %f to %f", m_filterMinRange, m_filterMaxRange);
00099
00100 m_privateNh.param("update_min_trans", m_observationThresholdTrans, m_observationThresholdTrans);
00101 m_privateNh.param("update_min_rot", m_observationThresholdRot, m_observationThresholdRot);
00102 m_privateNh.param("update_min_head_yaw", m_observationThresholdHeadYawRot, m_observationThresholdHeadYawRot);
00103 m_privateNh.param("update_min_head_pitch", m_observationThresholdHeadPitchRot, m_observationThresholdHeadPitchRot);
00104 m_privateNh.param("temporal_sampling_range", m_temporalSamplingRange, m_temporalSamplingRange);
00105 m_privateNh.param("transform_tolerance", m_transformTolerance, m_transformTolerance);
00106
00107 m_privateNh.param("use_imu", m_useIMU, m_useIMU);
00108 m_privateNh.param("constrain_motion_z", m_constrainMotionZ, m_constrainMotionZ);
00109 m_privateNh.param("constrain_motion_rp", m_constrainMotionRP, m_constrainMotionRP);
00110
00111
00112 m_privateNh.param("ground_filter_point_cloud", m_groundFilterPointCloud, m_groundFilterPointCloud);
00113 m_privateNh.param("ground_filter_distance", m_groundFilterDistance, m_groundFilterDistance);
00114 m_privateNh.param("ground_filter_angle", m_groundFilterAngle, m_groundFilterAngle);
00115 m_privateNh.param("ground_filter_plane_distance", m_groundFilterPlaneDistance, m_groundFilterPlaneDistance);
00116 m_privateNh.param("sensor_sampling_dist_ground_factor", m_sensorSampleDistGroundFactor, m_sensorSampleDistGroundFactor);
00117
00118 m_privateNh.param("use_timer", m_useTimer, m_useTimer);
00119 m_privateNh.param("timer_period", m_timerPeriod, m_timerPeriod);
00120
00121
00122
00123 m_motionModel = boost::shared_ptr<MotionModel>(new MotionModel(&m_privateNh, &m_rngEngine, &m_tfListener, m_odomFrameId, m_baseFrameId));
00124
00125 if (m_useRaycasting){
00126 m_mapModel = boost::shared_ptr<MapModel>(new OccupancyMap(&m_privateNh));
00127 m_observationModel = boost::shared_ptr<ObservationModel>(new RaycastingModel(&m_privateNh, m_mapModel, &m_rngEngine));
00128 } else{
00129 #ifndef SKIP_ENDPOINT_MODEL
00130
00131 m_mapModel = boost::shared_ptr<MapModel>(new OccupancyMap(&m_privateNh));
00132 m_observationModel = boost::shared_ptr<ObservationModel>(new EndpointModel(&m_privateNh, m_mapModel, &m_rngEngine));
00133 #else
00134 ROS_FATAL("EndpointModel not compiled due to missing dynamicEDT3D");
00135 exit(-1);
00136 #endif
00137 }
00138
00139
00140 m_particles.resize(m_numParticles);
00141 m_poseArray.poses.resize(m_numParticles);
00142 m_poseArray.header.frame_id = m_globalFrameId;
00143 m_tfListener.clear();
00144
00145
00146
00147 m_posePub = m_nh.advertise<geometry_msgs::PoseWithCovarianceStamped>("pose", 10);
00148 m_poseEvalPub = m_nh.advertise<geometry_msgs::PoseWithCovarianceStamped>("pose_eval", 10);
00149 m_poseOdomPub = m_privateNh.advertise<geometry_msgs::PoseStamped>("pose_odom_sync", 10);
00150 m_poseArrayPub = m_privateNh.advertise<geometry_msgs::PoseArray>("particlecloud", 10);
00151 m_bestPosePub = m_privateNh.advertise<geometry_msgs::PoseArray>("best_particle", 10);
00152 m_nEffPub = m_privateNh.advertise<std_msgs::Float32>("n_eff", 10);
00153 m_filteredPointCloudPub = m_privateNh.advertise<sensor_msgs::PointCloud2>("filtered_cloud", 1);
00154
00155
00156
00157 reset();
00158
00159
00160 m_globalLocSrv = m_nh.advertiseService("global_localization", &HumanoidLocalization::globalLocalizationCallback, this);
00161
00162
00163 m_laserSub = new message_filters::Subscriber<sensor_msgs::LaserScan>(m_nh, "scan", 100);
00164 m_laserFilter = new tf::MessageFilter<sensor_msgs::LaserScan>(*m_laserSub, m_tfListener, m_odomFrameId, 100);
00165 m_laserFilter->registerCallback(boost::bind(&HumanoidLocalization::laserCallback, this, _1));
00166
00167
00168 m_pointCloudSub = new message_filters::Subscriber<sensor_msgs::PointCloud2>(m_nh, "point_cloud", 100);
00169 m_pointCloudFilter = new tf::MessageFilter<sensor_msgs::PointCloud2>(*m_pointCloudSub, m_tfListener, m_odomFrameId, 100);
00170 m_pointCloudFilter->registerCallback(boost::bind(&HumanoidLocalization::pointCloudCallback, this, _1));
00171
00172
00173 m_initPoseSub = new message_filters::Subscriber<geometry_msgs::PoseWithCovarianceStamped>(m_nh, "initialpose", 2);
00174 m_initPoseFilter = new tf::MessageFilter<geometry_msgs::PoseWithCovarianceStamped>(*m_initPoseSub, m_tfListener, m_globalFrameId, 2);
00175 m_initPoseFilter->registerCallback(boost::bind(&HumanoidLocalization::initPoseCallback, this, _1));
00176
00177
00178 m_pauseIntegrationSub = m_privateNh.subscribe("pause_localization", 1, &HumanoidLocalization::pauseLocalizationCallback, this);
00179 m_pauseLocSrv = m_privateNh.advertiseService("pause_localization_srv", &HumanoidLocalization::pauseLocalizationSrvCallback, this);
00180 m_resumeLocSrv = m_privateNh.advertiseService("resume_localization_srv", &HumanoidLocalization::resumeLocalizationSrvCallback, this);
00181
00182 if (m_useIMU)
00183 m_imuSub = m_nh.subscribe("imu", 5, &HumanoidLocalization::imuCallback, this);
00184 if (m_useTimer)
00185 {
00186 m_timer = m_nh.createTimer(ros::Duration(m_timerPeriod), &HumanoidLocalization::timerCallback, this);
00187 ROS_INFO("Using timer with a period of %4f s", m_timerPeriod);
00188 }
00189
00190 ROS_INFO("NaoLocalization initialized with %d particles.", m_numParticles);
00191 }
00192
00193 HumanoidLocalization::~HumanoidLocalization() {
00194
00195 delete m_laserFilter;
00196 delete m_laserSub;
00197
00198 delete m_pointCloudFilter;
00199 delete m_pointCloudSub;
00200
00201 delete m_initPoseFilter;
00202 delete m_initPoseSub;
00203
00204 }
00205
00206 void HumanoidLocalization::timerCallback(const ros::TimerEvent & e){
00207 ros::Time transformExpiration = e.current_real + ros::Duration(m_transformTolerance);
00208 tf::StampedTransform tmp_tf_stamped(m_latest_transform, transformExpiration, m_globalFrameId, m_targetFrameId);
00209 m_tfBroadcaster.sendTransform(tmp_tf_stamped);
00210 }
00211
00212
00213 void HumanoidLocalization::reset(){
00214
00215 #if defined(_BENCH_TIME)
00216 ros::WallTime startTime = ros::WallTime::now();
00217 #endif
00218
00219 if (m_initGlobal){
00220 this->initGlobal();
00221 } else {
00222 geometry_msgs::PoseWithCovarianceStampedPtr posePtr(new geometry_msgs::PoseWithCovarianceStamped());
00223
00224 if (m_initFromTruepose){
00225 geometry_msgs::PoseStamped truePose;
00226 tf::Stamped<tf::Pose> truePoseTF;
00227 tf::Stamped<tf::Pose> ident (tf::Transform(tf::createIdentityQuaternion(), tf::Vector3(0,0,0)), ros::Time::now(), "torso_real");
00228
00229 ros::Time lookupTime = ros::Time::now();
00230 while(m_nh.ok() && !m_tfListener.waitForTransform(m_globalFrameId, ident.frame_id_, lookupTime, ros::Duration(1.0))){
00231 ROS_WARN("Waiting for transform %s --> %s for ground truth initialization failed, trying again...", m_globalFrameId.c_str(), ident.frame_id_.c_str());
00232 lookupTime = ros::Time::now();
00233 }
00234 ident.stamp_ = lookupTime;
00235
00236 m_tfListener.transformPose(m_globalFrameId, ident, truePoseTF);
00237 tf::poseStampedTFToMsg(truePoseTF, truePose);
00238 tf::poseTFToMsg(truePoseTF, posePtr->pose.pose);
00239 posePtr->header = truePose.header;
00240
00241
00242
00243 for(int j=0; j < 6; ++j){
00244 for (int i = 0; i < 6; ++i){
00245 if (i == j)
00246 posePtr->pose.covariance.at(i*6 +j) = m_initNoiseStd(i) * m_initNoiseStd(i);
00247 else
00248 posePtr->pose.covariance.at(i*6 +j) = 0.0;
00249 }
00250 }
00251
00252 } else{
00253 posePtr.reset(new geometry_msgs::PoseWithCovarianceStamped());
00254 for (int i = 0; i < 6; ++i){
00255 posePtr->pose.covariance.at(i*6 +i) = m_initNoiseStd(i) * m_initNoiseStd(i);
00256 }
00257
00258 double roll, pitch, z;
00259 initZRP(z, roll, pitch);
00260
00261
00262
00263 posePtr->pose.pose.position.x = m_initPose(0);
00264 posePtr->pose.pose.position.y = m_initPose(1);
00265 posePtr->pose.pose.position.z = z;
00266 tf::Quaternion quat;
00267 quat.setRPY(roll, pitch, m_initPose(5));
00268 tf::quaternionTFToMsg(quat, posePtr->pose.pose.orientation);
00269
00270 }
00271
00272 this->initPoseCallback(posePtr);
00273
00274 }
00275
00276
00277
00278
00279
00280 #if defined(_BENCH_TIME)
00281 double dt = (ros::WallTime::now() - startTime).toSec();
00282 ROS_INFO_STREAM("Initialization of "<< m_numParticles << " particles took "
00283 << dt << "s (="<<dt/m_numParticles<<"s/particle)");
00284 #endif
00285
00286
00287 }
00288
00289
00290 void HumanoidLocalization::initZRP(double& z, double& roll, double& pitch){
00291 if(m_initPoseRealZRP) {
00292
00293 tf::Stamped<tf::Pose> lastOdomPose;
00294 double poseHeight;
00295 if(m_motionModel->getLastOdomPose(lastOdomPose) &&
00296 lookupPoseHeight(lastOdomPose.stamp_, poseHeight)) {
00297 z = poseHeight;
00298 } else {
00299 ROS_WARN("Could not determine current pose height, falling back to init_pose_z");
00300 z = m_initPose(2);
00301 }
00302
00303
00304 if(!m_lastIMUMsgBuffer.empty()) {
00305 getRP(m_lastIMUMsgBuffer.back().orientation, roll, pitch);
00306 } else {
00307 ROS_WARN("Could not determine current roll and pitch, falling back to init_pose_{roll,pitch}");
00308 roll = m_initPose(3);
00309 pitch = m_initPose(4);
00310 }
00311 } else {
00312
00313 z = m_initPose(2);
00314 roll = m_initPose(3);
00315 pitch = m_initPose(4);
00316 }
00317
00318
00319 }
00320 void HumanoidLocalization::laserCallback(const sensor_msgs::LaserScanConstPtr& msg){
00321 ROS_DEBUG("Laser received (time: %f)", msg->header.stamp.toSec());
00322
00323 if (!m_initialized){
00324 ROS_WARN("Localization not initialized yet, skipping laser callback.");
00325 return;
00326 }
00327
00328 double timediff = (msg->header.stamp - m_lastLaserTime).toSec();
00329 if (m_receivedSensorData && timediff < 0){
00330 ROS_WARN("Ignoring received laser data that is %f s older than previous data!", timediff);
00331 return;
00332 }
00333
00334
00336 tf::Stamped<tf::Pose> odomPose;
00337
00338 if (!m_motionModel->lookupOdomPose(msg->header.stamp, odomPose))
00339 return;
00340
00341
00342 bool sensor_integrated = false;
00343 if (!m_paused && (!m_receivedSensorData || isAboveMotionThreshold(odomPose))) {
00344
00345
00346 PointCloud pc_filtered;
00347 std::vector<float> laserRangesSparse;
00348 prepareLaserPointCloud(msg, pc_filtered, laserRangesSparse);
00349
00350 sensor_integrated = localizeWithMeasurement(pc_filtered, laserRangesSparse, msg->range_max);
00351
00352 }
00353
00354 if(!sensor_integrated){
00355
00356
00357 tf::Transform odomTransform = m_motionModel->computeOdomTransform(odomPose);
00358 m_motionModel->applyOdomTransform(m_particles, odomTransform);
00359 constrainMotion(odomPose);
00360 }
00361 else
00362 {
00363 m_lastLocalizedPose = odomPose;
00364 }
00365
00366 m_motionModel->storeOdomPose(odomPose);
00367 publishPoseEstimate(msg->header.stamp, sensor_integrated);
00368 m_lastLaserTime = msg->header.stamp;
00369 }
00370
00371 void HumanoidLocalization::constrainMotion(const tf::Pose& odomPose){
00372
00373 if (!m_constrainMotionZ && !m_constrainMotionRP)
00374 return;
00375
00376
00377 double z = odomPose.getOrigin().getZ();
00378 double odomRoll, odomPitch, uselessYaw;
00379 odomPose.getBasis().getRPY(odomRoll, odomPitch, uselessYaw);
00380
00381 #pragma omp parallel for
00382 for (unsigned i=0; i < m_particles.size(); ++i){
00383 if (m_constrainMotionZ){
00384 tf::Vector3 pos = m_particles[i].pose.getOrigin();
00385 double floor_z = m_mapModel->getFloorHeight(m_particles[i].pose);
00386 pos.setZ(z+floor_z);
00387 m_particles[i].pose.setOrigin(pos);
00388 }
00389
00390 if (m_constrainMotionRP){
00391 double yaw = tf::getYaw(m_particles[i].pose.getRotation());
00392 m_particles[i].pose.setRotation(tf::createQuaternionFromRPY(odomRoll, odomPitch, yaw));
00393
00394 }
00395 }
00396 }
00397
00398 bool HumanoidLocalization::isAboveMotionThreshold(const tf::Pose& odomPose){
00399 tf::Transform odomTransform = m_lastLocalizedPose.inverse() * odomPose;
00400
00401 double yaw, pitch, roll;
00402 odomTransform.getBasis().getRPY(roll, pitch, yaw);
00403
00404 return (odomTransform.getOrigin().length() >= m_observationThresholdTrans
00405 || std::abs(yaw) >= m_observationThresholdRot);
00406 }
00407
00408 bool HumanoidLocalization::localizeWithMeasurement(const PointCloud& pc_filtered, const std::vector<float>& ranges, double max_range){
00409 ros::WallTime startTime = ros::WallTime::now();
00410 #if PCL_VERSION_COMPARE(>=,1,7,0)
00411 ros::Time t = pcl_conversions::fromPCL(pc_filtered.header).stamp;
00412 #else
00413 ros::Time t = pc_filtered.header.stamp;
00414 #endif
00415
00416 m_motionModel->applyOdomTransformTemporal(m_particles, t, m_temporalSamplingRange);
00417
00418
00419 tf::Stamped<tf::Transform> odomPose;
00420 if (!m_motionModel->lookupOdomPose(t, odomPose))
00421 return false;
00422 constrainMotion(odomPose);
00423
00424
00425
00426 tf::StampedTransform localSensorFrame;
00427 if (!m_motionModel->lookupLocalTransform(pc_filtered.header.frame_id, t, localSensorFrame))
00428 return false;
00429
00430 tf::Transform torsoToSensor(localSensorFrame.inverse());
00431
00432
00433 toLogForm();
00434
00435
00436 if (!(m_constrainMotionRP && m_constrainMotionZ)){
00437 bool imuMsgOk = false;
00438 double angleX, angleY;
00439 if(m_useIMU) {
00440 ros::Time imuStamp;
00441 imuMsgOk = getImuMsg(t, imuStamp, angleX, angleY);
00442 } else {
00443 tf::Stamped<tf::Pose> lastOdomPose;
00444 if(m_motionModel->lookupOdomPose(t, lastOdomPose)) {
00445 double dropyaw;
00446 lastOdomPose.getBasis().getRPY(angleX, angleY, dropyaw);
00447 imuMsgOk = true;
00448 }
00449 }
00450
00451 tf::StampedTransform footprintToTorso;
00452
00453 if(imuMsgOk) {
00454 if (!m_motionModel->lookupLocalTransform(m_baseFootprintId, t, footprintToTorso)) {
00455 ROS_WARN("Could not obtain pose height in localization, skipping Pose integration");
00456 } else {
00457 m_observationModel->integratePoseMeasurement(m_particles, angleX, angleY, footprintToTorso);
00458 }
00459 } else {
00460 ROS_WARN("Could not obtain roll and pitch measurement, skipping Pose integration");
00461 }
00462 }
00463
00464 m_filteredPointCloudPub.publish(pc_filtered);
00465 m_observationModel->integrateMeasurement(m_particles, pc_filtered, ranges, max_range, torsoToSensor);
00466
00467
00468 m_mapModel->verifyPoses(m_particles);
00469
00470
00471 normalizeWeights();
00472
00473
00474 double nEffParticles = nEff();
00475
00476 std_msgs::Float32 nEffMsg;
00477 nEffMsg.data = nEffParticles;
00478 m_nEffPub.publish(nEffMsg);
00479
00480 if (nEffParticles <= m_nEffFactor*m_particles.size()){
00481 ROS_INFO("Resampling, nEff=%f, numParticles=%zd", nEffParticles, m_particles.size());
00482 resample();
00483 } else {
00484 ROS_INFO("Skipped resampling, nEff=%f, numParticles=%zd", nEffParticles, m_particles.size());
00485 }
00486
00487 m_receivedSensorData = true;
00488
00489 double dt = (ros::WallTime::now() - startTime).toSec();
00490 ROS_INFO_STREAM("Observations for "<< m_numParticles << " particles took "
00491 << dt << "s (="<<dt/m_numParticles<<"s/particle)");
00492
00493 return true;
00494 }
00495
00496 void HumanoidLocalization::prepareLaserPointCloud(const sensor_msgs::LaserScanConstPtr& laser, PointCloud& pc, std::vector<float>& ranges) const{
00497 unsigned numBeams = laser->ranges.size();
00498
00499
00500
00501 unsigned step = 1;
00502
00503
00504
00505
00506 unsigned int numBeamsSkipped = 0;
00507
00508
00509 double laserMin = std::max(double(laser->range_min), m_filterMinRange);
00510
00511
00512
00513 ranges.reserve(50);
00514
00515
00516 #if PCL_VERSION_COMPARE(>=,1,7,0)
00517 pcl_conversions::toPCL(laser->header, pc.header);
00518 #else
00519 pc.header = laser->header;
00520 #endif
00521 pc.points.reserve(50);
00522 for (unsigned beam_idx = 0; beam_idx < numBeams; beam_idx+= step){
00523 float range = laser->ranges[beam_idx];
00524 if (range >= laserMin && range <= m_filterMaxRange){
00525 double laserAngle = laser->angle_min + beam_idx * laser->angle_increment;
00526 tf::Transform laserAngleRotation(tf::Quaternion(tf::Vector3(0.0, 0.0, 1.0), laserAngle));
00527 tf::Vector3 laserEndpointTrans(range, 0.0, 0.0);
00528 tf::Vector3 pt(laserAngleRotation * laserEndpointTrans);
00529
00530 pc.points.push_back(pcl::PointXYZ(pt.x(), pt.y(), pt.z()));
00531 ranges.push_back(range);
00532
00533 } else{
00534 numBeamsSkipped++;
00535 }
00536
00537 }
00538 pc.height = 1;
00539 pc.width = pc.points.size();
00540 pc.is_dense = true;
00541
00542
00543 pcl::UniformSampling<pcl::PointXYZ> uniformSampling;
00544 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudPtr;
00545 cloudPtr.reset(new pcl::PointCloud<pcl::PointXYZ> (pc));
00546 uniformSampling.setInputCloud(cloudPtr);
00547 uniformSampling.setRadiusSearch(m_sensorSampleDist);
00548 pcl::PointCloud<int> sampledIndices;
00549 uniformSampling.compute(sampledIndices);
00550 pcl::copyPointCloud(*cloudPtr, sampledIndices.points, pc);
00551
00552 std::vector<float> rangesSparse;
00553 rangesSparse.resize(sampledIndices.size());
00554 for (size_t i = 0; i < rangesSparse.size(); ++i){
00555 rangesSparse[i] = ranges[sampledIndices.points[i]];
00556 }
00557 ranges = rangesSparse;
00558 ROS_INFO("Laser PointCloud subsampled: %zu from %zu (%u out of valid range)", pc.size(), cloudPtr->size(), numBeamsSkipped);
00559 }
00560
00561 int HumanoidLocalization::filterUniform(const PointCloud & cloud_in, PointCloud & cloud_out, int numSamples) const{
00562 int numPoints = static_cast<int>(cloud_in.size() );
00563 numSamples = std::min( numSamples, numPoints);
00564 std::vector<unsigned int> indices;
00565 indices.reserve( numPoints );
00566 for (int i=0; i<numPoints; ++i)
00567 indices.push_back(i);
00568 random_shuffle ( indices.begin(), indices.end());
00569
00570 cloud_out.reserve( cloud_out.size() + numSamples );
00571 for ( int i = 0; i < numSamples; ++i)
00572 {
00573 cloud_out.push_back( cloud_in.at(indices[i]));
00574 }
00575 return numSamples;
00576 }
00577
00578
00579
00580 void HumanoidLocalization::filterGroundPlane(const PointCloud& pc, PointCloud& ground, PointCloud& nonground, double groundFilterDistance, double groundFilterAngle, double groundFilterPlaneDistance){
00581 ground.header = pc.header;
00582 nonground.header = pc.header;
00583
00584 if (pc.size() < 50){
00585 ROS_WARN("Pointcloud in HumanoidLocalization::filterGroundPlane too small, skipping ground plane extraction");
00586 nonground = pc;
00587 } else {
00588
00589 pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
00590 pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
00591
00592
00593 pcl::SACSegmentation<pcl::PointXYZ> seg;
00594 seg.setOptimizeCoefficients (true);
00595
00596 seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
00597 seg.setMethodType(pcl::SAC_RANSAC);
00598 seg.setMaxIterations(200);
00599 seg.setDistanceThreshold (groundFilterDistance);
00600 seg.setAxis(Eigen::Vector3f(0,0,1));
00601 seg.setEpsAngle(groundFilterAngle);
00602
00603
00604 PointCloud cloud_filtered(pc);
00605
00606 pcl::ExtractIndices<pcl::PointXYZ> extract;
00607 bool groundPlaneFound = false;
00608
00609 while(cloud_filtered.size() > 10 && !groundPlaneFound){
00610 seg.setInputCloud(cloud_filtered.makeShared());
00611 seg.segment (*inliers, *coefficients);
00612 if (inliers->indices.size () == 0){
00613 ROS_INFO("PCL segmentation did not find any plane.");
00614
00615 break;
00616 }
00617
00618 extract.setInputCloud(cloud_filtered.makeShared());
00619 extract.setIndices(inliers);
00620
00621 if (std::abs(coefficients->values.at(3)) < groundFilterPlaneDistance){
00622 ROS_DEBUG("Ground plane found: %zu/%zu inliers. Coeff: %f %f %f %f", inliers->indices.size(), cloud_filtered.size(),
00623 coefficients->values.at(0), coefficients->values.at(1), coefficients->values.at(2), coefficients->values.at(3));
00624 extract.setNegative (false);
00625 extract.filter (ground);
00626
00627
00628
00629 if(inliers->indices.size() != cloud_filtered.size()){
00630 extract.setNegative(true);
00631 PointCloud cloud_out;
00632 extract.filter(cloud_out);
00633 nonground += cloud_out;
00634 cloud_filtered = cloud_out;
00635 }
00636
00637 groundPlaneFound = true;
00638 } else{
00639 ROS_DEBUG("Horizontal plane (not ground) found: %zu/%zu inliers. Coeff: %f %f %f %f", inliers->indices.size(), cloud_filtered.size(),
00640 coefficients->values.at(0), coefficients->values.at(1), coefficients->values.at(2), coefficients->values.at(3));
00641 pcl::PointCloud<pcl::PointXYZ> cloud_out;
00642 extract.setNegative (false);
00643 extract.filter(cloud_out);
00644 nonground +=cloud_out;
00645
00646
00647
00648
00649
00650
00651 if(inliers->indices.size() != cloud_filtered.size()){
00652 extract.setNegative(true);
00653 cloud_out.points.clear();
00654 extract.filter(cloud_out);
00655 cloud_filtered = cloud_out;
00656 } else{
00657 cloud_filtered.points.clear();
00658 }
00659 }
00660
00661 }
00662
00663 if (!groundPlaneFound){
00664 ROS_WARN("No ground plane found in scan");
00665
00666
00667 pcl::PassThrough<pcl::PointXYZ> second_pass;
00668 second_pass.setFilterFieldName("z");
00669 second_pass.setFilterLimits(-groundFilterPlaneDistance, groundFilterPlaneDistance);
00670 second_pass.setInputCloud(pc.makeShared());
00671 second_pass.filter(ground);
00672
00673 second_pass.setFilterLimitsNegative (true);
00674 second_pass.filter(nonground);
00675 }
00676
00677
00678
00679
00680
00681
00682
00683 }
00684 }
00685
00686
00687
00688 void HumanoidLocalization::prepareGeneralPointCloud(const sensor_msgs::PointCloud2::ConstPtr& msg, PointCloud& pc, std::vector<float>& ranges) const{
00689
00690 pc.clear();
00691
00692 tf::StampedTransform sensorToBaseFootprint;
00693 try{
00694 m_tfListener.waitForTransform(m_baseFootprintId, msg->header.frame_id, msg->header.stamp, ros::Duration(0.2));
00695 m_tfListener.lookupTransform(m_baseFootprintId, msg->header.frame_id, msg->header.stamp, sensorToBaseFootprint);
00696
00697
00698 }catch(tf::TransformException& ex){
00699 ROS_ERROR_STREAM( "Transform error for pointCloudCallback: " << ex.what() << ", quitting callback.\n");
00700 return;
00701 }
00702
00703
00704
00705
00706 pcl::PassThrough<pcl::PointXYZ> pass;
00707 pcl::PointCloud<pcl::PointXYZ>::Ptr pcd_tmp(new pcl::PointCloud<pcl::PointXYZ>());
00708 #if PCL_VERSION_COMPARE(>=,1,7,0)
00709 pcl::PCLPointCloud2 pcd2_tmp;
00710 pcl_conversions::toPCL(*msg, pcd2_tmp);
00711 pcl::fromPCLPointCloud2(pcd2_tmp, *pcd_tmp);
00712 #else
00713 pcl::fromROSMsg(*msg, *pcd_tmp);
00714 #endif
00715 pass.setInputCloud (pcd_tmp);
00716 pass.setFilterFieldName ("z");
00717 pass.setFilterLimits (m_filterMinRange, m_filterMaxRange);
00718 pass.filter (pc);
00719
00720
00721 PointCloud ground, nonground;
00722 if (m_groundFilterPointCloud)
00723 {
00724 Eigen::Matrix4f matSensorToBaseFootprint, matBaseFootprintToSensor;
00725 pcl_ros::transformAsMatrix(sensorToBaseFootprint, matSensorToBaseFootprint);
00726 pcl_ros::transformAsMatrix(sensorToBaseFootprint.inverse(), matBaseFootprintToSensor);
00727
00728 pcl::transformPointCloud(pc, pc, matSensorToBaseFootprint );
00729 filterGroundPlane(pc, ground, nonground, m_groundFilterDistance, m_groundFilterAngle, m_groundFilterPlaneDistance);
00730
00731
00732 pc.clear();
00733 pcl::PointCloud<int> sampledIndices;
00734
00735 int numFloorPoints = 0;
00736 if (ground.size() > 0){
00737
00738 pcl::transformPointCloud(ground, ground, matBaseFootprintToSensor);
00739 voxelGridSampling(ground, sampledIndices, m_sensorSampleDist*m_sensorSampleDistGroundFactor);
00740 pcl::copyPointCloud(ground, sampledIndices.points, pc);
00741 numFloorPoints = sampledIndices.size();
00742 }
00743
00744
00745 int numNonFloorPoints = 0;
00746 if (nonground.size() > 0){
00747
00748 pcl::transformPointCloud(nonground, nonground, matBaseFootprintToSensor);
00749 voxelGridSampling( nonground, sampledIndices, m_sensorSampleDist);
00750 pcl::copyPointCloud( nonground, sampledIndices.points, nonground);
00751 numNonFloorPoints = sampledIndices.size();
00752 pc += nonground;
00753 }
00754
00755
00756
00757
00758 ROS_INFO("PointCloudGroundFiltering done. Added %d non-ground points and %d ground points (from %zu). Cloud size is %zu", numNonFloorPoints, numFloorPoints, ground.size(), pc.size());
00759
00760 ranges.resize(pc.size());
00761 for (unsigned int i=0; i<pc.size(); ++i)
00762 {
00763 pcl::PointXYZ p = pc.at(i);
00764 ranges[i] = sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
00765 }
00766
00767 }
00768 else
00769 {
00770 ROS_INFO("Starting uniform sampling");
00771
00772
00773 pcl::PointCloud<int> sampledIndices;
00774 voxelGridSampling(pc, sampledIndices, m_sensorSampleDist);
00775 pcl::copyPointCloud(pc, sampledIndices.points, pc);
00776
00777
00778 ranges.resize(sampledIndices.size());
00779 for (size_t i = 0; i < ranges.size(); ++i){
00780 pcl::PointXYZ p = pc[i];
00781 ranges[i] = sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
00782
00783
00784 }
00785 ROS_INFO("Done.");
00786
00787
00788 }
00789 return;
00790
00791 }
00792
00793 void HumanoidLocalization::voxelGridSampling(const PointCloud & pc, pcl::PointCloud<int> & sampledIndices, double search_radius) const
00794 {
00795 pcl::UniformSampling<pcl::PointXYZ> uniformSampling;
00796 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudPtr;
00797 cloudPtr.reset(new pcl::PointCloud<pcl::PointXYZ> (pc));
00798 uniformSampling.setInputCloud(cloudPtr);
00799 uniformSampling.setRadiusSearch(search_radius);
00800 uniformSampling.compute(sampledIndices);
00801 }
00802
00803 void HumanoidLocalization::pointCloudCallback(const sensor_msgs::PointCloud2::ConstPtr& msg) {
00804 ROS_DEBUG("PointCloud received (time: %f)", msg->header.stamp.toSec());
00805
00806 if (!m_initialized){
00807 ROS_WARN("Loclization not initialized yet, skipping PointCloud callback.");
00808 return;
00809 }
00810
00811 double timediff = (msg->header.stamp - m_lastPointCloudTime).toSec();
00812 if (m_receivedSensorData && timediff < 0){
00813 ROS_WARN("Ignoring received PointCloud data that is %f s older than previous data!", timediff);
00814 return;
00815 }
00816
00817
00819 tf::Stamped<tf::Pose> odomPose;
00820
00821 if (!m_motionModel->lookupOdomPose(msg->header.stamp, odomPose))
00822 return;
00823
00824
00825 bool sensor_integrated = false;
00826
00827
00828
00829 bool isAboveHeadMotionThreshold = false;
00830 double headYaw, headPitch, headRoll;
00831 tf::StampedTransform torsoToSensor;
00832 if (!m_motionModel->lookupLocalTransform(msg->header.frame_id, msg->header.stamp, torsoToSensor))
00833 return;
00834
00835
00836
00837 torsoToSensor.getBasis().getRPY(headRoll, headPitch, headYaw);
00838 double headYawRotationSinceScan = std::abs(headYaw - m_headYawRotationLastScan);
00839 double headPitchRotationSinceScan = std::abs(headPitch - m_headPitchRotationLastScan);
00840
00841 if (headYawRotationSinceScan>= m_observationThresholdHeadYawRot || headPitchRotationSinceScan >= m_observationThresholdHeadPitchRot)
00842 isAboveHeadMotionThreshold = true;
00843
00844
00845 if (!m_paused && (!m_receivedSensorData || isAboveHeadMotionThreshold || isAboveMotionThreshold(odomPose))) {
00846
00847
00848 PointCloud pc_filtered;
00849 std::vector<float> rangesSparse;
00850 prepareGeneralPointCloud(msg, pc_filtered, rangesSparse);
00851
00852 double maxRange = 10.0;
00853 ROS_DEBUG("Updating Pose Estimate from a PointCloud with %zu points and %zu ranges", pc_filtered.size(), rangesSparse.size());
00854 sensor_integrated = localizeWithMeasurement(pc_filtered, rangesSparse, maxRange);
00855
00856 }
00857 if(!sensor_integrated){
00858
00859 tf::Transform odomTransform = m_motionModel->computeOdomTransform(odomPose);
00860 m_motionModel->applyOdomTransform(m_particles, odomTransform);
00861 constrainMotion(odomPose);
00862 }
00863 else{
00864 m_lastLocalizedPose = odomPose;
00865
00866 m_headYawRotationLastScan = headYaw;
00867 m_headPitchRotationLastScan = headPitch;
00868 }
00869
00870 m_motionModel->storeOdomPose(odomPose);
00871 publishPoseEstimate(msg->header.stamp, sensor_integrated);
00872 m_lastPointCloudTime = msg->header.stamp;
00873 ROS_DEBUG("PointCloud callback complete.");
00874 }
00875
00876 void HumanoidLocalization::imuCallback(const sensor_msgs::ImuConstPtr& msg){
00877 m_lastIMUMsgBuffer.push_back(*msg);
00878 }
00879
00880 bool HumanoidLocalization::getImuMsg(const ros::Time& stamp, ros::Time& imuStamp, double& angleX, double& angleY) const {
00881 if(m_lastIMUMsgBuffer.empty())
00882 return false;
00883
00884 typedef boost::circular_buffer<sensor_msgs::Imu>::const_iterator ItT;
00885 const double maxAge = 0.2;
00886 double closestOlderStamp = std::numeric_limits<double>::max();
00887 double closestNewerStamp = std::numeric_limits<double>::max();
00888 ItT closestOlder = m_lastIMUMsgBuffer.end(), closestNewer = m_lastIMUMsgBuffer.end();
00889 for(ItT it = m_lastIMUMsgBuffer.begin(); it != m_lastIMUMsgBuffer.end(); it++) {
00890 const double age = (stamp - it->header.stamp).toSec();
00891 if(age >= 0.0 && age < closestOlderStamp) {
00892 closestOlderStamp = age;
00893 closestOlder = it;
00894 } else if(age < 0.0 && -age < closestNewerStamp) {
00895 closestNewerStamp = -age;
00896 closestNewer = it;
00897 }
00898 }
00899
00900 if(closestOlderStamp < maxAge && closestNewerStamp < maxAge && closestOlderStamp + closestNewerStamp > 0.0) {
00901
00902 const double weightOlder = closestNewerStamp / (closestNewerStamp + closestOlderStamp);
00903 const double weightNewer = 1.0 - weightOlder;
00904 imuStamp = ros::Time(weightOlder * closestOlder->header.stamp.toSec()
00905 + weightNewer * closestNewer->header.stamp.toSec());
00906 double olderX, olderY, newerX, newerY;
00907 getRP(closestOlder->orientation, olderX, olderY);
00908 getRP(closestNewer->orientation, newerX, newerY);
00909 angleX = weightOlder * olderX + weightNewer * newerX;
00910 angleY = weightOlder * olderY + weightNewer * newerY;
00911 ROS_DEBUG("Msg: %.3f, Interpolate [%.3f .. %.3f .. %.3f]\n", stamp.toSec(), closestOlder->header.stamp.toSec(),
00912 imuStamp.toSec(), closestNewer->header.stamp.toSec());
00913 return true;
00914 } else if(closestOlderStamp < maxAge || closestNewerStamp < maxAge) {
00915
00916 ItT it = (closestOlderStamp < closestNewerStamp) ? closestOlder : closestNewer;
00917 imuStamp = it->header.stamp;
00918 getRP(it->orientation, angleX, angleY);
00919 return true;
00920 } else {
00921 if(closestOlderStamp < closestNewerStamp)
00922 ROS_WARN("Closest IMU message is %.2f seconds too old, skipping pose integration", closestOlderStamp);
00923 else
00924 ROS_WARN("Closest IMU message is %.2f seconds too new, skipping pose integration", closestNewerStamp);
00925 return false;
00926 }
00927 }
00928
00929 void HumanoidLocalization::initPoseCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg){
00930 tf::Pose pose;
00931 tf::poseMsgToTF(msg->pose.pose, pose);
00932
00933 if (msg->header.frame_id != m_globalFrameId){
00934 ROS_WARN("Frame ID of \"initialpose\" (%s) is different from the global frame %s", msg->header.frame_id.c_str(), m_globalFrameId.c_str());
00935 }
00936
00937 std::vector<double> heights;
00938 double poseHeight = 0.0;
00939 if (std::abs(pose.getOrigin().getZ()) < 0.01){
00940 m_mapModel->getHeightlist(pose.getOrigin().getX(), pose.getOrigin().getY(), 0.6, heights);
00941 if (heights.size() == 0){
00942 ROS_WARN("No ground level to stand on found at map position, assuming 0");
00943 heights.push_back(0.0);
00944 }
00945
00946 bool poseHeightOk = false;
00947 if(m_initPoseRealZRP) {
00948 ros::Time stamp(msg->header.stamp);
00949 if(stamp.isZero()) {
00950
00951 tf::Stamped<tf::Pose> lastOdomPose;
00952 m_motionModel->getLastOdomPose(lastOdomPose);
00953 stamp = lastOdomPose.stamp_;
00954 }
00955 poseHeightOk = lookupPoseHeight(stamp, poseHeight);
00956 if(!poseHeightOk) {
00957 ROS_WARN("Could not determine current pose height, falling back to init_pose_z");
00958 }
00959 }
00960 if(!poseHeightOk) {
00961 ROS_INFO("Use pose height from init_pose_z");
00962 poseHeight = m_initPose(2);
00963 }
00964 }
00965
00966
00967 Matrix6d initCov;
00968 if ((std::abs(msg->pose.covariance.at(6*0+0) - 0.25) < 0.1) && (std::abs(msg->pose.covariance.at(6*1+1) -0.25) < 0.1)
00969 && (std::abs(msg->pose.covariance.at(6*3+3) - M_PI/12.0 * M_PI/12.0)<0.1)){
00970 ROS_INFO("Covariance originates from RViz, using default parameters instead");
00971 initCov = Matrix6d::Zero();
00972 initCov.diagonal() = m_initNoiseStd.cwiseProduct(m_initNoiseStd);
00973
00974
00975 bool ok = false;
00976 const double yaw = tf::getYaw(pose.getRotation());
00977 if(m_initPoseRealZRP) {
00978 bool useOdometry = true;
00979 if(m_useIMU) {
00980 if(m_lastIMUMsgBuffer.empty()) {
00981 ROS_WARN("Could not determine current roll and pitch because IMU message buffer is empty.");
00982 } else {
00983 double roll, pitch;
00984 if(msg->header.stamp.isZero()) {
00985
00986 getRP(m_lastIMUMsgBuffer.back().orientation, roll, pitch);
00987 ok = true;
00988 } else {
00989 ros::Time imuStamp;
00990 ok = getImuMsg(msg->header.stamp, imuStamp, roll, pitch);
00991 }
00992 if(ok) {
00993 ROS_INFO("roll and pitch not set in initPoseCallback, use IMU values (roll = %f, pitch = %f) instead", roll, pitch);
00994 pose.setRotation(tf::createQuaternionFromRPY(roll, pitch, yaw));
00995 useOdometry = false;
00996 } else {
00997 ROS_WARN("Could not determine current roll and pitch from IMU, falling back to odometry roll and pitch");
00998 useOdometry = true;
00999 }
01000 }
01001 }
01002
01003 if(useOdometry) {
01004 double roll, pitch, dropyaw;
01005 tf::Stamped<tf::Pose> lastOdomPose;
01006 ok = m_motionModel->getLastOdomPose(lastOdomPose);
01007 if(ok) {
01008 lastOdomPose.getBasis().getRPY(roll, pitch, dropyaw);
01009 pose.setRotation(tf::createQuaternionFromRPY(roll, pitch, yaw));
01010 ROS_INFO("roll and pitch not set in initPoseCallback, use odometry values (roll = %f, pitch = %f) instead", roll, pitch);
01011 } else {
01012 ROS_WARN("Could not determine current roll and pitch from odometry, falling back to init_pose_{roll,pitch} parameters");
01013 }
01014 }
01015 }
01016
01017 if(!ok) {
01018 ROS_INFO("roll and pitch not set in initPoseCallback, use init_pose_{roll,pitch} parameters instead");
01019 pose.setRotation(tf::createQuaternionFromRPY(m_initPose(3), m_initPose(4), yaw));
01020 }
01021 } else{
01022 for(int j=0; j < initCov.cols(); ++j){
01023 for (int i = 0; i < initCov.rows(); ++i){
01024 initCov(i,j) = msg->pose.covariance.at(i*initCov.cols() +j);
01025 }
01026 }
01027 }
01028
01029
01030 Matrix6d initCovL = initCov.llt().matrixL();
01031 tf::Transform transformNoise;
01032 unsigned idx = 0;
01033 for(Particles::iterator it = m_particles.begin(); it != m_particles.end(); ++it){
01034 Vector6d poseNoise;
01035 for (unsigned i = 0; i < 6; ++i){
01036 poseNoise(i) = m_rngNormal();
01037 }
01038 Vector6d poseCovNoise = initCovL * poseNoise;
01039
01040 for (unsigned i = 0; i < 6; ++i){
01041 if (std::abs(initCov(i,i)) < 0.00001)
01042 poseCovNoise(i) = 0.0;
01043 }
01044
01045
01046 transformNoise.setOrigin(tf::Vector3(poseCovNoise(0), poseCovNoise(1), poseCovNoise(2)));
01047 tf::Quaternion q;
01048 q.setRPY(poseCovNoise(3), poseCovNoise(4),poseCovNoise(5));
01049
01050 transformNoise.setRotation(q);
01051 it->pose = pose;
01052
01053 if (heights.size() > 0){
01054
01055 it->pose.getOrigin().setZ(heights.at(int(double(idx)/m_particles.size() * heights.size())) + poseHeight);
01056 }
01057
01058 it->pose *= transformNoise;
01059
01060 it->weight = 1.0/m_particles.size();
01061 idx++;
01062 }
01063
01064 ROS_INFO("Pose reset around mean (%f %f %f)", pose.getOrigin().getX(), pose.getOrigin().getY(), pose.getOrigin().getZ());
01065
01066
01067 m_motionModel->reset();
01068
01069 m_receivedSensorData = false;
01070 m_initialized = true;
01071
01072
01073
01074 ros::Time stampPublish = msg->header.stamp;
01075 if (stampPublish.isZero()){
01076 tf::Stamped<tf::Pose> lastOdomPose;
01077 m_motionModel->getLastOdomPose(lastOdomPose);
01078 stampPublish = lastOdomPose.stamp_;
01079 if (stampPublish.isZero())
01080 stampPublish = ros::Time::now();
01081
01082 }
01083
01084 publishPoseEstimate(stampPublish, false);
01085 }
01086
01087
01088
01089
01090 bool HumanoidLocalization::globalLocalizationCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
01091 {
01092
01093 initGlobal();
01094
01095 return true;
01096 }
01097
01098 void HumanoidLocalization::normalizeWeights() {
01099
01100 double wmin = std::numeric_limits<double>::max();
01101 double wmax = -std::numeric_limits<double>::max();
01102
01103 for (unsigned i=0; i < m_particles.size(); ++i){
01104 double weight = m_particles[i].weight;
01105 assert (!isnan(weight));
01106 if (weight < wmin)
01107 wmin = weight;
01108 if (weight > wmax){
01109 wmax = weight;
01110 m_bestParticleIdx = i;
01111 }
01112 }
01113 if (wmin > wmax){
01114 ROS_ERROR_STREAM("Error in weights: min=" << wmin <<", max="<<wmax<<", 1st particle weight="<< m_particles[1].weight<< std::endl);
01115
01116 }
01117
01118 double min_normalized_value;
01119 if (m_minParticleWeight > 0.0)
01120 min_normalized_value = std::max(log(m_minParticleWeight), wmin - wmax);
01121 else
01122 min_normalized_value = wmin - wmax;
01123
01124 double max_normalized_value = 0.0;
01125 double dn = max_normalized_value-min_normalized_value;
01126 double dw = wmax-wmin;
01127 if (dw == 0.0) dw = 1;
01128 double scale = dn/dw;
01129 if (scale < 0.0){
01130 ROS_WARN("normalizeWeights: scale is %f < 0, dw=%f, dn=%f", scale, dw, dn );
01131 }
01132 double offset = -wmax*scale;
01133 double weights_sum = 0.0;
01134
01135 #pragma omp parallel
01136 {
01137
01138 #pragma omp for
01139 for (unsigned i = 0; i < m_particles.size(); ++i){
01140 double w = m_particles[i].weight;
01141 w = exp(scale*w+offset);
01142 assert(!isnan(w));
01143 m_particles[i].weight = w;
01144 #pragma omp atomic
01145 weights_sum += w;
01146 }
01147
01148 assert(weights_sum > 0.0);
01149
01150 #pragma omp for
01151 for (unsigned i = 0; i < m_particles.size(); ++i){
01152 m_particles[i].weight /= weights_sum;
01153 }
01154
01155 }
01156 }
01157
01158 double HumanoidLocalization::getCumParticleWeight() const{
01159 double cumWeight=0.0;
01160
01161
01162 for (Particles::const_iterator it = m_particles.begin(); it != m_particles.end(); ++it){
01163 cumWeight += it->weight;
01164 }
01165
01166 return cumWeight;
01167 }
01168
01169 void HumanoidLocalization::resample(unsigned numParticles){
01170
01171 if (numParticles <= 0)
01172 numParticles = m_numParticles;
01173
01174
01175 double interval=getCumParticleWeight()/numParticles;
01176
01177
01178 double target=interval*m_rngUniform();
01179
01180
01181 double cumWeight=0;
01182 std::vector<unsigned> indices(numParticles);
01183
01184 unsigned n=0;
01185 for (unsigned i = 0; i < m_particles.size(); ++i){
01186 cumWeight += m_particles[i].weight;
01187 while(cumWeight > target && n < numParticles){
01188 if (m_bestParticleIdx >= 0 && i == unsigned(m_bestParticleIdx)){
01189 m_bestParticleIdx = n;
01190 }
01191
01192 indices[n++]=i;
01193 target+=interval;
01194 }
01195 }
01196
01197
01198 Particles oldParticles = m_particles;
01199 m_particles.resize(numParticles);
01200 m_poseArray.poses.resize(numParticles);
01201 double newWeight = 1.0/numParticles;
01202 #pragma omp parallel for
01203 for (unsigned i = 0; i < numParticles; ++i){
01204 m_particles[i].pose = oldParticles[indices[i]].pose;
01205 m_particles[i].weight = newWeight;
01206 }
01207 }
01208
01209 void HumanoidLocalization::initGlobal(){
01210 ROS_INFO("Initializing with uniform distribution");
01211
01212 double roll, pitch, z;
01213 initZRP(z, roll, pitch);
01214
01215 m_mapModel->initGlobal(m_particles, z, roll, pitch, m_initNoiseStd, m_rngUniform, m_rngNormal);
01216
01217
01218 ROS_INFO("Global localization done");
01219 m_motionModel->reset();
01220 m_receivedSensorData = false;
01221 m_initialized = true;
01222
01223 publishPoseEstimate(ros::Time::now(), false);
01224
01225 }
01226
01227 void HumanoidLocalization::publishPoseEstimate(const ros::Time& time, bool publish_eval){
01228
01230
01232
01233 m_poseArray.header.stamp = time;
01234
01235 if (m_poseArray.poses.size() != m_particles.size())
01236 m_poseArray.poses.resize(m_particles.size());
01237
01238 #pragma omp parallel for
01239 for (unsigned i = 0; i < m_particles.size(); ++i){
01240 tf::poseTFToMsg(m_particles[i].pose, m_poseArray.poses[i]);
01241 }
01242
01243 m_poseArrayPub.publish(m_poseArray);
01244
01246
01248 geometry_msgs::PoseWithCovarianceStamped p;
01249 p.header.stamp = time;
01250 p.header.frame_id = m_globalFrameId;
01251
01252 tf::Pose bestParticlePose;
01253 if (m_bestParticleAsMean)
01254 bestParticlePose = getMeanParticlePose();
01255 else
01256 bestParticlePose = getBestParticlePose();
01257
01258 tf::poseTFToMsg(bestParticlePose,p.pose.pose);
01259 m_posePub.publish(p);
01260
01261 if (publish_eval){
01262 m_poseEvalPub.publish(p);
01263 }
01264
01265 geometry_msgs::PoseArray bestPose;
01266 bestPose.header = p.header;
01267 bestPose.poses.resize(1);
01268 tf::poseTFToMsg(bestParticlePose, bestPose.poses[0]);
01269 m_bestPosePub.publish(bestPose);
01270
01272
01274 tf::Stamped<tf::Pose> lastOdomPose;
01275 if (m_motionModel->getLastOdomPose(lastOdomPose)){
01276 geometry_msgs::PoseStamped odomPoseMsg;
01277 tf::poseStampedTFToMsg(lastOdomPose, odomPoseMsg);
01278 m_poseOdomPub.publish(odomPoseMsg);
01279 }
01280
01281
01283
01284 tf::Stamped<tf::Pose> targetToMapTF;
01285 try{
01286 tf::Stamped<tf::Pose> baseToMapTF(bestParticlePose.inverse(),time, m_baseFrameId);
01287 m_tfListener.transformPose(m_targetFrameId, baseToMapTF, targetToMapTF);
01288 } catch (const tf::TransformException& e){
01289 ROS_WARN("Failed to subtract base to %s transform, will not publish pose estimate: %s", m_targetFrameId.c_str(), e.what());
01290 return;
01291 }
01292
01293 tf::Transform latestTF(tf::Quaternion(targetToMapTF.getRotation()), tf::Point(targetToMapTF.getOrigin()));
01294
01295
01296
01297
01298
01299 ros::Duration transformTolerance(m_transformTolerance);
01300 ros::Time transformExpiration = (time + transformTolerance);
01301
01302 tf::StampedTransform tmp_tf_stamped(latestTF.inverse(), transformExpiration, m_globalFrameId, m_targetFrameId);
01303 m_latest_transform = tmp_tf_stamped;
01304
01305 m_tfBroadcaster.sendTransform(tmp_tf_stamped);
01306
01307 }
01308
01309 unsigned HumanoidLocalization::getBestParticleIdx() const{
01310 if (m_bestParticleIdx < 0 || m_bestParticleIdx >= m_numParticles){
01311 ROS_WARN("Index (%d) of best particle not valid, using 0 instead", m_bestParticleIdx);
01312 return 0;
01313 }
01314
01315 return m_bestParticleIdx;
01316 }
01317
01318 tf::Pose HumanoidLocalization::getParticlePose(unsigned particleIdx) const{
01319 return m_particles.at(particleIdx).pose;
01320 }
01321
01322 tf::Pose HumanoidLocalization::getBestParticlePose() const{
01323 return getParticlePose(getBestParticleIdx());
01324 }
01325
01326 tf::Pose HumanoidLocalization::getMeanParticlePose() const{
01327 tf::Pose meanPose = tf::Pose::getIdentity();
01328
01329 double totalWeight = 0.0;
01330
01331 meanPose.setBasis(tf::Matrix3x3(0,0,0,0,0,0,0,0,0));
01332 for (Particles::const_iterator it = m_particles.begin(); it != m_particles.end(); ++it){
01333 meanPose.getOrigin() += it->pose.getOrigin() * it->weight;
01334 meanPose.getBasis()[0] += it->pose.getBasis()[0];
01335 meanPose.getBasis()[1] += it->pose.getBasis()[1];
01336 meanPose.getBasis()[2] += it->pose.getBasis()[2];
01337 totalWeight += it->weight;
01338 }
01339 assert(!isnan(totalWeight));
01340
01341
01342
01343
01344 meanPose.getOrigin() /= totalWeight;
01345
01346 meanPose.getBasis() = meanPose.getBasis().scaled(tf::Vector3(1.0/m_numParticles, 1.0/m_numParticles, 1.0/m_numParticles));
01347
01348
01349 meanPose.setRotation(meanPose.getRotation().normalized());
01350
01351 return meanPose;
01352 }
01353
01354 double HumanoidLocalization::nEff() const{
01355
01356 double sqrWeights=0.0;
01357 for (Particles::const_iterator it=m_particles.begin(); it!=m_particles.end(); ++it){
01358 sqrWeights+=(it->weight * it->weight);
01359 }
01360
01361 if (sqrWeights > 0.0)
01362 return 1./sqrWeights;
01363 else
01364 return 0.0;
01365 }
01366
01367 void HumanoidLocalization::toLogForm(){
01368
01369 #pragma omp parallel for
01370 for (unsigned i = 0; i < m_particles.size(); ++i){
01371 assert(m_particles[i].weight > 0.0);
01372 m_particles[i].weight = log(m_particles[i].weight);
01373 }
01374 }
01375
01376 void HumanoidLocalization::pauseLocalizationCallback(const std_msgs::BoolConstPtr& msg){
01377 if (msg->data){
01378 if (!m_paused){
01379 m_paused = true;
01380 ROS_INFO("Localization paused");
01381 } else{
01382 ROS_WARN("Received a msg to pause localizatzion, but is already paused.");
01383 }
01384 } else{
01385 if (m_paused){
01386 m_paused = false;
01387 ROS_INFO("Localization resumed");
01388
01389 m_receivedSensorData = false;
01390 } else {
01391 ROS_WARN("Received a msg to resume localization, is not paused.");
01392 }
01393
01394 }
01395
01396 }
01397
01398 bool HumanoidLocalization::pauseLocalizationSrvCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
01399 {
01400 if (!m_paused){
01401 m_paused = true;
01402 ROS_INFO("Localization paused");
01403 } else{
01404 ROS_WARN("Received a request to pause localizatzion, but is already paused.");
01405 }
01406
01407 return true;
01408 }
01409
01410 bool HumanoidLocalization::resumeLocalizationSrvCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
01411 {
01412 if (m_paused){
01413 m_paused = false;
01414 ROS_INFO("Localization resumed");
01415
01416 m_receivedSensorData = false;
01417 } else {
01418 ROS_WARN("Received a request to resume localization, but is not paused.");
01419 }
01420
01421 return true;
01422 }
01423
01424 bool HumanoidLocalization::lookupPoseHeight(const ros::Time& t, double& poseHeight) const{
01425 tf::StampedTransform tf;
01426 if (m_motionModel->lookupLocalTransform(m_baseFootprintId, t, tf)){
01427 poseHeight = tf.getOrigin().getZ();
01428 return true;
01429 } else
01430 return false;
01431 }
01432
01433 }
01434