40 " kf-slam - Part of the MRPT\n" 41 " MRPT C++ Library: %s - Sources timestamp: %s\n\n",
52 const std::string configFile = std::string(
argv[1]);
62 "MappingApplication",
"rawlog_file", std::string(
"log.rawlog"));
73 "MappingApplication",
"kf_implementation",
"CRangeBearingKFSLAM"));
75 if (kf_implementation ==
"CRangeBearingKFSLAM")
76 Run_KF_SLAM<mrpt::slam::CRangeBearingKFSLAM>();
77 else if (kf_implementation ==
"CRangeBearingKFSLAM2D")
78 Run_KF_SLAM<mrpt::slam::CRangeBearingKFSLAM2D>();
80 throw std::runtime_error(
81 "kf_implementation: Invalid value found in the config file.");
102 template <
class IMPL>
124 [[maybe_unused]]
const string& OUT_DIR)
159 for (
size_t i = 0; i < 6; i++)
160 fullCov(i, i) = max(fullCov(i, i), 1e-6);
163 H.
saveToTextFile(OUT_DIR +
string(
"/information_matrix_final.txt"));
166 H = H.array().abs().matrix();
170 imgF.
saveToFile(OUT_DIR +
string(
"/information_matrix_final.png"));
176 vector<std::vector<uint32_t>> landmarksMembership, partsInObsSpace;
179 for (
int i = 0; i < ERRS.
rows(); i++)
192 i + 1, landmarksMembership);
198 ERRS(i, 1) = partsInObsSpace.size();
200 landmarksMembership);
210 CVectorFloat ERRS_SWEEP(STEPS), ERRS_SWEEP_THRESHOLD(STEPS);
213 for (
size_t i = 0; i < STEPS; i++)
215 float th = (1.0f * i) / (STEPS - 1.0f);
216 ERRS_SWEEP_THRESHOLD[i] = th;
224 landmarksMembership);
227 ERRS_SWEEP.saveToTextFile(OUT_DIR +
string(
"/ERRORS_SWEEP.txt"));
229 OUT_DIR +
string(
"/ERRORS_SWEEP_THRESHOLD.txt"));
235 template <
class IMPL>
245 using ekfslam_t =
typename traits_t::ekfslam_t;
251 const unsigned int rawlog_offset =
252 cfgFile.read_int(
"MappingApplication",
"rawlog_offset", 0);
254 const unsigned int SAVE_LOG_FREQUENCY =
255 cfgFile.read_int(
"MappingApplication",
"SAVE_LOG_FREQUENCY", 1);
257 const bool SAVE_DA_LOG =
258 cfgFile.read_bool(
"MappingApplication",
"SAVE_DA_LOG",
true);
260 const bool SAVE_3D_SCENES =
261 cfgFile.read_bool(
"MappingApplication",
"SAVE_3D_SCENES",
true);
262 const bool SAVE_MAP_REPRESENTATIONS = cfgFile.read_bool(
263 "MappingApplication",
"SAVE_MAP_REPRESENTATIONS",
true);
265 cfgFile.read_bool(
"MappingApplication",
"SHOW_3D_LIVE",
false);
266 const bool CAMERA_3DSCENE_FOLLOWS_ROBOT = cfgFile.read_bool(
267 "MappingApplication",
"CAMERA_3DSCENE_FOLLOWS_ROBOT",
false);
269 #if !MRPT_HAS_WXWIDGETS 270 SHOW_3D_LIVE =
false;
273 string OUT_DIR = cfgFile.read_string(
274 "MappingApplication",
"logOutput_dir",
"OUT_KF-SLAM");
275 string ground_truth_file =
276 cfgFile.read_string(
"MappingApplication",
"ground_truth_file",
"");
277 string ground_truth_file_robot = cfgFile.read_string(
278 "MappingApplication",
"ground_truth_file_robot",
"");
280 string ground_truth_data_association = cfgFile.read_string(
281 "MappingApplication",
"ground_truth_data_association",
"");
292 mapping.loadOptions(cfgFile);
295 mapping.KF_options.dumpToTextStream(o);
296 mapping.options.dumpToTextStream(o);
307 if (ground_truth_file_robot.size() &&
fileExists(ground_truth_file_robot))
315 std::map<double, std::vector<int>> GT_DA;
318 if (!ground_truth_data_association.empty() &&
325 for (
int i = 0; i < mGT_DA.
rows(); i++)
327 std::vector<int>& v = GT_DA[mGT_DA(i, 0)];
328 if (v.size() <= mGT_DA(i, 1)) v.resize(mGT_DA(i, 1) + 1);
329 v[mGT_DA(i, 1)] = mGT_DA(i, 2);
332 "Loaded " << GT_DA.size() <<
" entries from DA ground truth file.");
336 std::ofstream out_da_performance_log;
338 const std::string f = std::string(
339 OUT_DIR + std::string(
"/data_association_performance.log"));
340 out_da_performance_log.open(f.c_str());
342 out_da_performance_log.is_open(),
343 std::string(
"Error writing to: ") + f);
346 out_da_performance_log
347 <<
"% TIMESTAMP INDEX_IN_OBS TruePos " 348 "FalsePos TrueNeg FalseNeg NoGroundTruthSoIDontKnow \n" 349 <<
"%--------------------------------------------------------------" 350 "--------------------------------------------------\n";
360 win3d->addTextMessage(0.01, 0.96,
"Red: Estimated path", 100);
361 win3d->addTextMessage(0.01, 0.93,
"Black: Ground truth path", 101);
365 std::ofstream out_da_log;
368 const std::string f =
369 std::string(OUT_DIR + std::string(
"/data_association.log"));
370 out_da_log.open(f.c_str());
371 ASSERTMSG_(out_da_log.is_open(), std::string(
"Error writing to: ") + f);
374 out_da_log <<
"% TIMESTAMP INDEX_IN_OBS ID " 375 " RANGE(m) YAW(rad) PITCH(rad) \n" 376 <<
"%-------------------------------------------------------" 377 "-------------------------------------\n";
384 size_t rawlogEntry = 0, step = 0;
386 vector<TPose3D> meanPath;
387 typename traits_t::posepdf_t robotPose;
388 const bool is_pose_3d = robotPose.state_length != 3;
390 std::vector<typename IMPL::landmark_point_t> LMs;
391 std::map<unsigned int, CLandmark::TLandmarkID> LM_IDs;
403 if (27 == pushKey)
break;
408 if (!CRawlog::readActionObservationPair(
409 rawlogArch, action, observations, rawlogEntry))
412 if (rawlogEntry >= rawlog_offset)
418 mapping.processActionObservation(action, observations);
420 const double tim_kf_iter = kftictac.
Tac();
424 mapping.getCurrentState(robotPose, LMs, LM_IDs, fullState, fullCov);
432 meanPath.push_back(robotPoseMean3D.asTPose());
435 if (!(step % SAVE_LOG_FREQUENCY))
437 const auto p = robotPose.mean.asVectorVal();
440 format(
"/robot_pose_%05u.txt", (
unsigned int)step));
444 if (!(step % SAVE_LOG_FREQUENCY))
447 OUT_DIR +
format(
"/full_cov_%05u.txt", (
unsigned int)step));
453 const typename ekfslam_t::TDataAssocInfo& da =
454 mapping.getLastDataAssociation();
465 for (
size_t i = 0; i < obsRB->
sensedData.size(); i++)
467 auto it = da.results.associations.find(i);
468 int assoc_ID_in_SLAM;
469 if (it != da.results.associations.end())
470 assoc_ID_in_SLAM = it->second;
474 auto itNewLM = da.newly_inserted_landmarks.find(i);
475 if (itNewLM != da.newly_inserted_landmarks.end())
476 assoc_ID_in_SLAM = itNewLM->second;
478 assoc_ID_in_SLAM = -1;
482 "%35.22f %8i %10i %10f %12f %12f\n", tim, (
int)i,
493 const typename ekfslam_t::TDataAssocInfo& da =
494 mapping.getLastDataAssociation();
505 auto itDA = GT_DA.find(tim);
507 for (
size_t i = 0; i < obsRB->
sensedData.size(); i++)
509 bool is_FP =
false, is_TP =
false, is_FN =
false,
512 if (itDA != GT_DA.end())
514 const std::vector<int>& vDA = itDA->second;
516 const int GT_ASSOC = vDA[i];
518 auto it = da.results.associations.find(i);
519 if (it != da.results.associations.end())
530 if (DA2GTDA_indices.
hasKey(it->second))
532 const int slam_asigned_LM_idx =
533 DA2GTDA_indices.
direct(it->second);
534 if (slam_asigned_LM_idx == GT_ASSOC)
552 da.newly_inserted_landmarks.find(i);
554 da.newly_inserted_landmarks.end())
556 const int new_LM_in_SLAM = itNewLM->second;
560 if (DA2GTDA_indices.
hasValue(GT_ASSOC))
571 new_LM_in_SLAM, GT_ASSOC);
585 out_da_performance_log <<
format(
586 "%35.22f %13i %8i %8i %8i %8i %8i\n", tim, (
int)i,
587 (
int)(is_TP ? 1 : 0), (
int)(is_FP ? 1 : 0),
588 (
int)(is_TN ? 1 : 0), (
int)(is_FN ? 1 : 0),
589 (
int)(!is_FP && !is_TP && !is_FN && !is_TN ? 1 : 0));
595 if (SAVE_MAP_REPRESENTATIONS && !(step % SAVE_LOG_FREQUENCY))
597 mapping.saveMapAndPath2DRepresentationAsMATLABFile(
598 OUT_DIR +
format(
"/slam_state_%05u.m", (
unsigned int)step));
602 if (win3d || (SAVE_3D_SCENES && !(step % SAVE_LOG_FREQUENCY)))
607 std::make_shared<opengl::CGridPlaneXY>(
608 -1000, 1000, -1000, 1000, 0, 5);
609 grid->setColor(0.4f, 0.4f, 0.4f);
610 scene3D->insert(grid);
616 std::make_shared<opengl::CSetOfLines>();
617 linesPath->setColor(1, 0, 0);
620 if (!meanPath.empty())
624 for (
auto& it : meanPath)
626 linesPath->appendLine(init_pose, it);
629 if (++path_decim > 10)
636 scene3D->insert(xyz);
639 scene3D->insert(linesPath);
646 xyz->setPose(robotPoseMean3D);
647 scene3D->insert(xyz);
651 if (win3d && CAMERA_3DSCENE_FOLLOWS_ROBOT)
653 win3d->setCameraPointingToPoint(
654 robotPoseMean3D.x(), robotPoseMean3D.y(),
655 robotPoseMean3D.z());
660 if (GT_PATH.
cols() == 6 || GT_PATH.
cols() == 3)
663 std::make_shared<opengl::CSetOfLines>();
664 GT_path->setColor(0, 0, 0);
666 std::min((
int)GT_PATH.
rows(), (int)meanPath.size());
668 if (GT_PATH.
cols() == 6)
670 double gtx0 = 0, gty0 = 0, gtz0 = 0;
671 for (
size_t i = 0; i < N; i++)
674 GT_PATH(i, 0), GT_PATH(i, 1), GT_PATH(i, 2),
675 GT_PATH(i, 3), GT_PATH(i, 4), GT_PATH(i, 5));
678 gtx0, gty0, gtz0, p.
x(), p.
y(), p.z());
684 else if (GT_PATH.
cols() == 3)
686 double gtx0 = 0, gty0 = 0;
687 for (
size_t i = 0; i < N; i++)
690 GT_PATH(i, 0), GT_PATH(i, 1), GT_PATH(i, 2));
692 GT_path->appendLine(gtx0, gty0, 0, p.
x(), p.
y(), 0);
697 scene3D->insert(GT_path);
702 const typename ekfslam_t::TDataAssocInfo& da =
703 mapping.getLastDataAssociation();
707 lins->setLineWidth(1.2f);
708 lins->setColor(1, 1, 1);
709 for (
auto it = da.results.associations.begin();
710 it != da.results.associations.end(); ++it)
715 typename ekfslam_t::KFArray_FEAT featMean;
716 mapping.getLandmarkMean(idxPred, featMean);
719 traits_t::landmark_to_3d(featMean, featMean3D);
723 robotPoseMean3D.x(), robotPoseMean3D.y(),
724 robotPoseMean3D.z(), featMean3D.
x, featMean3D.
y,
727 scene3D->insert(lins);
733 std::make_shared<opengl::CSetOfObjects>();
734 mapping.getAs3DObject(objs);
735 scene3D->insert(objs);
741 win3d->get3DSceneAndLock();
745 win3d->addTextMessage(
748 "Step %u - Landmarks in the map: %u",
749 (
unsigned int)step, (
unsigned int)LMs.size()),
752 win3d->addTextMessage(
756 ?
"Estimated pose: (x y z qr qx qy qz) = %s" 757 :
"Estimated pose: (x y yaw) = %s",
758 robotPose.mean.asString().c_str()),
761 static vector<double> estHz_vals;
762 const double curHz = 1.0 / std::max(1e-9, tim_kf_iter);
763 estHz_vals.push_back(curHz);
764 if (estHz_vals.size() > 50)
765 estHz_vals.erase(estHz_vals.begin());
768 win3d->addTextMessage(
771 "Iteration time: %7ss",
775 win3d->addTextMessage(
778 "Execution rate: %7sHz",
782 win3d->unlockAccess3DScene();
786 if (SAVE_3D_SCENES && !(step % SAVE_LOG_FREQUENCY))
791 format(
"/kf_state_%05u.3Dscene", (
unsigned int)step));
799 observations.reset();
804 "Step " << step <<
" - Rawlog entries processed: " << rawlogEntry);
810 traits_t::doPartitioningExperiment(mapping, fullCov, OUT_DIR);
813 if (ground_truth_file.size() &&
fileExists(ground_truth_file))
818 GT.loadFromTextFile(ground_truth_file);
820 catch (
const std::exception& e)
823 "Ignoring the following error loading ground truth file: " 827 if (GT.rows() > 0 && !LMs.empty())
833 for (
size_t i = 0; i < LMs.size(); i++)
837 for (
int r = 0; r < GT.rows(); r++)
839 if (std::abs(LM_IDs[i] - GT(r, 6)) < 1e-9)
841 const CPoint3D gtPt(GT(r, 0), GT(r, 1), GT(r, 2));
853 "Ground truth entry not found for landmark ID:" 873 MRPT_LOG_WARN(
"Close the 3D window to quit the application.");
mrpt::math::TPose3D asTPose() const
An implementation of EKF-based SLAM with range-bearing sensors, odometry, and a 2D (+heading) robot p...
double Tac() noexcept
Stops the stopwatch.
bool createDirectory(const std::string &dirName)
Creates a directory.
An implementation of EKF-based SLAM with range-bearing sensors, odometry, a full 6D robot pose...
std::string read_string(const std::string §ion, const std::string &name, const std::string &defaultValue, bool failIfNotFound=false) const
TPoint2D_< double > TPoint2D
Lightweight 2D point.
void getLastPartitionLandmarks(std::vector< std::vector< uint32_t >> &landmarksMembership) const
Return the partitioning of the landmarks in clusters accoring to the last partition.
double timestampToDouble(const mrpt::system::TTimeStamp t) noexcept
Transform from TTimeStamp to standard "time_t" (actually a double number, it can contain fractions of...
Template for column vectors of dynamic size, compatible with Eigen.
#define THROW_EXCEPTION(msg)
int getch() noexcept
An OS-independent version of getch, which waits until a key is pushed.
std::string std::string format(std::string_view fmt, ARGS &&... args)
void reconsiderPartitionsNow()
The partitioning of the entire map is recomputed again.
#define ASSERT_BELOW_(__A, __B)
void initialize(int argc, const char **argv)
Initializes the application from CLI parameters.
This file implements several operations that operate element-wise on individual or pairs of container...
size_t prediction_index_t
Used in mrpt::slam::TDataAssociationResults.
bool hasKey(const KEY &k) const
Return true if the given key 'k' is in the bi-map.
bool fileExists(const std::string &fileName)
Test if a given file (or directory) exists.
A high-performance stopwatch, with typical resolution of nanoseconds.
void insert(const KEY &k, const VALUE &v)
Insert a new pair KEY<->VALUE in the bi-map.
mrpt::vision::TStereoCalibParams params
static CDisplayWindow3D::Ptr Create(const std::string &windowCaption, unsigned int initialWindowWidth=400, unsigned int initialWindowHeight=300)
Class factory returning a smart pointer.
std::string MRPT_getCompilationDate()
Returns the MRPT source code timestamp, according to the Reproducible-Builds specifications: https://...
std::string rawlogFileName
rawlog to process
void saveToTextFile(const std::string &file, mrpt::math::TMatrixTextFileFormat fileFormat=mrpt::math::MATRIX_FORMAT_ENG, bool appendMRPTHeader=false, const std::string &userHeader=std::string()) const
Saves the vector/matrix to a file compatible with MATLAB/Octave text format.
Declares a class that represents a Probability Density function (PDF) of a 3D pose using a quaternion...
CIncrementalMapPartitioner::TOptions * mapPartitionOptions()
Provides access to the parameters of the map partitioning algorithm.
CArchiveStreamBase< STREAM > archiveFrom(STREAM &s)
Helper function to create a templatized wrapper CArchive object for a: MRPT's CStream, std::istream, std::ostream, std::stringstream.
mrpt::config::CConfigFileMemory params
Populated in initialize().
#define ASSERT_(f)
Defines an assertion mechanism.
void run()
Runs with the current parameter set.
This base provides a set of functions for maths stuff.
A bidirectional version of std::map, declared as bimap<KEY,VALUE> and which actually contains two std...
Declares a class that represents a Probability Density function (PDF) of a 2D pose ...
Versatile class for consistent logging and management of output messages.
CONTAINER::Scalar maximum(const CONTAINER &v)
This namespace contains representation of robot actions and observations.
void setFromMatrix(const MAT &m, bool matrix_is_normalized=true)
Set the image from a matrix, interpreted as grayscale intensity values, in the range [0...
TPoint3D_< double > TPoint3D
Lightweight 3D point.
#define ASSERTMSG_(f, __ERROR_MSG)
Defines an assertion mechanism.
double computeOffDiagonalBlocksApproximationError(const std::vector< std::vector< uint32_t >> &landmarksMembership) const
Computes the ratio of the missing information matrix elements which are ignored under a certain parti...
double x() const
Common members of all points & poses classes.
Derived inverse_LLt() const
Returns the inverse of a symmetric matrix using LLt.
#define ASSERT_ABOVEEQ_(__A, __B)
size_type rows() const
Number of rows in the matrix.
A class used to store a 3D point.
size_type cols() const
Number of columns in the matrix.
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
double partitionThreshold
!< N-cut partition threshold [0,2] (default=1)
#define MRPT_LOG_INFO_STREAM(__CONTENTS)
CONTAINER::Scalar minimum(const CONTAINER &v)
This class is a "CSerializable" wrapper for "CMatrixFloat".
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
mrpt::system::TTimeStamp timestamp
The associated UTC time-stamp.
static void doPartitioningExperiment([[maybe_unused]] ekfslam_t &mapping, [[maybe_unused]] CMatrixDouble &fullCov, [[maybe_unused]] const string &OUT_DIR)
mrpt::slam::CRangeBearingKFSLAM::TOptions options
std::string unitsFormat(const double val, int nDecimalDigits=2, bool middle_space=true)
This function implements formatting with the appropriate SI metric unit prefix: 1e-12->'p', 1e-9->'n', 1e-6->'u', 1e-3->'m', 1->'', 1e3->'K', 1e6->'M', 1e9->'G', 1e12->'T'.
A class used to store a 2D pose, including the 2D coordinate point and a heading (phi) angle...
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
void setContent(const std::vector< std::string > &stringList)
Changes the contents of the virtual "config file".
CSetOfObjects::Ptr CornerXYZSimple(float scale=1.0, float lineWidth=1.0)
Returns three arrows representing a X,Y,Z 3D corner (just thick lines instead of complex arrows for f...
void getLastPartitionLandmarksAsIfFixedSubmaps(size_t K, std::vector< std::vector< uint32_t >> &landmarksMembership)
For testing only: returns the partitioning as "getLastPartitionLandmarks" but as if a fixed-size subm...
This observation represents a number of range-bearing value pairs, each one for a detected landmark...
Lightweight 3D pose (three spatial coordinates, plus three angular coordinates).
The namespace for 3D scene representation and rendering.
bool kbhit() noexcept
An OS-independent version of kbhit, which returns true if a key has been pushed.
std::string file_get_contents(const std::string &fileName)
Loads an entire text file and return its contents as a single std::string.
double mean(const CONTAINER &v)
Computes the mean value of a vector.
std::string exception_to_str(const std::exception &e)
Builds a nice textual representation of a nested exception, which if generated using MRPT macros (THR...
bool deleteFilesInDirectory(const std::string &s, bool deleteDirectoryAsWell=false)
Delete all the files in a given directory (nothing done if directory does not exists, or path is a file).
#define MRPT_LOG_WARN(_STRING)
std::string trim(const std::string &str)
Removes leading and trailing spaces.
bool doPartitioningExperiment
If set to true (default=false), map will be partitioned using the method stated by partitioningMethod...
bool saveToFile(const std::string &fileName, int jpeg_quality=95) const
Save the image to a file, whose format is determined from the extension (internally uses OpenCV)...
std::string MRPT_getVersion()
Returns a string describing the MRPT version.
Saves data to a file and transparently compress the data using the given compression level...
void Tic() noexcept
Starts the stopwatch.
bool hasValue(const VALUE &v) const
Return true if the given value 'v' is in the bi-map.
#define ASSERT_FILE_EXISTS_(FIL)
#define MRPT_LOG_ERROR_STREAM(__CONTENTS)
bool direct(const KEY &k, VALUE &out_v) const
Get the value associated the given key, KEY->VALUE, returning false if not present.
static void doPartitioningExperiment(ekfslam_t &mapping, CMatrixDouble &fullCov, const string &OUT_DIR)
static void landmark_to_3d(const ARR &lm, TPoint3D &p)
#define MRPT_LOG_INFO_FMT(_FMT_STRING,...)
static Ptr Create(Args &&... args)
void getLastPartition(std::vector< std::vector< uint32_t >> &parts)
Return the last partition of the sequence of sensoryframes (it is NOT a partition of the map!!) Only ...
static void landmark_to_3d(const ARR &lm, TPoint3D &p)
A class for storing images as grayscale or RGB bitmaps.
TMeasurementList sensedData
The list of observed ranges:
#define MRPT_LOG_INFO(_STRING)
void loadFromTextFile(std::istream &f)
Loads a vector/matrix from a text file, compatible with MATLAB text format.