44 using namespace Eigen;
67 LOG_INFO_STREAM(
"PointMatcher::PointToPlaneErrorMinimizer - minimization will be in 2D.");
72 LOG_INFO_STREAM(
"PointMatcher::PointToPlaneErrorMinimizer - minimization will be in 4-DOF (yaw,x,y,z).");
76 LOG_INFO_STREAM(
"PointMatcher::PointToPlaneErrorMinimizer - minimization will be in 3D.");
94 LOG_INFO_STREAM(
"PointMatcher::PointToPlaneErrorMinimizer - minimization will be in 2D.");
99 LOG_INFO_STREAM(
"PointMatcher::PointToPlaneErrorMinimizer - minimization will be in 4-DOF (yaw,x,y,z).");
103 LOG_INFO_STREAM(
"PointMatcher::PointToPlaneErrorMinimizer - minimization will be in 3D.");
108 template<
typename T,
typename MatrixA,
typename Vector>
110 assert(A.cols() == A.rows());
111 assert(b.cols() == 1);
112 assert(b.rows() == A.rows());
113 assert(x.cols() == 1);
114 assert(x.rows() == A.cols());
118 BOOST_AUTO(Aqr, A.fullPivHouseholderQr());
119 if (!Aqr.isInvertible())
122 const int rank = Aqr.rank();
123 const int rows = A.rows();
124 const Matrix Q1t = Aqr.matrixQ().transpose().block(0, 0, rank, rows);
125 const Matrix R1 = (Q1t * A * Aqr.colsPermutation()).block(0, 0, rank, rows);
127 const bool findMinimalNormSolution =
true;
130 if(findMinimalNormSolution){
132 x = R1.template triangularView<Eigen::Upper>().transpose() * (R1 * R1.transpose()).llt().solve(Q1t * b);
135 x.block(0, 0, rank, 1) = R1.block(0, 0, rank, rank).template triangularView<Eigen::Upper>().solve(Q1t * b);
136 x.block(rank, 0, rows - rank, 1).setZero();
139 x = Aqr.colsPermutation() * x;
141 BOOST_AUTO(ax , (A * x).eval());
142 if (!b.isApprox(ax, 1e-5)) {
143 LOG_INFO_STREAM(
"PointMatcher::icp - encountered almost singular matrix while minimizing point to plane distance. QR solution was too inaccurate. Trying more accurate approach using double precision SVD.");
144 x = A.template cast<double>().jacobiSvd(ComputeThinU | ComputeThinV).solve(b.template cast<double>()).template cast<T>();
147 if((b - ax).norm() > 1e-5 * std::max(A.norm() * x.norm(), b.norm())){
148 LOG_WARNING_STREAM(
"PointMatcher::icp - encountered numerically singular matrix while minimizing point to plane distance and the current workaround remained inaccurate." 149 <<
" b=" << b.transpose()
150 <<
" !~ A * x=" << (ax).transpose().eval()
151 <<
": ||b- ax||=" << (b - ax).norm()
152 <<
", ||b||=" << b.norm()
153 <<
", ||ax||=" << ax.norm());
159 x = A.llt().solve(b);
178 int forcedDim = dim - 1;
192 assert(normalRef.rows() > 0);
206 matrixGamma << 0,-1, 0,
209 cross = ((matrixGamma*mPts.
reading.
features.block(0, 0, 3, nbPts)).transpose()*normalRef).diagonal().transpose();
215 Matrix wF(normalRef.rows()+ cross.rows(), normalRef.cols());
216 Matrix F(normalRef.rows()+ cross.rows(), normalRef.cols());
218 for(
int i=0; i < cross.rows(); i++)
220 wF.row(i) = mPts.
weights.array() * cross.row(i).array();
221 F.row(i) = cross.row(i);
223 for(
int i=0; i < normalRef.rows(); i++)
225 wF.row(i + cross.rows()) = mPts.
weights.array() * normalRef.row(i).array();
226 F.row(i + cross.rows()) = normalRef.row(i);
230 const Matrix A = wF * F.transpose();
235 Matrix dotProd = Matrix::Zero(1, normalRef.cols());
237 for(
int i=0; i<normalRef.rows(); i++)
239 dotProd += (deltas.row(i).array() * normalRef.row(i).array()).matrix();
243 const Vector b = -(wF * dotProd.transpose());
247 solvePossiblyUnderdeterminedLinearSystem<T>(A, b,
x);
253 Eigen::Transform<T, 3, Eigen::Affine> transform;
263 transform = Eigen::AngleAxis<T>(
x.head(3).norm(),
x.head(3).normalized());
268 transform = Eigen::AngleAxis<T>(
x(0), unitZ);
278 transform.translation() =
x.segment(3, 3);
281 transform.translation() =
x.segment(1, 3);
284 mOut = transform.matrix();
291 mOut.block(0, 0, dim-1, dim-1) = Matrix::Identity(dim-1, dim-1);
296 Eigen::Transform<T, 2, Eigen::Affine> transform;
297 transform = Eigen::Rotation2D<T> (
x(0));
298 transform.translation() =
x.segment(1, 2);
302 mOut = Matrix::Identity(dim, dim);
303 mOut.topLeftCorner(2, 2) = transform.matrix().topLeftCorner(2, 2);
304 mOut.topRightCorner(2, 1) = transform.matrix().topRightCorner(2, 1);
308 mOut = transform.matrix();
322 int forcedDim = dim - 1;
323 if(force2D && dim == 4)
336 assert(normalRef.rows() > 0);
341 Matrix dotProd = Matrix::Zero(1, normalRef.cols());
342 for(
int i = 0; i < normalRef.rows(); i++)
344 dotProd += (deltas.row(i).array() * normalRef.row(i).array()).matrix();
347 dotProd = (mPts.
weights.row(0).array() * dotProd.array().square()).matrix();
350 return dotProd.sum();
361 assert(matches.
ids.rows() > 0);
384 throw std::runtime_error(
"Error, last error element empty. Error minimizer needs to be called at least once before using this method.");
387 Eigen::Array<T, 1, Eigen::Dynamic> uncertainties(nbPoints);
390 if (hasReadingNoise && hasReferenceNoise && hasReferenceDensity)
395 vector<T> values(densities.data(), densities.data() + densities.size());
398 nth_element(values.begin(), values.begin() + (values.size() * 0.5), values.end());
401 const T medianDensity = values[values.size() * 0.5];
402 const T medianRadius = 1.0/
pow(medianDensity, 1/3.0);
404 uncertainties = (medianRadius +
408 else if(hasReadingNoise && hasReferenceNoise)
413 else if(hasReadingNoise)
417 else if(hasReferenceNoise)
423 LOG_INFO_STREAM(
"PointToPlaneErrorMinimizer - warning, no sensor noise and density. Using best estimate given outlier rejection instead.");
434 int nbUniquePoint = 1;
436 for(
int i=0; i < nbPoints; i++)
440 if(lastValidPoint != point)
449 lastValidPoint = point;
static Matrix crossProduct(const Matrix &A, const Matrix &B)
Helper funtion doing the cross product in 3D and a pseudo cross product in 2D.
#define LOG_INFO_STREAM(args)
constexpr T pow(const T base, const std::size_t exponent)
PointMatcher< T >::Matrix Matrix
A structure holding data ready for minimization. The data are "normalized", for instance there are no...
Parametrizable::ParametersDoc ParametersDoc
PointMatcher< T >::OutlierWeights OutlierWeights
ConstView getDescriptorViewByName(const std::string &name) const
Get a const view on a descriptor by name, throw an exception if it does not exist.
PointToPlaneErrorMinimizer(const Parameters ¶ms=Parameters())
DataPoints reference
reference point cloud
TransformationParameters compute_in_place(ErrorElements &mPts)
PointMatcherSupport::Parametrizable Parametrizable
An error minimizer will compute a transformation matrix such as to minimize the error between the rea...
virtual T getOverlap() const
If not redefined by child class, return the ratio of how many points were used (with weight) for erro...
Parametrizable::ParameterDoc ParameterDoc
PointMatcher< T >::Vector Vector
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > Matrix
A dense matrix over ScalarType.
int nbRejectedPoints
number of points with all matches set to zero weights
virtual TransformationParameters compute(const ErrorElements &mPts)
Find the transformation that minimizes the error given matched pair of points. This function most be ...
T getWeightedPointUsedRatio() const
Return the ratio of how many points were used (with weight) for error minimization.
std::map< std::string, Parameter > Parameters
Parameters stored as a map of string->string.
OutlierWeights weights
weights for every association
Parametrizable::ParametersDoc ParametersDoc
Result of the data-association step (Matcher::findClosests), before outlier rejection.
bool descriptorExists(const std::string &name) const
Look if a descriptor with a given name exist.
const M::mapped_type & get(const M &m, const typename M::key_type &k)
The documentation of a parameter.
void solvePossiblyUnderdeterminedLinearSystem(const MatrixA &A, const Vector &b, Vector &x)
#define LOG_WARNING_STREAM(args)
The superclass of classes that are constructed using generic parameters. This class provides the para...
std::vector< ParameterDoc > ParametersDoc
The documentation of all parameters.
ErrorElements lastErrorElements
memory of the last computed error
S get(const std::string ¶mName)
Return the value of paramName, lexically-casted to S.
virtual const std::string name()
virtual T getResidualError(const DataPoints &filteredReading, const DataPoints &filteredReference, const OutlierWeights &outlierWeights, const Matches &matches) const
If not redefined by child class, return max value for T.
PointMatcherSupport::Parametrizable P
Ids ids
identifiers of closest points
Matrix features
features of points in the cloud
Parametrizable::Parameters Parameters
DataPoints reading
reading point cloud
Matrix TransformationParameters
A matrix holding the parameters a transformation.
static T computeResidualError(ErrorElements mPts, const bool &force2D)
Parametrizable::Parameters Parameters
An expception thrown when the yaml config file contains invalid configuration (e.g., mutually exclusive settings)
static T anyabs(const T &v)