34#include <pcl/common/transforms.h>
35#include <pcl/io/pcd_io.h>
36#include <pcl/point_cloud.h>
38#include <SimoxUtility/json.h>
39#include <SimoxUtility/shapes/OrientedBox.h>
40#include <VirtualRobot/MathTools.h>
47#include <RobotAPI/libraries/armem_vision/client/laser_scans/Reader.h>
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"
57namespace fs = std::filesystem;
77 def->component(priorKnowledge);
78 def->component(workingMemory);
79 def->component(robotStateComponent);
81 def->component(remoteGuiInterface,
83 "remote_gui_provider",
84 "Name of the remote gui provider");
87 def->required(properties.mapToLoad,
"p.carto.mapToLoad",
"");
90 def->optional(properties.cartographerConfigPath,
"p.carto.configPath",
"");
91 def->optional(properties.mapPath,
"p.carto.mapPath",
"");
93 def->optional(properties.minLaserRange,
94 "p.algo.minLaserRange",
95 "All points closer to the robot than 'minLaserRange' will be removed from "
98 mappingDataReader->registerPropertyDefinitions(def);
104 mappingDataReader(
std::make_unique<
armem::vision::laser_scans::client::Reader>(*this)),
123 mappingDataReader->connect();
152 remoteGui = std::make_unique<armarx::cartographer_map_registration::RemoteGui>(
153 remoteGuiInterface, *
this);
171 return "CartographerMapRegistration";
174 std::vector<TimeRange>
177 const int64_t duration = timeRange.duration().toMicroSeconds();
178 const size_t nSplits = std::ceil(
static_cast<float>(duration) / maxDuration);
180 std::vector<TimeRange> timeRanges;
182 for (
size_t i = 0; i < nSplits; i++)
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)))};
189 tr.max = std::min(tr.max, timeRange.max);
191 timeRanges.push_back(tr);
202 arvizDrawer.drawFrames(world_T_map);
204 const std::string configSubfolder =
"mapping";
207 const auto configPath =
215 const fs::path mapToLoad = mapPath / properties.mapToLoad;
221 mapRegistration = std::make_unique<MapRegistration>(loadMap(mapToLoad, configPath));
223 const auto robotPoses = mapRegistration->robotPoses();
224 arvizDrawer.drawRobotPoses(robotPoses, world_T_map);
234 const TimeRange fullTimeRange = mapRegistration->mapTimeRange();
235 const auto timeRanges =
splitTimeRange(fullTimeRange, 10'000'000'000);
239 const std::string agentName = robot->getName();
241 for (
const auto& timeRange : timeRanges)
243 const armem::vision::laser_scans::client::Reader::Query query{
245 .timeRange = {timeRange.min, timeRange.max},
249 const armem::vision::laser_scans::client::Reader::Result result =
250 mappingDataReader->query(query);
255 const auto& laserScans = result.laserScans;
259 const auto& sensorFrames = result.sensors;
261 const auto sensorPose = [=](
const std::string& sensorFrame) -> Eigen::Affine3f
262 {
return Eigen::Affine3f(robot->getRobotNode(sensorFrame)->getPoseInRootFrame()); };
265 sensorPoseMap.reserve(sensorFrames.size());
267 std::transform(sensorFrames.begin(),
269 std::inserter(sensorPoseMap, sensorPoseMap.end()),
270 [&](
const std::string& sensorFrame)
271 { return std::make_pair(sensorFrame, sensorPose(sensorFrame)); });
273 aggregator = std::make_unique<LaserScanAggregator>(
274 sensorPoseMap, mapRegistration->robotPoseQueue(), properties.minLaserRange);
277 aggregator->add(laserScans);
282 ARMARX_INFO <<
"Point cloud size is " << aggregator->pointCloud()->size();
284 pcl::io::savePCDFile(
"/home/fabi/cartographer-registration-full.pcd",
285 *aggregator->pointCloud());
287 mapRegistration->computeBoundingBox(aggregator->pointCloud());
315 CartographerMapRegistration::selectedBoundingBoxCorner()
const
317 const armarx::cartographer::RotatedRect bb = mapRegistration->getBoundingBox();
318 return bb.cornerPose(boundingBoxCorner);
322 CartographerMapRegistration::updateArViz()
329 const auto robotPoses = mapRegistration->robotPoses();
337 arvizDrawer.
drawPointCloud(*aggregator->pointCloud(), world_T_map);
340 arvizDrawer.drawModelAssociations(associations, world_T_map);
348 boundingBoxCorner = i % 4;
359 world_T_map = Eigen::Translation3f(-3600, -200, 0) * selectedBoundingBoxCorner().inverse();
370 constexpr float widthPillars = 410.F;
371 constexpr float distancePillars = 3700.F + widthPillars;
377 models = std::vector<wykobi::Model>{pillarFront, pillarBack};
379 arvizDrawer.drawModels(models);
383 auto clusters = mapRegistration->detectClusters(aggregator->pointCloud());
384 arvizDrawer.drawClusters(clusters, world_T_map);
386 associations = mapRegistration->matchModelsToClusters(models, clusters, world_T_map);
396 std::vector<MapRegistration::ModelCorrection> corrections;
398 const MapRegistration::ModelCorrection combinedCorrection =
399 mapRegistration->computeCorrection(associations);
401 world_T_map = combinedCorrection.correction.inverse();
403 std::vector<MapRegistration::Cluster> clusters;
404 std::transform(associations.begin(),
406 std::back_inserter(clusters),
407 [](
const MapRegistration::Association& assoc)
408 { return MapRegistration::Cluster(assoc.cluster); });
410 arvizDrawer.drawClusters(clusters, world_T_map);
420 std::vector<MapRegistration::ModelCorrection> corrections;
423 for (
const auto& association : associations)
425 MapRegistration::ModelCorrection mc = mapRegistration->alignModelToCluster(
426 association.model, association.cluster, world_T_map);
427 corrections.push_back(mc);
429 ARMARX_INFO <<
"Correction" << mc.correction.translation();
434 MapRegistration::ModelCorrection combinedCorrection =
435 mapRegistration->computeCombinedCorrection(models, corrections);
437 world_T_map = world_T_map * combinedCorrection.correction.inverse();
439 std::vector<MapRegistration::Cluster> clusters;
440 std::transform(associations.begin(),
442 std::back_inserter(clusters),
443 [](
const MapRegistration::Association& assoc)
444 { return MapRegistration::Cluster(assoc.cluster); });
446 arvizDrawer.drawClusters(clusters, world_T_map);
457 fs::path jsonFilename = properties.mapToLoad;
458 jsonFilename.replace_extension(
"json");
459 const fs::path jsonStorePath = mapPath / jsonFilename;
461 ARMARX_IMPORTANT <<
"Storing registration result: " << jsonStorePath.string();
464 j[
"x"] =
static_cast<float>(world_T_map.translation().
x());
465 j[
"y"] =
static_cast<float>(world_T_map.translation().y());
467 static_cast<float>(VirtualRobot::MathTools::eigen3f2rpy(world_T_map.linear()).z());
469 std::ofstream o(jsonStorePath);
armarx::viz::Client arviz
static std::string resolvePath(const std::string &path, bool verbose=true)
Resolves environment variables and home paths and tries to make path absolute.
Default component property definition container.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
std::unordered_map< std::string, Eigen::Isometry3f > SensorPoseMap
VirtualRobot::RobotPtr createLocalClone()
Clones the structure of this remote robot to a local instance.
static bool synchronizeLocalClone(VirtualRobot::RobotPtr robot, RobotStateComponentInterfacePrx robotStatePrx)
void drawMapBoundaries(const RotatedRect &boundingRect, const Eigen::Affine3f &world_T_map)
void drawPointCloud(const MapRegistration::PointCloud &cloud, const Eigen::Affine3f &world_T_map)
void drawSelectedBoundingBoxCorner(const Eigen::Affine3f &boundingBoxCornerPose, const Eigen::Affine3f &world_T_map)
void drawFrames(const Eigen::Affine3f &world_T_map)
void drawRobotPoses(const std::vector< Eigen::Affine3f > &robotPoses, const Eigen::Affine3f &world_T_map)
void onInitComponent() override
void findAssociations() override
void onDisconnectComponent() override
virtual ~CartographerMapRegistration()
void loadDataFromMemory() override
void computeCoarseCorrection() override
CartographerMapRegistration()
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void loadGraph() override
void onConnectComponent() override
void alignBoundingBox() override
void computeFineCorrection() override
void onExitComponent() override
void storeRegistrationResult() override
std::string getDefaultName() const override
void selectBoundingBoxCorner(int i) override
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
std::vector< TimeRange > splitTimeRange(const TimeRange &timeRange, const int64_t &maxDuration)
Model makeCube(const Eigen::Vector2f &position, const float width)
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.