在看lio-mapping源码时发现,其定义的pose参数根据ceres::LocalParameterization,新版本ceres-solver库编译报错,因此改写该类。
ceres位姿参数类型,包含位置(三维向量)跟姿态(四元数),ceres-2.1以前自定义参数需要集成ceres::LocalParameterization类并重载类中的纯虚函数,ceres-solver-2.2以后取消了ceres::LocalParameterization类,并用ceres::Manifold代替,两者的具体区别可参考链接,下面给出使用ceres::Manifold定义的流形位姿参数。
头文件:
//
// Created by liuhuan on 24-5-16.
//
#ifndef SRC_POSEMANIFOLD_H
#define SRC_POSEMANIFOLD_H
#include <ceres/ceres.h>
#include <ceres/manifold.h>
#include <Eigen/Core>
namespace lio {
class PoseManifold : public ceres::Manifold{
public:
int AmbientSize() const override { return 7; }
int TangentSize() const override { return 6; }
bool Plus(const double* x,
const double* delta,
double* x_plus_delta) const override;
bool PlusJacobian(const double* x, double* jacobian) const override;
bool Minus(const double* y,
const double* x,
double* y_minus_x) const override;
bool MinusJacobian(const double* x, double* jacobian) const override;
};
} // lio
#endif //SRC_POSEMANIFOLD_H
源文件:
//
// Created by liuhuan on 24-5-16.
//
#include "PoseManifold.h"
namespace lio {
namespace {
struct CeresQuaternionOrder {
static constexpr int kW = 0;
static constexpr int kX = 1;
static constexpr int kY = 2;
static constexpr int kZ = 3;
};
struct EigenQuaternionOrder {
static constexpr int kW = 3;
static constexpr int kX = 0;
static constexpr int kY = 1;
static constexpr int kZ = 2;
};
template<typename Order>
inline void QuaternionPlusImpl(const double *x,
const double *delta,
double *x_plus_delta) {
// x_plus_delta = QuaternionProduct(q_delta, x), where q_delta is the
// quaternion constructed from delta.
const double norm_delta = std::hypot(delta[0], delta[1], delta[2]);
if (std::fpclassify(norm_delta) == FP_ZERO) {
// No change in rotation: return the quaternion as is.
std::copy_n(x, 4, x_plus_delta);
return;
}
const double sin_delta_by_delta = (std::sin(norm_delta) / norm_delta);
double q_delta[4];
q_delta[Order::kW] = std::cos(norm_delta);
q_delta[Order::kX] = sin_delta_by_delta * delta[0];
q_delta[Order::kY] = sin_delta_by_delta * delta[1];
q_delta[Order::kZ] = sin_delta_by_delta * delta[2];
x_plus_delta[Order::kW] =
q_delta[Order::kW] * x[Order::kW] - q_delta[Order::kX] * x[Order::kX] -
q_delta[Order::kY] * x[Order::kY] - q_delta[Order::kZ] * x[Order::kZ];
x_plus_delta[Order::kX] =
q_delta[Order::kW] * x[Order::kX] + q_delta[Order::kX] * x[Order::kW] +
q_delta[Order::kY] * x[Order::kZ] - q_delta[Order::kZ] * x[Order::kY];
x_plus_delta[Order::kY] =
q_delta[Order::kW] * x[Order::kY] - q_delta[Order::kX] * x[Order::kZ] +
q_delta[Order::kY] * x[Order::kW] + q_delta[Order::kZ] * x[Order::kX];
x_plus_delta[Order::kZ] =
q_delta[Order::kW] * x[Order::kZ] + q_delta[Order::kX] * x[Order::kY] -
q_delta[Order::kY] * x[Order::kX] + q_delta[Order::kZ] * x[Order::kW];
}
template<typename Order>
inline void QuaternionPlusJacobianImpl(const double *x, double *jacobian_ptr) {
Eigen::Map<Eigen::Matrix<double, 7, 6, Eigen::RowMajor>> jacobian(
jacobian_ptr);
jacobian.setZero();
jacobian(Order::kW+3, 0+3) = -x[Order::kX];
jacobian(Order::kW+3, 1+3) = -x[Order::kY];
jacobian(Order::kW+3, 2+3) = -x[Order::kZ];
jacobian(Order::kX+3, 0+3) = x[Order::kW];
jacobian(Order::kX+3, 1+3) = x[Order::kZ];
jacobian(Order::kX+3, 2+3) = -x[Order::kY];
jacobian(Order::kY+3, 0+3) = -x[Order::kZ];
jacobian(Order::kY+3, 1+3) = x[Order::kW];
jacobian(Order::kY+3, 2+3) = x[Order::kX];
jacobian(Order::kZ+3, 0+3) = x[Order::kY];
jacobian(Order::kZ+3, 1+3) = -x[Order::kX];
jacobian(Order::kZ+3, 2+3) = x[Order::kW];
}
template<typename Order>
inline void QuaternionMinusImpl(const double *y,
const double *x,
double *y_minus_x) {
// ambient_y_minus_x = QuaternionProduct(y, -x) where -x is the conjugate of
// x.
double ambient_y_minus_x[4];
ambient_y_minus_x[Order::kW] =
y[Order::kW] * x[Order::kW] + y[Order::kX] * x[Order::kX] +
y[Order::kY] * x[Order::kY] + y[Order::kZ] * x[Order::kZ];
ambient_y_minus_x[Order::kX] =
-y[Order::kW] * x[Order::kX] + y[Order::kX] * x[Order::kW] -
y[Order::kY] * x[Order::kZ] + y[Order::kZ] * x[Order::kY];
ambient_y_minus_x[Order::kY] =
-y[Order::kW] * x[Order::kY] + y[Order::kX] * x[Order::kZ] +
y[Order::kY] * x[Order::kW] - y[Order::kZ] * x[Order::kX];
ambient_y_minus_x[Order::kZ] =
-y[Order::kW] * x[Order::kZ] - y[Order::kX] * x[Order::kY] +
y[Order::kY] * x[Order::kX] + y[Order::kZ] * x[Order::kW];
const double u_norm = std::hypot(ambient_y_minus_x[Order::kX],
ambient_y_minus_x[Order::kY],
ambient_y_minus_x[Order::kZ]);
if (std::fpclassify(u_norm) != FP_ZERO) {
const double theta = std::atan2(u_norm, ambient_y_minus_x[Order::kW]);
y_minus_x[0] = theta * ambient_y_minus_x[Order::kX] / u_norm;
y_minus_x[1] = theta * ambient_y_minus_x[Order::kY] / u_norm;
y_minus_x[2] = theta * ambient_y_minus_x[Order::kZ] / u_norm;
} else {
std::fill_n(y_minus_x, 3, 0.0);
}
}
template<typename Order>
inline void QuaternionMinusJacobianImpl(const double *x, double *jacobian_ptr) {
Eigen::Map<Eigen::Matrix<double, 6, 7, Eigen::RowMajor>> jacobian(
jacobian_ptr);
jacobian.setZero();
jacobian(0+3, Order::kW+3) = -x[Order::kX];
jacobian(0+3, Order::kX+3) = x[Order::kW];
jacobian(0+3, Order::kY+3) = -x[Order::kZ];
jacobian(0+3, Order::kZ+3) = x[Order::kY];
jacobian(1+3, Order::kW+3) = -x[Order::kY];
jacobian(1+3, Order::kX+3) = x[Order::kZ];
jacobian(1+3, Order::kY+3) = x[Order::kW];
jacobian(1+3, Order::kZ+3) = -x[Order::kX];
jacobian(2+3, Order::kW+3) = -x[Order::kZ];
jacobian(2+3, Order::kX+3) = -x[Order::kY];
jacobian(2+3, Order::kY+3) = x[Order::kX];
jacobian(2+3, Order::kZ+3) = x[Order::kW];
}
using AmbientVector = Eigen::Matrix<double, 3, 1>;
using MatrixJacobian = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>;
void PosPlus(const double *x_ptr,
const double *delta_ptr,
double *x_plus_delta_ptr) {
Eigen::Map<const AmbientVector> x(x_ptr, 3);
Eigen::Map<const AmbientVector> delta(delta_ptr, 3);
Eigen::Map<AmbientVector> x_plus_delta(x_plus_delta_ptr, 3);
x_plus_delta = x + delta;
}
void PosPlusJacobian(const double *x_ptr, double *jacobian_ptr) {
Eigen::Map<MatrixJacobian> jacobian(jacobian_ptr, 3, 3);
jacobian.setIdentity();
}
void PosRightMultiplyByPlusJacobian(const double *x,
const int num_rows,
const double *ambient_matrix,
double *tangent_matrix) {
std::copy_n(ambient_matrix, num_rows * 3, tangent_matrix);
}
void PosMinus(const double *y_ptr,
const double *x_ptr,
double *y_minus_x_ptr) {
Eigen::Map<const AmbientVector> x(x_ptr, 3);
Eigen::Map<const AmbientVector> y(y_ptr, 3);
Eigen::Map<AmbientVector> y_minus_x(y_minus_x_ptr, 3);
y_minus_x = y - x;
}
void PosMinusJacobian(const double *x_ptr, double *jacobian_ptr) {
Eigen::Map<MatrixJacobian> jacobian(jacobian_ptr, 3, 3);
jacobian.setIdentity();
}
} // namespace
bool PoseManifold::Plus(const double* x,
const double* delta,
double* x_plus_delta) const {
const double p_x[3] = {x[0], x[1], x[2]};
const double p_delta[3] = {delta[0], delta[1], delta[2]};
const double q_x[4] = {x[3], x[4], x[5], x[6]};
const double q_delta[3] = {delta[3], delta[4], delta[5]};
double* p_x_plus_delta; double* q_x_plus_delta;
QuaternionPlusImpl<EigenQuaternionOrder>(q_x, q_delta, q_x_plus_delta);
PosPlus(p_x, p_delta, p_x_plus_delta);
Eigen::Map<Eigen::VectorXd> e_x_plus_delta(x_plus_delta, 7);
e_x_plus_delta.segment<3>(0) = Eigen::Map<Eigen::Vector3d>(p_x_plus_delta, 3);
e_x_plus_delta.segment<4>(3) = Eigen::Map<Eigen::Vector4d>(q_x_plus_delta, 4);
return true;
}
bool PoseManifold::PlusJacobian(const double* x, double* jacobian) const {
const double q_x[4] = {x[3], x[4], x[5], x[6]};
QuaternionPlusJacobianImpl<EigenQuaternionOrder>(q_x, jacobian);
Eigen::Map<Eigen::Matrix<double, 7, 6, Eigen::RowMajor>> jacobian_map(jacobian);
jacobian_map(0,0) = 1;
jacobian_map(1,1) = 1;
jacobian_map(2,2) = 1;
return true;
}
bool PoseManifold::Minus(const double* y, const double* x, double* y_minus_x) const {
const double p_x[3] = {x[0], x[1], x[2]};
const double p_y[3] = {y[0], y[1], y[2]};
const double q_x[4] = {x[3], x[4], x[5], x[6]};
const double q_y[4] = {y[3], y[4], y[5], y[6]};
double* p_y_minus_x;
double* q_y_minus_x;
QuaternionMinusImpl<EigenQuaternionOrder>(q_y, q_x, q_y_minus_x);
PosMinus(p_y, p_x, p_y_minus_x);
Eigen::Map<Eigen::VectorXd> e_y_minus_x(y_minus_x, 6);
e_y_minus_x.segment<3>(0) = Eigen::Map<Eigen::Vector3d>(p_y_minus_x, 3);
e_y_minus_x.segment<3>(3) = Eigen::Map<Eigen::Vector3d>(q_y_minus_x, 3);
return true;
}
bool PoseManifold::MinusJacobian(const double* x, double* jacobian) const {
const double q_x[4] = {x[3], x[4], x[5], x[6]};
QuaternionMinusJacobianImpl<EigenQuaternionOrder>(q_x, jacobian);
Eigen::Map<Eigen::Matrix<double, 6, 7, Eigen::RowMajor>> jacobian_map(jacobian);
jacobian_map(0,0) = 1;
jacobian_map(1,1) = 1;
jacobian_map(2,2) = 1;
return true;
}
} // lio