19 #include <Eigen/Geometry>
22 #include <IceUtil/Time.h>
25 #include <pcl/common/transforms.h>
26 #include <pcl/io/pcd_io.h>
27 #include <pcl/point_cloud.h>
28 #include <pcl/point_types.h>
31 #include <opencv2/core/mat.hpp>
32 #include <opencv2/imgcodecs.hpp>
35 #include <SimoxUtility/json/json.hpp>
36 #include <SimoxUtility/math.h>
48 #include <cartographer/common/configuration_file_resolver.h>
49 #include <cartographer/common/time.h>
50 #include <cartographer/io/file_writer.h>
51 #include <cartographer/io/points_processor_pipeline_builder.h>
52 #include <cartographer/io/proto_stream.h>
53 #include <cartographer/mapping/2d/grid_2d.h>
54 #include <cartographer/mapping/2d/probability_grid.h>
55 #include <cartographer/mapping/2d/submap_2d.h>
56 #include <cartographer/mapping/internal/2d/tsdf_2d.h>
57 #include <cartographer/mapping/map_builder.h>
58 #include <cartographer/mapping/map_builder_interface.h>
59 #include <cartographer/mapping/submaps.h>
60 #include <cartographer/mapping/trajectory_builder_interface.h>
61 #include <cartographer/mapping/trajectory_node.h>
62 #include <cartographer/sensor/point_cloud.h>
63 #include <cartographer/sensor/rangefinder_point.h>
64 #include <cartographer/transform/rigid_transform.h>
65 #include <cartographer/transform/transform.h>
76 #include "util/laser_scanner_conversion.h"
78 namespace carto = ::cartographer;
82 namespace fs = ::std::filesystem;
90 struct tm* timeinfo {};
91 std::array < char, 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 > buffer;
94 timeinfo = localtime(&rawtime);
96 strftime(buffer.data(), buffer.size(),
"%Y-%m-%d-%H-%M", timeinfo);
113 mapBuilder->FinishTrajectory(trajectoryId);
122 const std::filesystem::path& mapPath,
123 const std::filesystem::path& configPath,
126 const std::optional<std::filesystem::path>& mapToLoad,
127 const std::optional<Eigen::Affine3f>& map_T_robot_prior)
128 : config(config), mapPath(mapPath), slamDataCallable(slamDataCallable)
130 const std::string mapBuilderLuaCode = R
"text(
131 include "map_builder.lua"
132 return MAP_BUILDER)text";
134 const std::string trajectoryBuilderLuaCode = R
"text(
135 include "trajectory_builder.lua"
136 return TRAJECTORY_BUILDER)text";
139 const auto mapBuilderOptions =
140 carto::mapping::CreateMapBuilderOptions(mapBuilderParameters.get());
142 const auto trajectoryBuilderParameters =
144 trajectoryBuilderOptions =
145 carto::mapping::CreateTrajectoryBuilderOptions(trajectoryBuilderParameters.get());
148 mapBuilder = carto::mapping::CreateMapBuilder(mapBuilderOptions);
152 sensorSet.insert(sensorIdLaser);
157 sensorSet.insert(sensorIdOdom);
162 sensorSet.insert(sensorIdIMU);
165 std::vector<std::string> sensorSetNames;
168 std::back_inserter(sensorSetNames),
169 [](
const auto & sensorId)
181 [&](
const auto & laserScannerName)
183 const SensorId sensorId{.type = SensorId::SensorType::RANGE,
184 .id = laserScannerName};
185 laserSensorMap.insert(
188 LaserScannerSensor(sensorId, Eigen::Affine3f::Identity())});
194 carto::io::ProtoStreamReader reader(mapToLoad->string());
195 mapBuilder->LoadState(&reader,
true );
196 mapBuilder->pose_graph()->RunFinalOptimization();
199 if (map_T_robot_prior)
201 ARMARX_INFO <<
"Using pose prior " << std::endl << map_T_robot_prior->matrix();
203 ::cartographer::mapping::proto::InitialTrajectoryPose* initial_trajectory_pose = new ::cartographer::mapping::proto::InitialTrajectoryPose;
205 initial_trajectory_pose->set_allocated_relative_pose(new ::cartographer::transform::proto::Rigid3d(::cartographer::transform::ToProto(
fromEigen(*map_T_robot_prior))));
207 trajectoryBuilderOptions.set_allocated_initial_trajectory_pose(std::move(initial_trajectory_pose));
213 trajectoryId = mapBuilder->AddTrajectoryBuilder(
214 sensorSet, trajectoryBuilderOptions, [ = ](
auto&& ...args)
216 return getLocalSlamResultCallback(std::forward<decltype(args)>(args)...);
220 const auto dt =
static_cast<int64_t
>(1
'000'000 / config.frequency);
221 const float dtHistoryLength = 1
'000'000;
225 approxTimeQueue = std::make_unique<ApproximateTimeQueue>(
dt, dtHistoryLength, config.laserScanners, *
this);
228 void CartographerAdapter::onTimedDataAvailable(
const TimedData& data)
241 auto isOdomSensor = [](
const SensorId & sensorId)
243 return sensorId.type == SensorId::SensorType::ODOMETRY;
246 if (not std::any_of(sensorSet.begin(), sensorSet.end(), isOdomSensor))
255 const Eigen::Vector3f p = odomPose.
pose.translation();
257 carto::sensor::OdometryData odomData;
258 odomData.pose = carto::transform::Rigid3d::FromArrays(
259 std::array<double, 4> {
q.w(),
q.x(),
q.y(),
q.z()},
260 std::array<double, 3> {p.x(), p.y(), p.z()});
266 std::lock_guard guard{cartoInsertMtx};
267 mapBuilder->GetTrajectoryBuilder(trajectoryId)
268 ->AddSensorData(sensorIdOdom.id, odomData);
274 void CartographerAdapter::pushInLaserData(
const LaserMessages& laserMessages)
277 carto::sensor::TimedPointCloudData timedPointCloudData;
278 timedPointCloudData.origin = Eigen::Vector3f::Zero();
280 for (
const auto&
message : laserMessages)
282 const auto& data =
message.data;
285 const LaserScannerSensor& sensor = lookupSensor(data.frame);
287 ARMARX_DEBUG <<
"Sensor " << data.frame <<
" position is "
288 << sensor.pose.translation();
290 const Eigen::Affine3f robot_T_sensor = sensor.pose;
293 timedPointCloudData.ranges.reserve(timedPointCloudData.ranges.size() +
296 ARMARX_DEBUG <<
"Laser range data: " << data.scan.size() <<
" points";
300 std::back_inserter(timedPointCloudData.ranges),
301 [&](
const auto & scanStep)
302 -> carto::sensor::TimedRangefinderPoint
304 auto rangePoint = toCartesian<Eigen::Vector3f>(scanStep);
307 carto::sensor::TimedRangefinderPoint point;
309 point.position = robot_T_sensor* rangePoint;
315 auto cloud =
toPCL(timedPointCloudData.ranges);
319 std::lock_guard guard{cartoInsertMtx};
321 auto trajBuilder = mapBuilder->GetTrajectoryBuilder(trajectoryId);
328 trajBuilder->AddSensorData(sensorIdLaser.id, timedPointCloudData);
334 void CartographerAdapter::processOdometryPose(
OdomData odom)
339 odomPose.
pose.setIdentity();
343 approxTimeQueue->insertOdomData(std::move(odomPose));
352 auto isSensorRegistered = [&](
const std::string & sensorId)
355 sensorSet.begin(), sensorSet.end(), [&sensorId](
const SensorId &
id) ->
bool
357 return id.id == sensorId;
361 if (
false and not isSensorRegistered(data.
frame))
364 <<
" not registered";
368 approxTimeQueue->insertLaserData(std::move(data));
374 void CartographerAdapter::getLocalSlamResultCallback(
375 const int trajectoryId,
377 const ::cartographer::transform::Rigid3d localPose,
378 ::cartographer::sensor::RangeData rangeDataInLocal,
379 const std::unique_ptr <
380 const ::cartographer::mapping::TrajectoryBuilderInterface::InsertionResult >
385 ARMARX_DEBUG <<
"Callback (getLocalSlamResultCallback)";
406 const auto timestampInMicroSeconds =
fromCarto(time);
409 << IceUtil::Time::microSeconds(timestampInMicroSeconds);
412 localSlamData.
misses =
toPCL(rangeDataInLocal.misses.points());
414 localSlamData.
timestamp = timestampInMicroSeconds;
416 toEigen(mapBuilder->pose_graph()->GetLocalToGlobalTransform(trajectoryId));
418 ARMARX_DEBUG <<
"Callbacks will be triggered (getLocalSlamResultCallback)";
420 slamDataCallable.onLocalSlamData(localSlamData);
423 ARMARX_DEBUG <<
"Callback done (getLocalSlamResultCallback)";
432 bool CartographerAdapter::hasReceivedSensorData() const noexcept
434 std::lock_guard g{cartoInsertMtx};
436 const auto trajectoryNodes = mapBuilder->pose_graph()->GetTrajectoryNodes();
438 auto matchesTrajectoryId = [&](
const auto & node) ->
bool
440 return node.id.trajectory_id == trajectoryId;
444 return std::any_of(trajectoryNodes.begin(), trajectoryNodes.end(), matchesTrajectoryId);
449 std::ofstream ofs(jsonFile, std::ios::out);
459 ARMARX_INFO <<
"Creating dummy registration file `" << jsonFile <<
"`";
465 bool CartographerAdapter::createMap()
467 const std::string filenameNoExtension =
timestamp();
469 const fs::path mapFilename = mapPath / (filenameNoExtension +
".carto");
470 ARMARX_DEBUG <<
"The concrete map file is " << mapFilename.string();
471 assert(!fs::exists(map_filename));
476 const int trajectoryIdToBeOptimized = trajectoryId;
478 std::lock_guard g{cartoInsertMtx};
480 trajectoryId = mapBuilder->AddTrajectoryBuilder(
481 sensorSet, trajectoryBuilderOptions, [&](
auto... args)
483 return getLocalSlamResultCallback(std::forward<decltype(args)>(args)...);
487 mapBuilder->FinishTrajectory(trajectoryIdToBeOptimized);
488 mapBuilder->pose_graph()->RunFinalOptimization();
491 carto::io::ProtoStreamWriter writer(mapFilename);
492 mapBuilder->SerializeState(
true, &writer);
497 const fs::path registrationFilename = mapPath / (filenameNoExtension +
".json");
498 ARMARX_INFO <<
"The default registration data will be stored in " << registrationFilename;
508 CartographerAdapter::lookupSensor(
const std::string& device)
const
510 return laserSensorMap.at(device);
513 void CartographerAdapter::setSensorPose(
const std::string& sensorId,
514 const Eigen::Affine3f& pose)
516 laserSensorMap.at(sensorId).
pose = pose;
519 std::vector<std::string> CartographerAdapter::laserSensorIds()
const