@@ -22,7 +22,7 @@ LandmarkESKF::LandmarkESKF(const EskfParams& params) : ESKF(params) {
2222Eigen::Vector3d LandmarkESKF::compute_quaternion_error (
2323 const Eigen::Quaterniond& q_meas,
2424 const Eigen::Quaterniond& q_est) {
25- Eigen::Quaterniond q_err = (q_meas * q_est.inverse ()).normalized ();
25+ Eigen::Quaterniond q_err = (q_est.inverse () * q_meas ).normalized ();
2626
2727 if (q_err.w () < 0.0 ) {
2828 q_err.coeffs () = -q_err.coeffs ();
@@ -202,10 +202,42 @@ void LandmarkESKF::landmark_update(const LandmarkMeasurement& landmark_meas) {
202202 return ;
203203 }
204204
205- // Transform VO measurement to navigation frame
205+ // Transform raw VO measurement to navigation frame
206206 Eigen::Vector3d p_nav = p_nav_vo_ + q_nav_vo_ * landmark_meas.pos ;
207207 Eigen::Quaterniond q_nav = (q_nav_vo_ * landmark_meas.quat ).normalized ();
208208
209+ // Measurement Jacobian
210+ Eigen::Matrix<double , 6 , 15 > H = Eigen::Matrix<double , 6 , 15 >::Zero ();
211+ H.block <3 , 3 >(0 , 0 ) = Eigen::Matrix3d::Identity ();
212+ H.block <3 , 3 >(3 , 6 ) = Eigen::Matrix3d::Identity ();
213+
214+ // Measurement noise with floors applied
215+ Eigen::Matrix<double , 6 , 6 > R = landmark_meas.R ;
216+ const double p_floor_sq = vo_cfg_.pos_floor * vo_cfg_.pos_floor ;
217+ const double a_floor_sq = vo_cfg_.att_floor * vo_cfg_.att_floor ;
218+ for (int i = 0 ; i < 3 ; ++i) {
219+ R (i, i) = std::max (R (i, i), p_floor_sq);
220+ }
221+ for (int i = 3 ; i < 6 ; ++i) {
222+ R (i, i) = std::max (R (i, i), a_floor_sq);
223+ }
224+
225+ // Gate on raw measurement
226+ const Eigen::Matrix15d P = current_error_state_.covariance ;
227+ const Eigen::Matrix<double , 6 , 6 > S = H * P * H.transpose () + R;
228+
229+ Eigen::Matrix<double , 6 , 1 > y_raw;
230+ y_raw.head <3 >() = p_nav - current_nom_state_.pos ;
231+ y_raw.segment <3 >(3 ) =
232+ compute_quaternion_error (q_nav, current_nom_state_.quat );
233+
234+ const double gate = !gating_enabled_ ? 1e9 : vo_cfg_.nis_gate_pose ;
235+ if (compute_nis (y_raw, S) > gate) {
236+ consecutive_rejects_++;
237+ return ;
238+ }
239+
240+ // Measurement passed gating
209241 if (vo_cfg_.use_sw ) {
210242 sw_add (landmark_meas.stamp , p_nav, q_nav);
211243 sw_prune (landmark_meas.stamp );
@@ -239,41 +271,9 @@ void LandmarkESKF::landmark_update(const LandmarkMeasurement& landmark_meas) {
239271 have_prev_ = true ;
240272
241273 Eigen::Matrix<double , 6 , 1 > y;
242-
243- // Position innovation
244274 y.head <3 >() = p_nav - current_nom_state_.pos ;
245275 y.segment <3 >(3 ) = compute_quaternion_error (q_nav, current_nom_state_.quat );
246276
247- // Measurement Jacobian
248- Eigen::Matrix<double , 6 , 15 > H = Eigen::Matrix<double , 6 , 15 >::Zero ();
249- H.block <3 , 3 >(0 , 0 ) = Eigen::Matrix3d::Identity ();
250- H.block <3 , 3 >(3 , 6 ) = Eigen::Matrix3d::Identity ();
251-
252- // Measurement noise floors
253- Eigen::Matrix<double , 6 , 6 > R = landmark_meas.R ;
254-
255- const double p_floor_sq = vo_cfg_.pos_floor * vo_cfg_.pos_floor ;
256- const double a_floor_sq = vo_cfg_.att_floor * vo_cfg_.att_floor ;
257-
258- for (int i = 0 ; i < 3 ; ++i) {
259- R (i, i) = std::max (R (i, i), p_floor_sq);
260- }
261- for (int i = 3 ; i < 6 ; ++i) {
262- R (i, i) = std::max (R (i, i), a_floor_sq);
263- }
264-
265- // Innovation covariance and NIS
266- const Eigen::Matrix15d P = current_error_state_.covariance ;
267- const Eigen::Matrix<double , 6 , 6 > S = H * P * H.transpose () + R;
268- const double nis = compute_nis (y, S);
269-
270- const double gate = !gating_enabled_ ? 1e9 : vo_cfg_.nis_gate_pose ;
271-
272- if (nis > gate) {
273- consecutive_rejects_++;
274- return ;
275- }
276-
277277 Eigen::Matrix<double , 15 , 6 > K = P * H.transpose () * S.inverse ();
278278 current_error_state_.set_from_vector (K * y);
279279
0 commit comments