/* * Copyright 2016 The Cartographer Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using CartographerSharp.Models.Transform; using CartographerSharp.Sensor; using CartographerSharp.Transform; namespace CartographerSharp.Mapping; /// /// Base class for pose graph implementations. /// public abstract class PoseGraph : IPoseGraph { /// /// Initial trajectory pose information. /// public struct InitialTrajectoryPose(int toTrajectoryId, Rigid3d relativePose, long time) { public int ToTrajectoryId { get; set; } = toTrajectoryId; public Rigid3d RelativePose { get; set; } = relativePose; public long Time { get; set; } = time; } protected PoseGraph() { } /// /// Gets the total number of work items added to the work queue. /// public abstract int WorkItemsAdded { get; } /// /// Gets the total number of work items completed by the work queue. /// public abstract int WorkItemsCompleted { get; } /// /// Gets the number of work items currently pending in the work queue. /// public abstract int WorkItemsPending { get; } /// /// Gets the current number of items in the work queue. /// public abstract int WorkQueueCount { get; } /// /// Gets the number of nodes started in the constraint builder. /// public abstract int ConstraintBuilderNodesStarted { get; } /// /// Gets the number of nodes finished in the constraint builder. /// public abstract int ConstraintBuilderNodesFinished { get; } /// /// Gets the total number of trajectory nodes in the pose graph. /// Used for progress tracking during optimization. /// public abstract int TrajectoryNodesCount { get; } /// /// Gets the total number of constraint tasks dispatched for scan matching. /// public abstract int ConstraintTasksTotal { get; } /// /// Gets the number of constraint tasks that have finished scan matching. /// public abstract int ConstraintTasksFinished { get; } /// /// Inserts an IMU measurement. /// public abstract void AddImuData(int trajectoryId, ImuData imuData); /// /// Inserts an odometry measurement. /// public abstract void AddOdometryData(int trajectoryId, OdometryData odometryData); /// /// Inserts a fixed frame pose measurement. /// public abstract void AddFixedFramePoseData(int trajectoryId, FixedFramePoseData fixedFramePoseData); /// /// Inserts landmarks observations. /// public abstract void AddLandmarkData(int trajectoryId, LandmarkData landmarkData); /// /// Drains the work queue to ensure all pending operations are completed. /// This should be called before finishing a trajectory to avoid race conditions. /// Default implementation does nothing (for pose graphs without work queues). /// public virtual void DrainWorkQueue() { // Default implementation: do nothing (for pose graphs without work queues) } /// /// Finishes the given trajectory. /// public abstract void FinishTrajectory(int trajectoryId); /// /// Freezes a trajectory. Poses in this trajectory will not be optimized. /// public abstract void FreezeTrajectory(int trajectoryId); /// /// Adds a 'submap' from a proto with the given 'global_pose' to the /// appropriate trajectory. /// public abstract void AddSubmapFromProto(Rigid3d globalPose, Models.Mapping.Submap submap); /// /// Adds a 'node' from a proto with the given 'global_pose' to the /// appropriate trajectory. /// public abstract void AddNodeFromProto(Rigid3d globalPose, Models.Mapping.Node node); /// /// Sets the trajectory data from a proto. /// public abstract void SetTrajectoryDataFromProto(Models.Mapping.TrajectoryData data); /// /// Adds information that 'node_id' was inserted into 'submap_id'. The submap /// has to be deserialized first. /// public abstract void AddNodeToSubmap(NodeId nodeId, SubmapId submapId); /// /// Adds serialized constraints. The corresponding trajectory nodes and submaps /// have to be deserialized before calling this function. /// public abstract void AddSerializedConstraints(List constraints); /// /// Adds a 'trimmer'. It will be used after all data added before it has been /// included in the pose graph. /// public abstract void AddTrimmer(PoseGraphTrimmer trimmer); /// /// Gets the current trajectory clusters. /// public abstract List> GetConnectedTrajectories(); /// /// Returns the IMU data. /// public abstract Dictionary> GetImuData(); /// /// Returns the odometry data. /// public abstract Dictionary> GetOdometryData(); /// /// Returns the fixed frame pose data. /// public abstract Dictionary> GetFixedFramePoseData(); /// /// Returns the landmark data. /// public abstract Dictionary GetLandmarkNodes(); /// /// Sets a relative initial pose 'relative_pose' for 'from_trajectory_id' with /// respect to 'to_trajectory_id' at time 'time'. /// public abstract void SetInitialTrajectoryPose( int fromTrajectoryId, int toTrajectoryId, Rigid3d pose, long time); /// /// Sets localization initial poses for relocalizing against the map. /// Match C++: SetLocalizationInitialPoses (pose_graph.h:139) /// C++ signature: const std::vector<transform::Rigid3d> & (const reference) /// C# equivalent: IReadOnlyList<Rigid3d> (read-only collection) /// public abstract void SetLocalizationInitialPoses(IReadOnlyList localizationInitialPoses); public abstract void RunFinalOptimization(); public abstract MapById GetAllSubmapData(); public abstract IPoseGraph.SubmapData GetSubmapData(SubmapId submapId); public abstract MapById GetAllSubmapPoses(); public abstract Rigid3d GetLocalToGlobalTransform(int trajectoryId); public abstract MapById GetTrajectoryNodes(); public abstract MapById GetTrajectoryNodePoses(); public abstract Dictionary GetTrajectoryStates(); public abstract Dictionary GetLandmarkPoses(); public abstract void SetLandmarkPose(string landmarkId, Rigid3d globalPose, bool frozen = false); public abstract void DeleteTrajectory(int trajectoryId); public abstract bool IsTrajectoryFinished(int trajectoryId); public abstract bool IsTrajectoryFrozen(int trajectoryId); public abstract Dictionary GetTrajectoryData(); public abstract List Constraints(); public abstract Models.Mapping.PoseGraph ToProto(bool includeUnfinishedSubmaps); public abstract void SetGlobalSlamOptimizationCallback(GlobalSlamOptimizationCallback callback); public abstract void SetTransformToMap(Rigid3d transform); public abstract Rigid3d GetTransformToMap(); // Manual compute methods (match C++ PoseGraphInterface) public abstract (double Score, IPoseGraph.Constraint? Constraint) ManualComputeConstraint(NodeId nodeId, SubmapId submapId); public abstract double ManualComputeConstraintScore(NodeId nodeId, SubmapId submapId, Rigid3d initialPose); public abstract double ManualComputeScanMatcher(NodeId nodeId, SubmapId submapId, Rigid3d initialPose, out Rigid3d poseManualEstimate); public abstract bool ManualRelocalization(int trajectoryId, out double score, Rigid2d initialPose, LocalizationResultCallback? callback); } /// /// Pose graph trimmer interface. /// public abstract class PoseGraphTrimmer { /// /// Trims the pose graph. /// public abstract void Trim(ITrimmable trimmable); } /// /// Interface for trimmable pose graph operations. /// public interface ITrimmable { int NumSubmaps(int trajectoryId); List GetSubmapIds(int trajectoryId); MapById GetOptimizedSubmapData(); MapById GetTrajectoryNodes(); List GetConstraints(); void TrimSubmap(SubmapId submapId); bool IsFinished(int trajectoryId); void SetTrajectoryState(int trajectoryId, IPoseGraph.TrajectoryState state); } /// /// Conversion utilities for constraints. /// public static class ConstraintOperations { /// /// Converts constraint to proto. /// Match C++: ToProto in pose_graph.cc:147-169 /// public static Models.Mapping.PoseGraph.Constraint ToProto(IPoseGraph.Constraint constraint) { // Match C++: convert tag (line 161 in pose_graph.cc) var tag = constraint.ConstraintTag == IPoseGraph.Constraint.Tag.IntraSubmap ? Models.Mapping.PoseGraph.Constraint.Tag.IntraSubmap : Models.Mapping.PoseGraph.Constraint.Tag.InterSubmap; // Match C++: convert state (line 166 in pose_graph.cc) var state = constraint.ConstraintState == IPoseGraph.Constraint.State.Enabled ? Models.Mapping.PoseGraph.Constraint.State.Enabled : Models.Mapping.PoseGraph.Constraint.State.Disabled; // Match C++: create constraint proto with all fields (lines 147-168 in pose_graph.cc) return new Models.Mapping.PoseGraph.Constraint( new Models.Mapping.PoseGraph.SubmapId(constraint.SubmapId.TrajectoryId, constraint.SubmapId.SubmapIndex), new Models.Mapping.PoseGraph.NodeId(constraint.NodeId.TrajectoryId, constraint.NodeId.NodeIndex), (Rigid3dProto)constraint.ConstraintPose.ZbarIj, constraint.ConstraintPose.TranslationWeight, constraint.ConstraintPose.RotationWeight, tag, constraint.Score, // CRITICAL FIX: include score field (line 162 in pose_graph.cc) state // CRITICAL FIX: include state field (line 166 in pose_graph.cc) ); } /// /// Creates constraint from proto. /// Match C++: FromProto in pose_graph.cc:77-97 /// public static List FromProto(List constraintProtos) { var constraints = new List(); foreach (var constraintProto in constraintProtos) { // Match C++: convert tag IPoseGraph.Constraint.Tag tag; if (constraintProto.ConstraintTag == Models.Mapping.PoseGraph.Constraint.Tag.IntraSubmap) { tag = IPoseGraph.Constraint.Tag.IntraSubmap; } else { tag = IPoseGraph.Constraint.Tag.InterSubmap; } // Match C++: convert state (lines 93 in pose_graph.cc) IPoseGraph.Constraint.State state; if (constraintProto.ConstraintState == Models.Mapping.PoseGraph.Constraint.State.Enabled) { state = IPoseGraph.Constraint.State.Enabled; } else { state = IPoseGraph.Constraint.State.Disabled; } // Match C++: extract score (line 92 in pose_graph.cc) var score = constraintProto.Score; // Match C++: create constraint with all fields (line 94 in pose_graph.cc) var constraint = new IPoseGraph.Constraint( new SubmapId(constraintProto.SubmapId.TrajectoryId, constraintProto.SubmapId.SubmapIndex), new NodeId(constraintProto.NodeId.TrajectoryId, constraintProto.NodeId.NodeIndex), new IPoseGraph.Constraint.Pose( (Rigid3d)constraintProto.RelativePose, constraintProto.TranslationWeight, constraintProto.RotationWeight), tag, score, // CRITICAL FIX: include score field state // CRITICAL FIX: include state field ); constraints.Add(constraint); } return constraints; } }