jedeschaud / ct_icp

CT-ICP: Continuous-Time LiDAR Odometry

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fail to use CERES Analytic Derivation for CT-ICP

ZikangYuan opened this issue · comments

Hi,

I found you used auto derivation of CERES when minimizing "CTPointToPlaneFactor" residuals. However, when I try to use analytic derivation instead of auto derivation, I found the variables (e.g., pose_begin and pose_end) cannot be updated normally and are always identity matrix. Have you successfully used analytic derivation for CT-ICP? I doubt the analytic derivation of CERES may not be applicable to your "CTPointToPlaneFactor".

Heres is my code for the analytic derivation of "CTPointToPlaneFactor":

class CTPointToPlaneFactor : public ceres::SizedCostFunction<1, 7, 7>
{

public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    CTPointToPlaneFactor(const Eigen::Vector3d &reference_point_, const Eigen::Vector3d &raw_keypoint_,
                          const Eigen::Vector3d &reference_normal_, double alpha_time_, double weight_ = 1.0);

    virtual ~CTPointToPlaneFactor() {}

    virtual bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const;

    Eigen::Vector3d raw_keypoint;
    Eigen::Vector3d reference_point;
    Eigen::Vector3d reference_normal;
    double alpha_time;
    double weight = 1.0;
};

CTPointToPlaneFactor::CTPointToPlaneFactor(const Eigen::Vector3d &reference_point_, const Eigen::Vector3d &raw_keypoint_,
                                           const Eigen::Vector3d &reference_normal_, double alpha_time_, double weight_)
{
    raw_keypoint = raw_keypoint_;
    reference_point = reference_point_;
    reference_normal = reference_normal_;
    alpha_time = alpha_time_;
    weight = weight_;
}

bool CTPointToPlaneFactor::Evaluate(double const* const* parameters, double* residuals, double** jacobians) const
{
    const Eigen::Quaterniond rot_begin(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);
    const Eigen::Quaterniond rot_end(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);
    const Eigen::Vector3d tran_begin(parameters[0][0], parameters[0][1], parameters[0][2]);
    const Eigen::Vector3d tran_end(parameters[1][0], parameters[1][1], parameters[1][2]);

    Eigen::Quaterniond rot_slerp = rot_begin.slerp(alpha_time, rot_end);
    rot_slerp.normalize();
    Eigen::Vector3d tran_slerp = tran_begin * (1 - alpha_time) + tran_end * alpha_time;

    Eigen::Vector3d point_world = rot_slerp * raw_keypoint + tran_slerp;
    double distance = weight * (reference_point - point_world).transpose() * reference_normal;

    residuals[0] = distance;

    if (jacobians)
    {
        if (jacobians[0])
        {
            Eigen::Map<Eigen::Matrix<double, 1, 7, Eigen::RowMajor>> jacobian_pose_begin(jacobians[0]);
            jacobian_pose_begin.setZero();

            jacobian_pose_begin.block<1, 3>(0, 3) = - reference_normal.transpose() * rot_begin.toRotationMatrix() * numType::skewSymmetric(raw_keypoint) * weight * (1 - alpha_time);
            jacobian_pose_begin.block<1, 3>(0, 0) = reference_normal.transpose() * weight * (1 - alpha_time);
        }

        if (jacobians[1])
        {
            Eigen::Map<Eigen::Matrix<double, 1, 7, Eigen::RowMajor>> jacobian_pose_end(jacobians[1]);
            jacobian_pose_end.setZero();

            jacobian_pose_end.block<1, 3>(0, 3) = - reference_normal.transpose() * rot_end.toRotationMatrix() * numType::skewSymmetric(raw_keypoint) * weight * alpha_time;
            jacobian_pose_end.block<1, 3>(0, 0) = reference_normal.transpose() * weight * alpha_time;
        }
    }

    return true;
}

Dear @ZikangYuan , Did you find out the reason why parameters cannot be updated normally and are always identity matrix? I am trying to implement analytical Jacobians and facing similar thing. Thank you in advance!

Hi,

I found you used auto derivation of CERES when minimizing "CTPointToPlaneFactor" residuals. However, when I try to use analytic derivation instead of auto derivation, I found the variables (e.g., pose_begin and pose_end) cannot be updated normally and are always identity matrix. Have you successfully used analytic derivation for CT-ICP? I doubt the analytic derivation of CERES may not be applicable to your "CTPointToPlaneFactor".

Heres is my code for the analytic derivation of "CTPointToPlaneFactor":

class CTPointToPlaneFactor : public ceres::SizedCostFunction<1, 7, 7>
{

public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    CTPointToPlaneFactor(const Eigen::Vector3d &reference_point_, const Eigen::Vector3d &raw_keypoint_,
                          const Eigen::Vector3d &reference_normal_, double alpha_time_, double weight_ = 1.0);

    virtual ~CTPointToPlaneFactor() {}

    virtual bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const;

    Eigen::Vector3d raw_keypoint;
    Eigen::Vector3d reference_point;
    Eigen::Vector3d reference_normal;
    double alpha_time;
    double weight = 1.0;
};

CTPointToPlaneFactor::CTPointToPlaneFactor(const Eigen::Vector3d &reference_point_, const Eigen::Vector3d &raw_keypoint_,
                                           const Eigen::Vector3d &reference_normal_, double alpha_time_, double weight_)
{
    raw_keypoint = raw_keypoint_;
    reference_point = reference_point_;
    reference_normal = reference_normal_;
    alpha_time = alpha_time_;
    weight = weight_;
}

bool CTPointToPlaneFactor::Evaluate(double const* const* parameters, double* residuals, double** jacobians) const
{
    const Eigen::Quaterniond rot_begin(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);
    const Eigen::Quaterniond rot_end(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);
    const Eigen::Vector3d tran_begin(parameters[0][0], parameters[0][1], parameters[0][2]);
    const Eigen::Vector3d tran_end(parameters[1][0], parameters[1][1], parameters[1][2]);

    Eigen::Quaterniond rot_slerp = rot_begin.slerp(alpha_time, rot_end);
    rot_slerp.normalize();
    Eigen::Vector3d tran_slerp = tran_begin * (1 - alpha_time) + tran_end * alpha_time;

    Eigen::Vector3d point_world = rot_slerp * raw_keypoint + tran_slerp;
    double distance = weight * (reference_point - point_world).transpose() * reference_normal;

    residuals[0] = distance;

    if (jacobians)
    {
        if (jacobians[0])
        {
            Eigen::Map<Eigen::Matrix<double, 1, 7, Eigen::RowMajor>> jacobian_pose_begin(jacobians[0]);
            jacobian_pose_begin.setZero();

            jacobian_pose_begin.block<1, 3>(0, 3) = - reference_normal.transpose() * rot_begin.toRotationMatrix() * numType::skewSymmetric(raw_keypoint) * weight * (1 - alpha_time);
            jacobian_pose_begin.block<1, 3>(0, 0) = reference_normal.transpose() * weight * (1 - alpha_time);
        }

        if (jacobians[1])
        {
            Eigen::Map<Eigen::Matrix<double, 1, 7, Eigen::RowMajor>> jacobian_pose_end(jacobians[1]);
            jacobian_pose_end.setZero();

            jacobian_pose_end.block<1, 3>(0, 3) = - reference_normal.transpose() * rot_end.toRotationMatrix() * numType::skewSymmetric(raw_keypoint) * weight * alpha_time;
            jacobian_pose_end.block<1, 3>(0, 0) = reference_normal.transpose() * weight * alpha_time;
        }
    }

    return true;
}

@ZikangYuan @KaziiBotashev Maybe there's a minus sign missing from the derivation;d(r)/d(point_world) = -reference_normal;