@@ -144,7 +144,7 @@ void StateEstimationSmoother::initialize(const mrpt::containers::yaml& cfg)
144144 auto lck = mrpt::lockHelper (stateMutex_);
145145
146146 // This also resets the GTSAM pimpl unique_ptr in state_
147- reset ();
147+ reset_locked ();
148148
149149 // Load params:
150150 params_.loadFrom (cfg[" params" ]);
@@ -224,9 +224,14 @@ void StateEstimationSmoother::spinOnce()
224224 return ;
225225 }
226226
227- auto lck = mrpt::lockHelper (stateMutex_);
227+ // Read the extrapolated stamp under the lock, then release before calling estimated_navstate()
228+ // to avoid recursive locking (estimated_navstate() acquires the mutex internally).
229+ std::optional<mrpt::Clock::time_point> tNowOpt;
230+ {
231+ auto lck = mrpt::lockHelper (stateMutex_);
232+ tNowOpt = state_.get_current_extrapolated_stamp ();
233+ }
228234
229- const auto tNowOpt = state_.get_current_extrapolated_stamp ();
230235 if (!tNowOpt)
231236 {
232237 MRPT_LOG_THROTTLE_WARN (5.0 , " Cannot publish vehicle pose (no input data yet?)" );
@@ -260,11 +265,11 @@ void StateEstimationSmoother::spinOnce()
260265void StateEstimationSmoother::reset ()
261266{
262267 auto lck = mrpt::lockHelper (stateMutex_);
263-
264- // reset:
265- state_ = State ();
268+ reset_locked ();
266269}
267270
271+ void StateEstimationSmoother::reset_locked () { state_ = State (); }
272+
268273void StateEstimationSmoother::fuse_odometry (
269274 const mrpt::obs::CObservationOdometry& odom, const std::string& odomName)
270275{
@@ -318,15 +323,15 @@ void StateEstimationSmoother::fuse_odometry(
318323 state_.last_wheels_odometry = odom.odometry ;
319324
320325 // Fuse this new probabilistic pose observation:
321- this -> fuse_pose (odom.timestamp , newOdomPosePdf, odomName);
326+ fuse_pose_locked (odom.timestamp , newOdomPosePdf, odomName);
322327}
323328
324329void StateEstimationSmoother::fuse_imu (const mrpt::obs::CObservationIMU& imu)
325330{
326331 auto lck = mrpt::lockHelper (stateMutex_);
327332
328333 // Create a new KF id (or reuse a very close match):
329- const auto this_kf_id = create_or_get_keyframe_by_timestamp (
334+ const auto this_kf_id = create_or_get_keyframe_by_timestamp_locked (
330335 imu.timestamp , params_.imu_nearby_keyframe_stamp_tolerance );
331336
332337 MRPT_LOG_DEBUG_FMT (
@@ -459,7 +464,7 @@ void StateEstimationSmoother::fuse_gnss(const mrpt::obs::CObservationGPS& gps)
459464 mrpt::topography::geodeticToENU_WGS84 (geoCoords, ENU_point, *refGeoCoords);
460465
461466 // Create a new KF id (or reuse a very close match):
462- const auto this_kf_id = create_or_get_keyframe_by_timestamp (
467+ const auto this_kf_id = create_or_get_keyframe_by_timestamp_locked (
463468 gps.timestamp , params_.gnss_nearby_keyframe_stamp_tolerance );
464469
465470 MRPT_LOG_DEBUG_FMT (
@@ -471,7 +476,7 @@ void StateEstimationSmoother::fuse_gnss(const mrpt::obs::CObservationGPS& gps)
471476 const auto observedEnu = mrpt::gtsam_wrappers::toPoint3 (ENU_point);
472477 const auto enuNoise = gtsam::noiseModel::Gaussian::Covariance (gps.covariance_enu ->asEigen ());
473478 auto enuNoiseRobust = gtsam::noiseModel::Robust::Create (
474- gtsam::noiseModel::mEstimator::Huber::Create (1.5 ), enuNoise);
479+ gtsam::noiseModel::mEstimator::Huber::Create (1.5 ), enuNoise);
475480
476481 state_.gtsam ->newFactors .emplace_shared <mola::factors::FactorGnssMapEnu>(
477482 symbol_T_enu_to_map, T (this_kf_id), sensorOnVehicle, observedEnu, enuNoiseRobust);
@@ -482,12 +487,18 @@ void StateEstimationSmoother::fuse_pose(
482487 const std::string& frame_id)
483488{
484489 auto lck = mrpt::lockHelper (stateMutex_);
490+ fuse_pose_locked (timestamp, pose, frame_id);
491+ }
485492
493+ void StateEstimationSmoother::fuse_pose_locked (
494+ const mrpt::Clock::time_point& timestamp, const mrpt::poses::CPose3DPDFGaussian& pose,
495+ const std::string& frame_id)
496+ {
486497 // get this numerical frame_id :
487498 const auto frame_id_idx = add_or_get_odom_frame_id (frame_id);
488499
489500 // Create a new KF id (or reuse a very close match):
490- const auto this_kf_id = create_or_get_keyframe_by_timestamp (timestamp);
501+ const auto this_kf_id = create_or_get_keyframe_by_timestamp_locked (timestamp);
491502
492503 MRPT_LOG_DEBUG_FMT (
493504 " [fuse_pose]: kf_idx=%zu t=%f frame='%s' (idx=%zu) p=%s sigmas=%.02e %.02e %.02e (m) %.02e "
@@ -539,7 +550,7 @@ void StateEstimationSmoother::fuse_twist(
539550 gtsam::Matrix3 wCov = twistCov.asEigen ().block <3 , 3 >(3 , 3 );
540551
541552 // Create a new KF id (or reuse a very close match):
542- const auto this_kf_id = create_or_get_keyframe_by_timestamp (timestamp);
553+ const auto this_kf_id = create_or_get_keyframe_by_timestamp_locked (timestamp);
543554
544555 {
545556 auto noiseV = gtsam::noiseModel::Gaussian::Covariance (vCov);
@@ -588,13 +599,14 @@ void StateEstimationSmoother::fuse_twist(
588599std::optional<NavState> StateEstimationSmoother::estimated_navstate (
589600 const mrpt::Clock::time_point& timestamp, const std::string& frame_id)
590601{
602+ auto lck = mrpt::lockHelper (stateMutex_);
603+
591604 // 1) Make sure we processed all pending sensor data, and have updated the cached values from
592605 // GTSAM values
593- process_pending_gtsam_updates ();
606+ process_pending_gtsam_updates_locked ();
594607
595608 // 2) Get the vehicle state from cached optimized values:
596609 // Look for the closest frame and extrapolate.
597- auto lck = mrpt::lockHelper (stateMutex_);
598610
599611 std::optional<double > closestFrameDt;
600612 double closestFrameDtSigned = 0 ;
@@ -943,9 +955,15 @@ void StateEstimationSmoother::delete_too_old_entries()
943955StateEstimationSmoother::frame_index_t StateEstimationSmoother::create_or_get_keyframe_by_timestamp (
944956 const mrpt::Clock::time_point& t, const std::optional<double >& overrideCloseEnough)
945957{
946- const auto tle = mola::ProfilerEntry (profiler_, " create_or_get_keyframe_by_timestamp" );
947-
948958 auto lck = mrpt::lockHelper (stateMutex_);
959+ return create_or_get_keyframe_by_timestamp_locked (t, overrideCloseEnough);
960+ }
961+
962+ StateEstimationSmoother::frame_index_t
963+ StateEstimationSmoother::create_or_get_keyframe_by_timestamp_locked (
964+ const mrpt::Clock::time_point& t, const std::optional<double >& overrideCloseEnough)
965+ {
966+ const auto tle = mola::ProfilerEntry (profiler_, " create_or_get_keyframe_by_timestamp" );
949967
950968 const double threshold = overrideCloseEnough ? *overrideCloseEnough
951969 : params_.min_time_difference_to_create_new_frame ;
@@ -1055,9 +1073,13 @@ StateEstimationSmoother::odometry_frameid_t StateEstimationSmoother::add_or_get_
10551073
10561074void StateEstimationSmoother::process_pending_gtsam_updates ()
10571075{
1058- const auto tle = mola::ProfilerEntry (profiler_, " process_pending_gtsam_updates" );
1059-
10601076 auto lck = mrpt::lockHelper (stateMutex_);
1077+ process_pending_gtsam_updates_locked ();
1078+ }
1079+
1080+ void StateEstimationSmoother::process_pending_gtsam_updates_locked ()
1081+ {
1082+ const auto tle = mola::ProfilerEntry (profiler_, " process_pending_gtsam_updates" );
10611083
10621084 // Even if we have no new factors/values, do update the stamps of "persistent" variables:
10631085 if (state_.last_observation_stamp .has_value ())
@@ -1432,10 +1454,9 @@ void StateEstimationSmoother::initialize_new_frame(
14321454 // Add planar constraints:
14331455 if (params_.enforce_planar_motion )
14341456 {
1435- const auto planar_z_noise = gtsam::noiseModel::Diagonal::Sigmas (
1436- gtsam::Vector6 (
1437- PLANAR_Z_SIGMA , PLANAR_Z_SIGMA , PLANAR_XY_SIGMA , // rx≈0, ry≈0, rz free
1438- PLANAR_XY_SIGMA , PLANAR_XY_SIGMA , PLANAR_Z_SIGMA ) // tx free, ty free, tz≈0
1457+ const auto planar_z_noise = gtsam::noiseModel::Diagonal::Sigmas (gtsam::Vector6 (
1458+ PLANAR_Z_SIGMA , PLANAR_Z_SIGMA , PLANAR_XY_SIGMA , // rx≈0, ry≈0, rz free
1459+ PLANAR_XY_SIGMA , PLANAR_XY_SIGMA , PLANAR_Z_SIGMA ) // tx free, ty free, tz≈0
14391460 );
14401461
14411462 state_.gtsam ->newFactors .addPrior (T (id), gtsam::Pose3::Identity (), planar_z_noise);
@@ -1532,7 +1553,13 @@ std::optional<mrpt::poses::CPose3DPDFGaussian> StateEstimationSmoother::estimate
15321553 const
15331554{
15341555 auto lck = mrpt::lockHelper (stateMutex_);
1556+ return estimated_T_enu_to_map_locked ();
1557+ }
15351558
1559+ std::optional<mrpt::poses::CPose3DPDFGaussian>
1560+ StateEstimationSmoother::estimated_T_enu_to_map_locked () const
1561+ {
1562+ // Called with stateMutex_ already held by the caller.
15361563 auto it = state_.last_estimated_frames .find (REFERENCE_FRAME_ID );
15371564 if (it == state_.last_estimated_frames .end ())
15381565 {
@@ -1545,8 +1572,7 @@ std::optional<mrpt::poses::CPose3DPDFGaussian>
15451572 StateEstimationSmoother::get_estimated_T_map_to_odometry_frame (const frame_index_t idx) const
15461573{
15471574 ASSERT_GE_ (idx, 1 );
1548- auto lck = mrpt::lockHelper (stateMutex_);
1549-
1575+ // Called with stateMutex_ already held by the caller.
15501576 auto it = state_.last_estimated_frames .find (idx);
15511577 if (it == state_.last_estimated_frames .end ())
15521578 {
@@ -1623,7 +1649,7 @@ void StateEstimationSmoother::publishEstimatedGeoreferencing()
16231649 return ;
16241650 }
16251651
1626- auto T_enu_map_opt = estimated_T_enu_to_map ();
1652+ auto T_enu_map_opt = estimated_T_enu_to_map_locked ();
16271653 if (!T_enu_map_opt.has_value ())
16281654 {
16291655 return ;
0 commit comments