dissimilarity_getter.cpp
Go to the documentation of this file.
00001 #include <place_matcher_csm/dissimilarity_getter.h>
00002 
00003 namespace place_matcher_csm
00004 {
00005 
00010 void polygonToLDP(const geometry_msgs::Polygon& poly, LDP& ldp)
00011 {
00012   const size_t n = poly.points.size();
00013 
00014   ldp = ld_alloc_new(n);
00015 
00016   for (size_t i = 0; i < n; ++i)
00017   {
00018     // calculate position in laser frame
00019     if (is_nan(poly.points[i].x) || is_nan(poly.points[i].y))
00020     {
00021       ROS_WARN("place_matcher_csm: polygon input contains NaN values. \
00022                 Please use a filtered polygon input.");
00023     }
00024     else
00025     {
00026       ldp->valid[i] = 1;
00027       ldp->points[i].p[0] = poly.points[i].x;
00028       ldp->points[i].p[1] = poly.points[i].y;
00029       ldp->points[i].rho = GSL_NAN;
00030       ldp->points[i].phi = GSL_NAN;
00031     }
00032     ldp->cluster[i]  = -1;
00033   }
00034 
00035   ldp->odometry[0] = 0.0;
00036   ldp->odometry[1] = 0.0;
00037   ldp->odometry[2] = 0.0;
00038 
00039   ldp->true_pose[0] = 0.0;
00040   ldp->true_pose[1] = 0.0;
00041   ldp->true_pose[2] = 0.0;
00042 }
00043 
00044 DissimilarityGetter::DissimilarityGetter(ros::NodeHandle& nh_private) :
00045   optimization_count_(36),
00046   nh_private_(nh_private)
00047 {
00048   initParams();
00049 }
00050 
00051 /* Init parameters of the csm method
00052  */
00053 void DissimilarityGetter::initParams()
00054 {
00055   csm_input_.min_reading = 0;
00056   csm_input_.max_reading = std::numeric_limits<double>::max();
00057 
00058   nh_private_.getParam("optimization_count", optimization_count_);
00059   if (optimization_count_ < 1)
00060   {
00061     ROS_WARN_STREAM(nh_private_.getNamespace() << "/optimization_count should be at least 1, setting to default");
00062     optimization_count_ = 36;
00063     nh_private_.setParam("optimization_count", optimization_count_);
00064   }
00065 
00066   // **** CSM parameters - comments copied from algos.h (by Andrea Censi)
00067   // Defaults taken from the laser_scan_matcher package.
00068 
00069   // Maximum angular displacement between scans
00070   csm_input_.max_angular_correction_deg = 180.0;
00071   nh_private_.getParam("max_angular_correction_deg", csm_input_.max_angular_correction_deg);
00072 
00073   // Maximum translation between scans (m)
00074   csm_input_.max_linear_correction = 50.0;
00075   nh_private_.getParam("max_linear_correction", csm_input_.max_linear_correction);
00076 
00077   // Maximum ICP cycle iterations
00078   csm_input_.max_iterations = 10;
00079   nh_private_.getParam("max_iterations", csm_input_.max_iterations);
00080 
00081   // A threshold for stopping (m)
00082   csm_input_.epsilon_xy = 0.000001;
00083   nh_private_.getParam("epsilon_xy", csm_input_.epsilon_xy);
00084 
00085   // A threshold for stopping (rad)
00086   csm_input_.epsilon_theta = 0.000001;
00087   nh_private_.getParam("epsilon_theta", csm_input_.epsilon_theta);
00088 
00089   // Maximum distance for a correspondence to be valid
00090   csm_input_.max_correspondence_dist = 0.5;
00091   nh_private_.getParam("max_correspondence_dist", csm_input_.max_correspondence_dist);
00092 
00093   // Noise in the scan (m)
00094   csm_input_.sigma = 0.010;
00095   nh_private_.getParam("sigma", csm_input_.sigma);
00096 
00097   // Use smart tricks for finding correspondences.
00098   csm_input_.use_corr_tricks = 1;
00099   nh_private_.getParam("use_corr_tricks", csm_input_.use_corr_tricks);
00100 
00101   // Restart: Restart if error is over threshold
00102   csm_input_.restart = 0;
00103   nh_private_.getParam("restart", csm_input_.restart);
00104 
00105   // Restart: Threshold for restarting
00106   csm_input_.restart_threshold_mean_error = 0.01;
00107   nh_private_.getParam("restart_threshold_mean_error", csm_input_.restart_threshold_mean_error);
00108 
00109   // Restart: displacement for restarting. (m)
00110   csm_input_.restart_dt = 1.0;
00111   nh_private_.getParam("restart_dt", csm_input_.restart_dt);
00112 
00113   // Restart: displacement for restarting. (rad)
00114   csm_input_.restart_dtheta = 0.1;
00115   nh_private_.getParam("restart_dtheta", csm_input_.restart_dtheta);
00116 
00117   // Max distance for staying in the same clustering
00118   csm_input_.clustering_threshold = 0.25;
00119   nh_private_.getParam("clustering_threshold", csm_input_.clustering_threshold);
00120 
00121   // Number of neighbour rays used to estimate the orientation
00122   csm_input_.orientation_neighbourhood = 20;
00123   nh_private_.getParam("orientation_neighbourhood", csm_input_.orientation_neighbourhood);
00124 
00125   // If 0, it's vanilla ICP
00126   csm_input_.use_point_to_line_distance = 1;
00127   nh_private_.getParam("use_point_to_line_distance", csm_input_.use_point_to_line_distance);
00128 
00129   // Discard correspondences based on the angles
00130   csm_input_.do_alpha_test = 0;
00131   nh_private_.getParam("do_alpha_test", csm_input_.do_alpha_test);
00132 
00133   // Discard correspondences based on the angles - threshold angle, in degrees
00134   csm_input_.do_alpha_test_thresholdDeg = 20.0;
00135   nh_private_.getParam("do_alpha_test_thresholdDeg", csm_input_.do_alpha_test_thresholdDeg);
00136 
00137   // Percentage of correspondences to consider: if 0.9,
00138   // always discard the top 10% of correspondences with more error
00139   csm_input_.outliers_maxPerc = 0.90;
00140   nh_private_.getParam("outliers_maxPerc", csm_input_.outliers_maxPerc);
00141 
00142   // Parameters describing a simple adaptive algorithm for discarding.
00143   //  1) Order the errors.
00144   //  2) Choose the percentile according to outliers_adaptive_order.
00145   //     (if it is 0.7, get the 70% percentile)
00146   //  3) Define an adaptive threshold multiplying outliers_adaptive_mult
00147   //     with the value of the error at the chosen percentile.
00148   //  4) Discard correspondences over the threshold.
00149   //  This is useful to be conservative; yet remove the biggest errors.
00150   csm_input_.outliers_adaptive_order = 0.7;
00151   nh_private_.getParam("outliers_adaptive_order", csm_input_.outliers_adaptive_order);
00152 
00153   csm_input_.outliers_adaptive_mult = 2.0;
00154   nh_private_.getParam("outliers_adaptive_mult", csm_input_.outliers_adaptive_mult);
00155 
00156   // If you already have a guess of the solution, you can compute the polar angle
00157   // of the points of one scan in the new position. If the polar angle is not a monotone
00158   // function of the readings index, it means that the surface is not visible in the
00159   // next position. If it is not visible, then we don't use it for matching.
00160   csm_input_.do_visibility_test = 0;
00161   nh_private_.getParam("do_visibility_test", csm_input_.do_visibility_test);
00162 
00163   // no two points in laser_sens can have the same corr.
00164   csm_input_.outliers_remove_doubles = 1;
00165   nh_private_.getParam("outliers_remove_doubles", csm_input_.outliers_remove_doubles);
00166 
00167   // If 1, computes the covariance of ICP using the method http://purl.org/censi/2006/icpcov
00168   csm_input_.do_compute_covariance = 0;
00169   nh_private_.getParam("do_compute_covariance", csm_input_.do_compute_covariance);
00170 
00171   // Checks that find_correspondences_tricks gives the right answer
00172   csm_input_.debug_verify_tricks = 0;
00173   nh_private_.getParam("debug_verify_tricks", csm_input_.debug_verify_tricks);
00174 
00175   // If 1, the field 'true_alpha' (or 'alpha') in the first scan is used to compute the
00176   // incidence beta, and the factor (1/cos^2(beta)) used to weight the correspondence.");
00177   csm_input_.use_ml_weights = 0;
00178   nh_private_.getParam("use_ml_weights", csm_input_.use_ml_weights);
00179 
00180   // If 1, the field 'readings_sigma' in the second scan is used to weight the
00181   // correspondence by 1/sigma^2
00182   csm_input_.use_sigma_weights = 0;
00183   nh_private_.getParam("use_sigma_weights", csm_input_.use_sigma_weights);
00184 }
00185 
00186 /* Compute the scan matching
00187  *
00188  * This is a modified copy of the original sm_icp that doesn't do any checks
00189  * and doesn't compute Cartesian positions from laser ranges (as we already
00190  * have them).
00191  */
00192 void sm_icp_xy(struct sm_params* params, struct sm_result* res)
00193 {
00194         res->valid = 0;
00195 
00196         LDP laser_ref  = params->laser_ref;
00197         LDP laser_sens = params->laser_sens;
00198 
00199         if(params->use_corr_tricks || params->debug_verify_tricks)
00200   {
00201                 ld_create_jump_tables(laser_ref);
00202   }
00203 
00204   /* We don't set readings and theta so we can't use
00205    * do_alpha_test
00206    */
00207 
00208         gsl_vector* x_new = gsl_vector_alloc(3);
00209         gsl_vector* x_old = vector_from_array(3, params->first_guess);
00210 
00211         if(params->do_visibility_test)
00212   {
00213                 sm_debug("laser_ref:\n");
00214     visibilityTest(laser_ref, x_old);
00215 
00216                 sm_debug("laser_sens:\n");
00217                 gsl_vector* minus_x_old = gsl_vector_alloc(3);
00218                 ominus(x_old, minus_x_old);
00219                 visibilityTest(laser_sens, minus_x_old);
00220                 gsl_vector_free(minus_x_old);
00221         }
00222 
00223         double error;
00224         int iterations;
00225         int nvalid;
00226         if(!icp_loop(params, x_old->data, x_new->data, &error, &nvalid, &iterations))
00227   {
00228                 ROS_ERROR("icp: ICP failed for some reason. \n");
00229                 res->valid = 0;
00230                 res->iterations = iterations;
00231                 res->nvalid = 0;
00232         }
00233   else
00234   {
00235                 /* It was succesfull */
00236 
00237                 double best_error = error;
00238                 gsl_vector* best_x = gsl_vector_alloc(3);
00239                 gsl_vector_memcpy(best_x, x_new);
00240 
00241                 if (params->restart && (error/nvalid) > (params->restart_threshold_mean_error))
00242     {
00243                         ROS_DEBUG("Restarting: %f > %f \n", error/nvalid, params->restart_threshold_mean_error);
00244                         double dt  = params->restart_dt;
00245                         double dth = params->restart_dtheta;
00246                         ROS_DEBUG("icp_loop: dt = %f dtheta= %f deg\n", dt, rad2deg(dth));
00247 
00248                         double perturb[6][3] = {
00249                                 {dt, 0, 0}, {-dt, 0, 0},
00250                                 {0, dt, 0}, {0, -dt, 0},
00251                                 {0, 0, dth}, {0, 0, -dth}
00252                         };
00253 
00254                         for(int a = 0; a < 6; a++)
00255       {
00256                                 ROS_DEBUG("-- Restarting with perturbation #%d\n", a);
00257                                 struct sm_params my_params = *params;
00258                                 gsl_vector* start = gsl_vector_alloc(3);
00259         gvs(start, 0, gvg(x_new, 0) + perturb[a][0]);
00260         gvs(start, 1, gvg(x_new, 1) + perturb[a][1]);
00261         gvs(start, 2, gvg(x_new, 2) + perturb[a][2]);
00262                                 gsl_vector* x_a = gsl_vector_alloc(3);
00263                                 double my_error;
00264         int my_valid;
00265         int my_iterations;
00266                                 if (!icp_loop(&my_params, start->data, x_a->data, &my_error, &my_valid, &my_iterations))
00267         {
00268                                         ROS_ERROR("Error during restart #%d/%d. \n", a, 6);
00269                                         break;
00270                                 }
00271                                 iterations += my_iterations;
00272 
00273                                 if (my_error < best_error)
00274         {
00275                                         ROS_DEBUG("--Perturbation #%d resulted in error %f < %f\n", a, my_error, best_error);
00276                                         gsl_vector_memcpy(best_x, x_a);
00277                                         best_error = my_error;
00278                                 }
00279                                 gsl_vector_free(x_a);
00280         gsl_vector_free(start);
00281                         }
00282                 }
00283 
00284                 /* At last, we did it. */
00285                 res->valid = 1;
00286                 vector_to_array(best_x, res->x);
00287                 ROS_DEBUG("icp: final x =  %s  \n", gsl_friendly_pose(best_x));
00288 
00289                 if(params->do_compute_covariance)
00290     {
00291                         val cov0_x;
00292       val dx_dy1;
00293       val dx_dy2;
00294                         compute_covariance_exact(
00295                                 laser_ref, laser_sens, best_x,
00296                                 &cov0_x, &dx_dy1, &dx_dy2);
00297 
00298                         val cov_x = sc(square(params->sigma), cov0_x);
00299 
00300                         res->cov_x_m = egsl_v2gslm(cov_x);
00301                         res->dx_dy1_m = egsl_v2gslm(dx_dy1);
00302                         res->dx_dy2_m = egsl_v2gslm(dx_dy2);
00303 
00304                 }
00305 
00306                 res->error = best_error;
00307                 res->iterations = iterations;
00308                 res->nvalid = nvalid;
00309 
00310                 gsl_vector_free(best_x);
00311         }
00312   gsl_vector_free(x_new);
00313   gsl_vector_free(x_old);
00314 }
00315 
00316 bool DissimilarityGetter::getDissimilarity(place_matcher_msgs::PolygonDissimilarityRequest& req, place_matcher_msgs::PolygonDissimilarityResponse& res)
00317 {
00318   ros::WallTime start = ros::WallTime::now();
00319 
00320   LDP ldp1;
00321   polygonToLDP(req.polygon1, ldp1);
00322   csm_input_.laser_ref = ldp1;
00323 
00324   LDP ldp2;
00325   polygonToLDP(req.polygon2, ldp2);
00326   csm_input_.laser_sens = ldp2;
00327  
00328   // Run ICP several times, each with a different angle, to workaround local
00329   // optimum.
00330   double best_error = std::numeric_limits<double>::max();
00331   double best_x = 0.0;
00332   double best_y = 0.0;
00333   double best_yaw = 0.0;
00334   for (int i = 0; i < optimization_count_; ++i)
00335   {
00336     const double init_yaw = 2 * M_PI * i / optimization_count_;
00337 
00338     csm_input_.first_guess[2] = init_yaw;
00339 
00340     // Polygon match - using point to line icp from CSM.
00341     sm_result csm_output;
00342     sm_icp_xy(&csm_input_, &csm_output);
00343     if (!csm_output.valid)
00344     {
00345       ROS_ERROR("Canonical scan match failed with initial angle %.3f rad", init_yaw);
00346       continue;
00347     }
00348     if (csm_output.error < best_error)
00349     {
00350       best_x = csm_output.x[0];
00351       best_y = csm_output.x[1];
00352       best_yaw = csm_output.x[2];
00353       best_error = csm_output.error;
00354     }
00355     ROS_DEBUG("Canonical scan match return value = %f with initial angle %.3f rad", csm_output.error, init_yaw);
00356   }
00357 
00358   if (best_error == std::numeric_limits<double>::max())
00359   {
00360     ROS_INFO("Canonical scan match failed for all initial angles");
00361     res.raw_dissimilarity = best_error;
00362     res.pose.orientation.w = 1.0;
00363     res.processing_time = ros::Duration((ros::WallTime::now() - start).toSec());
00364     return false;
00365   }
00366 
00367   res.raw_dissimilarity = best_error;
00368   // The output of csm is the transformation from sens to ref.
00369   // We need the opposite.
00370   const double cos_ = std::cos(best_yaw);
00371   const double sin_ = std::sin(best_yaw);
00372   res.pose.position.x = -best_x * cos_ - best_y * sin_;
00373   res.pose.position.y = best_x * sin_ - best_y * cos_;
00374   res.pose.orientation = tf::createQuaternionMsgFromYaw(-best_yaw);
00375   res.processing_time = ros::Duration((ros::WallTime::now() - start).toSec());
00376 
00377   csm_input_.laser_ref = NULL;
00378   csm_input_.laser_sens = NULL;
00379   ld_free(ldp1);
00380   ld_free(ldp2);
00381   return true;
00382 }
00383 
00384 } /* namespace place_matcher_csm */
00385 


place_matcher_csm
Author(s): Gaƫl Ecorchard
autogenerated on Sat Jun 8 2019 19:52:59