struct mp2p_icp::PointWeightByRange

Overview

How much a correspondence counts, as a function of the range at which its point was measured.

Disabled by default (alpha = 0, every point weighs the same), which is the behavior of every release before this existed.

\[w(r) = \mathrm{clamp}\left( \left(\frac{r_{ref}}{\max(r,\epsilon)}\right)^{\alpha}, w_{min}, w_{max}\right)\]

With the default maxWeight = 1 and a positive alpha, this is a knee: everything closer than refRange counts fully, and beyond it the weight decays as a power of the range. Two exponents have a physical reading:

  • alpha = 1 : the lateral footprint of a beam grows linearly with range (beam divergence), so the position uncertainty of a point does too.

  • alpha = 2 : additionally, a surface is sampled at a density falling as \(1/r^2\), so a far point also stands for more surface than a near one.

A negative alpha up-weights the far field instead. That direction is the useful one on a spinning LiDAR, where a fixed-size decimation voxel cannot thin the far field and the near field ends up over-represented in the correspondence set relative to its information content.

With a negative alpha, minWeight is what keeps this safe. A steep exponent drives the NEAR field to zero (at alpha = -2 a return at a tenth of refRange is weighted 0.01) and a scene that needs its near returns then loses them. Measured on one 127 m scene, holding the exponent at -2 and changing only the floor:

  • minWeight = 0 : ATE 0.011 m -> 0.383 m (and 0.712 m with the ceiling also removed), i.e. divergence.

  • minWeight = 1 : ATE 0.011 m -> 0.011 m, entirely benign.

So either keep |alpha| at 1.5 or below, or set minWeight = 1 so no point can count for less than it does today and the weighting can only add emphasis to the far field. The latter is also the conservative choice for a scene smaller than refRange, where it reduces to the identity.

maxWeight bounds the opposite end and matters much less: at a fixed alpha = -1, ceilings of 2, 5 and 20 span about 0.6 mm.

The consumer multiplies a correspondence’s information matrix by this weight, so it acts as an inverse variance. Only the SHAPE of the curve matters when the solver rescales the whole data block (for example Solver_GaussNewton ‘s Birge-ratio balancing against the prior), since a constant factor is absorbed there.

#include <PointWeightByRange.h>

struct PointWeightByRange
{
    // fields

    double alpha = 0.0;
    double refRange = 20.0;
    double minWeight = 0.01;
    double maxWeight = 1.0;

    // methods

    bool enabled() const;
    double operator () (double range) const;
};

Fields

double alpha = 0.0

Decay exponent. 0 disables this entirely (the default).

double refRange = 20.0

Range [m] below which the weight saturates at maxWeight.

double minWeight = 0.01

Floor, so a far point is never dropped outright.

double maxWeight = 1.0

Ceiling. Keep at 1 for a plain knee.

Methods

double operator () (double range) const

Weight of a correspondence measured at range meters.