void NearestNeighboursON2::getNearestNeighbours(
        const Scan &queryScan,
        unsigned int queryToReferenceMapping[][2],
        double nearestDistances[]) {

    const Scan &refScan = *(this->refScan);
    const unsigned int queryScanSize = queryScan.size(), refScanSize = refScan.size();

    for(unsigned int i = 0; i < queryScanSize; i++) {
        TopValues<2> tv;

        for(unsigned int j = 0; j < refScanSize; j++) {
            double dist =
                    square(refScan[j].getX() - queryScan[i].getX()) +
                    square(refScan[j].getY() - queryScan[i].getY());
            tv.add(dist, j);
        }

        queryToReferenceMapping[i][0] = tv.index(0);
        queryToReferenceMapping[i][1] = tv.index(1);
        nearestDistances[i] = tv.value(0);
    }
}
Example #2
0
void ICP::scanMatch(
        const Scan &refScan,
        const Scan &queryScan,
        Eigen::Vector3d &rototranslation,
        Eigen::Matrix3d &covariance,
        const Eigen::Vector3d &rototranslationGuess) {

    Scan rotatedScan;
    Rototranslation2D RTcompose(rototranslationGuess);

    unsigned int queryScanSize = queryScan.size();

    unsigned int queryToReferenceMapping[queryScanSize][2];
    double nearestDistances[queryScanSize];

    const int maxIterations = config::icp_max_iter;
    const double eps = config::icp_trim_ratio;
    const double convError = config::icp_conv_error;

    nearestNeighbours->initialize(refScan);

    //std::cout << "ListAnimate[{";

    for (int iteration = 0; iteration < maxIterations; iteration++) {
        Rototranslation2D RT;
        double distanceToTrim;
        int trimSize;
        Eigen::Vector3d q;

        /* 1 - Rototranslate the second scan using the current RT estimate              */
        rotatedScan = RTcompose * queryScan;

        /* 2 - Find for each (xxTi,yyTi) the corresponding segment                      */
        nearestNeighbours->getNearestNeighbours(
                rotatedScan, queryToReferenceMapping, nearestDistances);

        /* 3 - Use a trimming procedure (The Trimmed Iterative Closest Point Algorithm) */
        /* 3.1 - Order the distance in ascending order and select only eps*N pairs      */
        std::vector<double> distSorted(nearestDistances, nearestDistances + queryScanSize);
        std::sort (distSorted.begin(), distSorted.end());
        trimSize = (unsigned int) std::floor(eps * queryScanSize);
        distanceToTrim = distSorted[trimSize - 1];

        //std::cout << "Show[{Graphics[{Green";

        /* 3.2 - Assign the right pairs in order to do the correct minimization         */
        unsigned int queryIndices[trimSize];
        for (unsigned int j = 0, k = 0; k < queryScanSize; k++) {
            if (nearestDistances[k] <= distanceToTrim) {
                // TODO: WTF?
                // Remove the bias given by matching always the first or last point
                // if ((distNearest[k] < distanceToTrim) && (idxRef[k][0] != 0)
                //        && (idxRef[k][0] != static_cast<unsigned int>(sizeR-1))) {
                queryIndices[j++] = k;
                //std::cout << ",Line[{" << rotatedScan[k] << "," << refScan[queryToReferenceMapping[k][0]] << "}]";
            }
        }
        //std::cout << "}]";


        //std::cout << ",\nListPlot[{" << refScan << "," << rotatedScan <<
        //        "},AspectRatio->1,PlotStyle->{Red,Blue}]},PlotRange->{{-4,4},{-2,3}}]," << std::endl;


        /* 4 - Optimize, find the optimal transformation                                */
        q = this->getBestRototranslationVector(
                refScan, rotatedScan, queryToReferenceMapping, queryIndices, trimSize);

        /* 5 - Use compound to get the new RTCompose                                    */
        RT = Rototranslation2D(q).inverse();
        RTcompose = RT * RTcompose;

        //std::cout << "max(" << q[0] << "," << q[1] << "," << q[2] << ") < " << convError
        //        << "? " << (std::max(std::max(q[0], q[1]), q[2]) < convError) << std::endl;

        /* 6 - Termination condition                                                    */
        if(std::max(std::max(q[0], q[1]), q[2]) < convError) {
            break;
        }
    }

    //std::cout << "ListPlot[{" << refScan << "," << RTcompose * queryScan <<
    //        "},AspectRatio->1,PlotStyle->{Red,Blue},PlotRange->{{-4,4},{-2,3}}]}]" << std::endl;

    nearestNeighbours->deinitialize();
    rototranslation = RTcompose.getVectorForm();

    covariance = this->computeCovariance(
            refScan, queryScan, RTcompose, queryToReferenceMapping);
}
Eigen::Matrix3d PointToLineICP::computeCensiCovariance(
        const Scan &refScan,
        const Scan &queryScan,
        const Rototranslation2D &rototranslation,
        const unsigned int queryToReferenceMapping[][2]) {

    // This routine assumes that only the range of the laser are subject to
    // error and not the angle

    const unsigned int refScanSize = refScan.size();
    const unsigned int queryScanSize = queryScan.size();
    const Eigen::Vector3d v = rototranslation.getVectorForm();
    const double tx = v[0], ty = v[1], a = v[2];

    // Hessian of point to line cost function wrt rototranslation
    Eigen::Matrix3d H_xx = Eigen::Matrix3d::Zero();

    // Hessian of point to line cost function wrt rototranslation and the norm
    // (polar form) of the points from both scans
    Eigen::MatrixXd H_xp = Eigen::MatrixXd::Zero(3, refScanSize + queryScanSize);

    for(unsigned int i = 0; i < queryScanSize; i++) {
        unsigned int j0 = queryToReferenceMapping[i][0];
        unsigned int j1 = queryToReferenceMapping[i][1];

        // Load the three points, two from the reference scan, one from the query
        const Point &ref0  = refScan  [j0];
        const Point &ref1  = refScan  [j1];
        const Point &query = queryScan[i];

        j0 += queryScanSize;
        j1 += queryScanSize;

        // Put the points in polar form
        const double rho0 = ref0.getRho(), rho1 = ref1.getRho(), rhoq = query.getRho();
        const double ct0  = ref0.getX()  / rho0, st0 = ref0.getY()  / rho0;
        const double ct1  = ref1.getX()  / rho1, st1 = ref1.getY()  / rho1;
        const double ctq  = query.getX() / rhoq, stq = query.getY() / rhoq;

        // Automatically optimised code for the computation of the Hessian wrt rototranslation
        const double tmp_xx_0 = rho0*st0;
        const double tmp_xx_1 = -(rho1*st1);
        const double tmp_xx_2 = tmp_xx_0 + tmp_xx_1;
        const double tmp_xx_3 = (tmp_xx_2*tmp_xx_2);
        const double tmp_xx_4 = (rho0*rho0);
        const double tmp_xx_5 = 1/((rho1*rho1) - 2*rho0*rho1*(ct0*ct1 + st0*st1) + tmp_xx_4);
        const double tmp_xx_6 = ct0*rho0 - ct1*rho1;
        const double tmp_xx_7 = -2*tmp_xx_2*tmp_xx_5*tmp_xx_6;
        const double tmp_xx_8 = std::cos(a);
        const double tmp_xx_9 = std::sin(a);
        const double tmp_xx_10 = (ct0*ctq*rho0 + rho0*st0*stq - rho1*(ct1*ctq + st1*stq))*
                tmp_xx_8 + (ctq*rho0*st0 - ctq*rho1*st1 - ct0*rho0*stq + ct1*rho1*stq)*tmp_xx_9;
        const double tmp_xx_11 = -2*rhoq*tmp_xx_10*tmp_xx_2*tmp_xx_5;
        const double tmp_xx_12 = 2*rhoq*tmp_xx_10*tmp_xx_5*tmp_xx_6;
        const double tmp_xx_13 = stq*tmp_xx_8 + ctq*tmp_xx_9;
        const double tmp_xx_14 = ctq*tmp_xx_8 - stq*tmp_xx_9;
        const double tmp_xx_15 = -(rho0*st0);

        // Component of Hessian wrt rototranslation
        H_xx(0,0) += 2*tmp_xx_3*tmp_xx_5;
        H_xx(0,1) += tmp_xx_7;
        H_xx(0,2) += tmp_xx_11;
        H_xx(1,0) += tmp_xx_7;
        H_xx(1,1) += 2*tmp_xx_5*(tmp_xx_6*tmp_xx_6);
        H_xx(1,2) += tmp_xx_12;
        H_xx(2,0) += tmp_xx_11;
        H_xx(2,1) += tmp_xx_12;
        H_xx(2,2) += 2*tmp_xx_5*(std::pow(rhoq*tmp_xx_13*tmp_xx_2 + rhoq*tmp_xx_14*tmp_xx_6,2) +
                (rhoq*tmp_xx_14*tmp_xx_2 - rhoq*tmp_xx_13*tmp_xx_6)* ((rho1*st1 + tmp_xx_15)*
                        (-(ct0*rho0) + ctq*rhoq*tmp_xx_8 - rhoq*stq*tmp_xx_9 + tx) + tmp_xx_6*
                        (tmp_xx_15 + rhoq*stq*tmp_xx_8 + ctq*rhoq*tmp_xx_9 + ty)));

        // Automatically optimised code for the computation of the Hessian wrt rototranslation and
        // points
        const double tmp_xp_0 = -(rho0*st0);
        const double tmp_xp_1 = rho1*st1;
        const double tmp_xp_2 = tmp_xp_0 + tmp_xp_1;
        const double tmp_xp_3 = (rho0*rho0);
        const double tmp_xp_4 = (rho1*rho1);
        const double tmp_xp_5 = ct0*ct1;
        const double tmp_xp_6 = st0*st1 + tmp_xp_5;
        const double tmp_xp_7 = -2*rho0*rho1*tmp_xp_6;
        const double tmp_xp_8 = tmp_xp_3 + tmp_xp_4 + tmp_xp_7;
        const double tmp_xp_9 = 1/tmp_xp_8;
        const double tmp_xp_10 = ct0*rho0;
        const double tmp_xp_11 = -(ct1*rho1) + tmp_xp_10;
        const double tmp_xp_12 = std::cos(a);
        const double tmp_xp_13 = std::sin(a);
        const double tmp_xp_14 = stq*tmp_xp_12 + ctq*tmp_xp_13;
        const double tmp_xp_15 = ctq*tmp_xp_12 - stq*tmp_xp_13;
        const double tmp_xp_16 = tmp_xp_11*tmp_xp_14 + tmp_xp_15*tmp_xp_2;
        const double tmp_xp_17 = std::pow(tmp_xp_8,-2);
        const double tmp_xp_18 = ct0*ctq + st0*stq;
        const double tmp_xp_19 = ct1*rho1*st0 - ct0*rho1*st1 + rhoq*(-(ctq*st0) + ct0*stq)*
                tmp_xp_12 + rhoq*tmp_xp_13*tmp_xp_18 - st0*tx + ct0*ty;
        const double tmp_xp_20 = -rho0 + ct0*ct1*rho1 + rho1*st0*st1;
        const double tmp_xp_21 = tmp_xp_0 + rhoq*stq*tmp_xp_12 + ctq*rhoq*tmp_xp_13 + ty;
        const double tmp_xp_22 = -(ct0*rho0) + ctq*rhoq*tmp_xp_12 - rhoq*stq*tmp_xp_13 + tx;
        const double tmp_xp_23 = tmp_xp_11*tmp_xp_21 + tmp_xp_2*tmp_xp_22;
        const double tmp_xp_24 = -(ct1*tmp_xp_21) + st1*tmp_xp_22;
        const double tmp_xp_25 = ct0*ct1*rho0 - rho1 + rho0*st0*st1;
        const double tmp_xp_26 = ct1*ctq + st1*stq;
        const double tmp_xp_27 = ct0*ctq*rho0 + rho0*st0*stq - rho1*tmp_xp_26;
        const double tmp_xp_28 = rho0*st0;
        const double tmp_xp_29 = -(rho1*st1);
        const double tmp_xp_30 = rhoq*tmp_xp_11*tmp_xp_15 + rhoq*tmp_xp_14*(tmp_xp_28 + tmp_xp_29);

        // Component of Hessian wrt rototranslation and query point norm
        H_xp(0,i) += 2*tmp_xp_16*tmp_xp_2*tmp_xp_9;
        H_xp(1,i) += 2*tmp_xp_11*tmp_xp_16*tmp_xp_9;
        H_xp(2,i) += 2*((ctq*rho0*st0 - ctq*rho1*st1 - ct0*rho0*stq + ct1*rho1*stq)*tmp_xp_13 +
                tmp_xp_12*tmp_xp_27)*tmp_xp_9*(2*rhoq* ((-(ctq*rho0*st0) + ctq*rho1*st1 +
                        ct0*rho0*stq - ct1*rho1*stq)* tmp_xp_12 + tmp_xp_13*tmp_xp_27) - rho0*st0*tx
                        + rho1*st1*tx + ct1*rho1*(tmp_xp_28 - ty) + ct0*rho0*(tmp_xp_29 + ty));

        // Component of Hessian wrt rototranslation and reference point 1 norm
        H_xp(0,j0) += 2*tmp_xp_17*(2*tmp_xp_2*tmp_xp_20*tmp_xp_23 + tmp_xp_19*tmp_xp_2*tmp_xp_8 -
                st0*tmp_xp_23*tmp_xp_8);
        H_xp(1,j0) += 2*tmp_xp_17*(2*tmp_xp_11*tmp_xp_20*tmp_xp_23 + tmp_xp_11*tmp_xp_19*tmp_xp_8 +
                ct0*tmp_xp_23*tmp_xp_8);
        H_xp(2,j0) += 2*tmp_xp_17*(2*tmp_xp_20*tmp_xp_23*tmp_xp_30 + rhoq*((ctq*st0 - ct0*stq)*
                tmp_xp_13 + tmp_xp_12*tmp_xp_18)*tmp_xp_23*tmp_xp_8 + tmp_xp_19*tmp_xp_30*tmp_xp_8);

        // Component of Hessian wrt rototranslation and reference point 2 norm
        H_xp(0,j1) += 2*tmp_xp_17*(2*tmp_xp_2*tmp_xp_23*tmp_xp_25 + st1*tmp_xp_23*tmp_xp_8 +
                tmp_xp_2*tmp_xp_24*tmp_xp_8);
        H_xp(1,j1) += 2*tmp_xp_17*(2*tmp_xp_11*tmp_xp_23*tmp_xp_25 - ct1*tmp_xp_23*tmp_xp_8 +
                tmp_xp_11*tmp_xp_24*tmp_xp_8);
        H_xp(2,j1) += 2*tmp_xp_17*(2*tmp_xp_23*tmp_xp_25*tmp_xp_30 - rhoq*tmp_xp_23*((ctq*st1 -
                ct1*stq)*tmp_xp_13 + tmp_xp_12*tmp_xp_26)*tmp_xp_8 + tmp_xp_24*tmp_xp_30*tmp_xp_8);

    }

    // Jacobian of minimisation algorithm wrt the data (the norm of the scan points)
    Eigen::MatrixXd J = H_xx.inverse() * H_xp;

    // Covariance is J * R * J.transpose(), where R is the covariance of the input data; if we
    // assume all point norms to be uncorrelated and with variance sigma2 it becomes:
    return config::sigma2 * J * J.transpose();
}