apps/mp2p-icp-run/main.cpp
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  * A repertory of multi primitive-to-primitive (MP2P) ICP algorithms in C++
3  * Copyright (C) 2018-2021 Jose Luis Blanco, University of Almeria
4  * See LICENSE for license information.
5  * ------------------------------------------------------------------------- */
6 
14 #include <mp2p_icp/ICP.h>
16 #include <mp2p_icp/load_xyz_file.h>
17 #include <mp2p_icp/metricmap.h>
20 #include <mrpt/3rdparty/tclap/CmdLine.h>
21 #include <mrpt/core/Clock.h>
22 #include <mrpt/img/CImage.h>
23 #include <mrpt/obs/CRawlog.h>
24 #include <mrpt/system/datetime.h>
25 #include <mrpt/system/filesystem.h>
26 
27 #include <fstream>
28 
29 // CLI flags:
30 static TCLAP::CmdLine cmd("mp2p-icp-run");
31 
32 static TCLAP::ValueArg<std::string> argInputLocal(
33  "", "input-local",
34  "Local input point cloud/map."
35  "It is interpreted as a rawlog entry if using the "
36  "format `<RAWLOG_FILE.rawlog>:<N>` to select the N-th entry in the "
37  "rawlog; otherwise, if the file extension is `.mm` it is loaded as a "
38  "serialized metric_map_t object; if it is a `.icplog` file, the local map "
39  "from that icp log is taken as input; in any other case, the file is "
40  "assumed to be a 3D pointcloud stored as a Nx3 ASCII matrix file.",
41  true, "pointcloud1.txt", "pointcloud1.txt", cmd);
42 
43 static TCLAP::ValueArg<std::string> argInputGlobal(
44  "", "input-global",
45  "Global input point cloud/map. Same format than input-local. ", true,
46  "pointcloud2.txt", "pointcloud2.txt", cmd);
47 
48 static TCLAP::ValueArg<std::string> argYamlConfigFile(
49  "c", "config",
50  "YAML config file describing the ICP pipeline. See docs:\n"
51  " https://docs.mola-slam.org/latest/"
52  "module-mp2p-icp.html#yaml-pipeline-definition-files",
53  true, "icp-config.yaml", "icp-config.yaml", cmd);
54 
55 static TCLAP::ValueArg<std::string> argYamlConfigFileGenerators(
56  "", "config-generators",
57  "YAML config file describing the Generators. Can be also defined via an "
58  "entry `generators` in the main `--config` yaml file. "
59  "Can be used when processing a rawlog as input; if not present, a default "
60  "Generator object will be used.",
61  false, "generators-config.yaml", "generators-config.yaml", cmd);
62 
63 static TCLAP::ValueArg<std::string> argYamlConfigFileFiltersLocal(
64  "", "config-filters-local",
65  "YAML config file describing a filtering pipeline for local map."
66  "If not provided, and the main --config yaml file contains a "
67  "`filters` entry (can be overriden with --entry-name-filters-local), it "
68  "will be used instead.",
69  false, "filters-config.yaml", "filters-config.yaml", cmd);
70 
71 static TCLAP::ValueArg<std::string> argYamlConfigFileFiltersGlobal(
72  "", "config-filters-global",
73  "YAML config file describing a filtering pipeline for global map."
74  "If not provided, and the main --config yaml file contains a"
75  "`filters` entry (can be overriden with --entry-name-filters-global), it "
76  "will be used instead.",
77  false, "filters-config.yaml", "filters-config.yaml", cmd);
78 
79 static TCLAP::ValueArg<std::string> argCfgNameFiltersGlobal(
80  "", "entry-name-filters-global",
81  "Overrides the map name in the YAML configuration file for global map "
82  "filter.",
83  false, "filters", "filters", cmd);
84 
85 static TCLAP::ValueArg<std::string> argCfgNameFiltersLocal(
86  "", "entry-name-filters-local",
87  "Overrides the map name in the YAML configuration file for local map "
88  "filter.",
89  false, "filters", "filters", cmd);
90 
91 static TCLAP::ValueArg<std::string> argInitialGuess(
92  "", "guess",
93  "SE(3) transformation of local wrt global, to use as initial guess for the "
94  "ICP algorithm. "
95  "Format: \"[x y z yaw_deg pitch_deg roll_deg]\"",
96  false, "[0 0 0 0 0 0]", "[0 0 0 0 0 0]", cmd);
97 
98 static TCLAP::SwitchArg argGenerateDebugFiles(
99  "d", "generate-debug-log",
100  "Enforces generation of the .icplog debug log files for posterior "
101  "visualization with mp2p-icp-log-viewer, overriding the "
102  "`generateDebugFiles` value in the configuration YAML file.",
103  cmd);
104 
105 // To avoid reading the same .rawlog file twice:
106 static std::map<std::string, mrpt::obs::CRawlog::Ptr> rawlogsCache;
107 
108 static mrpt::obs::CRawlog::Ptr load_rawlog(const std::string& filename)
109 {
110  ASSERT_FILE_EXISTS_(filename);
111 
112  // enable loading externally-stored lazy load objects:
113  mrpt::img::CImage::setImagesPathBase(
114  mrpt::obs::CRawlog::detectImagesDirectory(filename));
115 
116  auto& r = rawlogsCache[filename];
117  if (r) return r;
118  r = mrpt::obs::CRawlog::Create();
119 
120  std::cout << "Loading rawlog file `" << filename << "`..." << std::endl;
121 
122  bool rawlogReadOk = r->loadFromRawLogFile(filename, true);
123  ASSERT_(rawlogReadOk);
124 
125  std::cout << "Done, " << r->size() << " entries." << std::endl;
126 
127  return r;
128 }
129 
131 
132 static mp2p_icp::metric_map_t::Ptr pc_from_rawlog(
133  const mrpt::obs::CRawlog& r, const size_t index)
134 {
135  ASSERT_LT_(index, r.size());
136 
137  if (generators.empty())
138  {
139  std::cout
140  << "[warning] Using default mp2p_icp_filters::Generator since no "
141  "YAML file was given describing custom generators.\n";
142 
143  auto defaultGen = mp2p_icp_filters::Generator::Create();
144  defaultGen->initialize({});
145  generators.push_back(defaultGen);
146  }
147 
148  auto pc = mp2p_icp::metric_map_t::Create();
149 
150  auto o = r.getAsGeneric(index);
151  ASSERT_(o);
152 
153  if (auto sf = std::dynamic_pointer_cast<mrpt::obs::CSensoryFrame>(o); sf)
154  {
155  // Sensory-frame format:
157  }
158  else if (auto obs = std::dynamic_pointer_cast<mrpt::obs::CObservation>(o);
159  obs)
160  {
162  }
163  else
164  {
165  auto e = r.getAsGeneric(index);
166  THROW_EXCEPTION_FMT(
167  "Rawlog index %u is neither CSensoryFrame or CObservation. Found "
168  "class name: '%s'",
169  static_cast<unsigned int>(index), e->GetRuntimeClass()->className);
170  }
171 
172  return pc;
173 }
174 
175 static mp2p_icp::metric_map_t::Ptr load_input_pc(
176  const std::string& filename, bool local)
177 {
178  // rawlog?
179  if (auto extPos = filename.find(".rawlog:"); extPos != std::string::npos)
180  {
181  const auto sepPos = extPos + 7;
182  const auto fil = filename.substr(0, sepPos);
183  const auto rawlogIndex = std::stod(filename.substr(sepPos + 1));
184 
185  const auto r = load_rawlog(fil);
186 
187  return pc_from_rawlog(*r, rawlogIndex);
188  }
189 
190  // serialized metric_map_t object?
191  if (auto extPos = filename.find(".mm"); extPos != std::string::npos)
192  {
193  auto r = mp2p_icp::metric_map_t::Create();
194 
195  bool readOk = r->load_from_file(filename);
196  ASSERT_(readOk);
197 
198  return r;
199  }
200 
201  // icplog?
202  if (auto extPos = filename.find(".icplog"); extPos != std::string::npos)
203  {
205  bool icplogFileReadOK = lr.load_from_file(filename);
206  ASSERT_(icplogFileReadOK);
207 
208  auto r = mp2p_icp::metric_map_t::Create();
209  *r = local ? *lr.pcLocal : *lr.pcGlobal;
210  return r;
211  }
212 
213  // Otherwise: assume it's an ASCII point cloud file:
214  mrpt::maps::CSimplePointsMap::Ptr points =
215  mp2p_icp::load_xyz_file(filename);
216 
217  auto pc = mp2p_icp::metric_map_t::Create();
218  pc->layers[mp2p_icp::metric_map_t::PT_LAYER_RAW] = points;
219 
220  return pc;
221 }
222 
223 void runIcp()
224 {
225  const auto cfg =
226  mrpt::containers::yaml::FromFile(argYamlConfigFile.getValue());
227 
228  // ------------------------------
229  // Generators set
230  // ------------------------------
231  if (argYamlConfigFileGenerators.isSet())
232  {
233  const auto& f = argYamlConfigFileGenerators.getValue();
234 
236 
237  std::cout << "Created " << generators.size()
238  << " generators from: " << f << std::endl;
239  }
240  else if (cfg.has("generators"))
241  {
243  }
244 
245  // ------------------------------
246  // Original input point clouds
247  // ------------------------------
248  auto pcLocal = load_input_pc(argInputLocal.getValue(), true);
249  auto pcGlobal = load_input_pc(argInputGlobal.getValue(), false);
250 
251  std::cout << "Input point cloud #1: " << pcLocal->contents_summary()
252  << std::endl;
253  std::cout << "Input point cloud #2: " << pcGlobal->contents_summary()
254  << std::endl;
255 
256  // ------------------------------
257  // Build ICP pipeline:
258  // ------------------------------
259  auto [icp, icpParams] = mp2p_icp::icp_pipeline_from_yaml(cfg);
260 
261  if (argGenerateDebugFiles.isSet()) icpParams.generateDebugFiles = true;
262 
263  const auto initialGuess =
264  mrpt::math::TPose3D::FromString(argInitialGuess.getValue());
265 
266  // -----------------------------------------
267  // Apply filtering pipeline, if defined
268  // -----------------------------------------
269  {
271  if (argYamlConfigFileFiltersLocal.isSet())
272  {
274  argYamlConfigFileFiltersLocal.getValue());
275  }
276  else if (cfg.has(argCfgNameFiltersLocal.getValue()))
277  {
279  cfg[argCfgNameFiltersLocal.getValue()]);
280  }
281  if (!filtersLocal.empty())
282  {
283  mp2p_icp_filters::apply_filter_pipeline(filtersLocal, *pcLocal);
284  std::cout << "Filtered local map: " << pcLocal->contents_summary()
285  << std::endl;
286  }
287  }
288  {
289  mp2p_icp_filters::FilterPipeline filtersGlobal;
290  if (argYamlConfigFileFiltersGlobal.isSet())
291  {
293  argYamlConfigFileFiltersGlobal.getValue());
294  }
295  else if (cfg.has(argCfgNameFiltersGlobal.getValue()))
296  {
298  cfg[argCfgNameFiltersGlobal.getValue()]);
299  }
300  if (!filtersGlobal.empty())
301  {
302  mp2p_icp_filters::apply_filter_pipeline(filtersGlobal, *pcGlobal);
303  std::cout << "Filtered global map: " << pcGlobal->contents_summary()
304  << std::endl;
305  }
306  }
307 
308  const double t_ini = mrpt::Clock::nowDouble();
309 
310  mp2p_icp::Results icpResults;
311  icp->align(*pcLocal, *pcGlobal, initialGuess, icpParams, icpResults);
312 
313  const double t_end = mrpt::Clock::nowDouble();
314 
315  std::cout << "ICP result:\n";
316  icpResults.print(std::cout);
317 
318  std::cout << "- time to solve: "
319  << mrpt::system::formatTimeInterval(t_end - t_ini) << "\n";
320 }
321 
322 int main(int argc, char** argv)
323 {
324  try
325  {
326  // Parse arguments:
327  if (!cmd.parse(argc, argv)) return 1; // should exit.
328 
329  runIcp();
330  }
331  catch (const std::exception& e)
332  {
333  std::cerr << mrpt::exception_to_str(e);
334  return 1;
335  }
336  return 0;
337 }
metric_map_t::ConstPtr pcGlobal
Definition: LogRecord.h:42
FilterPipeline filter_pipeline_from_yaml(const mrpt::containers::yaml &c, const mrpt::system::VerbosityLevel &vLevel=mrpt::system::LVL_INFO)
Definition: FilterBase.cpp:34
Loads and setup an ICP pipeline from a YAML configuration file.
static TCLAP::CmdLine cmd("mp2p-icp-run")
static TCLAP::ValueArg< std::string > argInputLocal("", "input-local", "Local input point cloud/map." "It is interpreted as a rawlog entry if using the " "format `<RAWLOG_FILE.rawlog>:<N>` to select the N-th entry in the " "rawlog; otherwise, if the file extension is `.mm` it is loaded as a " "serialized metric_map_t object; if it is a `.icplog` file, the local map " "from that icp log is taken as input; in any other case, the file is " "assumed to be a 3D pointcloud stored as a Nx3 ASCII matrix file.", true, "pointcloud1.txt", "pointcloud1.txt", cmd)
GeneratorSet generators_from_yaml_file(const std::string &filename, const mrpt::system::VerbosityLevel &vLevel=mrpt::system::LVL_INFO)
Definition: Generator.cpp:285
static TCLAP::ValueArg< std::string > argYamlConfigFile("c", "config", "YAML config file describing the ICP pipeline. See docs:\ " https://docs.mola-slam.org/latest/" "module-mp2p-icp.html#yaml-pipeline-definition-files", true, "icp-config.yaml", "icp-config.yaml", cmd)
std::vector< Generator::Ptr > GeneratorSet
Definition: Generator.h:153
bool load_from_file(const std::string &fileName)
Definition: LogRecord.cpp:85
static TCLAP::ValueArg< std::string > argInputGlobal("", "input-global", "Global input point cloud/map. Same format than input-local. ", true, "pointcloud2.txt", "pointcloud2.txt", cmd)
Generic ICP algorithm container.
Base virtual class for point cloud filters.
metric_map_t::ConstPtr pcLocal
Definition: LogRecord.h:42
static TCLAP::ValueArg< std::string > argYamlConfigFileGenerators("", "config-generators", "YAML config file describing the Generators. Can be also defined via an " "entry `generators` in the main `--config` yaml file. " "Can be used when processing a rawlog as input; if not present, a default " "Generator object will be used.", false, "generators-config.yaml", "generators-config.yaml", cmd)
::std::string string
Definition: gtest.h:1979
static mp2p_icp::metric_map_t::Ptr load_input_pc(const std::string &filename, bool local)
Generic representation of pointcloud(s) and/or extracted features.
mrpt::maps::CSimplePointsMap::Ptr load_xyz_file(const std::string &fil)
static mp2p_icp::metric_map_t::Ptr pc_from_rawlog(const mrpt::obs::CRawlog &r, const size_t index)
Unit tests common utilities.
Base virtual class for point cloud filters.
static constexpr const char * PT_LAYER_RAW
Definition: metricmap.h:56
static TCLAP::ValueArg< std::string > argYamlConfigFileFiltersLocal("", "config-filters-local", "YAML config file describing a filtering pipeline for local map." "If not provided, and the main --config yaml file contains a " "`filters` entry (can be overriden with --entry-name-filters-local), it " "will be used instead.", false, "filters-config.yaml", "filters-config.yaml", cmd)
void apply_filter_pipeline(const FilterPipeline &filters, mp2p_icp::metric_map_t &inOut)
Definition: FilterBase.cpp:24
std::tuple< mp2p_icp::ICP::Ptr, mp2p_icp::Parameters > icp_pipeline_from_yaml(const mrpt::containers::yaml &config, const mrpt::system::VerbosityLevel &vLevel=mrpt::system::LVL_INFO)
int main(int argc, char **argv)
static mrpt::obs::CRawlog::Ptr load_rawlog(const std::string &filename)
void print(std::ostream &o) const
Definition: Results.cpp:50
static TCLAP::SwitchArg argGenerateDebugFiles("d", "generate-debug-log", "Enforces generation of the .icplog debug log files for posterior " "visualization with mp2p-icp-log-viewer, overriding the " "`generateDebugFiles` value in the configuration YAML file.", cmd)
FilterPipeline filter_pipeline_from_yaml_file(const std::string &filename, const mrpt::system::VerbosityLevel &vLevel=mrpt::system::LVL_INFO)
Definition: FilterBase.cpp:66
static TCLAP::ValueArg< std::string > argYamlConfigFileFiltersGlobal("", "config-filters-global", "YAML config file describing a filtering pipeline for global map." "If not provided, and the main --config yaml file contains a" "`filters` entry (can be overriden with --entry-name-filters-global), it " "will be used instead.", false, "filters-config.yaml", "filters-config.yaml", cmd)
static TCLAP::ValueArg< std::string > argCfgNameFiltersLocal("", "entry-name-filters-local", "Overrides the map name in the YAML configuration file for local map " "filter.", false, "filters", "filters", cmd)
GeneratorSet generators_from_yaml(const mrpt::containers::yaml &c, const mrpt::system::VerbosityLevel &vLevel=mrpt::system::LVL_INFO)
Definition: Generator.cpp:253
static mp2p_icp_filters::GeneratorSet generators
static TCLAP::ValueArg< std::string > argInitialGuess("", "guess", "SE(3) transformation of local wrt global, to use as initial guess for the " "ICP algorithm. " "Format: \x y z yaw_deg pitch_deg roll_deg]\, false, "[0 0 0 0 0 0]", "[0 0 0 0 0 0]", cmd)
std::vector< FilterBase::Ptr > FilterPipeline
Definition: FilterBase.h:61
static TCLAP::ValueArg< std::string > argCfgNameFiltersGlobal("", "entry-name-filters-global", "Overrides the map name in the YAML configuration file for global map " "filter.", false, "filters", "filters", cmd)
void runIcp()
void apply_generators(const GeneratorSet &generators, const mrpt::obs::CObservation &obs, mp2p_icp::metric_map_t &output)
Definition: Generator.cpp:209
static std::map< std::string, mrpt::obs::CRawlog::Ptr > rawlogsCache


mrpt_local_obstacles
Author(s): Jose-Luis Blanco-Claraco
autogenerated on Thu Jun 1 2023 03:06:43