9 #ifndef CscSlamEngine_h
10 #define CscSlamEngine_h
16 #include <shared_mutex>
18 #include <mrpt/math/TPose2D.h>
19 #include <mrpt/poses/CPose3D.h>
20 #include <mrpt/maps/CSimplePointsMap.h>
26 #include <unordered_set>
29 class COccupancyGridMap2D;
33 class CObservation2DRangeScan;
37 class CMonteCarloLocalization2D;
38 class CMetricMapBuilderRBPF;
43 using mrpt::maps::CSimplePointsMap;
44 using mrpt::math::TPoint2D, mrpt::math::TPose2D, mrpt::obs::CObservation2DRangeScan, mrpt::maps::COccupancyGridMap2D, mrpt::slam::CMonteCarloLocalization2D, mrpt::slam::CMetricMapBuilderRBPF;
45 using mrpt::poses::CPose3D;
46 using std::recursive_mutex, std::pair, std::lock_guard, std::queue;
47 using std::shared_lock, std::shared_mutex, std::unique_lock;
49 namespace fs = std::filesystem;
57 class LidarValuesSnapshot;
83 void toMrptPoint(
float voxelSizeMeters,
float &outX,
float &outY,
float &outZ)
const;
85 void toCscPoint(
float voxelSizeMeters,
double &outX,
double &outY,
double &outZ)
const;
91 float voxelSizeMeters);
98 ENUM(CscSlam3dMapVoxelDiffType, SLAM3D_VOXEL_DIFF_TYPE_ADD, SLAM3D_VOXEL_DIFF_TYPE_REMOVE);
105 CscSlam3dMapVoxelDiffType type = SLAM3D_VOXEL_DIFF_TYPE_ADD;
109 ENUM(CscSlam3dMapVoxelChangesMode, SLAM3D_VOXEL_CHANGES_MODE_DIFFS, SLAM3D_VOXEL_CHANGES_MODE_FULL_REBUILD);
116 CscSlam3dMapVoxelChangesMode
mode;
118 vector<CscSlam3dMapVoxelDiff>
diffs;
124 bool isFullRebuild()
const;
134 using VoxelSet = std::unordered_set<CscSlam3dMapVoxelKey, CscSlam3dMapVoxelKeyHash>;
138 const VoxelSet &getVoxelsUnsafe()
const;
153 std::optional<CscSlam3dMapVoxelDiff> addPointConscience(
const CscPoint3d &point);
158 std::optional<CscSlam3dMapVoxelDiff> removePointConscience(
const CscPoint3d &point);
160 std::optional<CscSlam3dMapVoxelDiff> addPointMrpt(
double x,
double y,
double z);
161 std::optional<CscSlam3dMapVoxelDiff> removePointMrpt(
double x,
double y,
double z);
163 vector<CscSlam3dMapVoxelKey> getAllVoxelsCopy()
const;
186 uint64_t firstSeenFrame = 0;
187 uint64_t lastSeenFrame = 0;
189 float confidence = 0.0f;
191 void addHit(
double x,
double y,
double z, uint64_t frameIndex);
192 void addMiss(uint64_t frameIndex);
193 void getCentroidMrpt(
float &x,
float &y,
float &z)
const;
195 bool isVisible(
float visibleThreshold)
const;
196 bool shouldErase(
float eraseThreshold)
const;
201 using VoxelMap = std::unordered_map<
212 uint64_t frameIndex);
214 std::optional<CscSlam3dMapVoxelDiff> addMissVoxel(
216 uint64_t frameIndex);
221 std::vector<CscSlam3dMapVoxelDiff> integrateRayMrpt(
229 std::unordered_set<CscSlam3dMapVoxelKey, CscSlam3dMapVoxelKeyHash> &frameHitVoxels,
230 std::unordered_set<CscSlam3dMapVoxelKey, CscSlam3dMapVoxelKeyHash> &frameMissVoxels);
232 optional<CscSlam3dMapVoxelDiff> addPointMrpt(
236 uint64_t frameIndex);
243 float getVoxelSizeMeters()
const;
244 const VoxelMap &getVoxelsUnsafe()
const;
249 float getVisibleThreshold()
const;
256 void eraseDeadVoxels();
258 size_t visibleVoxelCount()
const;
260 std::vector<CscSlam3dMapVoxelKey> collectFarNeighborCandidates(
261 int minNeighborCount,
264 std::vector<CscSlam3dMapVoxelKey> purgeFarNeighborCandidates(
265 const std::vector<CscSlam3dMapVoxelKey> &candidates,
266 int minNeighborCount,
270 size_t visibleVoxelCountCache = 0;
271 float voxelSizeMeters = 0.03f;
272 const float visibleThreshold = 1.0f;
273 double maxRayDistanceMeters = 6.5;
274 double minRayDistanceMeters = 0.10;
275 double missEndMarginMeters = 0.08;
277 float eraseThreshold = 0.01f;
287 vector<CscSlam3dMapVoxelChanges> consumeAll();
303 void appendLocalFrame(
304 const std::vector<CscPoint3d> &localPointsCm,
306 size_t decimationStep,
307 double minPointDistanceMeters,
308 double maxPointDistanceMeters);
315 vector<CscPoint3d *> pointsCopyPtrs(
const CscPoint3d &entityStartPosition,
const CscQuaternion &entityStartOrientation)
const;
322 return voxelGrid.getAllVoxelsCopy();
325 size_t probabilisticVoxelGridSize()
const;
330 bool isEmpty()
const;
333 void remove3dMapVoxelChangesListener(uint64_t listenerId);
343 void withProbabilisticVoxelGridReadLock(
350 size_t visibleProbabilisticVoxelGridSize()
const;
356 void purgeFarNeighbors(
int minNeighborCount = 2,
int minHits = 3);
359 mutable std::shared_mutex updateMutex;
362 mutex voxelChangesListenersMutex;
363 vector<CscSlam3dMapVoxelChangesListener> voxelChangesListeners;
364 std::atomic<uint64_t> nextVoxelChangesListenerId = 1;
366 vector<CscSlam3dMapVoxelChangesListener> voxelChangesListeners;
368 lock_guard lock(voxelChangesListenersMutex);
369 voxelChangesListeners = this->voxelChangesListeners;
371 for (
const auto &listener : voxelChangesListeners) {
372 listener.callback(changes);
376 CscSlam3dProbabilisticVoxelGrid probabilisticVoxelGrid;
377 uint64_t probabilisticFrameIndex = 0;
382 static constexpr
double defaultCellOccupiedThreshold = 0.14;
389 double overlapRatio{0.0};
399 std::chrono::steady_clock::time_point
lastHit;
403 bool isSimulatedEntity,
416 const CscPoint3d &getEntityStartPosition()
const;
430 fs::path saveCartography();
431 bool loadCartography(
const fs::path &cartographyDir);
432 bool loadLatestCartography();
433 bool hasLoadedCartography();
435 bool isStarted()
const;
436 bool isStartedAndReady()
const;
438 void consumeLidarValues();
440 void forceLocalisationPosition(
double xInMeters,
double zInMeters,
double headingInRad);
447 optional<TPose2D> entityPose = nullopt;
473 const CObservation2DRangeScan *lidarScan =
nullptr;
476 void saveMrpt2dMapToPng(
const string &filename,
479 CscPoint3d *transformToWorld3dPosition(
const TPoint2D &pointSlam2d)
const;
480 vector<CscPoint3d *> getMapAsWorldPoints(
double threshold = defaultCellOccupiedThreshold)
const;
483 if (cellValue < 0.0
f || std::abs(cellValue - 0.5
f) < 1e-6
f) {
484 return CscSlamCellOccupancyStatus::SlamCellOccupancyStatus_Unknown;
486 if (cellValue <= occupiedThreshold) {
487 return CscSlamCellOccupancyStatus::SlamCellOccupancyStatus_Occupied;
489 return CscSlamCellOccupancyStatus::SlamCellOccupancyStatus_Free;
492 void clearCartography();
500 mrpt::math::TPose2D getEntityPose2dMrpt()
const;
518 bool is3dSlamInitialized()
const;
528 void remove3dMapVoxelChangesListener(uint64_t listenerId);
535 vector<CscSlam3dMapVoxelKey> get3dMapVoxelsCopy()
const;
543 size_t decimationStep = 1;
547 size_t decimationStepVoxelToPointCloud = 1;
548 size_t minFramePoints = 120;
549 size_t minMapPointsBeforeIcp = 1500;
551 double minPointDistanceMeters = 0.20;
552 double maxPointDistanceMeters = 15.0;
556 void save3dMapTopViewPngUnlocked(
const string &filename,
double metersPerPixel,
int imageSize,
const CscPoint3dOriented *entityPosition =
nullptr,
const CscSize3d *entityDimensions =
nullptr)
const;
557 void save3dMapToPcdAsciiUnlocked(
const string &filename)
const;
558 class PhantomClusterTracker {
560 PhantomClusterTracker(
double overlapThreshold = 0.3,
int maxAgeSeconds = 300);
563 void update(
const vector<PhantomCluster> &newClusters);
565 const vector<PhantomClusterTracked> &tracked()
const;
567 string getSummary()
const;
570 vector<PhantomClusterTracked> trackedClusters;
571 const double overlapThreshold;
572 const int maxAgeSeconds;
575 bool clustersSimilar(
const vector<std::pair<int, int>> &a,
576 const vector<std::pair<int, int>> &
b)
const;
579 const string entitySerialNumber;
580 const bool isSimulatedEntity;
590 void clearSlamData();
592 CMonteCarloLocalization2D *slamLocalization =
nullptr;
594 mutable recursive_mutex slamInstanceMutex;
595 CMetricMapBuilderRBPF *slamInstance =
nullptr;
598 bool slamInitialized =
false;
599 double entityStartYaw = 0;
603 queue<const LidarValuesSnapshot *> lidarValuesQueue;
605 unique_ptr<CscLogger>
logger;
607 unsigned long long lastLidarValuesTimestamp = 0;
609 mutable mutex lastRangeScanMutex;
612 std::chrono::steady_clock::time_point lastPngExportTime = std::chrono::steady_clock::now();
617 bool started =
false;
618 bool odoDiffInitialized =
false;
620 unsigned int lastMapSize = 0;
621 unsigned int identicalCount = 0;
623 std::chrono::steady_clock::time_point lastTimeSlamOdo = std::chrono::steady_clock::now();
625 struct CscSlamMapDiscrepancyState {
626 size_t discrepantScanCount = 0;
627 size_t ticksSinceDiscrepancyStart = 0;
630 size_t initializationTicksCount = 0;
633 CscSlamMapDiscrepancyState discrepancyState;
635 struct PhantomClusterParams {
640 double maxDistanceMeters = 8;
645 double minOverlapRatio = 0.2;
650 int supportRadius = 2;
654 int clusterThresh = 10;
659 int maxClusterShiftCells = 14;
664 int gapTolerance = 2;
669 int mergeDistanceCells = 3;
672 struct PhantomClusterResult {
673 bool hasPhantoms{
false};
674 vector<PhantomCluster> clusters;
675 vector<uint8_t> visible;
678 PhantomClusterResult detectPhantomClusters(
const COccupancyGridMap2D &slamMap,
679 const CObservation2DRangeScan &scan,
681 const PhantomClusterParams ¶ms,
684 PhantomClusterResult lastClustersResult;
685 PhantomClusterTracker phantomClusterTracker;
689 Slam3dParams slam3dParams;
691 mutable std::mutex slam3dMutex;
693 CscSlam3dMap *slam3dMap =
nullptr;
694 CPose3D slam3dPose = CPose3D(0, 0, 0, 0, 0, 0);
696 bool slam3dInitialized =
false;
697 size_t slam3dFramesIntegrated = 0;
699 bool shouldUse3dSlam(
const LidarValuesSnapshot &snap)
const;
700 void initMrptSlam3d();
702 CPose3D odometryToMrptPose3d(
const OdometryData &odometry)
const;
704 void consumeLidarValues3d(
const LidarValuesSnapshot &snap,
705 const CPose3D &odoIncrement, optional<uint64_t> timeStamp = {});
711 return std::hypot(pointA.x - pointB.x, pointA.y - pointB.y);
723 return CscPoint3d(x / 100.0 , -z / 100.0 , y);
740 namespace directions {
759 const vector<CscAvailableDirection> &authorizedDirections,
762 map<int, double> *computedDirections =
nullptr);
766 const std::string &bestDirName,
767 const fs::path &filename,
769 double entityLength);
772 const mrpt::obs::CObservation2DRangeScan &scan,
773 double entityWidthMeters,
774 double entityLengthMeters,
775 double minClusterDistanceMeters = 0.10,
776 int minClusterSize = 3,
777 double maxDepthMeters = 2.0);
809 double voxelSizeMeters,
810 int minVoxelPointCount,
811 double maxDepthMeters,
812 double minObstacleYMeters,
813 double maxObstacleYMeters = std::numeric_limits<double>::infinity());