这里,采用了4阶龙格库塔方法对两个IMU帧之间的位置和速度进行积分。
首先,考虑IMU的位置和速度微分公式:
p
˙
(
t
)
=
v
(
t
)
v
˙
(
t
)
=
q
(
t
)
a
+
g
\dot{p}(t) = v(t) \\ \dot{v}(t)=q(t)a+g
p˙(t)=v(t)v˙(t)=q(t)a+g
这里的
q
(
t
)
是
I
M
U
的
姿
态
q(t)是IMU的姿态
q(t)是IMU的姿态。
龙格库塔方法可以简单理解为,通过多个斜率逼近微分方程的导数。
4阶龙格库塔方法可以表示为:
这里的
q
(
t
+
Δ
t
)
q(t+\Delta t)
q(t+Δt)的计算又涉及到位姿的微分方程:
q
˙
(
t
)
=
1
2
Ω
(
w
(
t
)
)
q
(
t
)
\dot{q}(t)= \frac{1}{2}\Omega(w(t))q(t)
q˙(t)=21Ω(w(t))q(t)
所以,
q
(
k
+
1
)
/
q
(
k
)
=
e
x
p
(
1
2
Ω
(
w
(
t
)
)
Δ
t
)
q(k+1)/q(k) = exp(\frac{1}{2}\Omega(w(t))\Delta t)
q(k+1)/q(k)=exp(21Ω(w(t))Δt)
对上式泰勒展开并取极限,
最后,将其代入龙格库塔方程求解。
代码:
void MsckfVio::predictNewState(const double& dt,
const Vector3d& gyro,
const Vector3d& acc) {
// TODO: Will performing the forward integration using
// the inverse of the quaternion give better accuracy?
double gyro_norm = gyro.norm();
Matrix4d Omega = Matrix4d::Zero();
Omega.block<3, 3>(0, 0) = -skewSymmetric(gyro);
Omega.block<3, 1>(0, 3) = gyro;
Omega.block<1, 3>(3, 0) = -gyro;
Vector4d& q = state_server.imu_state.orientation;
Vector3d& v = state_server.imu_state.velocity;
Vector3d& p = state_server.imu_state.position;
// Some pre-calculation
Vector4d dq_dt, dq_dt2;
if (gyro_norm > 1e-5) {
dq_dt = (cos(gyro_norm*dt*0.5)*Matrix4d::Identity() +
1/gyro_norm*sin(gyro_norm*dt*0.5)*Omega) * q;
dq_dt2 = (cos(gyro_norm*dt*0.25)*Matrix4d::Identity() +
1/gyro_norm*sin(gyro_norm*dt*0.25)*Omega) * q;
}
else {
dq_dt = (Matrix4d::Identity()+0.5*dt*Omega) *
cos(gyro_norm*dt*0.5) * q;
dq_dt2 = (Matrix4d::Identity()+0.25*dt*Omega) *
cos(gyro_norm*dt*0.25) * q;
}
Matrix3d dR_dt_transpose = quaternionToRotation(dq_dt).transpose();
Matrix3d dR_dt2_transpose = quaternionToRotation(dq_dt2).transpose();
// k1 = f(tn, yn)
Vector3d k1_v_dot = quaternionToRotation(q).transpose()*acc +
IMUState::gravity;
Vector3d k1_p_dot = v;
// k2 = f(tn+dt/2, yn+k1*dt/2)
Vector3d k1_v = v + k1_v_dot*dt/2;
Vector3d k2_v_dot = dR_dt2_transpose*acc +
IMUState::gravity;
Vector3d k2_p_dot = k1_v;
// k3 = f(tn+dt/2, yn+k2*dt/2)
Vector3d k2_v = v + k2_v_dot*dt/2;
Vector3d k3_v_dot = dR_dt2_transpose*acc +
IMUState::gravity;
Vector3d k3_p_dot = k2_v;
// k4 = f(tn+dt, yn+k3*dt)
Vector3d k3_v = v + k3_v_dot*dt;
Vector3d k4_v_dot = dR_dt_transpose*acc +
IMUState::gravity;
Vector3d k4_p_dot = k3_v;
// yn+1 = yn + dt/6*(k1+2*k2+2*k3+k4)
q = dq_dt;
quaternionNormalize(q);
v = v + dt/6*(k1_v_dot+2*k2_v_dot+2*k3_v_dot+k4_v_dot);
p = p + dt/6*(k1_p_dot+2*k2_p_dot+2*k3_p_dot+k4_p_dot);
return;
}