CartographerMapRegistration.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package RobotComponents::ArmarXObjects::CartographerMapRegistration
17  * @author Fabian Reister ( fabian dot reister at kit dot edu )
18  * @date 2021
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
24 
25 #include <algorithm>
26 #include <cmath>
27 #include <cstddef>
28 #include <filesystem>
29 #include <iterator>
30 #include <memory>
31 #include <numeric>
32 #include <utility>
33 
34 #include <pcl/common/transforms.h>
35 #include <pcl/io/pcd_io.h>
36 #include <pcl/point_cloud.h>
37 
38 #include <SimoxUtility/json.h>
39 #include <SimoxUtility/shapes/OrientedBox.h>
40 #include <VirtualRobot/MathTools.h>
41 
45 
47 #include <RobotAPI/libraries/armem_vision/client/laser_scans/Reader.h>
49 
50 #include "RobotComponents/components/CartographerMappingAndLocalization/CartographerMappingAndLocalization.h"
51 #include "RobotComponents/libraries/cartographer/map_registration/LaserScanAggregator.h"
52 #include "RobotComponents/libraries/cartographer/map_registration/MapRegistration.h"
53 #include "RobotComponents/libraries/cartographer/map_registration/models.h"
54 #include "RobotComponents/libraries/cartographer/util/cartographer_utils.h"
55 #include "RobotComponents/libraries/cartographer/util/laser_scanner_conversion.h"
56 
57 namespace fs = std::filesystem;
58 
59 namespace armarx::cartographer
60 {
61 
63 
66  {
69 
70  // Publish to a topic (passing the TopicListenerPrx).
71  // def->topic(myTopicListener);
72 
73  // Subscribe to a topic (passing the topic name).
74  // def->topic<PlatformUnitListener>("MyTopic");
75 
76  // Use (and depend on) another component (passing the ComponentInterfacePrx).
77  def->component(priorKnowledge);
78  def->component(workingMemory);
79  def->component(robotStateComponent);
80 
81  def->component(remoteGuiInterface,
82  "RemoteGuiProvider",
83  "remote_gui_provider",
84  "Name of the remote gui provider");
85 
86  // Add a required property.
87  def->required(properties.mapToLoad, "p.carto.mapToLoad", "");
88 
89  // Add an optional property.
90  def->optional(properties.cartographerConfigPath, "p.carto.configPath", "");
91  def->optional(properties.mapPath, "p.carto.mapPath", "");
92 
93  def->optional(properties.minLaserRange,
94  "p.algo.minLaserRange",
95  "All points closer to the robot than 'minLaserRange' will be removed from "
96  "the point cloud.");
97 
98  mappingDataReader->registerPropertyDefinitions(def);
99 
100  return def;
101  }
102 
104  mappingDataReader(std::make_unique<armem::vision::laser_scans::client::Reader>(*this)),
105  arvizDrawer(arviz)
106  {
107  }
108 
109  void
111  {
112  // Topics and properties defined above are automagically registered.
113 
114  // Keep debug observer data until calling `sendDebugObserverBatch()`.
115  // (Requies the armarx::DebugObserverComponentPluginUser.)
116  // setDebugObserverBatchModeEnabled(true);
117  }
118 
119  void
121  {
122  // establish connection to memory
123  mappingDataReader->connect();
124 
125  // Do things after connecting to topics and components.
126 
127  /* (Requies the armarx::DebugObserverComponentPluginUser.)
128  // Use the debug observer to log data over time.
129  // The data can be viewed in the ObserverView and the LivePlotter.
130  // (Before starting any threads, we don't need to lock mutexes.)
131  {
132  setDebugObserverDatafield("numBoxes", properties.numBoxes);
133  setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
134  sendDebugObserverBatch();
135  }
136  */
137 
138  /* (Requires the armarx::ArVizComponentPluginUser.)
139  // Draw boxes in ArViz.
140  // (Before starting any threads, we don't need to lock mutexes.)
141  drawBoxes(properties, arviz);
142  */
143 
144  /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
145  // Setup the remote GUI.
146  {
147  createRemoteGuiTab();
148  RemoteGui_startRunningTask();
149  }
150  */
151 
152  remoteGui = std::make_unique<armarx::cartographer_map_registration::RemoteGui>(
153  remoteGuiInterface, *this);
154 
155  arvizDrawer.clear();
156  }
157 
158  void
160  {
161  }
162 
163  void
165  {
166  }
167 
168  std::string
170  {
171  return "CartographerMapRegistration";
172  }
173 
174  std::vector<TimeRange>
175  splitTimeRange(const TimeRange& timeRange, const int64_t& maxDuration)
176  {
177  const int64_t duration = timeRange.duration().toMicroSeconds();
178  const size_t nSplits = std::ceil(static_cast<float>(duration) / maxDuration);
179 
180  std::vector<TimeRange> timeRanges;
181 
182  for (size_t i = 0; i < nSplits; i++)
183  {
184  TimeRange tr{.min = IceUtil::Time::microSeconds(static_cast<int64_t>(
185  timeRange.min.toMicroSeconds() + maxDuration * i)),
186  .max = IceUtil::Time::microSeconds(static_cast<int64_t>(
187  timeRange.min.toMicroSeconds() + maxDuration * (i + 1)))};
188 
189  tr.max = std::min(tr.max, timeRange.max);
190 
191  timeRanges.push_back(tr);
192  }
193 
194  return timeRanges;
195  }
196 
197  void
199  {
200  ARMARX_IMPORTANT << "Loading SLAM graph";
201 
202  arvizDrawer.drawFrames(world_T_map);
203 
204  const std::string configSubfolder = "mapping";
205  const fs::path mapPath = ArmarXDataPath::resolvePath(properties.mapPath);
206 
207  const auto configPath =
208  fs::path(ArmarXDataPath::resolvePath(properties.cartographerConfigPath)) /
209  configSubfolder;
210 
211  ARMARX_IMPORTANT << "Using config files from " << configPath;
212  ARMARX_CHECK(fs::is_directory(configPath));
213 
214  // load the map in localization mode
215  const fs::path mapToLoad = mapPath / properties.mapToLoad;
216 
217  // (1) get the map name / load the map
218  ARMARX_IMPORTANT << "Loading map from " << mapToLoad.string();
219  ARMARX_CHECK(fs::is_regular_file(mapToLoad));
220 
221  mapRegistration = std::make_unique<MapRegistration>(loadMap(mapToLoad, configPath));
222 
223  const auto robotPoses = mapRegistration->robotPoses();
224  arvizDrawer.drawRobotPoses(robotPoses, world_T_map);
225  }
226 
227  void
229  {
230 
231  // (2) get all data from the memory
232  ARMARX_IMPORTANT << "Getting sensor data from memory";
233 
234  const TimeRange fullTimeRange = mapRegistration->mapTimeRange();
235  const auto timeRanges = splitTimeRange(fullTimeRange, 10'000'000'000);
236 
237  auto robot = RemoteRobot::createLocalClone(robotStateComponent);
238  RemoteRobot::synchronizeLocalClone(robot, robotStateComponent);
239  const std::string agentName = robot->getName();
240 
241  for (const auto& timeRange : timeRanges)
242  {
243  const armem::vision::laser_scans::client::Reader::Query query{
244  .agent = agentName,
245  .timeRange = {timeRange.min, timeRange.max},
246  .sensorList = {} // all
247  };
248 
249  const armem::vision::laser_scans::client::Reader::Result result =
250  mappingDataReader->query(query);
251 
252  ARMARX_IMPORTANT << "Received data from memory";
253  ARMARX_IMPORTANT << "Number of point clouds: " << result.laserScans.size();
254 
255  const auto& laserScans = result.laserScans;
256 
257  if (not aggregator)
258  {
259  const auto& sensorFrames = result.sensors;
260 
261  const auto sensorPose = [=](const std::string& sensorFrame) -> Eigen::Affine3f
262  { return Eigen::Affine3f(robot->getRobotNode(sensorFrame)->getPoseInRootFrame()); };
263 
264  LaserScanAggregator::SensorPoseMap sensorPoseMap;
265  sensorPoseMap.reserve(sensorFrames.size());
266 
267  std::transform(sensorFrames.begin(),
268  sensorFrames.end(),
269  std::inserter(sensorPoseMap, sensorPoseMap.end()),
270  [&](const std::string& sensorFrame)
271  { return std::make_pair(sensorFrame, sensorPose(sensorFrame)); });
272 
273  aggregator = std::make_unique<LaserScanAggregator>(
274  sensorPoseMap, mapRegistration->robotPoseQueue(), properties.minLaserRange);
275  }
276 
277  aggregator->add(laserScans);
278  }
279 
280  ARMARX_CHECK_NOT_NULL(aggregator);
281 
282  ARMARX_INFO << "Point cloud size is " << aggregator->pointCloud()->size();
283 
284  pcl::io::savePCDFile("/home/fabi/cartographer-registration-full.pcd",
285  *aggregator->pointCloud());
286 
287  mapRegistration->computeBoundingBox(aggregator->pointCloud());
288 
289  // update the data that we have so far
290  updateArViz();
291 
292  /************************/
293 
294  // mapRegistration.visualizeResult(cloud, models, combinedCorrection);
295 
296  // const auto slicedObjects = getSlicedObjectsFromPriorKnowledge();
297 
298  // const auto pointCloudData = getPointCloudsFromMemory(timeRange);
299  // mapRegistration.aggregate(pointCloudData);
300 
301  // (2.1) "static" objects from prior knowledge + slice objects
302  // (2.2) point clouds (SLAM data) from ArMem
303 
304  // mapRegistration.align(slicedObjects);
305 
306  // (3) do the magic. align to map
307 
308  // (4) update working memory
309  // (5) update prior knowledge (create snapshot??)
310 
311  updateArViz();
312  }
313 
314  Eigen::Affine3f
315  CartographerMapRegistration::selectedBoundingBoxCorner() const
316  {
317  const armarx::cartographer::RotatedRect bb = mapRegistration->getBoundingBox();
318  return bb.cornerPose(boundingBoxCorner);
319  }
320 
321  void
322  CartographerMapRegistration::updateArViz()
323  {
324  arvizDrawer.drawFrames(world_T_map);
325 
326  // update the data that we have so far
327  if (mapRegistration)
328  {
329  const auto robotPoses = mapRegistration->robotPoses();
330  arvizDrawer.drawRobotPoses(robotPoses, world_T_map);
331  arvizDrawer.drawMapBoundaries(mapRegistration->getBoundingBox(), world_T_map);
332  arvizDrawer.drawSelectedBoundingBoxCorner(selectedBoundingBoxCorner(), world_T_map);
333  }
334 
335  if (aggregator)
336  {
337  arvizDrawer.drawPointCloud(*aggregator->pointCloud(), world_T_map);
338  }
339 
340  arvizDrawer.drawModelAssociations(associations, world_T_map);
341  }
342 
343  void
344  CartographerMapRegistration::selectBoundingBoxCorner(int i)
345  {
346  ARMARX_IMPORTANT << "Selecting bounding box corner " << i;
347 
348  boundingBoxCorner = i % 4;
349 
350  updateArViz();
351  }
352 
353  void
354  CartographerMapRegistration::alignBoundingBox()
355  {
356  ARMARX_IMPORTANT << "Aligning using bounding box";
357 
358  // update prior based on bounding rect
359  world_T_map = Eigen::Translation3f(-3600, -200, 0) * selectedBoundingBoxCorner().inverse();
360 
361  updateArViz();
362  }
363 
364  void
365  CartographerMapRegistration::findAssociations()
366  {
367  ARMARX_IMPORTANT << "Finding associations";
368 
369  // TODO(fabian.reister): this could also be "loadScene" (until drawModels())
370  constexpr float widthPillars = 410.F;
371  constexpr float distancePillars = 3700.F + widthPillars;
372 
373  const wykobi::Model pillarBack = wykobi::makeCube({-570, 3800}, widthPillars);
374  const wykobi::Model pillarFront =
375  wykobi::makeCube({-590, 3800 + distancePillars}, widthPillars);
376 
377  models = std::vector<wykobi::Model>{pillarFront, pillarBack};
378 
379  arvizDrawer.drawModels(models);
380 
381  // find matches
382 
383  auto clusters = mapRegistration->detectClusters(aggregator->pointCloud());
384  arvizDrawer.drawClusters(clusters, world_T_map);
385 
386  associations = mapRegistration->matchModelsToClusters(models, clusters, world_T_map);
387 
388  updateArViz();
389  }
390 
391  void
392  CartographerMapRegistration::computeCoarseCorrection()
393  {
394  ARMARX_IMPORTANT << "Computing local coarse correction";
395 
396  std::vector<MapRegistration::ModelCorrection> corrections;
397 
398  const MapRegistration::ModelCorrection combinedCorrection =
399  mapRegistration->computeCorrection(associations);
400 
401  world_T_map = combinedCorrection.correction.inverse();
402 
403  std::vector<MapRegistration::Cluster> clusters;
404  std::transform(associations.begin(),
405  associations.end(),
406  std::back_inserter(clusters),
407  [](const MapRegistration::Association& assoc)
408  { return MapRegistration::Cluster(assoc.cluster); });
409 
410  arvizDrawer.drawClusters(clusters, world_T_map); // clear clusters
411 
412  updateArViz();
413  }
414 
415  void
416  CartographerMapRegistration::computeFineCorrection()
417  {
418  ARMARX_IMPORTANT << "Computing local correction";
419 
420  std::vector<MapRegistration::ModelCorrection> corrections;
421 
422  int j = 0;
423  for (const auto& association : associations)
424  {
425  MapRegistration::ModelCorrection mc = mapRegistration->alignModelToCluster(
426  association.model, association.cluster, world_T_map);
427  corrections.push_back(mc);
428 
429  ARMARX_INFO << "Correction" << mc.correction.translation();
430 
431  j++;
432  }
433 
434  MapRegistration::ModelCorrection combinedCorrection =
435  mapRegistration->computeCombinedCorrection(models, corrections);
436 
437  world_T_map = world_T_map * combinedCorrection.correction.inverse();
438 
439  std::vector<MapRegistration::Cluster> clusters;
440  std::transform(associations.begin(),
441  associations.end(),
442  std::back_inserter(clusters),
443  [](const MapRegistration::Association& assoc)
444  { return MapRegistration::Cluster(assoc.cluster); });
445 
446  arvizDrawer.drawClusters(clusters, world_T_map); // clear clusters
447 
448  updateArViz();
449  }
450 
451  void
452  CartographerMapRegistration::storeRegistrationResult()
453  {
454  // store data alongside the map file
455  const fs::path mapPath = ArmarXDataPath::resolvePath(properties.mapPath);
456 
457  fs::path jsonFilename = properties.mapToLoad;
458  jsonFilename.replace_extension("json");
459  const fs::path jsonStorePath = mapPath / jsonFilename;
460 
461  ARMARX_IMPORTANT << "Storing registration result: " << jsonStorePath.string();
462 
463  nlohmann::json j;
464  j["x"] = static_cast<float>(world_T_map.translation().x());
465  j["y"] = static_cast<float>(world_T_map.translation().y());
466  j["yaw"] =
467  static_cast<float>(VirtualRobot::MathTools::eigen3f2rpy(world_T_map.linear()).z());
468 
469  std::ofstream o(jsonStorePath);
470  o << j;
471  }
472 
473 } // namespace armarx::cartographer
armarx::ArmarXDataPath::resolvePath
static std::string resolvePath(const std::string &path, bool verbose=true)
Resolves environment variables and home paths and tries to make path absolute.
Definition: ArmarXDataPath.cpp:538
RemoteRobot.h
armarx::cartographer::CartographerMapRegistration::onConnectComponent
void onConnectComponent() override
Definition: CartographerMapRegistration.cpp:120
armarx::localization_and_mapping::cartographer_adapter::util::loadMap
std::unique_ptr<::cartographer::mapping::MapBuilderInterface > loadMap(const std::filesystem::path &mapPath, const std::filesystem::path &configPath)
Creates a map builder object from a stored map.
Definition: cartographer_utils.cpp:45
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:190
armarx::cartographer::ArVizDrawer::drawFrames
void drawFrames(const Eigen::Affine3f &world_T_map)
Definition: ArVizDrawer.cpp:57
armarx::armem
Definition: LegacyRobotStateMemoryAdapter.cpp:32
armarx::cartographer::splitTimeRange
std::vector< TimeRange > splitTimeRange(const TimeRange &timeRange, const int64_t &maxDuration)
Definition: CartographerMapRegistration.cpp:175
armarx::cartographer::CartographerMapRegistration::~CartographerMapRegistration
virtual ~CartographerMapRegistration()
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::cartographer::CartographerMapRegistration::onInitComponent
void onInitComponent() override
Definition: CartographerMapRegistration.cpp:110
armarx::cartographer
Definition: MapRegistration.cpp:42
CartographerMapRegistration.h
armarx::cartographer::ArVizDrawer::clear
void clear()
Definition: ArVizDrawer.cpp:38
armarx::cartographer::CartographerMapRegistration::getDefaultName
std::string getDefaultName() const override
Definition: CartographerMapRegistration.cpp:169
armarx::cartographer::CartographerMapRegistration::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: CartographerMapRegistration.cpp:65
armarx::armem::client::query_fns::timeRange
std::function< void(query::SnapshotSelector &)> timeRange(Time min, Time max)
Definition: query_fns.h:93
armarx::cartographer::CartographerMapRegistration::onExitComponent
void onExitComponent() override
Definition: CartographerMapRegistration.cpp:164
ExpressionException.h
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:79
armarx::cartographer::ArVizDrawer::drawRobotPoses
void drawRobotPoses(const std::vector< Eigen::Affine3f > &robotPoses, const Eigen::Affine3f &world_T_map)
Definition: ArVizDrawer.cpp:162
armarx::cartographer::CartographerMapRegistration::CartographerMapRegistration
CartographerMapRegistration()
Definition: CartographerMapRegistration.cpp:103
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:69
std
Definition: Application.h:66
IceUtil::Handle< class PropertyDefinitionContainer >
types.h
armarx::cartographer::CartographerMapRegistration::loadDataFromMemory
void loadDataFromMemory() override
Definition: CartographerMapRegistration.cpp:228
Logging.h
armarx::cartographer::CartographerMapRegistration::loadGraph
void loadGraph() override
Definition: CartographerMapRegistration.cpp:198
min
T min(T t1, T t2)
Definition: gdiam.h:44
httplib::detail::make_unique
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
Definition: httplib.h:333
ArmarXDataPath.h
armarx::cartographer::CartographerMapRegistration::onDisconnectComponent
void onDisconnectComponent() override
Definition: CartographerMapRegistration.cpp:159