norlab-ulaval / libpointmatcher

An Iterative Closest Point (ICP) library for 2D and 3D mapping in Robotics
BSD 3-Clause "New" or "Revised" License
1.62k stars 547 forks source link

RigidTransformation: Error, rotation matrix is not orthogonal. #40

Closed jahead closed 10 years ago

jahead commented 10 years ago

Hey so I keep getting this error randomly showing up, in my icp runs. "RigidTransformation: Error, rotation matrix is not orthogonal." I don't really understand why it would be throwing this error, or how to solve it.

For example, here is two point clouds that have caused this error.

screen shot 2014-07-01 at 3 20 35 am a gist of it can be found here: https://gist.github.com/jahead/cb5b3add89fc1815452a and my icp setup is this.


    icp.transformations.clear();
    icp.readingDataPointsFilters.clear();
    icp.readingStepDataPointsFilters.clear();
    icp.referenceDataPointsFilters.clear();
    icp.matcher.reset();
    icp.outlierFilters.clear();
    icp.errorMinimizer.reset();
    icp.transformationCheckers.clear();
    icp.inspector.reset();

    icp.transformations.push_back(new typename TransformationsImpl<float>::RigidTransformation());    

icp.readingDataPointsFilters.push_back(new typename DataPointsFiltersImpl<float>::RemoveNaNDataPointsFilter());
    icp.readingDataPointsFilters.push_back(pointMatcher::get().DataPointsFilterRegistrar.create(
                                                                                                "MaxPointCountDataPointsFilter",
                                                                                                PointMatcherSupport::map_list_of
                                                                                                ("prob",PointMatcherSupport::toParam(1))
                                                                                                ("maxCount", PointMatcherSupport::toParam(100))
                                                                                                )
                                           );
    icp.referenceDataPointsFilters.push_back(new typename DataPointsFiltersImpl<float>::RemoveNaNDataPointsFilter());
    icp.referenceDataPointsFilters.push_back(pointMatcher::get().DataPointsFilterRegistrar.create(
                                                                                                  "MaxPointCountDataPointsFilter",
                                                                                                  PointMatcherSupport::map_list_of
                                                                                                  ("prob",PointMatcherSupport::toParam(1))
                                                                                                  ("maxCount", PointMatcherSupport::toParam(100))
                                                                                                  )
                                             );

    icp.outlierFilters.push_back(pointMatcher::get().OutlierFilterRegistrar.create("MedianDistOutlierFilter",
                                                                                   PointMatcherSupport::map_list_of
                                                                                   ("factor",PointMatcherSupport::toParam(1.1))
                                                                                   ));
    icp.matcher.reset(new typename MatchersImpl<float>::KDTreeMatcher());
    icp.errorMinimizer.reset(new typename ErrorMinimizersImpl<float>::PointToPointErrorMinimizer());
    icp.transformationCheckers.push_back(pointMatcher::get().TransformationCheckerRegistrar.create(
                                                                                                   "CounterTransformationChecker",
                                                                                                   PointMatcherSupport::map_list_of
                                                                                                   ("maxIterationCount", PointMatcherSupport::toParam(100))
                                                                                                   )
                                         );

    icp.transformationCheckers.push_back(new typename TransformationCheckersImpl<float>::DifferentialTransformationChecker());

I also realise that there is a correctParameters function. But your ICP isn't using it once it detects the transformation error being thrown; it's just throwing an exception. It seems odd that the ICP can't do this correction on the fly. By just calling the correctParameters function when checkParameters returns false. Or maybe I'm looking at the problem wrong.

Oh and ps. I don't load from yaml. Could this be a problem? And I'm running the icp at high frequency as i'm trying converge two datasets from two different leap motions.

Oh and btw. I'm loving your library

pomerlef commented 10 years ago

Hi Jahead,

Thanks for reporting. I'll need to investigate your problem more precisely. In your example, there are few points and you use an outlier filter which will reduce even more the number of points. Maybe this exception is throw before/instead of a proper one which should say "not enough points" or something like that. I'm on the road for some days so I'll check that later. If you could check that, it would be great.

Meanwhile, here are the answers to your questions:

If you develop new modules to suit your needs, we are always happy to have more contributors!

jahead commented 10 years ago

Sorry for the late reply. Thank you for your response.

It does seems the low point count is a problem. The new beta version of the leap motion sdk, is now giving us more points so I've been updating and porting the code to handle a larger points set.

I"ll try not using an outlier filter when I get around to use ICP again; which should be soon. But I wasn't getting the "not enough points" exception. we concluded (not tested) that the icp was not having enough pairs to do its work and the only pairs that were detected were becoming co linear. Our the data points are only approximations that get approximated based separately, therefore the points be can related to the same 'joint' but have slightly different xyz points.

Hope that gives you a bit more context.

jahead commented 10 years ago

The larger the point cloud did help. 22 points and we aren't seeing this exception. Which is interesting that I wasn't getting the "not enough points" exception.

pomerlef commented 10 years ago

Hi jahead,

So looking at the code a bit more. There is no safety for a minimal number of in the errorMinimizer. There was a check for singularity in point-to-plane based on the rank of the matrix but it was commented out because it was throwing too often.

It is tricky to put a hard threshold on the number of points given that in theory it should be 3, but in practice it is often more than that.

I'll reopen another issue with a clearer description in case somebody has already though about it.