省流
激光雷达数据经过一系列的分发处理, 最终在LocalTrajectoryBuilder2D::AddRangeData( const std::string& sensor_id, const sensor::TimedPointCloudData& unsynchronized_data) ;中进行算法处理 另外插入处理的结果(更新部分)通过int MapBuilder::AddTrajectoryBuilder( const std::set& expected_sensor_ids, const proto::TrajectoryBuilderOptions& trajectory_options, LocalSlamResultCallback local_slam_result_callback);传递出来
总结
搞了两天,终于绘制出测试数据,若干坑: 输入的数据,需要满足一定规范,激光雷达数据进去总是报错,Range data collator filling buffer
,更换的单元测试中的数据就ok 绘图,所需要的数据很难找。考虑过将.pbstream
转成jpg,有点麻烦,没成功 最后用的是回调函数中的数据
源码分析
起点:mapping::TrajectoryBuilderInterface::AddSensorData( const std::string& sensor_id, const sensor::TimedPointCloudData& timed_point_cloud_data) TrajectoryBuilderInterface有多个子类 在int MapBuilder::AddTrajectoryBuilder()查看,使用的是哪一个。项目中配置的是2d。
...
else {
std::unique_ptr<LocalTrajectoryBuilder2D> local_trajectory_builder;
if (trajectory_options.has_trajectory_builder_2d_options()) {
local_trajectory_builder = absl::make_unique<LocalTrajectoryBuilder2D>(
trajectory_options.trajectory_builder_2d_options(),
SelectRangeSensorIds(expected_sensor_ids));
}
DCHECK(dynamic_cast<PoseGraph2D*>(pose_graph_.get()));
trajectory_builders_.push_back(absl::make_unique<CollatedTrajectoryBuilder>(
trajectory_options, sensor_collator_.get(), trajectory_id,
expected_sensor_ids,
CreateGlobalTrajectoryBuilder2D(
std::move(local_trajectory_builder), trajectory_id,
static_cast<PoseGraph2D*>(pose_graph_.get()),
local_slam_result_callback, pose_graph_odometry_motion_filter)));
}
...
代码中看起来,用的是 class CollatedTrajectoryBuilder : public TrajectoryBuilderInterface{},继续看AddSensorData()
void AddSensorData(
const std::string& sensor_id,
const sensor::TimedPointCloudData& timed_point_cloud_data) override {
AddData(sensor::MakeDispatchable(sensor_id, timed_point_cloud_data));
}
void CollatedTrajectoryBuilder::AddData(std::unique_ptr<sensor::Data> data) {
sensor_collator_->AddSensorData(trajectory_id_, std::move(data));
}
void TrajectoryCollator::AddSensorData(const int trajectory_id,
std::unique_ptr<Data> data) {
QueueKey queue_key{trajectory_id, data->GetSensorId()};
auto* metric = GetOrCreateSensorMetric(data->GetSensorId(), trajectory_id);
metric->Increment();
trajectory_to_queue_.at(trajectory_id)
.Add(std::move(queue_key), std::move(data));
}
//collator_metrics_family_ 用于存储计数器的指标家族。
//metrics_map_ 用于存储每个传感器 ID 对应的计数器,追踪与传感器相关的事件数量。
//trajectory_to_queue_ 将每个轨迹 ID 映射到一个有序多队列,用于管理与轨迹相关的多个队列。
//trajectory_to_queue_keys_ 用于存储每个轨迹 ID 所有关联的队列键,可能用于进一步的队列管理和数据调度。
/// 数据传入到 absl::flat_hash_map<int, OrderedMultiQueue> trajectory_to_queue_;
metrics::Counter* TrajectoryCollator::GetOrCreateSensorMetric(
const std::string& sensor_id, int trajectory_id) {
const std::string trajectory_id_str = absl::StrCat(trajectory_id);
const std::string map_key = absl::StrCat(sensor_id, "/", trajectory_id_str);
auto metrics_map_itr = metrics_map_.find(map_key);
if (metrics_map_itr != metrics_map_.end()) {
return metrics_map_itr->second;
}
LOG(INFO) << "Create metrics handler for key: " << map_key;
auto new_counter = collator_metrics_family_->Add(
{{"sensor_id", sensor_id}, {"trajectory_id", trajectory_id_str}});
metrics_map_[map_key] = new_counter;
return new_counter;
}
//GetOrCreateSensorMetric 函数的主要作用是:
// 查找已有计数器:它首先尝试在 metrics_map_ 中查找是否已经存在与给定 sensor_id 和 trajectory_id 相关的计数器。
// 创建新计数器:如果没有找到,它会创建一个新的计数器,并将其添加到 metrics_map_ 中。
// 返回计数器:无论是找到已有计数器还是创建新计数器,最终都会返回该计数器。
void OrderedMultiQueue::Add(const QueueKey& queue_key,
std::unique_ptr<Data> data) {
auto it = queues_.find(queue_key);
if (it == queues_.end()) {
LOG_EVERY_N(WARNING, 1000)
<< "Ignored data for queue: '" << queue_key << "'";
return;
}
it->second.queue.Push(std::move(data));
Dispatch();
}
/// 数据分发给另一队列 struct Queue {}
/// 从多个队列中提取时间顺序最早的数据,并且按照合并排序的方式依次分发
Dispatch();
/// 目标是以合并排序的方式将各个队列中的数据按时间顺序分发出去,确保每个队列的数据都能在正确的时间顺序中被处理
struct Queue {
common::BlockingQueue<std::unique_ptr<Data>> queue;
Callback callback;
bool finished = false;
};
void OrderedMultiQueue::Dispatch() {
while (true) {
const Data* next_data = nullptr;
Queue* next_queue = nullptr;
QueueKey next_queue_key;
for (auto it = queues_.begin(); it != queues_.end();) {
const auto* data = it->second.queue.Peek<Data>();
if (data == nullptr) {
if (it->second.finished) {
queues_.erase(it++);
continue;
}
CannotMakeProgress(it->first);
return;
}
if (next_data == nullptr || data->GetTime() < next_data->GetTime()) {
next_data = data;
next_queue = &it->second;
next_queue_key = it->first;
}
CHECK_LE(last_dispatched_time_, next_data->GetTime())
<< "Non-sorted data added to queue: '" << it->first << "'";
++it;
}
if (next_data == nullptr) {
CHECK(queues_.empty());
return;
}
// If we haven't dispatched any data for this trajectory yet, fast forward
// all queues of this trajectory until a common start time has been reached.
const common::Time common_start_time =
GetCommonStartTime(next_queue_key.trajectory_id);
if (next_data->GetTime() >= common_start_time) {
// Happy case, we are beyond the 'common_start_time' already.
last_dispatched_time_ = next_data->GetTime();
next_queue->callback(next_queue->queue.Pop());
} else if (next_queue->queue.Size() < 2) {
if (!next_queue->finished) {
// We cannot decide whether to drop or dispatch this yet.
CannotMakeProgress(next_queue_key);
return;
}
last_dispatched_time_ = next_data->GetTime();
next_queue->callback(next_queue->queue.Pop());
} else {
// We take a peek at the time after next data. If it also is not beyond
// 'common_start_time' we drop 'next_data', otherwise we just found the
// first packet to dispatch from this queue.
std::unique_ptr<Data> next_data_owner = next_queue->queue.Pop();
if (next_queue->queue.Peek<Data>()->GetTime() > common_start_time) {
last_dispatched_time_ = next_data->GetTime();
next_queue->callback(std::move(next_data_owner));
}
}
}
}
// 数据在哪处理?callback比较可疑
/// 预先设置了OrderedMultiQueue的回调
void TrajectoryCollator::AddTrajectory(
const int trajectory_id,
const absl::flat_hash_set<std::string>& expected_sensor_ids,
const Callback& callback) {
CHECK_EQ(trajectory_to_queue_.count(trajectory_id), 0);
for (const auto& sensor_id : expected_sensor_ids) {
const auto queue_key = QueueKey{trajectory_id, sensor_id};
trajectory_to_queue_[trajectory_id].AddQueue(
queue_key, [callback, sensor_id](std::unique_ptr<Data> data) {
callback(sensor_id, std::move(data));
});
trajectory_to_queue_keys_[trajectory_id].push_back(queue_key);
}
}
/// 再往前找,CollatedTrajectoryBuilder
CollatedTrajectoryBuilder::CollatedTrajectoryBuilder(
const proto::TrajectoryBuilderOptions& trajectory_options,
sensor::CollatorInterface* const sensor_collator, const int trajectory_id,
const std::set<SensorId>& expected_sensor_ids,
std::unique_ptr<TrajectoryBuilderInterface> wrapped_trajectory_builder)
: sensor_collator_(sensor_collator),
collate_landmarks_(trajectory_options.collate_landmarks()),
collate_fixed_frame_(trajectory_options.collate_fixed_frame()),
trajectory_id_(trajectory_id),
wrapped_trajectory_builder_(std::move(wrapped_trajectory_builder)),
last_logging_time_(std::chrono::steady_clock::now()) {
absl::flat_hash_set<std::string> expected_sensor_id_strings;
for (const auto& sensor_id : expected_sensor_ids) {
if (sensor_id.type == SensorId::SensorType::LANDMARK &&
!collate_landmarks_) {
continue;
}
if (sensor_id.type == SensorId::SensorType::FIXED_FRAME_POSE &&
!collate_fixed_frame_) {
continue;
}
expected_sensor_id_strings.insert(sensor_id.id);
}
sensor_collator_->AddTrajectory(
trajectory_id, expected_sensor_id_strings,
[this](const std::string& sensor_id, std::unique_ptr<sensor::Data> data) {
HandleCollatedSensorData(sensor_id, std::move(data));
});
}
/// 数据被添加到 GlobalTrajectoryBuilder2D
void CollatedTrajectoryBuilder::HandleCollatedSensorData(
const std::string& sensor_id, std::unique_ptr<sensor::Data> data) {
auto it = rate_timers_.find(sensor_id);
if (it == rate_timers_.end()) {
it = rate_timers_
.emplace(
std::piecewise_construct, std::forward_as_tuple(sensor_id),
std::forward_as_tuple(
common::FromSeconds(kSensorDataRatesLoggingPeriodSeconds)))
.first;
}
it->second.Pulse(data->GetTime());
if (std::chrono::steady_clock::now() - last_logging_time_ >
common::FromSeconds(kSensorDataRatesLoggingPeriodSeconds)) {
for (const auto& pair : rate_timers_) {
LOG(INFO) << pair.first << " rate: " << pair.second.DebugString();
}
last_logging_time_ = std::chrono::steady_clock::now();
}
data->AddToTrajectoryBuilder(wrapped_trajectory_builder_.get());
}
/// 最后添加到 GlobalTrajectoryBuilder
void AddSensorData(
const std::string& sensor_id,
const sensor::TimedPointCloudData& timed_point_cloud_data) override {
CHECK(local_trajectory_builder_)
<< "Cannot add TimedPointCloudData without a LocalTrajectoryBuilder.";
std::unique_ptr<typename LocalTrajectoryBuilder::MatchingResult>
matching_result = local_trajectory_builder_->AddRangeData(
sensor_id, timed_point_cloud_data);
if (matching_result == nullptr) {
// The range data has not been fully accumulated yet.
return;
}
kLocalSlamMatchingResults->Increment();
std::unique_ptr<InsertionResult> insertion_result;
if (matching_result->insertion_result != nullptr) {
kLocalSlamInsertionResults->Increment();
auto node_id = pose_graph_->AddNode(
matching_result->insertion_result->constant_data, trajectory_id_,
matching_result->insertion_result->insertion_submaps);
CHECK_EQ(node_id.trajectory_id, trajectory_id_);
insertion_result = absl::make_unique<InsertionResult>(InsertionResult{
node_id, matching_result->insertion_result->constant_data,
std::vector<std::shared_ptr<const Submap>>(
matching_result->insertion_result->insertion_submaps.begin(),
matching_result->insertion_result->insertion_submaps.end())});
}
if (local_slam_result_callback_) {
local_slam_result_callback_(
trajectory_id_, matching_result->time, matching_result->local_pose,
std::move(matching_result->range_data_in_local),
std::move(insertion_result));
}
}
/// local_trajectory_builder_->AddRangeData 数据到了local_trajectory_builder_ (LocalTrajectoryBuilder2D)
/// 返回 matching_result
struct MatchingResult {
common::Time time;
transform::Rigid3d local_pose;
sensor::RangeData range_data_in_local;
// 'nullptr' if dropped by the motion filter.
std::unique_ptr<const InsertionResult> insertion_result;
};
/// matching_result 添加到 pose_graph_
/// 通过回调函数传递出去 local_slam_result_callback_,这个回调函数正是
int MapBuilderStub::AddTrajectoryBuilder(
const std::set<SensorId>& expected_sensor_ids,
const mapping::proto::TrajectoryBuilderOptions& trajectory_options,
LocalSlamResultCallback local_slam_result_callback);设置的的
struct InsertionResult {
std::shared_ptr<const TrajectoryNode::Data> constant_data;
std::vector<std::shared_ptr<const Submap2D>> insertion_submaps;
};
/// 前面分析的都是代码逻辑,终于到算法部分了!
/// 不过我们主要关注数据存储的数据结构
/// 激光雷达数据:
// 激光雷达数据存储在 synchronized_data 中,尤其是 synchronized_data.ranges 中。
// synchronized_data 是从 range_data_collator_.AddRangeData(sensor_id,unsynchronized_data) 返回的结果,这个数据包含了经过同步处理的激光雷达数据(即每个点云数据的时间戳和位置)。
//地图数据:
// 地图数据的累积存储在 accumulated_range_data_ 中,这个变量保存了从激光雷达数据中提取的有效范围数据(returns 和 misses)。
// 当积累的数据达到预设的数量时(由 options_.num_accumulated_range_data() 控制),就会生成最终的地图数据并返回。
// range_data_poses 中保存的是经过外推的激光雷达数据的位姿信息(即每个点云的相对位置)。
//而 synchronized_data.ranges 则是激光雷达点云数据本身
std::unique_ptr<LocalTrajectoryBuilder2D::MatchingResult>
LocalTrajectoryBuilder2D::AddRangeData(
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);
}
if (extrapolator_ == nullptr) {
// Until we've initialized the extrapolator with our first IMU message, we
// cannot compute the orientation of the rangefinder.
LOG(INFO) << "Extrapolator not yet initialized.";
return nullptr;
}
CHECK(!synchronized_data.ranges.empty());
// TODO(gaschler): Check if this can strictly be 0.
CHECK_LE(synchronized_data.ranges.back().point_time.time, 0.f);
const common::Time time_first_point =
time +
common::FromSeconds(synchronized_data.ranges.front().point_time.time);
if (time_first_point < extrapolator_->GetLastPoseTime()) {
LOG(INFO) << "Extrapolator is still initializing.";
return nullptr;
}
std::vector<transform::Rigid3f> range_data_poses;
range_data_poses.reserve(synchronized_data.ranges.size());
bool warned = false;
for (const auto& range : synchronized_data.ranges) {
common::Time time_point = time + common::FromSeconds(range.point_time.time);
if (time_point < extrapolator_->GetLastExtrapolatedTime()) {
if (!warned) {
LOG(ERROR)
<< "Timestamp of individual range data point jumps backwards from "
<< extrapolator_->GetLastExtrapolatedTime() << " to " << time_point;
warned = true;
}
time_point = extrapolator_->GetLastExtrapolatedTime();
}
range_data_poses.push_back(
extrapolator_->ExtrapolatePose(time_point).cast<float>());
}
if (num_accumulated_ == 0) {
// 'accumulated_range_data_.origin' is uninitialized until the last
// accumulation.
accumulated_range_data_ = sensor::RangeData{{}, {}, {}};
}
// Drop any returns below the minimum range and convert returns beyond the
// maximum range into misses.
for (size_t i = 0; i < synchronized_data.ranges.size(); ++i) {
const sensor::TimedRangefinderPoint& hit =
synchronized_data.ranges[i].point_time;
const Eigen::Vector3f origin_in_local =
range_data_poses[i] *
synchronized_data.origins.at(synchronized_data.ranges[i].origin_index);
sensor::RangefinderPoint hit_in_local =
range_data_poses[i] * sensor::ToRangefinderPoint(hit);
const Eigen::Vector3f delta = hit_in_local.position - origin_in_local;
const float range = delta.norm();
if (range >= options_.min_range()) {
if (range <= options_.max_range()) {
accumulated_range_data_.returns.push_back(hit_in_local);
} else {
hit_in_local.position =
origin_in_local +
options_.missing_data_ray_length() / range * delta;
accumulated_range_data_.misses.push_back(hit_in_local);
}
}
}
++num_accumulated_;
if (num_accumulated_ >= options_.num_accumulated_range_data()) {
const common::Time current_sensor_time = synchronized_data.time;
absl::optional<common::Duration> sensor_duration;
if (last_sensor_time_.has_value()) {
sensor_duration = current_sensor_time - last_sensor_time_.value();
}
last_sensor_time_ = current_sensor_time;
num_accumulated_ = 0;
const transform::Rigid3d gravity_alignment = transform::Rigid3d::Rotation(
extrapolator_->EstimateGravityOrientation(time));
// 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(
gravity_alignment.cast<float>() * range_data_poses.back().inverse(),
accumulated_range_data_),
gravity_alignment, sensor_duration);
}
return nullptr;
}
/// 主要功能是将未同步的传感器数据(即 unsynchronized_data)与其他数据进行同步,并将其处理为累积的范围数据。
/// 这一过程涉及多项操作,包括数据同步、姿态外推(pose extrapolation)、范围数据的筛选(如去除不合法的距离)以及将数据存储并返回