Extrapolator Result for motion compensation (#1731)

This makes use of the ExtrapolationResult returned by the pose extrapolator. ExtrapolationResult contains poses corresponding to the timestamps of range data measurements to be used for motion compensation. When using the PoseExtrapolator implementation there is no functional difference. This PR prepares the integration of ImuBasedPoseExtrapolator, which requires using ExtrapolationResult returned by ExtrapolatePosesWithGravity to run efficiently.

Signed-off-by: mschworer <mschworer@lyft.com>
master
Martin Schwörer 2020-07-30 09:14:02 +02:00 committed by GitHub
parent efc64934d6
commit d3794a420a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 91 deletions

View File

@ -25,6 +25,7 @@
#include "cartographer/mapping/proto/3d/submaps_options_3d.pb.h" #include "cartographer/mapping/proto/3d/submaps_options_3d.pb.h"
#include "cartographer/mapping/proto/scan_matching//ceres_scan_matcher_options_3d.pb.h" #include "cartographer/mapping/proto/scan_matching//ceres_scan_matcher_options_3d.pb.h"
#include "cartographer/mapping/proto/scan_matching//real_time_correlative_scan_matcher_options.pb.h" #include "cartographer/mapping/proto/scan_matching//real_time_correlative_scan_matcher_options.pb.h"
#include "cartographer/transform/timestamped_transform.h"
#include "glog/logging.h" #include "glog/logging.h"
namespace cartographer { namespace cartographer {
@ -55,7 +56,6 @@ LocalTrajectoryBuilder3D::LocalTrajectoryBuilder3D(
options_.real_time_correlative_scan_matcher_options())), options_.real_time_correlative_scan_matcher_options())),
ceres_scan_matcher_(absl::make_unique<scan_matching::CeresScanMatcher3D>( ceres_scan_matcher_(absl::make_unique<scan_matching::CeresScanMatcher3D>(
options_.ceres_scan_matcher_options())), options_.ceres_scan_matcher_options())),
accumulated_range_data_{Eigen::Vector3f::Zero(), {}, {}},
range_data_collator_(expected_range_sensor_ids) {} range_data_collator_(expected_range_sensor_ids) {}
LocalTrajectoryBuilder3D::~LocalTrajectoryBuilder3D() {} LocalTrajectoryBuilder3D::~LocalTrajectoryBuilder3D() {}
@ -116,14 +116,13 @@ std::unique_ptr<LocalTrajectoryBuilder3D::MatchingResult>
LocalTrajectoryBuilder3D::AddRangeData( LocalTrajectoryBuilder3D::AddRangeData(
const std::string& sensor_id, const std::string& sensor_id,
const sensor::TimedPointCloudData& unsynchronized_data) { const sensor::TimedPointCloudData& unsynchronized_data) {
const auto synchronized_data = auto synchronized_data =
range_data_collator_.AddRangeData(sensor_id, unsynchronized_data); range_data_collator_.AddRangeData(sensor_id, unsynchronized_data);
if (synchronized_data.ranges.empty()) { if (synchronized_data.ranges.empty()) {
LOG(INFO) << "Range data collator filling buffer."; LOG(INFO) << "Range data collator filling buffer.";
return nullptr; return nullptr;
} }
const common::Time& current_sensor_time = synchronized_data.time;
if (extrapolator_ == nullptr) { if (extrapolator_ == nullptr) {
// Until we've initialized the extrapolator with our first IMU message, we // Until we've initialized the extrapolator with our first IMU message, we
// cannot compute the orientation of the rangefinder. // cannot compute the orientation of the rangefinder.
@ -134,82 +133,104 @@ LocalTrajectoryBuilder3D::AddRangeData(
CHECK(!synchronized_data.ranges.empty()); CHECK(!synchronized_data.ranges.empty());
CHECK_LE(synchronized_data.ranges.back().point_time.time, 0.f); CHECK_LE(synchronized_data.ranges.back().point_time.time, 0.f);
const common::Time time_first_point = const common::Time time_first_point =
current_sensor_time + synchronized_data.time +
common::FromSeconds(synchronized_data.ranges.front().point_time.time); common::FromSeconds(synchronized_data.ranges.front().point_time.time);
if (time_first_point < extrapolator_->GetLastPoseTime()) { if (time_first_point < extrapolator_->GetLastPoseTime()) {
LOG(INFO) << "Extrapolator is still initializing."; LOG(INFO) << "Extrapolator is still initializing.";
return nullptr; return nullptr;
} }
std::vector<sensor::TimedPointCloudOriginData::RangeMeasurement> hits = if (num_accumulated_ == 0) {
accumulated_point_cloud_origin_data_.clear();
}
synchronized_data.ranges =
sensor::VoxelFilter(0.5f * options_.voxel_filter_size()) sensor::VoxelFilter(0.5f * options_.voxel_filter_size())
.Filter(synchronized_data.ranges); .Filter(synchronized_data.ranges);
accumulated_point_cloud_origin_data_.emplace_back(
std::move(synchronized_data));
++num_accumulated_;
if (num_accumulated_ < options_.num_accumulated_range_data()) {
return nullptr;
}
num_accumulated_ = 0;
std::vector<transform::Rigid3f> hits_poses;
hits_poses.reserve(hits.size());
bool warned = false; bool warned = false;
std::vector<common::Time> hit_times;
for (const auto& hit : hits) { common::Time prev_time_point = extrapolator_->GetLastExtrapolatedTime();
common::Time time_point = for (const auto& point_cloud_origin_data :
current_sensor_time + common::FromSeconds(hit.point_time.time); accumulated_point_cloud_origin_data_) {
if (time_point < extrapolator_->GetLastExtrapolatedTime()) { for (const auto& hit : point_cloud_origin_data.ranges) {
common::Time time_point = point_cloud_origin_data.time +
common::FromSeconds(hit.point_time.time);
if (time_point < prev_time_point) {
if (!warned) { if (!warned) {
LOG(ERROR) LOG(ERROR) << "Timestamp of individual range data point jumps "
<< "Timestamp of individual range data point jumps backwards from " "backwards from "
<< extrapolator_->GetLastExtrapolatedTime() << " to " << time_point; << prev_time_point << " to " << time_point;
warned = true; warned = true;
} }
time_point = extrapolator_->GetLastExtrapolatedTime(); time_point = prev_time_point;
}
hits_poses.push_back(
extrapolator_->ExtrapolatePose(time_point).cast<float>());
} }
if (num_accumulated_ == 0) { hit_times.push_back(time_point);
// 'accumulated_range_data_.origin' is not used. prev_time_point = time_point;
accumulated_range_data_ = sensor::RangeData{{}, {}, {}};
} }
}
hit_times.push_back(accumulated_point_cloud_origin_data_.back().time);
for (size_t i = 0; i < hits.size(); ++i) { const PoseExtrapolatorInterface::ExtrapolationResult extrapolation_result =
sensor::RangefinderPoint hit_in_local = extrapolator_->ExtrapolatePosesWithGravity(hit_times);
hits_poses[i] * sensor::ToRangefinderPoint(hits[i].point_time); std::vector<transform::Rigid3f> hits_poses(
std::move(extrapolation_result.previous_poses));
hits_poses.push_back(extrapolation_result.current_pose.cast<float>());
CHECK_EQ(hits_poses.size(), hit_times.size());
sensor::RangeData accumulated_range_data;
std::vector<transform::Rigid3f>::const_iterator hits_poses_it =
hits_poses.begin();
for (const auto& point_cloud_origin_data :
accumulated_point_cloud_origin_data_) {
for (const auto& hit : point_cloud_origin_data.ranges) {
const Eigen::Vector3f hit_in_local =
*hits_poses_it * hit.point_time.position;
const Eigen::Vector3f origin_in_local = const Eigen::Vector3f origin_in_local =
hits_poses[i] * synchronized_data.origins.at(hits[i].origin_index); *hits_poses_it * point_cloud_origin_data.origins.at(hit.origin_index);
const Eigen::Vector3f delta = hit_in_local.position - origin_in_local; const Eigen::Vector3f delta = hit_in_local - origin_in_local;
const float range = delta.norm(); const float range = delta.norm();
if (range >= options_.min_range()) { if (range >= options_.min_range()) {
if (range <= options_.max_range()) { if (range <= options_.max_range()) {
accumulated_range_data_.returns.push_back(hit_in_local); accumulated_range_data.returns.push_back(
sensor::RangefinderPoint{hit_in_local});
} else { } else {
// We insert a ray cropped to 'max_range' as a miss for hits beyond the // We insert a ray cropped to 'max_range' as a miss for hits beyond
// maximum range. This way the free space up to the maximum range will // the maximum range. This way the free space up to the maximum range
// be updated. // will be updated.
hit_in_local.position = accumulated_range_data.misses.push_back(sensor::RangefinderPoint{
origin_in_local + options_.max_range() / range * delta; origin_in_local + options_.max_range() / range * delta});
accumulated_range_data_.misses.push_back(hit_in_local);
} }
} }
++hits_poses_it;
} }
++num_accumulated_; }
CHECK(std::next(hits_poses_it) == hits_poses.end());
if (num_accumulated_ >= options_.num_accumulated_range_data()) { const common::Time current_sensor_time = synchronized_data.time;
absl::optional<common::Duration> sensor_duration; absl::optional<common::Duration> sensor_duration;
if (last_sensor_time_.has_value()) { if (last_sensor_time_.has_value()) {
sensor_duration = current_sensor_time - last_sensor_time_.value(); sensor_duration = current_sensor_time - last_sensor_time_.value();
} }
last_sensor_time_ = current_sensor_time; last_sensor_time_ = current_sensor_time;
num_accumulated_ = 0;
transform::Rigid3f current_pose =
extrapolator_->ExtrapolatePose(current_sensor_time).cast<float>();
const common::Time current_time = hit_times.back();
const auto voxel_filter_start = std::chrono::steady_clock::now(); const auto voxel_filter_start = std::chrono::steady_clock::now();
const sensor::RangeData filtered_range_data = { const sensor::RangeData filtered_range_data = {
current_pose.translation(), extrapolation_result.current_pose.translation().cast<float>(),
sensor::VoxelFilter(options_.voxel_filter_size()) sensor::VoxelFilter(options_.voxel_filter_size())
.Filter(accumulated_range_data_.returns), .Filter(accumulated_range_data.returns),
sensor::VoxelFilter(options_.voxel_filter_size()) sensor::VoxelFilter(options_.voxel_filter_size())
.Filter(accumulated_range_data_.misses)}; .Filter(accumulated_range_data.misses)};
const auto voxel_filter_stop = std::chrono::steady_clock::now(); const auto voxel_filter_stop = std::chrono::steady_clock::now();
const auto voxel_filter_duration = voxel_filter_stop - voxel_filter_start; const auto voxel_filter_duration = voxel_filter_stop - voxel_filter_start;
@ -221,24 +242,25 @@ LocalTrajectoryBuilder3D::AddRangeData(
} }
return AddAccumulatedRangeData( return AddAccumulatedRangeData(
current_sensor_time, current_time,
sensor::TransformRangeData(filtered_range_data, current_pose.inverse()), sensor::TransformRangeData(
sensor_duration); filtered_range_data,
} extrapolation_result.current_pose.inverse().cast<float>()),
return nullptr; sensor_duration, extrapolation_result.current_pose,
extrapolation_result.gravity_from_tracking);
} }
std::unique_ptr<LocalTrajectoryBuilder3D::MatchingResult> std::unique_ptr<LocalTrajectoryBuilder3D::MatchingResult>
LocalTrajectoryBuilder3D::AddAccumulatedRangeData( LocalTrajectoryBuilder3D::AddAccumulatedRangeData(
const common::Time time, const common::Time time,
const sensor::RangeData& filtered_range_data_in_tracking, const sensor::RangeData& filtered_range_data_in_tracking,
const absl::optional<common::Duration>& sensor_duration) { const absl::optional<common::Duration>& sensor_duration,
const transform::Rigid3d& pose_prediction,
const Eigen::Quaterniond& gravity_alignment) {
if (filtered_range_data_in_tracking.returns.empty()) { if (filtered_range_data_in_tracking.returns.empty()) {
LOG(WARNING) << "Dropped empty range data."; LOG(WARNING) << "Dropped empty range data.";
return nullptr; return nullptr;
} }
const transform::Rigid3d pose_prediction =
extrapolator_->ExtrapolatePose(time);
const auto scan_matcher_start = std::chrono::steady_clock::now(); const auto scan_matcher_start = std::chrono::steady_clock::now();
@ -278,8 +300,6 @@ LocalTrajectoryBuilder3D::AddAccumulatedRangeData(
kLocalSlamScanMatcherFraction->Set(scan_matcher_fraction); kLocalSlamScanMatcherFraction->Set(scan_matcher_fraction);
} }
const Eigen::Quaterniond gravity_alignment =
extrapolator_->EstimateGravityOrientation(time);
sensor::RangeData filtered_range_data_in_local = sensor::TransformRangeData( sensor::RangeData filtered_range_data_in_local = sensor::TransformRangeData(
filtered_range_data_in_tracking, pose_estimate->cast<float>()); filtered_range_data_in_tracking, pose_estimate->cast<float>());

View File

@ -33,6 +33,7 @@
#include "cartographer/sensor/internal/voxel_filter.h" #include "cartographer/sensor/internal/voxel_filter.h"
#include "cartographer/sensor/odometry_data.h" #include "cartographer/sensor/odometry_data.h"
#include "cartographer/sensor/range_data.h" #include "cartographer/sensor/range_data.h"
#include "cartographer/sensor/timed_point_cloud_data.h"
#include "cartographer/transform/rigid_transform.h" #include "cartographer/transform/rigid_transform.h"
namespace cartographer { namespace cartographer {
@ -78,7 +79,9 @@ class LocalTrajectoryBuilder3D {
std::unique_ptr<MatchingResult> AddAccumulatedRangeData( std::unique_ptr<MatchingResult> AddAccumulatedRangeData(
common::Time time, common::Time time,
const sensor::RangeData& filtered_range_data_in_tracking, const sensor::RangeData& filtered_range_data_in_tracking,
const absl::optional<common::Duration>& sensor_duration); const absl::optional<common::Duration>& sensor_duration,
const transform::Rigid3d& pose_prediction,
const Eigen::Quaterniond& gravity_alignment);
std::unique_ptr<InsertionResult> InsertIntoSubmap( std::unique_ptr<InsertionResult> InsertIntoSubmap(
common::Time time, const sensor::RangeData& filtered_range_data_in_local, common::Time time, const sensor::RangeData& filtered_range_data_in_local,
@ -106,7 +109,8 @@ class LocalTrajectoryBuilder3D {
std::unique_ptr<mapping::PoseExtrapolatorInterface> extrapolator_; std::unique_ptr<mapping::PoseExtrapolatorInterface> extrapolator_;
int num_accumulated_ = 0; int num_accumulated_ = 0;
sensor::RangeData accumulated_range_data_; std::vector<sensor::TimedPointCloudOriginData>
accumulated_point_cloud_origin_data_;
absl::optional<std::chrono::steady_clock::time_point> last_wall_time_; absl::optional<std::chrono::steady_clock::time_point> last_wall_time_;
absl::optional<double> last_thread_cpu_time_seconds_; absl::optional<double> last_thread_cpu_time_seconds_;