norlab-ulaval / libpointmatcher

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

ICP without scaling #175

Closed ieatmosquitos closed 8 years ago

ieatmosquitos commented 8 years ago

Hi.

I would like to run ICP to get a transform that minimizes the error by only considering translations and rotations, while it currently considers also scalings. I am using the default yaml configuration file, as I found no place in the configurations where to inhibit the scaling. To the best of my comprehension, the key part is the Error Minimizer. I would gladly avoid writing a new Error Minimizer. Is there a way I am ignoring?

Thank you very much

pomerlef commented 8 years ago

First, are you integrating the library into your own application or are you using on of the demo executable (like pmicp)?

In both cases, I suggest you to build your own yaml file because the default configuration rarely fit the needs for a specific application. Also, there is no involvement of the scaling in the default configuration. The full description of the default configuration can be found here: http://libpointmatcher.readthedocs.io/en/latest/DefaultICPConfig/#default-configuration-of-the-icp-chain

The default error minimizer used is PointToPlaneErrorMinimizer.

You can confirm that by checking if the sum of each column of the rotation components (first three columns) equals to one.

To build your own yaml file, you can check those tutorials: http://libpointmatcher.readthedocs.io/en/latest/ICPIntro/

ieatmosquitos commented 8 years ago

I am integrating the library in an application

I think there was an error on my side: I was not loading the configuration from the "default.yaml" configuration file. I was using the setDefault() function instead. By using a yaml configuration file, I actually get no scaling when running the icp algorithm.

I suppose the issue is SOLVED (even if I don't get why I get the scaling factor when using setDefault())

Thanks for your attention and sorry for my error

pomerlef commented 8 years ago

I double checked in the code for the default config and it's still PointToPlaneErrorMinimizer (it could have chane without me noticing it).

Here is the line where setDefault() is defined.

How did you detect that the transformation with scaling was used? There might be something else somewhere, but I would have to reproduce the problem on my side.

ieatmosquitos commented 8 years ago

To detect whether a scaling is being applied, I copy the transformation that icp outputs and copy it into an Eigen::isometry3f and use the function "computeRotationScaling" or "computeScalingRotation" to get the rotational and scaling matrices that, chained, give the same transformation.

I here have a small example of the output a program that performs icp between two pointclouds, first using setDefault, and then loading a yaml file. Here, the scaling is borderline perceptible (0.999999), which leads me to think that that is just a disguised 1 (which would mean I am not able to reproduce the error). Nonetheless the output is different from the time I load the yaml file:

=============

final transformation using setDefault:
   0.973109   -0.224804  -0.0502279  -0.0149907
   0.224476    0.974404  -0.0121649  -0.0265446
   0.051677 0.000562803    0.998664  -0.0141397
          0           0           0           1

rot_d:
   0.973108   -0.224804  -0.0502279
   0.224476    0.974404  -0.0121649
   0.051677 0.000562806    0.998664

scal_d:
    0.999999  6.98492e-09 -7.45058e-09
 7.45058e-09     0.999999  7.45058e-09
-7.45058e-09            0            1

scal_d x rot_d:
   0.973108   -0.224804  -0.0502279
   0.224475    0.974404  -0.0121649
  0.0516769 0.000562807    0.998663

=============

final transformation using yaml file:
   0.972352    -0.22466  -0.0637202  0.00647198
   0.224146    0.974437  -0.0151924   -0.027111
  0.0655045 0.000489678    0.997852   0.0115668
          0           0           0           1

rot_y:
   0.972352    -0.22466  -0.0637203
   0.224146    0.974437  -0.0151922
  0.0655045 0.000489902    0.997852

scal_y:
           1  -1.3411e-07  2.23517e-08
-1.19209e-07            1 -2.10479e-07
 7.45058e-09 -2.10479e-07            1

scal_d x rot_d:
   0.972352    -0.22466  -0.0637203
   0.224146    0.974438  -0.0151925
  0.0655045 0.000489695    0.997853

The yaml file I am using is the following:

readingDataPointsFilters:
  - RandomSamplingDataPointsFilter:
      prob: 0.5  

referenceDataPointsFilters:
  - SamplingSurfaceNormalDataPointsFilter:
      knn: 10

matcher:
  KDTreeMatcher:
    knn: 1
    epsilon: 0

outlierFilters:
  - TrimmedDistOutlierFilter:
      ratio: 0.75

errorMinimizer: 
  PointToPlaneErrorMinimizer

transformationCheckers:
  - CounterTransformationChecker:
      maxIterationCount: 40
  - DifferentialTransformationChecker:
      minDiffRotErr: 0.001 
      minDiffTransErr: 0.01 
      smoothLength: 4

#inspector:     
#  NullInspector

inspector:
 VTKFileInspector:
     baseFileName: pointmatcher-run1
     dumpPerfOnExit: 0
     dumpStats: 0
     dumpIterationInfo: 1 
     dumpDataLinks: 0 
     dumpReading: 0
     dumpReference: 0 

logger:         
  NullLogger    
#  FileLogger           

I copied it from the example default configuration. Maybe it's not the very same configuration that setDefault sets...

pomerlef commented 8 years ago

No there are not the same, here function setDefault() uses the default values for all the parameters. The default.yaml was used for demonstration/example purpose and diverge slowly to fit the example point clouds we had. The function should be equivalent to the following yaml:

readingDataPointsFilters:
  - RandomSamplingDataPointsFilter:
      prob: 0.75  

referenceDataPointsFilters:
  - SamplingSurfaceNormalDataPointsFilter:
      ratio: 0.5

matcher:
  KDTreeMatcher:
    knn: 1
    epsilon: 0

outlierFilters:
  - TrimmedDistOutlierFilter:
      ratio: 0.85

errorMinimizer: 
  PointToPlaneErrorMinimizer

transformationCheckers:
  - CounterTransformationChecker:
      maxIterationCount: 40
  - DifferentialTransformationChecker:
      minDiffRotErr: 0.001 
      minDiffTransErr: 0.001 
      smoothLength: 3

inspector:
 VTKFileInspector

logger:         
  NullLogger     

If you used the type float in your code, it is expected to have 0.999999... instead of a clear one. Unless you see other details, I would say that everything is fine.