/* * 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.Mapping; using CartographerSharp.Models.Transform; using CartographerSharp.Sensor; using CartographerSharp.Transform; using RobotNet10.Shared.Numbers; namespace CartographerSharp.Mapping; /// /// Constant pose data (time and local pose). /// public struct ConstantPoseData { public long Time { get; set; } // Universal Time Scale ticks public Rigid3d LocalPose { get; set; } } /// /// Trajectory node pose information. /// public struct TrajectoryNodePose { /// /// The node pose in the global SLAM frame. /// public Rigid3d GlobalPose { get; set; } /// /// Optional constant pose data. /// public ConstantPoseData? ConstantPoseData { get; set; } } /// /// Trajectory node containing sensor data and pose information. /// public class TrajectoryNode { /// /// Constant data associated with a trajectory node. /// public class Data { public long Time { get; set; } // Universal Time Scale ticks /// /// Transform to approximately gravity align the tracking frame as /// determined by local SLAM. /// public Quaternion GravityAlignment { get; set; } /// /// Used for loop closure in 2D: voxel filtered returns in the /// 'gravity_alignment' frame. /// public PointCloud? FilteredGravityAlignedPointCloud { get; set; } /// /// Used for loop closure in 3D. /// public PointCloud? HighResolutionPointCloud { get; set; } public PointCloud? LowResolutionPointCloud { get; set; } public double[]? RotationalScanMatcherHistogram { get; set; } /// /// The node pose in the local SLAM frame. /// public Rigid3d LocalPose { get; set; } } /// /// Gets the time of this node. /// public long Time => ConstantData?.Time ?? 0; /// /// This must be a shared reference. If the data is used for visualization while the /// node is being trimmed, it must survive until all use finishes. /// public Data? ConstantData { get; set; } /// /// The node pose in the global SLAM frame. /// public Rigid3d GlobalPose { get; set; } /// /// Creates a shallow clone of this TrajectoryNode. /// The ConstantData reference is shared (not deep cloned) since it's immutable after creation. /// Only GlobalPose is independent in the clone. /// Used for Copy-on-Write optimization to avoid data races. /// public TrajectoryNode ShallowClone() { return new TrajectoryNode { ConstantData = this.ConstantData, // Shared reference (immutable) GlobalPose = this.GlobalPose // Value type copy }; } } /// /// Conversion utilities for TrajectoryNode. /// public static class TrajectoryNodeOperations { /// /// Converts to proto representation. /// public static TrajectoryNodeData ToProto(TrajectoryNode.Data constantData) { var proto = new TrajectoryNodeData( constantData.Time, (Quaterniond)constantData.GravityAlignment, (Rigid3dProto)constantData.LocalPose, constantData.FilteredGravityAlignedPointCloud != null ? new Sensor.CompressedPointCloud(constantData.FilteredGravityAlignedPointCloud).ToProto() : null, constantData.HighResolutionPointCloud != null ? new Sensor.CompressedPointCloud(constantData.HighResolutionPointCloud).ToProto() : null, constantData.LowResolutionPointCloud != null ? new Sensor.CompressedPointCloud(constantData.LowResolutionPointCloud).ToProto() : null, constantData.RotationalScanMatcherHistogram?.ToList() ); return proto; } /// /// Creates from proto representation. /// public static TrajectoryNode.Data FromProto(TrajectoryNodeData proto) { return new TrajectoryNode.Data { Time = proto.Timestamp, GravityAlignment = (Quaternion)proto.GravityAlignment, LocalPose = (Rigid3d)proto.LocalPose, FilteredGravityAlignedPointCloud = proto.FilteredGravityAlignedPointCloud.HasValue ? Sensor.CompressedPointCloud.FromProto(proto.FilteredGravityAlignedPointCloud.Value).Decompress() : null, HighResolutionPointCloud = proto.HighResolutionPointCloud.HasValue ? Sensor.CompressedPointCloud.FromProto(proto.HighResolutionPointCloud.Value).Decompress() : null, LowResolutionPointCloud = proto.LowResolutionPointCloud.HasValue ? Sensor.CompressedPointCloud.FromProto(proto.LowResolutionPointCloud.Value).Decompress() : null, RotationalScanMatcherHistogram = proto.RotationalScanMatcherHistogram?.ToArray() }; } }