Пример #1
0
tuple CICP_AlignPDF2(CICP &self, CSimplePointsMap &m1, CSimplePointsMap &m2, CPosePDFGaussian &initialEstimationPDF)
{
    CPosePDFGaussian posePDF;
    float runningTime;
    CICP::TReturnInfo info;

    CPosePDFPtr posePDFPtr = self.AlignPDF(&m1, &m2, initialEstimationPDF, &runningTime, &info);
    posePDF.copyFrom(*posePDFPtr);

    boost::python::list ret_val;
    ret_val.append(posePDF);
    ret_val.append(runningTime);
    ret_val.append(info);
    return tuple(ret_val);
}
Пример #2
0
/*---------------------------------------------------------------
	Copy Constructor from 2D PDF
  ---------------------------------------------------------------*/
CPose3DPDFGaussian::CPose3DPDFGaussian( const CPosePDFGaussian &o )
	 : mean( o.mean.x(),o.mean.y(),0,o.mean.phi(),0,0 ),
	   cov()
{
	for (size_t i=0;i<3;i++)
	{
		const size_t ii= (i==2) ? 3 : i;
		for (size_t j=0;j<3;j++)
		{
			const size_t jj= (j==2) ? 3 : j;
			cov(ii,jj) = o.cov(i,j);
		}
	}
}
/** The PF algorithm implementation for "optimal sampling" approximated with scan matching (Stachniss method)
  */
void  CLSLAM_RBPF_2DLASER::prediction_and_update_pfOptimalProposal(
	CLocalMetricHypothesis	*LMH,
	const mrpt::slam::CActionCollection	* actions,
	const mrpt::slam::CSensoryFrame		* sf,
	const bayes::CParticleFilter::TParticleFilterOptions &PF_options )
{
	MRPT_START

	CTicTac tictac;

	// Get the current robot pose estimation:
	TPoseID		currentPoseID = LMH->m_currentRobotPose;

	// ----------------------------------------------------------------------
	//	  We can execute optimal PF only when we have both, an action, and
	//     a valid observation from which to compute the likelihood:
	//   Accumulate odometry/actions until we have a valid observation, then
	//    process them simultaneously.
	// ----------------------------------------------------------------------
	bool  SFhasValidObservations = false;
	// A valid action?
	if (actions!=NULL)
	{
		CActionRobotMovement2DPtr act = actions->getBestMovementEstimation();	// Find a robot movement estimation:
		if (!act) THROW_EXCEPTION("Action list does not contain any CActionRobotMovement2D derived object!");

		if (!LMH->m_accumRobotMovementIsValid) // Reset accum.
		{
			act->poseChange->getMean( LMH->m_accumRobotMovement.rawOdometryIncrementReading );
			LMH->m_accumRobotMovement.motionModelConfiguration = act->motionModelConfiguration;
		}
		else
			LMH->m_accumRobotMovement.rawOdometryIncrementReading =
				LMH->m_accumRobotMovement.rawOdometryIncrementReading +
				act->poseChange->getMeanVal();

		LMH->m_accumRobotMovementIsValid = true;
	}

	if (sf!=NULL)
	{
		ASSERT_(LMH->m_particles.size()>0);
		SFhasValidObservations = (*LMH->m_particles.begin()).d->metricMaps.canComputeObservationsLikelihood( *sf );
	}

	// All the needed things?
	if (!LMH->m_accumRobotMovementIsValid || !SFhasValidObservations)
		return; // Nothing we can do here...
	ASSERT_(sf!=NULL);
	ASSERT_(!PF_options.adaptiveSampleSize);

	// OK, we have all we need, let's start!

	// The odometry-based increment since last step is
	// in:   LMH->m_accumRobotMovement.rawOdometryIncrementReading
	const CPose2D initialPoseEstimation = LMH->m_accumRobotMovement.rawOdometryIncrementReading;
	LMH->m_accumRobotMovementIsValid = false; // To reset odometry at next iteration!


	// ----------------------------------------------------------------------
	//						1) FIXED SAMPLE SIZE VERSION
	// ----------------------------------------------------------------------

	// ICP used if "pfOptimalProposal_mapSelection" = 0 or 1
	CICP					icp;
	CICP::TReturnInfo	icpInfo;

	// ICP options
	// ------------------------------
	icp.options.maxIterations = 80;
	icp.options.thresholdDist = 0.50f;
	icp.options.thresholdAng = DEG2RAD( 20 );
	icp.options.smallestThresholdDist = 0.05f;
	icp.options.ALFA		  = 0.5f;
	icp.options.onlyClosestCorrespondences = true;
	icp.options.doRANSAC = false;

	// Build the map of points to align:
	CSimplePointsMap	localMapPoints;

	ASSERT_( LMH->m_particles[0].d->metricMaps.m_gridMaps.size() > 0);
	//float	minDistBetweenPointsInLocalMaps = 0.02f; //3.0f * m_particles[0].d->metricMaps.m_gridMaps[0]->getResolution();

	// Build local map:
	localMapPoints.clear();
	localMapPoints.insertionOptions.minDistBetweenLaserPoints =  0.02;
	sf->insertObservationsInto( &localMapPoints );

	// Process the particles
	const size_t M = LMH->m_particles.size();
	LMH->m_log_w_metric_history.resize(M);

	for (size_t i=0;i<M;i++)
	{
		CLocalMetricHypothesis::CParticleData  &part = LMH->m_particles[i];
		CPose3D *part_pose = LMH->getCurrentPose(i);

		if ( LMH->m_SFs.empty() )
		{
			// The first robot pose in the SLAM execution: All m_particles start
			// at the same point (this is the lowest bound of subsequent uncertainty):
			// New pose = old pose.
			// part_pose: Unmodified
		}
		else
		{
			// ICP and add noise:
			CPosePDFGaussian	icpEstimation;

			// Select map to use with ICP:
			CMetricMap *mapalign;

			if (!part.d->metricMaps.m_pointsMaps.empty())
				mapalign = part.d->metricMaps.m_pointsMaps[0].pointer();
			else if (!part.d->metricMaps.m_gridMaps.empty())
				mapalign = part.d->metricMaps.m_gridMaps[0].pointer();
			else
				THROW_EXCEPTION("There is no point or grid map. At least one needed for ICP.");

			// Use ICP to align to each particle's map:
			CPosePDFPtr alignEst = icp.Align(
				mapalign,
				&localMapPoints,
				initialPoseEstimation,
				NULL,
				&icpInfo);

			icpEstimation.copyFrom( *alignEst );

#if 0
			// HACK:
			CPose3D Ap = finalEstimatedPoseGauss.mean - ith_last_pose;
			double  Ap_dist = Ap.norm();
			finalEstimatedPoseGauss.cov.zeros();
			finalEstimatedPoseGauss.cov(0,0) = square( fabs(Ap_dist)*0.01 );
			finalEstimatedPoseGauss.cov(1,1) = square( fabs(Ap_dist)*0.01 );
			finalEstimatedPoseGauss.cov(2,2) = square( fabs(Ap.yaw())*0.02 );
#endif

			// Generate gaussian-distributed 2D-pose increments according to "finalEstimatedPoseGauss":
			// -------------------------------------------------------------------------------------------
			// Set the gaussian pose:
			CPose3DPDFGaussian finalEstimatedPoseGauss( icpEstimation );

			CPose3D noisy_increment;
			finalEstimatedPoseGauss.drawSingleSample(noisy_increment);

			CPose3D new_pose;
			new_pose.composeFrom(*part_pose,noisy_increment);

			CPose2D new_pose2d = CPose2D(new_pose);

			// Add the pose to the path:
			part.d->robotPoses[ LMH->m_currentRobotPose ] = new_pose;

			// Update the weight:
			// ---------------------------------------------------------------------------
			const double log_lik =
				PF_options.powFactor *
				auxiliarComputeObservationLikelihood(
					PF_options,
					LMH,
					i,
					sf,
					&new_pose2d);

			part.log_w += log_lik;

			// Add to historic record of log_w weights:
			LMH->m_log_w_metric_history[i][currentPoseID] += log_lik;

		} // end else we can do ICP

	} // end for each particle i

	// Accumulate the log likelihood of this LMH as a whole:
	double out_max_log_w;
	LMH->normalizeWeights( &out_max_log_w );	// Normalize weights:
	LMH->m_log_w += out_max_log_w;

	printf("[CLSLAM_RBPF_2DLASER] Overall likelihood = %.2e\n",out_max_log_w);
	printf("[CLSLAM_RBPF_2DLASER] Done in %.03fms\n",1e3*tictac.Tac());

	MRPT_END
}
Пример #4
0
/*---------------------------------------------------------------

					robustRigidTransformation

  The technique was described in the paper:
	J.L. Blanco, J. Gonzalez-Jimenez and J.A. Fernandez-Madrigal.
	"A robust, multi-hypothesis approach to matching occupancy grid maps".
	Robotica, available on CJO2013. doi:10.1017/S0263574712000732.
	http://journals.cambridge.org/action/displayAbstract?aid=8815308

 This works as follows:
	- Repeat "results.ransac_iters" times:
		- Randomly pick TWO correspondences from the set "in_correspondences".
		- Compute the associated rigid transformation.
		- For "ransac_maxSetSize" randomly selected correspondences, test for
 "consensus" with the current group:
			- If if is compatible (ransac_maxErrorXY, ransac_maxErrorPHI), grow
 the "consensus set"
			- If not, do not add it.
  ---------------------------------------------------------------*/
bool tfest::se2_l2_robust(
	const mrpt::tfest::TMatchingPairList& in_correspondences,
	const double normalizationStd, const TSE2RobustParams& params,
	TSE2RobustResult& results)
{
	//#define DO_PROFILING

#ifdef DO_PROFILING
	CTimeLogger timlog;
#endif

	const size_t nCorrs = in_correspondences.size();

	// Default: 2 * normalizationStd ("noise level")
	const double MAX_RMSE_TO_END =
		(params.max_rmse_to_end <= 0 ? 2 * normalizationStd
									 : params.max_rmse_to_end);

	MRPT_START

	// Asserts:
	if (nCorrs < params.ransac_minSetSize)
	{
		// Nothing to do!
		results.transformation.clear();
		results.largestSubSet = TMatchingPairList();
		return false;
	}

#ifdef DO_PROFILING
	timlog.enter("ransac.find_max*");
#endif
	// Find the max. index of "this" and "other:
	unsigned int maxThis = 0, maxOther = 0;
	for (const auto& in_correspondence : in_correspondences)
	{
		maxThis = max(maxThis, in_correspondence.this_idx);
		maxOther = max(maxOther, in_correspondence.other_idx);
	}
#ifdef DO_PROFILING
	timlog.leave("ransac.find_max*");
#endif

#ifdef DO_PROFILING
	timlog.enter("ransac.count_unique_corrs");
#endif

	// Fill out 2 arrays indicating whether each element has a correspondence:
	std::vector<bool> hasCorrThis(maxThis + 1, false);
	std::vector<bool> hasCorrOther(maxOther + 1, false);
	unsigned int howManyDifCorrs = 0;
	for (const auto& in_correspondence : in_correspondences)
	{
		if (!hasCorrThis[in_correspondence.this_idx] &&
			!hasCorrOther[in_correspondence.other_idx])
		{
			hasCorrThis[in_correspondence.this_idx] = true;
			hasCorrOther[in_correspondence.other_idx] = true;
			howManyDifCorrs++;
		}
	}
#ifdef DO_PROFILING
	timlog.leave("ransac.count_unique_corrs");
#endif

	// Clear the set of output particles:
	results.transformation.clear();

	// If there are less different correspondences than the minimum required,
	// quit:
	if (howManyDifCorrs < params.ransac_minSetSize)
	{
		// Nothing we can do here!!! :~$
		results.transformation.clear();
		results.largestSubSet = TMatchingPairList();
		return false;
	}

#ifdef AVOID_MULTIPLE_CORRESPONDENCES
	unsigned k;
	// Find duplicated landmarks (from SIFT features with different
	// descriptors,etc...)
	//   this is to avoid establishing multiple correspondences for the same
	//   physical point!
	// ------------------------------------------------------------------------------------------------
	std::vector<std::vector<int>> listDuplicatedLandmarksThis(maxThis + 1);
	ASSERT_(nCorrs >= 1);
	for (k = 0; k < nCorrs - 1; k++)
	{
		std::vector<int> duplis;
		for (unsigned j = k; j < nCorrs - 1; j++)
		{
			if (in_correspondences[k].this_x == in_correspondences[j].this_x &&
				in_correspondences[k].this_y == in_correspondences[j].this_y &&
				in_correspondences[k].this_z == in_correspondences[j].this_z)
				duplis.push_back(in_correspondences[j].this_idx);
		}
		listDuplicatedLandmarksThis[in_correspondences[k].this_idx] = duplis;
	}

	std::vector<std::vector<int>> listDuplicatedLandmarksOther(maxOther + 1);
	for (k = 0; k < nCorrs - 1; k++)
	{
		std::vector<int> duplis;
		for (unsigned j = k; j < nCorrs - 1; j++)
		{
			if (in_correspondences[k].other_x ==
					in_correspondences[j].other_x &&
				in_correspondences[k].other_y ==
					in_correspondences[j].other_y &&
				in_correspondences[k].other_z == in_correspondences[j].other_z)
				duplis.push_back(in_correspondences[j].other_idx);
		}
		listDuplicatedLandmarksOther[in_correspondences[k].other_idx] = duplis;
	}
#endif

	std::deque<TMatchingPairList> alreadyAddedSubSets;

	CPosePDFGaussian referenceEstimation;
	CPoint2DPDFGaussian pt_this;

	const double ransac_consistency_test_chi2_quantile = 0.99;
	const double chi2_thres_dim1 =
		mrpt::math::chi2inv(ransac_consistency_test_chi2_quantile, 1);

	// -------------------------
	//		The RANSAC loop
	// -------------------------
	size_t largest_consensus_yet = 0;  // Used for dynamic # of steps
	double largestSubSet_RMSE = std::numeric_limits<double>::max();

	results.ransac_iters = params.ransac_nSimulations;
	const bool use_dynamic_iter_number = results.ransac_iters == 0;
	if (use_dynamic_iter_number)
	{
		ASSERT_(
			params.probability_find_good_model > 0 &&
			params.probability_find_good_model < 1);
		// Set an initial # of iterations:
		results.ransac_iters = 10;  // It doesn't matter actually, since will be
		// changed in the first loop
	}

	std::vector<bool> alreadySelectedThis, alreadySelectedOther;

	if (!params.ransac_algorithmForLandmarks)
	{
		alreadySelectedThis.assign(maxThis + 1, false);
		alreadySelectedOther.assign(maxOther + 1, false);
	}
	// else -> It will be done anyway inside the for() below

	// First: Build a permutation of the correspondences to pick from it
	// sequentially:
	std::vector<size_t> corrsIdxs(nCorrs), corrsIdxsPermutation;
	for (size_t i = 0; i < nCorrs; i++) corrsIdxs[i] = i;

	size_t iter_idx;
	for (iter_idx = 0; iter_idx < results.ransac_iters;
		 iter_idx++)  // results.ransac_iters can be dynamic
	{
#ifdef DO_PROFILING
		CTimeLoggerEntry tle(timlog, "ransac.iter");
#endif

#ifdef DO_PROFILING
		timlog.enter("ransac.permute");
#endif
		getRandomGenerator().permuteVector(corrsIdxs, corrsIdxsPermutation);

#ifdef DO_PROFILING
		timlog.leave("ransac.permute");
#endif

		TMatchingPairList subSet;

		// Select a subset of correspondences at random:
		if (params.ransac_algorithmForLandmarks)
		{
#ifdef DO_PROFILING
			timlog.enter("ransac.reset_selection_marks");
#endif
			alreadySelectedThis.assign(maxThis + 1, false);
			alreadySelectedOther.assign(maxOther + 1, false);
#ifdef DO_PROFILING
			timlog.leave("ransac.reset_selection_marks");
#endif
		}
		else
		{
			// For points: Do not repeat the corrs, and take the number of corrs
			// as weights
		}

// Try to build a subsetof "ransac_maxSetSize" (maximum) elements that achieve
// consensus:
// ------------------------------------------------------------------------------------------
#ifdef DO_PROFILING
		timlog.enter("ransac.inner_loops");
#endif
		for (unsigned int j = 0;
			 j < nCorrs && subSet.size() < params.ransac_maxSetSize; j++)
		{
			const size_t idx = corrsIdxsPermutation[j];

			const TMatchingPair& corr_j = in_correspondences[idx];

			// Don't pick the same features twice!
			if (alreadySelectedThis[corr_j.this_idx] ||
				alreadySelectedOther[corr_j.other_idx])
				continue;

			// Additional user-provided filter:
			if (params.user_individual_compat_callback)
			{
				mrpt::tfest::TPotentialMatch pm;
				pm.idx_this = corr_j.this_idx;
				pm.idx_other = corr_j.other_idx;
				if (!params.user_individual_compat_callback(pm))
					continue;  // Skip this one!
			}

			if (subSet.size() < 2)
			{
				// ------------------------------------------------------------------------------------------------------
				// If we are within the first two correspondences, just add them
				// to the subset:
				// ------------------------------------------------------------------------------------------------------
				subSet.push_back(corr_j);
				markAsPicked(corr_j, alreadySelectedThis, alreadySelectedOther);

				if (subSet.size() == 2)
				{
					// Consistency Test: From

					// Check the feasibility of this pair "idx1"-"idx2":
					//  The distance between the pair of points in MAP1 must be
					//  very close
					//   to that of their correspondences in MAP2:
					const double corrs_dist1 =
						mrpt::math::distanceBetweenPoints(
							subSet[0].this_x, subSet[0].this_y,
							subSet[1].this_x, subSet[1].this_y);

					const double corrs_dist2 =
						mrpt::math::distanceBetweenPoints(
							subSet[0].other_x, subSet[0].other_y,
							subSet[1].other_x, subSet[1].other_y);

					// Is is a consistent possibility?
					//  We use a chi2 test (see paper for the derivation)
					const double corrs_dist_chi2 =
						square(square(corrs_dist1) - square(corrs_dist2)) /
						(8.0 * square(normalizationStd) *
						 (square(corrs_dist1) + square(corrs_dist2)));

					bool is_acceptable = (corrs_dist_chi2 < chi2_thres_dim1);

					if (is_acceptable)
					{
						// Perform estimation:
						tfest::se2_l2(subSet, referenceEstimation);
						// Normalized covariance: scale!
						referenceEstimation.cov *= square(normalizationStd);

						// Additional filter:
						//  If the correspondences as such the transformation
						//  has a high ambiguity, we discard it!
						is_acceptable =
							(referenceEstimation.cov(2, 2) <
							 square(DEG2RAD(5.0f)));
					}

					if (!is_acceptable)
					{
						// Remove this correspondence & try again with a
						// different pair:
						subSet.erase(subSet.begin() + (subSet.size() - 1));
					}
					else
					{
						// Only mark as picked if we're really keeping it:
						markAsPicked(
							corr_j, alreadySelectedThis, alreadySelectedOther);
					}
				}
			}
			else
			{
#ifdef DO_PROFILING
				timlog.enter("ransac.test_consistency");
#endif

				// ------------------------------------------------------------------------------------------------------
				// The normal case:
				//  - test for "consensus" with the current group:
				//		- If it is compatible (ransac_maxErrorXY,
				// ransac_maxErrorPHI), grow the "consensus set"
				//		- If not, do not add it.
				// ------------------------------------------------------------------------------------------------------

				// Test for the mahalanobis distance between:
				//  "referenceEstimation (+) point_other" AND "point_this"
				referenceEstimation.composePoint(
					mrpt::math::TPoint2D(corr_j.other_x, corr_j.other_y),
					pt_this);

				const double maha_dist = pt_this.mahalanobisDistanceToPoint(
					corr_j.this_x, corr_j.this_y);

				const bool passTest =
					maha_dist < params.ransac_mahalanobisDistanceThreshold;

				if (passTest)
				{
					// OK, consensus passed:
					subSet.push_back(corr_j);
					markAsPicked(
						corr_j, alreadySelectedThis, alreadySelectedOther);
				}
				// else -> Test failed

#ifdef DO_PROFILING
				timlog.leave("ransac.test_consistency");
#endif
			}  // end else "normal case"

		}  // end for j
#ifdef DO_PROFILING
		timlog.leave("ransac.inner_loops");
#endif

		const bool has_to_eval_RMSE =
			(subSet.size() >= params.ransac_minSetSize);

		// Compute the RMSE of this matching and the corresponding
		// transformation (only if we'll use this value below)
		double this_subset_RMSE = 0;
		if (has_to_eval_RMSE)
		{
#ifdef DO_PROFILING
			CTimeLoggerEntry tle(timlog, "ransac.comp_rmse");
#endif

			// Recompute referenceEstimation from all the corrs:
			tfest::se2_l2(subSet, referenceEstimation);
			// Normalized covariance: scale!
			referenceEstimation.cov *= square(normalizationStd);

			for (size_t k = 0; k < subSet.size(); k++)
			{
				double gx, gy;
				referenceEstimation.mean.composePoint(
					subSet[k].other_x, subSet[k].other_y, gx, gy);

				this_subset_RMSE +=
					mrpt::math::distanceSqrBetweenPoints<double>(
						subSet[k].this_x, subSet[k].this_y, gx, gy);
			}
			this_subset_RMSE /= std::max(static_cast<size_t>(1), subSet.size());
		}
		else
		{
			this_subset_RMSE = std::numeric_limits<double>::max();
		}

		// Save the estimation result as a "particle", only if the subSet
		// contains
		//  "ransac_minSetSize" elements at least:
		if (subSet.size() >= params.ransac_minSetSize)
		{
			// If this subset was previously added to the SOG, just increment
			// its weight
			//  and do not add a new mode:
			int indexFound = -1;

			// JLBC Added DEC-2007: An alternative (optional) method to fuse
			// Gaussian modes:
			if (!params.ransac_fuseByCorrsMatch)
			{
				// Find matching by approximate match in the X,Y,PHI means
				// -------------------------------------------------------------------
				for (size_t i = 0; i < results.transformation.size(); i++)
				{
					double diffXY =
						results.transformation.get(i).mean.distanceTo(
							referenceEstimation.mean);
					double diffPhi = fabs(math::wrapToPi(
						results.transformation.get(i).mean.phi() -
						referenceEstimation.mean.phi()));
					if (diffXY < params.ransac_fuseMaxDiffXY &&
						diffPhi < params.ransac_fuseMaxDiffPhi)
					{
						// printf("Match by distance found: distXY:%f distPhi=%f
						// deg\n",diffXY,RAD2DEG(diffPhi));
						indexFound = i;
						break;
					}
				}
			}
			else
			{
				// Find matching mode by exact match in the list of
				// correspondences:
				// -------------------------------------------------------------------
				// Sort "subSet" in order to compare them easily!
				// std::sort( subSet.begin(), subSet.end() );

				// Try to find matching corrs:
				for (size_t i = 0; i < alreadyAddedSubSets.size(); i++)
				{
					if (subSet == alreadyAddedSubSets[i])
					{
						indexFound = i;
						break;
					}
				}
			}

			if (indexFound != -1)
			{
				// This is an already added mode:
				if (params.ransac_algorithmForLandmarks)
					results.transformation.get(indexFound).log_w = log(
						1 + exp(results.transformation.get(indexFound).log_w));
				else
					results.transformation.get(indexFound).log_w =
						log(subSet.size() +
							exp(results.transformation.get(indexFound).log_w));
			}
			else
			{
				// Add a new mode to the SOG:
				alreadyAddedSubSets.push_back(subSet);

				CPosePDFSOG::TGaussianMode newSOGMode;
				if (params.ransac_algorithmForLandmarks)
					newSOGMode.log_w = 0;  // log(1);
				else
					newSOGMode.log_w = log(static_cast<double>(subSet.size()));

				newSOGMode.mean = referenceEstimation.mean;
				newSOGMode.cov = referenceEstimation.cov;

				// Add a new mode to the SOG!
				results.transformation.push_back(newSOGMode);
			}
		}  // end if subSet.size()>=ransac_minSetSize

		const size_t ninliers = subSet.size();
		if (largest_consensus_yet < ninliers)
		{
			largest_consensus_yet = ninliers;

			// Dynamic # of steps:
			if (use_dynamic_iter_number)
			{
				// Update estimate of nCorrs, the number of trials to ensure we
				// pick,
				// with probability p, a data set with no outliers.
				const double fracinliers =
					ninliers /
					static_cast<double>(howManyDifCorrs);  // corrsIdxs.size());
				double pNoOutliers =
					1 - pow(fracinliers, static_cast<double>(
											 2.0 /*minimumSizeSamplesToFit*/));

				pNoOutliers = std::max(
					std::numeric_limits<double>::epsilon(),
					pNoOutliers);  // Avoid division by -Inf
				pNoOutliers = std::min(
					1.0 - std::numeric_limits<double>::epsilon(),
					pNoOutliers);  // Avoid division by 0.
				// Number of
				results.ransac_iters =
					log(1 - params.probability_find_good_model) /
					log(pNoOutliers);

				results.ransac_iters = std::max(
					results.ransac_iters, params.ransac_min_nSimulations);

				if (params.verbose)
					cout << "[tfest::RANSAC] Iter #" << iter_idx
						 << ":est. # iters=" << results.ransac_iters
						 << " pNoOutliers=" << pNoOutliers
						 << " #inliers: " << ninliers << endl;
			}
		}

		// Save the largest subset:
		if (subSet.size() >= params.ransac_minSetSize &&
			this_subset_RMSE < largestSubSet_RMSE)
		{
			if (params.verbose)
				cout << "[tfest::RANSAC] Iter #" << iter_idx
					 << " Better subset: " << subSet.size()
					 << " inliers, RMSE=" << this_subset_RMSE << endl;

			results.largestSubSet = subSet;
			largestSubSet_RMSE = this_subset_RMSE;
		}

		// Is the found subset good enough?
		if (subSet.size() >= params.ransac_minSetSize &&
			this_subset_RMSE < MAX_RMSE_TO_END)
		{
			break;  // end RANSAC iterations.
		}

#ifdef DO_PROFILING
		timlog.leave("ransac.iter");
#endif
	}  // end for each iteration

	if (params.verbose)
		cout << "[tfest::RANSAC] Finished after " << iter_idx
			 << " iterations.\n";

	// Set the weights of the particles to sum the unity:
	results.transformation.normalizeWeights();

	// Done!

	MRPT_END_WITH_CLEAN_UP(
		printf("nCorrs=%u\n", static_cast<unsigned int>(nCorrs));
		printf("Saving '_debug_in_correspondences.txt'...");
		in_correspondences.dumpToFile("_debug_in_correspondences.txt");
		printf("Ok\n"); printf("Saving '_debug_results.transformation.txt'...");
		results.transformation.saveToTextFile(
			"_debug_results.transformation.txt");
		printf("Ok\n"););
Пример #5
0
// ------------------------------------------------------
//				TestICP
// ------------------------------------------------------
void TestICP()
{
	CSimplePointsMap		m1,m2;
	float					runningTime;
	CICP::TReturnInfo		info;
	CICP					ICP;

	// Load scans:
	CObservation2DRangeScan	scan1;
	scan1.aperture = M_PIf;
	scan1.rightToLeft = true;
	scan1.validRange.resize( SCANS_SIZE );
	scan1.scan.resize(SCANS_SIZE);
	ASSERT_( sizeof(SCAN_RANGES_1) == sizeof(float)*SCANS_SIZE );

	memcpy( &scan1.scan[0], SCAN_RANGES_1, sizeof(SCAN_RANGES_1) );
	memcpy( &scan1.validRange[0], SCAN_VALID_1, sizeof(SCAN_VALID_1) );

	CObservation2DRangeScan	scan2 = scan1;
	memcpy( &scan2.scan[0], SCAN_RANGES_2, sizeof(SCAN_RANGES_2) );
	memcpy( &scan2.validRange[0], SCAN_VALID_2, sizeof(SCAN_VALID_2) );

	// Build the points maps from the scans:
	m1.insertObservation( &scan1 );
	m2.insertObservation( &scan2 );

#if MRPT_HAS_PCL
	cout << "Saving map1.pcd and map2.pcd in PCL format...\n";
	m1.savePCDFile("map1.pcd", false);
	m2.savePCDFile("map2.pcd", false);
#endif

	// -----------------------------------------------------
//	ICP.options.ICP_algorithm = icpLevenbergMarquardt;
//	ICP.options.ICP_algorithm = icpClassic;
	ICP.options.ICP_algorithm = (TICPAlgorithm)ICP_method;

	ICP.options.maxIterations			= 100;
	ICP.options.thresholdAng			= DEG2RAD(10.0f);
	ICP.options.thresholdDist			= 0.75f;
	ICP.options.ALFA					= 0.5f;
	ICP.options.smallestThresholdDist	= 0.05f;
	ICP.options.doRANSAC = false;

	ICP.options.dumpToConsole();
	// -----------------------------------------------------

	CPose2D		initialPose(0.8f,0.0f,(float)DEG2RAD(0.0f));

	CPosePDFPtr pdf = ICP.Align(
		&m1,
		&m2,
		initialPose,
		&runningTime,
		(void*)&info);

	printf("ICP run in %.02fms, %d iterations (%.02fms/iter), %.01f%% goodness\n -> ",
			runningTime*1000,
			info.nIterations,
			runningTime*1000.0f/info.nIterations,
			info.goodness*100 );

	cout << "Mean of estimation: " << pdf->getMeanVal() << endl<< endl;

	CPosePDFGaussian  gPdf;
	gPdf.copyFrom(*pdf);

	cout << "Covariance of estimation: " << endl << gPdf.cov << endl;

	cout << " std(x): " << sqrt( gPdf.cov(0,0) ) << endl;
	cout << " std(y): " << sqrt( gPdf.cov(1,1) ) << endl;
	cout << " std(phi): " << RAD2DEG(sqrt( gPdf.cov(2,2) )) << " (deg)" << endl;

	//cout << "Covariance of estimation (MATLAB format): " << endl << gPdf.cov.inMatlabFormat()  << endl;

	cout << "-> Saving reference map as scan1.txt" << endl;
	m1.save2D_to_text_file("scan1.txt");

	cout << "-> Saving map to align as scan2.txt" << endl;
	m2.save2D_to_text_file("scan2.txt");

	cout << "-> Saving transformed map to align as scan2_trans.txt" << endl;
	CSimplePointsMap m2_trans = m2;
	m2_trans.changeCoordinatesReference( gPdf.mean );
	m2_trans.save2D_to_text_file("scan2_trans.txt");


	cout << "-> Saving MATLAB script for drawing 2D ellipsoid as view_ellip.m" << endl;
	CMatrixFloat COV22 =  CMatrixFloat( CMatrixDouble( gPdf.cov ));
	COV22.setSize(2,2);
	CVectorFloat MEAN2D(2);
	MEAN2D[0] = gPdf.mean.x();
	MEAN2D[1] = gPdf.mean.y();
	{
		ofstream f("view_ellip.m");
		f << math::MATLAB_plotCovariance2D( COV22, MEAN2D, 3.0f);
	}


	// If we have 2D windows, use'em:
#if MRPT_HAS_WXWIDGETS
	if (!skip_window)
	{
		gui::CDisplayWindowPlots	win("ICP results");

		// Reference map:
		vector<float>   map1_xs, map1_ys, map1_zs;
		m1.getAllPoints(map1_xs,map1_ys,map1_zs);
		win.plot( map1_xs, map1_ys, "b.3", "map1");

		// Translated map:
		vector<float>   map2_xs, map2_ys, map2_zs;
		m2_trans.getAllPoints(map2_xs,map2_ys,map2_zs);
		win.plot( map2_xs, map2_ys, "r.3", "map2");

		// Uncertainty
		win.plotEllipse(MEAN2D[0],MEAN2D[1],COV22,3.0,"b2", "cov");

		win.axis(-1,10,-6,6);
		win.axis_equal();

		cout << "Close the window to exit" << endl;
		win.waitForKey();
	}
#endif


}