diff --git a/cartographer/mapping/internal/2d/local_trajectory_builder_2d.cc b/cartographer/mapping/internal/2d/local_trajectory_builder_2d.cc index 54eb614..81fef4f 100644 --- a/cartographer/mapping/internal/2d/local_trajectory_builder_2d.cc +++ b/cartographer/mapping/internal/2d/local_trajectory_builder_2d.cc @@ -34,13 +34,15 @@ static auto* kScanMatcherResidualDistanceMetric = metrics::Histogram::Null(); static auto* kScanMatcherResidualAngleMetric = metrics::Histogram::Null(); LocalTrajectoryBuilder2D::LocalTrajectoryBuilder2D( - const proto::LocalTrajectoryBuilderOptions2D& options) + const proto::LocalTrajectoryBuilderOptions2D& options, + const std::vector& expected_range_sensor_ids) : options_(options), active_submaps_(options.submaps_options()), motion_filter_(options_.motion_filter_options()), real_time_correlative_scan_matcher_( options_.real_time_correlative_scan_matcher_options()), - ceres_scan_matcher_(options_.ceres_scan_matcher_options()) {} + ceres_scan_matcher_(options_.ceres_scan_matcher_options()), + range_data_collator_(expected_range_sensor_ids) {} LocalTrajectoryBuilder2D::~LocalTrajectoryBuilder2D() {} @@ -101,7 +103,16 @@ std::unique_ptr LocalTrajectoryBuilder2D::ScanMatch( std::unique_ptr LocalTrajectoryBuilder2D::AddRangeData( - const common::Time time, const sensor::TimedRangeData& range_data) { + const std::string& sensor_id, + const sensor::TimedPointCloudData& unsynchronized_data) { + auto synchronized_data = + range_data_collator_.AddRangeData(sensor_id, unsynchronized_data); + if (synchronized_data.ranges.empty()) { + LOG(INFO) << "Range data collator filling buffer."; + return nullptr; + } + + const common::Time& time = synchronized_data.time; // Initialize extrapolator now if we do not ever use an IMU. if (!options_.use_imu_data()) { InitializeExtrapolator(time); @@ -114,10 +125,12 @@ LocalTrajectoryBuilder2D::AddRangeData( return nullptr; } - CHECK(!range_data.returns.empty()); - CHECK_EQ(range_data.returns.back()[3], 0); + CHECK(!synchronized_data.ranges.empty()); + // TODO(gaschler): Check if this can strictly be 0. + CHECK_LE(synchronized_data.ranges.back().point_time[3], 0.f); const common::Time time_first_point = - time + common::FromSeconds(range_data.returns.front()[3]); + time + + common::FromSeconds(synchronized_data.ranges.front().point_time[3]); if (time_first_point < extrapolator_->GetLastPoseTime()) { LOG(INFO) << "Extrapolator is still initializing."; return nullptr; @@ -128,10 +141,10 @@ LocalTrajectoryBuilder2D::AddRangeData( } std::vector range_data_poses; - range_data_poses.reserve(range_data.returns.size()); + range_data_poses.reserve(synchronized_data.ranges.size()); bool warned = false; - for (const Eigen::Vector4f& hit : range_data.returns) { - common::Time time_point = time + common::FromSeconds(hit[3]); + for (const auto& range : synchronized_data.ranges) { + common::Time time_point = time + common::FromSeconds(range.point_time[3]); if (time_point < extrapolator_->GetLastExtrapolatedTime()) { if (!warned) { LOG(ERROR) @@ -153,10 +166,11 @@ LocalTrajectoryBuilder2D::AddRangeData( // Drop any returns below the minimum range and convert returns beyond the // maximum range into misses. - for (size_t i = 0; i < range_data.returns.size(); ++i) { - const Eigen::Vector4f& hit = range_data.returns[i]; + for (size_t i = 0; i < synchronized_data.ranges.size(); ++i) { + const Eigen::Vector4f& hit = synchronized_data.ranges[i].point_time; const Eigen::Vector3f origin_in_local = - range_data_poses[i] * range_data.origin; + range_data_poses[i] * + synchronized_data.origins.at(synchronized_data.ranges[i].origin_index); const Eigen::Vector3f hit_in_local = range_data_poses[i] * hit.head<3>(); const Eigen::Vector3f delta = hit_in_local - origin_in_local; const float range = delta.norm(); @@ -176,8 +190,9 @@ LocalTrajectoryBuilder2D::AddRangeData( num_accumulated_ = 0; const transform::Rigid3d gravity_alignment = transform::Rigid3d::Rotation( extrapolator_->EstimateGravityOrientation(time)); - accumulated_range_data_.origin = - range_data_poses.back() * range_data.origin; + // TODO(gaschler): This assumes that 'range_data_poses.back()' is at time + // 'time'. + accumulated_range_data_.origin = range_data_poses.back().translation(); return AddAccumulatedRangeData( time, TransformToGravityAlignedFrameAndFilter( diff --git a/cartographer/mapping/internal/2d/local_trajectory_builder_2d.h b/cartographer/mapping/internal/2d/local_trajectory_builder_2d.h index 9cd3b6b..86b7d96 100644 --- a/cartographer/mapping/internal/2d/local_trajectory_builder_2d.h +++ b/cartographer/mapping/internal/2d/local_trajectory_builder_2d.h @@ -26,6 +26,7 @@ #include "cartographer/mapping/internal/2d/scan_matching/ceres_scan_matcher_2d.h" #include "cartographer/mapping/internal/2d/scan_matching/real_time_correlative_scan_matcher_2d.h" #include "cartographer/mapping/internal/motion_filter.h" +#include "cartographer/mapping/internal/range_data_collator.h" #include "cartographer/mapping/pose_extrapolator.h" #include "cartographer/metrics/family_factory.h" #include "cartographer/sensor/imu_data.h" @@ -55,7 +56,8 @@ class LocalTrajectoryBuilder2D { }; explicit LocalTrajectoryBuilder2D( - const proto::LocalTrajectoryBuilderOptions2D& options); + const proto::LocalTrajectoryBuilderOptions2D& options, + const std::vector& expected_range_sensor_ids); ~LocalTrajectoryBuilder2D(); LocalTrajectoryBuilder2D(const LocalTrajectoryBuilder2D&) = delete; @@ -63,11 +65,12 @@ class LocalTrajectoryBuilder2D { // Returns 'MatchingResult' when range data accumulation completed, // otherwise 'nullptr'. Range data must be approximately horizontal - // for 2D SLAM. `time` is when the last point in `range_data` was - // acquired, `range_data` contains the relative time of point with - // respect to `time`. + // for 2D SLAM. `TimedPointCloudData::time` is when the last point in + // `range_data` was acquired, `TimedPointCloudData::ranges` contains the + // relative time of point with respect to `TimedPointCloudData::time`. std::unique_ptr AddRangeData( - common::Time, const sensor::TimedRangeData& range_data); + const std::string& sensor_id, + const sensor::TimedPointCloudData& range_data); void AddImuData(const sensor::ImuData& imu_data); void AddOdometryData(const sensor::OdometryData& odometry_data); @@ -109,6 +112,8 @@ class LocalTrajectoryBuilder2D { int num_accumulated_ = 0; sensor::RangeData accumulated_range_data_; std::chrono::steady_clock::time_point accumulation_started_; + + RangeDataCollator range_data_collator_; }; } // namespace mapping diff --git a/cartographer/mapping/internal/3d/local_trajectory_builder_3d.cc b/cartographer/mapping/internal/3d/local_trajectory_builder_3d.cc index 4aac2c2..a1036cc 100644 --- a/cartographer/mapping/internal/3d/local_trajectory_builder_3d.cc +++ b/cartographer/mapping/internal/3d/local_trajectory_builder_3d.cc @@ -38,7 +38,8 @@ static auto* kScanMatcherResidualDistanceMetric = metrics::Histogram::Null(); static auto* kScanMatcherResidualAngleMetric = metrics::Histogram::Null(); LocalTrajectoryBuilder3D::LocalTrajectoryBuilder3D( - const mapping::proto::LocalTrajectoryBuilderOptions3D& options) + const mapping::proto::LocalTrajectoryBuilderOptions3D& options, + const std::vector& expected_range_sensor_ids) : options_(options), active_submaps_(options.submaps_options()), motion_filter_(options.motion_filter_options()), @@ -48,7 +49,8 @@ LocalTrajectoryBuilder3D::LocalTrajectoryBuilder3D( ceres_scan_matcher_( common::make_unique( options_.ceres_scan_matcher_options())), - accumulated_range_data_{Eigen::Vector3f::Zero(), {}, {}} {} + accumulated_range_data_{Eigen::Vector3f::Zero(), {}, {}}, + range_data_collator_(expected_range_sensor_ids) {} LocalTrajectoryBuilder3D::~LocalTrajectoryBuilder3D() {} @@ -68,7 +70,16 @@ void LocalTrajectoryBuilder3D::AddImuData(const sensor::ImuData& imu_data) { std::unique_ptr LocalTrajectoryBuilder3D::AddRangeData( - const common::Time time, const sensor::TimedRangeData& range_data) { + const std::string& sensor_id, + const sensor::TimedPointCloudData& unsynchronized_data) { + auto synchronized_data = + range_data_collator_.AddRangeData(sensor_id, unsynchronized_data); + if (synchronized_data.ranges.empty()) { + LOG(INFO) << "Range data collator filling buffer."; + return nullptr; + } + + const common::Time& time = synchronized_data.time; if (extrapolator_ == nullptr) { // Until we've initialized the extrapolator with our first IMU message, we // cannot compute the orientation of the rangefinder. @@ -76,10 +87,11 @@ LocalTrajectoryBuilder3D::AddRangeData( return nullptr; } - CHECK(!range_data.returns.empty()); - CHECK_EQ(range_data.returns.back()[3], 0); + CHECK(!synchronized_data.ranges.empty()); + CHECK_LE(synchronized_data.ranges.back().point_time[3], 0.f); const common::Time time_first_point = - time + common::FromSeconds(range_data.returns.front()[3]); + time + + common::FromSeconds(synchronized_data.ranges.front().point_time[3]); if (time_first_point < extrapolator_->GetLastPoseTime()) { LOG(INFO) << "Extrapolator is still initializing."; return nullptr; @@ -89,15 +101,15 @@ LocalTrajectoryBuilder3D::AddRangeData( accumulation_started_ = std::chrono::steady_clock::now(); } - sensor::TimedPointCloud hits = + std::vector hits = sensor::VoxelFilter(0.5f * options_.voxel_filter_size()) - .Filter(range_data.returns); + .Filter(synchronized_data.ranges); std::vector hits_poses; hits_poses.reserve(hits.size()); bool warned = false; - for (const Eigen::Vector4f& hit : hits) { - common::Time time_point = time + common::FromSeconds(hit[3]); + for (const auto& hit : hits) { + common::Time time_point = time + common::FromSeconds(hit.point_time[3]); if (time_point < extrapolator_->GetLastExtrapolatedTime()) { if (!warned) { LOG(ERROR) @@ -117,8 +129,10 @@ LocalTrajectoryBuilder3D::AddRangeData( } for (size_t i = 0; i < hits.size(); ++i) { - const Eigen::Vector3f hit_in_local = hits_poses[i] * hits[i].head<3>(); - const Eigen::Vector3f origin_in_local = hits_poses[i] * range_data.origin; + const Eigen::Vector3f hit_in_local = + hits_poses[i] * hits[i].point_time.head<3>(); + const Eigen::Vector3f origin_in_local = + hits_poses[i] * synchronized_data.origins.at(hits[i].origin_index); const Eigen::Vector3f delta = hit_in_local - origin_in_local; const float range = delta.norm(); if (range >= options_.min_range()) { @@ -140,7 +154,7 @@ LocalTrajectoryBuilder3D::AddRangeData( transform::Rigid3f current_pose = extrapolator_->ExtrapolatePose(time).cast(); const sensor::RangeData filtered_range_data = { - current_pose * range_data.origin, + current_pose.translation(), sensor::VoxelFilter(options_.voxel_filter_size()) .Filter(accumulated_range_data_.returns), sensor::VoxelFilter(options_.voxel_filter_size()) diff --git a/cartographer/mapping/internal/3d/local_trajectory_builder_3d.h b/cartographer/mapping/internal/3d/local_trajectory_builder_3d.h index 3fc9218..99ca19c 100644 --- a/cartographer/mapping/internal/3d/local_trajectory_builder_3d.h +++ b/cartographer/mapping/internal/3d/local_trajectory_builder_3d.h @@ -26,6 +26,7 @@ #include "cartographer/mapping/internal/3d/scan_matching/ceres_scan_matcher_3d.h" #include "cartographer/mapping/internal/3d/scan_matching/real_time_correlative_scan_matcher_3d.h" #include "cartographer/mapping/internal/motion_filter.h" +#include "cartographer/mapping/internal/range_data_collator.h" #include "cartographer/mapping/pose_extrapolator.h" #include "cartographer/metrics/family_factory.h" #include "cartographer/sensor/imu_data.h" @@ -54,7 +55,8 @@ class LocalTrajectoryBuilder3D { }; explicit LocalTrajectoryBuilder3D( - const mapping::proto::LocalTrajectoryBuilderOptions3D& options); + const mapping::proto::LocalTrajectoryBuilderOptions3D& options, + const std::vector& expected_range_sensor_ids); ~LocalTrajectoryBuilder3D(); LocalTrajectoryBuilder3D(const LocalTrajectoryBuilder3D&) = delete; @@ -62,11 +64,12 @@ class LocalTrajectoryBuilder3D { void AddImuData(const sensor::ImuData& imu_data); // Returns 'MatchingResult' when range data accumulation completed, - // otherwise 'nullptr'. `time` is when the last point in `range_data` - // was acquired, `range_data` contains the relative time of point with - // respect to `time`. + // otherwise 'nullptr'. `TimedPointCloudData::time` is when the last point in + // `range_data` was acquired, `TimedPointCloudData::ranges` contains the + // relative time of point with respect to `TimedPointCloudData::time`. std::unique_ptr AddRangeData( - common::Time time, const sensor::TimedRangeData& range_data); + const std::string& sensor_id, + const sensor::TimedPointCloudData& range_data); void AddOdometryData(const sensor::OdometryData& odometry_data); static void RegisterMetrics(metrics::FamilyFactory* family_factory); @@ -97,6 +100,8 @@ class LocalTrajectoryBuilder3D { int num_accumulated_ = 0; sensor::RangeData accumulated_range_data_; std::chrono::steady_clock::time_point accumulation_started_; + + RangeDataCollator range_data_collator_; }; } // namespace mapping diff --git a/cartographer/mapping/internal/3d/local_trajectory_builder_3d_test.cc b/cartographer/mapping/internal/3d/local_trajectory_builder_3d_test.cc index 0cc156a..9879abb 100644 --- a/cartographer/mapping/internal/3d/local_trajectory_builder_3d_test.cc +++ b/cartographer/mapping/internal/3d/local_trajectory_builder_3d_test.cc @@ -35,6 +35,8 @@ namespace cartographer { namespace mapping { namespace { +constexpr char kSensorId[] = "sensor_id"; + class LocalTrajectoryBuilderTest : public ::testing::Test { protected: struct TrajectoryNode { @@ -255,8 +257,8 @@ class LocalTrajectoryBuilderTest : public ::testing::Test { const auto range_data = GenerateRangeData(node.pose); const std::unique_ptr matching_result = local_trajectory_builder_->AddRangeData( - node.time, sensor::TimedRangeData{ - range_data.origin, range_data.returns, {}}); + kSensorId, sensor::TimedPointCloudData{ + node.time, range_data.origin, range_data.returns}); if (matching_result != nullptr) { EXPECT_THAT(matching_result->local_pose, transform::IsNearly(node.pose, 1e-1)); @@ -271,8 +273,8 @@ class LocalTrajectoryBuilderTest : public ::testing::Test { }; TEST_F(LocalTrajectoryBuilderTest, MoveInsideCubeUsingOnlyCeresScanMatcher) { - local_trajectory_builder_.reset( - new LocalTrajectoryBuilder3D(CreateTrajectoryBuilderOptions3D())); + local_trajectory_builder_.reset(new LocalTrajectoryBuilder3D( + CreateTrajectoryBuilderOptions3D(), {kSensorId})); VerifyAccuracy(GenerateCorkscrewTrajectory(), 1e-1); } diff --git a/cartographer/mapping/internal/global_trajectory_builder.cc b/cartographer/mapping/internal/global_trajectory_builder.cc index 2296e10..0290428 100644 --- a/cartographer/mapping/internal/global_trajectory_builder.cc +++ b/cartographer/mapping/internal/global_trajectory_builder.cc @@ -56,10 +56,7 @@ class GlobalTrajectoryBuilder : public mapping::TrajectoryBuilderInterface { << "Cannot add TimedPointCloudData without a LocalTrajectoryBuilder."; std::unique_ptr matching_result = local_trajectory_builder_->AddRangeData( - timed_point_cloud_data.time, - sensor::TimedRangeData{timed_point_cloud_data.origin, - timed_point_cloud_data.ranges, - {}}); + sensor_id, timed_point_cloud_data); if (matching_result == nullptr) { // The range data has not been fully accumulated yet. return; diff --git a/cartographer/mapping/map_builder.cc b/cartographer/mapping/map_builder.cc index 0f5d52e..669afc3 100644 --- a/cartographer/mapping/map_builder.cc +++ b/cartographer/mapping/map_builder.cc @@ -49,6 +49,17 @@ proto::MapBuilderOptions CreateMapBuilderOptions( return options; } +std::vector SelectRangeSensorIds( + const std::set& expected_sensor_ids) { + std::vector range_sensor_ids; + for (const MapBuilder::SensorId& sensor_id : expected_sensor_ids) { + if (sensor_id.type == MapBuilder::SensorId::SensorType::RANGE) { + range_sensor_ids.push_back(sensor_id.id); + } + } + return range_sensor_ids; +} + MapBuilder::MapBuilder(const proto::MapBuilderOptions& options) : options_(options), thread_pool_(options.num_background_threads()) { if (options.use_trajectory_builder_2d()) { @@ -81,7 +92,8 @@ int MapBuilder::AddTrajectoryBuilder( std::unique_ptr local_trajectory_builder; if (trajectory_options.has_trajectory_builder_3d_options()) { local_trajectory_builder = common::make_unique( - trajectory_options.trajectory_builder_3d_options()); + trajectory_options.trajectory_builder_3d_options(), + SelectRangeSensorIds(expected_sensor_ids)); } DCHECK(dynamic_cast(pose_graph_.get())); trajectory_builders_.push_back( @@ -95,7 +107,8 @@ int MapBuilder::AddTrajectoryBuilder( std::unique_ptr local_trajectory_builder; if (trajectory_options.has_trajectory_builder_2d_options()) { local_trajectory_builder = common::make_unique( - trajectory_options.trajectory_builder_2d_options()); + trajectory_options.trajectory_builder_2d_options(), + SelectRangeSensorIds(expected_sensor_ids)); } DCHECK(dynamic_cast(pose_graph_.get())); trajectory_builders_.push_back(