Skip to content

Commit

Permalink
2021-05-29 23-09 end
Browse files Browse the repository at this point in the history
  • Loading branch information
xiangli0608 committed May 29, 2021
1 parent 7c4b0cb commit c80ce7f
Show file tree
Hide file tree
Showing 24 changed files with 184 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ build_isolated/
devel_isolated/
install_isolated/
*~
src/CMakeLists.txt
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
# cartographer 超详细注释

***

基于2021.04.20日在github上下载的master的代码的注释版本, 会增加更规范、详细的注释, 包括:

1、增加关键地方的公式推导

2、难理解部分代码画图展示原理

3、原作者代码本身疑似/确定bug的说明
3、c++11新标准的语法的标注与简介

# 推荐环境
ubuntu 16.04/18.04 版本
ROS Kinetic/Melodic 版本
vscode
vscode中推荐的插件有:
- C/C++
- C++ Intellisense
- Doxygen Documentation Generator
- Msg Language Support
- XML Tools
- Todo Tree: 注释的高亮显示




**by lixiang**
Expand Down
1 change: 0 additions & 1 deletion src/CMakeLists.txt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,6 @@ class BlockingQueue {
}

private:

// todo: EXCLUSIVE_LOCKS_REQUIRED


// Returns true iff the queue is empty.
// 如果队列为空, 则返回true
bool QueueEmptyCondition() EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
Expand Down
28 changes: 28 additions & 0 deletions src/cartographer/cartographer/common/task.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,56 +26,81 @@ Task::~Task() {
}
}

// 返回本Task当前状态
Task::State Task::GetState() {
absl::MutexLock locker(&mutex_);
return state_;
}

// 设置本Task需要执行的任务 (函数)
void Task::SetWorkItem(const WorkItem& work_item) {
absl::MutexLock locker(&mutex_);
CHECK_EQ(state_, NEW);
work_item_ = work_item;
}

// c++11: std::weak_ptr weak_ptr被设计为与shared_ptr共同工作,
// 可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权
// 但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
// 同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。
// weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因
// 但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。

// 为本任务添加依赖
void Task::AddDependency(std::weak_ptr<Task> dependency) {
std::shared_ptr<Task> shared_dependency;
{
absl::MutexLock locker(&mutex_);
CHECK_EQ(state_, NEW);
// 如果指针指针成功获取对象
if ((shared_dependency = dependency.lock())) {
++uncompleted_dependencies_;
}
}
if (shared_dependency) {
// 将本task加入到shared_dependency的集合dependent_tasks_中
shared_dependency->AddDependentTask(this);
}
}

// 将线程池与本任务连接起来, 如果没有未完成的依赖, 则告诉线程池可以将本任务放入到执行队列中
void Task::SetThreadPool(ThreadPoolInterface* thread_pool) {
absl::MutexLock locker(&mutex_);
CHECK_EQ(state_, NEW);

// 将任务状态设置为 DISPATCHED
state_ = DISPATCHED;

// 将thread_pool_to_notify_指针指向传入的thread_pool
thread_pool_to_notify_ = thread_pool;

// 如果本Task没有未完成的依赖, 则通知线程池可以将本任务放入到执行队列中
if (uncompleted_dependencies_ == 0) {
state_ = DEPENDENCIES_COMPLETED;
CHECK(thread_pool_to_notify_);
thread_pool_to_notify_->NotifyDependenciesCompleted(this);
}
}

// 添加依赖本Task的Task
void Task::AddDependentTask(Task* dependent_task) {
absl::MutexLock locker(&mutex_);

// 如果本Task完成了, 那就通知依赖dependent_task
if (state_ == COMPLETED) {
dependent_task->OnDependenyCompleted();
return;
}
// 将依赖本任务的任务放入set中
bool inserted = dependent_tasks_.insert(dependent_task).second;
CHECK(inserted) << "Given dependency is already a dependency.";
}

// 本任务依赖的任务完成了, 可以将本任务加入到线程池的待处理列表中了
void Task::OnDependenyCompleted() {
absl::MutexLock locker(&mutex_);
CHECK(state_ == NEW || state_ == DISPATCHED);
// 依赖的任务减一
--uncompleted_dependencies_;
if (uncompleted_dependencies_ == 0 && state_ == DISPATCHED) {
state_ = DEPENDENCIES_COMPLETED;
Expand All @@ -84,6 +109,7 @@ void Task::OnDependenyCompleted() {
}
}

// 执行本任务, 也就是传入的函数work_item_
void Task::Execute() {
{
absl::MutexLock locker(&mutex_);
Expand All @@ -98,6 +124,8 @@ void Task::Execute() {

absl::MutexLock locker(&mutex_);
state_ = COMPLETED;

// 通知依赖本任务的其他任务, 本任务执行完了
for (Task* dependent_task : dependent_tasks_) {
dependent_task->OnDependenyCompleted();
}
Expand Down
11 changes: 11 additions & 0 deletions src/cartographer/cartographer/common/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ class Task {
using WorkItem = std::function<void()>;
enum State { NEW, DISPATCHED, DEPENDENCIES_COMPLETED, RUNNING, COMPLETED };

/**
NEW:新建任务,还未schedule到线程池
DISPATCHED: 任务已经schedule 到线程池
DEPENDENCIES_COMPLETED: 任务依赖已经执行完成
RUNNING: 任务执行中
COMPLETED: 任务完成
对任一个任务的状态转换顺序为:
NEW->DISPATCHED->DEPENDENCIES_COMPLETED->RUNNING->COMPLETED
*/

Task() = default;
~Task();

Expand Down
23 changes: 23 additions & 0 deletions src/cartographer/cartographer/common/thread_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@
namespace cartographer {
namespace common {

// 执行传入的 task的Execute()函数
void ThreadPoolInterface::Execute(Task* task) { task->Execute(); }

// 执行传入的 task的SetThreadPool()函数
void ThreadPoolInterface::SetThreadPool(Task* task) {
task->SetThreadPool(this);
}

// 根据传入的数字, 进行线程池的构造, DoWork()函数开始了一个始终执行的for循环
ThreadPool::ThreadPool(int num_threads) {
CHECK_GT(num_threads, 0) << "ThreadPool requires a positive num_threads!";
absl::MutexLock locker(&mutex_);
Expand All @@ -44,6 +47,7 @@ ThreadPool::ThreadPool(int num_threads) {
}
}

// 只有等待 pool_ 结束所有的线程(join是等待直到线程结束),ThreadPool才能析构完成
ThreadPool::~ThreadPool() {
{
absl::MutexLock locker(&mutex_);
Expand All @@ -55,42 +59,59 @@ ThreadPool::~ThreadPool() {
}
}

// task的依赖都结束了, 可以将task放入可执行任务的队列task_queue_中了
void ThreadPool::NotifyDependenciesCompleted(Task* task) {
absl::MutexLock locker(&mutex_);

// 找到task的索引
auto it = tasks_not_ready_.find(task);
CHECK(it != tasks_not_ready_.end());

// 加入到任务队列中
task_queue_.push_back(it->second);
// 从为准备好的任务队列中删除task
tasks_not_ready_.erase(it);
}

// 将task插入到tasks_not_ready_队列中, 并执行task的SetThreadPool()函数
std::weak_ptr<Task> ThreadPool::Schedule(std::unique_ptr<Task> task) {
std::shared_ptr<Task> shared_task;
{
absl::MutexLock locker(&mutex_);
auto insert_result =
tasks_not_ready_.insert(std::make_pair(task.get(), std::move(task)));

// map::insert() 会返回pair<map::iterator,bool> 类型,
// 第一个值为迭代器, 第二个值为插入操作是否成功
CHECK(insert_result.second) << "Schedule called twice";
shared_task = insert_result.first->second;
}
SetThreadPool(shared_task.get());
return shared_task;
}

// 开始一个不停止的for循环, 如果任务队列不为空, 就执行第一个task
void ThreadPool::DoWork() {
#ifdef __linux__
// This changes the per-thread nice level of the current thread on Linux. We
// do this so that the background work done by the thread pool is not taking
// away CPU resources from more important foreground threads.
CHECK_NE(nice(10), -1);
#endif

const auto predicate = [this]() EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
return !task_queue_.empty() || !running_;
};

// 始终执行, 直到running_为false时停止执行
for (;;) {
std::shared_ptr<Task> task;
{
absl::MutexLock locker(&mutex_);
mutex_.Await(absl::Condition(&predicate));

// map_builder.lua中设置的线程数, 4个线程处理同一个task_queue_
// 如果任务队列不为空, 那就取出第一个task
if (!task_queue_.empty()) {
task = std::move(task_queue_.front());
task_queue_.pop_front();
Expand All @@ -100,6 +121,8 @@ void ThreadPool::DoWork() {
}
CHECK(task);
CHECK_EQ(task->GetState(), common::Task::DEPENDENCIES_COMPLETED);

// 执行task
Execute(task.get());
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/cartographer/cartographer/common/thread_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ThreadPoolInterface {
void SetThreadPool(Task* task);

private:
// 声明 Task 为友元类,Task类的所有成员函数就都可以访问ThreadPoolInterface类的对象的私有成员。
friend class Task;

virtual void NotifyDependenciesCompleted(Task* task) = 0;
Expand All @@ -54,6 +55,12 @@ class ThreadPoolInterface {
// in a background thread. The queue must be empty before calling the
// destructor. The thread pool will then wait for the currently executing work
// items to finish and then destroy the threads.
// 固定数量的线程处理任务。 添加任务不会阻塞。
// 无论是否完成依赖,都可以添加任务。
// 当一个任务的所有依赖都完成后,它会在后台线程中排队等待执行。
// 在调用析构函数之前队列必须为空。 然后线程池将等待当前正在执行的工作项完成,然后销毁线程


class ThreadPool : public ThreadPoolInterface {
public:
explicit ThreadPool(int num_threads);
Expand All @@ -64,6 +71,7 @@ class ThreadPool : public ThreadPoolInterface {

// When the returned weak pointer is expired, 'task' has certainly completed,
// so dependants no longer need to add it as a dependency.
// 当返回的弱指针过期时,'task'肯定已经完成,因此依赖者不再需要将其添加为依赖项。
std::weak_ptr<Task> Schedule(std::unique_ptr<Task> task)
LOCKS_EXCLUDED(mutex_) override;

Expand All @@ -73,9 +81,14 @@ class ThreadPool : public ThreadPoolInterface {
void NotifyDependenciesCompleted(Task* task) LOCKS_EXCLUDED(mutex_) override;

absl::Mutex mutex_;

// 结束线程的标志
bool running_ GUARDED_BY(mutex_) = true;
std::vector<std::thread> pool_ GUARDED_BY(mutex_);
// 线程池
std::vector<std::thread> pool_ GUARDED_BY(mutex_);
// 准备执行的task
std::deque<std::shared_ptr<Task>> task_queue_ GUARDED_BY(mutex_);
// 未准备好的 task, task可能有依赖还未完成
absl::flat_hash_map<Task*, std::shared_ptr<Task>> tasks_not_ready_
GUARDED_BY(mutex_);
};
Expand Down
1 change: 1 addition & 0 deletions src/cartographer/cartographer/mapping/2d/submap_2d.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ std::vector<std::shared_ptr<const Submap2D>> ActiveSubmaps2D::submaps() const {
submaps_.end());
}

// 将点云数据写入到submap中
std::vector<std::shared_ptr<const Submap2D>> ActiveSubmaps2D::InsertRangeData(
const sensor::RangeData& range_data) {
if (submaps_.empty() ||
Expand Down
10 changes: 10 additions & 0 deletions src/cartographer/cartographer/mapping/id.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ namespace cartographer {
namespace mapping {
namespace internal {

// c++11: decltype 可以从一个变量或表达式中得到类型
// 在 C++11 中, auto可以和decltype一起用, 将auto放置在返回值类型上当做占位符, 不表达实际意思
// 在参数列表后添加 -> decltype( ), 这是后置返回类型, 代表返回的类型是 () 中的类型
// 在 C++14 中,则可以不用 decltype

template <class T>
auto GetTimeImpl(const T& t, int) -> decltype(t.time()) {
return t.time();
Expand All @@ -54,6 +59,8 @@ common::Time GetTime(const T& t) {

// Uniquely identifies a trajectory node using a combination of a unique
// trajectory ID and a zero-based index of the node inside that trajectory.
// 使用唯一的轨迹ID和该轨迹内节点的从零开始的索引的组合来唯一地标识轨迹节点。
// 一个是轨迹ID,一个是节点的序号
struct NodeId {
NodeId(int trajectory_id, int node_index)
: trajectory_id(trajectory_id), node_index(node_index) {}
Expand Down Expand Up @@ -86,6 +93,7 @@ inline std::ostream& operator<<(std::ostream& os, const NodeId& v) {
// Uniquely identifies a submap using a combination of a unique trajectory ID
// and a zero-based index of the submap inside that trajectory.
// 使用唯一的轨迹ID和该轨迹内子图的从零开始的索引的组合来唯一地标识子图。
// 一个是轨迹ID,一个是子图的序号
struct SubmapId {
SubmapId(int trajectory_id, int submap_index)
: trajectory_id(trajectory_id), submap_index(submap_index) {}
Expand Down Expand Up @@ -134,6 +142,7 @@ class Range {
// 'SubmapId'.
// Note: This container will only ever contain non-empty trajectories. Trimming
// the last remaining node of a trajectory drops the trajectory.
// std::map的封装
template <typename IdType, typename DataType>
class MapById {
private:
Expand All @@ -147,6 +156,7 @@ class MapById {

class ConstIterator {
public:
// c++11: std::bidirectional_iterator_tag 用于将迭代器的类别标识为双向迭代器
using iterator_category = std::bidirectional_iterator_tag;
using value_type = IdDataReference;
using difference_type = int64;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,17 +322,31 @@ LocalTrajectoryBuilder2D::AddAccumulatedRangeData(
std::move(insertion_result)});
}

/**
* @brief
*
* @param[in] time
* @param[in] range_data_in_local
* @param[in] filtered_gravity_aligned_point_cloud
* @param[in] pose_estimate
* @param[in] gravity_alignment
* @return std::unique_ptr<LocalTrajectoryBuilder2D::InsertionResult>
*/
std::unique_ptr<LocalTrajectoryBuilder2D::InsertionResult>
LocalTrajectoryBuilder2D::InsertIntoSubmap(
const common::Time time, const sensor::RangeData& range_data_in_local,
const sensor::PointCloud& filtered_gravity_aligned_point_cloud,
const transform::Rigid3d& pose_estimate,
const Eigen::Quaterniond& gravity_alignment) {
// 如果移动距离过小, 或者时间过短, 不进行地图的更新
if (motion_filter_.IsSimilar(time, pose_estimate)) {
return nullptr;
}
// 将点云数据写入到submap中
std::vector<std::shared_ptr<const Submap2D>> insertion_submaps =
active_submaps_.InsertRangeData(range_data_in_local);

// 生成InsertionResult格式的数据
return absl::make_unique<InsertionResult>(InsertionResult{
std::make_shared<const TrajectoryNode::Data>(TrajectoryNode::Data{
time,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace mapping {
// todo: LocalTrajectoryBuilder2D
class LocalTrajectoryBuilder2D {
public:
// tag: LocalTrajectoryBuilder2D::InsertionResult
struct InsertionResult {
std::shared_ptr<const TrajectoryNode::Data> constant_data;
std::vector<std::shared_ptr<const Submap2D>> insertion_submaps;
Expand Down
Loading

0 comments on commit c80ce7f

Please sign in to comment.