公式來源 IMU preintegration on manifold for efficient visual-inertial maximum-a-posteriori estimation 的 supplementary material
公式和思路和預積分論文是一致的,和OKVIS IJRR論文裏說的不一樣!
ceres 優化時,迭代更新狀態量,需要計算IMU的error。因爲預積分時需要用到狀態量IMU的偏置,而狀態量在迭代中是變化的,所以當偏置變化小時,根據狀態量對偏置的雅克比更新偏置變化後的積分值,當偏置變化大時,再重新積分。
誤差來源
// imuParametersVec_ 元素個數等於 IMU 個數,這裏只有一個 IMU
// add IMU error terms
for (size_t i = 0; i < imuParametersVec_.size(); ++i) {
std::shared_ptr<ceres::ImuError> imuError(
new ceres::ImuError(imuMeasurements, imuParametersVec_.at(i),
lastElementIterator->second.timestamp,
states.timestamp));
/*::ceres::ResidualBlockId id = */mapPtr_->addResidualBlock(
imuError,
NULL,
mapPtr_->parameterBlockPtr(lastElementIterator->second.id),
mapPtr_->parameterBlockPtr(
lastElementIterator->second.sensors.at(SensorStates::Imu).at(i).at(
ImuSensorStates::SpeedAndBias).id),
mapPtr_->parameterBlockPtr(states.id),
mapPtr_->parameterBlockPtr(
states.sensors.at(SensorStates::Imu).at(i).at(
ImuSensorStates::SpeedAndBias).id));
// Construct with measurements and parameters.
ImuError::ImuError(const okvis::ImuMeasurementDeque & imuMeasurements,
const okvis::ImuParameters & imuParameters,
const okvis::Time& t_0, const okvis::Time& t_1) {
setImuMeasurements(imuMeasurements);
setImuParameters(imuParameters);
setT0(t_0);
setT1(t_1);
OKVIS_ASSERT_TRUE_DBG(Exception,
t_0 >= imuMeasurements.front().timeStamp,
"First IMU measurement included in ImuError is not old enough!");
OKVIS_ASSERT_TRUE_DBG(Exception,
t_1 <= imuMeasurements.back().timeStamp,
"Last IMU measurement included in ImuError is not new enough!");
}
和 ceres 接口 Evaluate 函數
// This evaluates the error term and additionally computes the Jacobians.
bool ImuError::Evaluate(double const* const * parameters, double* residuals,
double** jacobians) const {
return EvaluateWithMinimalJacobians(parameters, residuals, jacobians, NULL);
}
// This evaluates the error term and additionally computes
// the Jacobians in the minimal internal representation.
bool ImuError::EvaluateWithMinimalJacobians(double const* const * parameters,
double* residuals,
double** jacobians,
double** jacobiansMinimal) const {
// get poses
const okvis::kinematics::Transformation T_WS_0(
Eigen::Vector3d(parameters[0][0], parameters[0][1], parameters[0][2]),
Eigen::Quaterniond(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]));
const okvis::kinematics::Transformation T_WS_1(
Eigen::Vector3d(parameters[2][0], parameters[2][1], parameters[2][2]),
Eigen::Quaterniond(parameters[2][6], parameters[2][3], parameters[2][4], parameters[2][5]));
// get speed and bias
okvis::SpeedAndBias speedAndBiases_0;
okvis::SpeedAndBias speedAndBiases_1;
for (size_t i = 0; i < 9; ++i) {
speedAndBiases_0[i] = parameters[1][i];
speedAndBiases_1[i] = parameters[3][i];
}
// this will NOT be changed:
const Eigen::Matrix3d C_WS_0 = T_WS_0.C();
const Eigen::Matrix3d C_S0_W = C_WS_0.transpose();
輸入參數:
上一幀 sensor 繫到世界系的轉換矩陣:
當前幀 sensor 繫到世界系的轉換矩陣:
上一幀 sensor 繫到世界系轉換矩陣旋轉部分:
上一幀:
當前幀:
// call the propagation
const double Delta_t = (t1_ - t0_).toSec();
Eigen::Matrix<double, 6, 1> Delta_b;
// ensure unique access
{
std::lock_guard<std::mutex> lock(preintegrationMutex_);
Delta_b = speedAndBiases_0.tail<6>()
- speedAndBiases_ref_.tail<6>();
}
redo_ = redo_ || (Delta_b.head<3>().norm() * Delta_t > 0.0001);
if (redo_) {
/* 當偏置變換大時,再重新積分,預積分實際只和優化的狀態量上一幀的偏置有關,
所以這裏當偏置變化大的時重新計算預積分的值,當偏置變化不大時,根據雅克比更新預積分的值。*/
redoPreintegration(T_WS_0, speedAndBiases_0);
redoCounter_++;
Delta_b.setZero();
redo_ = false;
/*if (redoCounter_ > 1) {
std::cout << "pre-integration no. " << redoCounter_ << std::endl;
}*/
}
if redo_ || ||Δbg||Δt>0.0001
redoPreintegration(T_WS_0, speedAndBiases_0);
redoCounter_++;
Delta_b.setZero();
redo_ = false;
end if
actual propagation output
// actual propagation output:
{
std::lock_guard<std::mutex> lock(preintegrationMutex_); // this is a bit stupid, but shared read-locks only come in C++14
const Eigen::Vector3d g_W = imuParameters_.g * Eigen::Vector3d(0, 0, 6371009).normalized();
// assign Jacobian w.r.t. x0
Eigen::Matrix<double,15,15> F0 =
Eigen::Matrix<double,15,15>::Identity(); // holds for d/db_g, d/db_a
const Eigen::Vector3d delta_p_est_W =
T_WS_0.r() - T_WS_1.r() + speedAndBiases_0.head<3>()*Delta_t - 0.5*g_W*Delta_t*Delta_t;
const Eigen::Vector3d delta_v_est_W =
speedAndBiases_0.head<3>() - speedAndBiases_1.head<3>() - g_W*Delta_t;
const Eigen::Quaterniond Dq = okvis::kinematics::deltaQ(-dalpha_db_g_*Delta_b.head<3>())*Delta_q_;
讀入的配置參數,重力加速度:
F0.block<3,3>(0,0) = C_S0_W;
F0.block<3,3>(0,3) = C_S0_W * okvis::kinematics::crossMx(delta_p_est_W);
F0.block<3,3>(0,6) = C_S0_W * Eigen::Matrix3d::Identity()*Delta_t;
F0.block<3,3>(0,9) = dp_db_g_;
F0.block<3,3>(0,12) = -C_doubleintegral_;
F0.block<3,3>(3,3) = (okvis::kinematics::plus(Dq*T_WS_1.q().inverse()) *
okvis::kinematics::oplus(T_WS_0.q())).topLeftCorner<3,3>();
F0.block<3,3>(3,9) = (okvis::kinematics::oplus(T_WS_1.q().inverse()*T_WS_0.q())*
okvis::kinematics::oplus(Dq)).topLeftCorner<3,3>()*(-dalpha_db_g_);
F0.block<3,3>(6,3) = C_S0_W * okvis::kinematics::crossMx(delta_v_est_W);
F0.block<3,3>(6,6) = C_S0_W;
F0.block<3,3>(6,9) = dv_db_g_;
F0.block<3,3>(6,12) = -C_integral_;
redoPreintegration 中賦值:
// assign Jacobian w.r.t. x1
Eigen::Matrix<double,15,15> F1 =
-Eigen::Matrix<double,15,15>::Identity(); // holds for the biases
F1.block<3,3>(0,0) = -C_S0_W;
F1.block<3,3>(3,3) = -(okvis::kinematics::plus(Dq) *
okvis::kinematics::oplus(T_WS_0.q()) *
okvis::kinematics::plus(T_WS_1.q().inverse())).topLeftCorner<3,3>();
F1.block<3,3>(6,6) = -C_S0_W;
輸出的 error
// the overall error vector
Eigen::Matrix<double, 15, 1> error;
error.segment<3>(0) = C_S0_W * delta_p_est_W + acc_doubleintegral_ + F0.block<3,6>(0,9)*Delta_b;
error.segment<3>(3) = 2*(Dq*(T_WS_1.q().inverse()*T_WS_0.q())).vec(); //2*T_WS_0.q()*Dq*T_WS_1.q().inverse();//
error.segment<3>(6) = C_S0_W * delta_v_est_W + acc_integral_ + F0.block<3,6>(6,9)*Delta_b;
error.tail<6>() = speedAndBiases_0.tail<6>() - speedAndBiases_1.tail<6>();
// error weighting
Eigen::Map<Eigen::Matrix<double, 15, 1> > weighted_error(residuals);
weighted_error = squareRootInformation_ * error;
輸出雅各比
需要計算對參數:
雅各比:
if (jacobians != NULL) {
if (jacobians[0] != NULL) {
// Jacobian w.r.t. minimal perturbance
Eigen::Matrix<double, 15, 6> J0_minimal = squareRootInformation_
* F0.block<15, 6>(0, 0);
// pseudo inverse of the local parametrization Jacobian:
Eigen::Matrix<double, 6, 7, Eigen::RowMajor> J_lift;
PoseLocalParameterization::liftJacobian(parameters[0], J_lift.data());
redoPreintegration 中賦值:
// hallucinate Jacobian w.r.t. state
Eigen::Map<Eigen::Matrix<double, 15, 7, Eigen::RowMajor> > J0(
jacobians[0]);
J0 = J0_minimal * J_lift;
// if requested, provide minimal Jacobians
if (jacobiansMinimal != NULL) {
if (jacobiansMinimal[0] != NULL) {
Eigen::Map<Eigen::Matrix<double, 15, 6, Eigen::RowMajor> > J0_minimal_mapped(
jacobiansMinimal[0]);
J0_minimal_mapped = J0_minimal;
}
}
}
if (jacobians[1] != NULL) {
Eigen::Map<Eigen::Matrix<double, 15, 9, Eigen::RowMajor> > J1(
jacobians[1]);
J1 = squareRootInformation_ * F0.block<15, 9>(0, 6);
// if requested, provide minimal Jacobians
if (jacobiansMinimal != NULL) {
if (jacobiansMinimal[1] != NULL) {
Eigen::Map<Eigen::Matrix<double, 15, 9, Eigen::RowMajor> > J1_minimal_mapped(
jacobiansMinimal[1]);
J1_minimal_mapped = J1;
}
}
}
if (jacobians[2] != NULL) {
// Jacobian w.r.t. minimal perturbance
Eigen::Matrix<double, 15, 6> J2_minimal = squareRootInformation_
* F1.block<15, 6>(0, 0);
// pseudo inverse of the local parametrization Jacobian:
Eigen::Matrix<double, 6, 7, Eigen::RowMajor> J_lift;
PoseLocalParameterization::liftJacobian(parameters[2], J_lift.data());
// hallucinate Jacobian w.r.t. state
Eigen::Map<Eigen::Matrix<double, 15, 7, Eigen::RowMajor> > J2(
jacobians[2]);
J2 = J2_minimal * J_lift;
// if requested, provide minimal Jacobians
if (jacobiansMinimal != NULL) {
if (jacobiansMinimal[2] != NULL) {
Eigen::Map<Eigen::Matrix<double, 15, 6, Eigen::RowMajor> > J2_minimal_mapped(
jacobiansMinimal[2]);
J2_minimal_mapped = J2_minimal;
}
}
}
if (jacobians[3] != NULL) {
Eigen::Map<Eigen::Matrix<double, 15, 9, Eigen::RowMajor> > J3(jacobians[3]);
J3 = squareRootInformation_ * F1.block<15, 9>(0, 6);
// if requested, provide minimal Jacobians
if (jacobiansMinimal != NULL) {
if (jacobiansMinimal[3] != NULL) {
Eigen::Map<Eigen::Matrix<double, 15, 9, Eigen::RowMajor> > J3_minimal_mapped(
jacobiansMinimal[3]);
J3_minimal_mapped = J3;
}
}
}