@@ -756,8 +756,35 @@ void StateEstimationSmoother::onNewObservation(const CObservation::ConstPtr& o)
756756 // so each source gets its own odometry frame in the factor graph.
757757 // Falls back to reference_frame_name for backward compatibility
758758 // (e.g. ground truth robot pose observations without a label).
759- const std::string& frameId =
760- obsPose->sensorLabel .empty () ? params_.reference_frame_name : obsPose->sensorLabel ;
759+ std::string frameId = params_.reference_frame_name ;
760+ if (!obsPose->sensorLabel .empty ())
761+ {
762+ // Normalize: replace illegal chars (keep alphanumeric, '_', '-', '/')
763+ std::string normalized;
764+ normalized.reserve (obsPose->sensorLabel .size ());
765+ for (char c : obsPose->sensorLabel )
766+ normalized += (std::isalnum (static_cast <unsigned char >(c)) || c == ' _' ||
767+ c == ' -' || c == ' /' )
768+ ? c
769+ : ' _' ;
770+
771+ // Enforce max length
772+ constexpr std::size_t MAX_FRAME_ID_LEN = 64 ;
773+ if (normalized.size () > MAX_FRAME_ID_LEN ) normalized.resize (MAX_FRAME_ID_LEN );
774+
775+ // Reject reserved names
776+ if (normalized == params_.vehicle_frame_name || normalized == params_.enu_frame_name )
777+ {
778+ MRPT_LOG_WARN_FMT (
779+ " CObservationRobotPose sensorLabel '%s' is a reserved frame name; "
780+ " falling back to reference_frame_name '%s'" ,
781+ obsPose->sensorLabel .c_str (), params_.reference_frame_name .c_str ());
782+ }
783+ else
784+ {
785+ frameId = normalized;
786+ }
787+ }
761788
762789 this ->fuse_pose (obsPose->timestamp , sensedSensorPose, frameId);
763790 }
0 commit comments