C++ Cartographer源码中关于Sensor的数据走向分析


本篇内容主要讲解“C++Cartographer源码中关于Sensor的数据走向分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++Cartographer源码中关于Sensor的数据走向分析”吧!详细了解了MapBuilder类, 发现其构造函数, 以及AddTrajectory中有使用到SensorBridge这个类还有sensor_collator_这个变量, 并且似乎是用这个类进行传感器数据的传递的. 当然啦, 如果想建立一个完整的轨迹(Trajectory)和SLAM功能, 我们肯定需要有传感器的数据灌入的.在MapBuilder的入口类-MapBuilderBridge中, 可以看到一个变量sensor_bridges_std::unordered_map> sensor_bridges_;sensor_bridges_存储了一系列SensorBridge类的实例, 并且在MapBuilderBridge::AddTrajectory的第二步使用, 作用是为当前轨迹添加一个SensorBridge.所以我们重点看看SensorBridge这个类. 我们以最重要的sensor类-LaserScan为例子我们都知道, ros中软实时的程序数据的入口都是从subscriber的回调函数开始. 之前已经讲过了Node的LaunchSubscriber函数, 用来专门启动所有传感器的订阅. 咱们就看看Node类的HandleLaserScanMessage:我们可以看到, 他最终是调用了MapBuilderBridge类的sensor_bridge的成员函数-HandleLaserScanMessage来处理传入的传感器的数据.咱们再去SensorBridge中看看, 以下是主要代码部分前几节也讲过这部分SensorBridge::HandleLaserScanMessage调用了SensorBridge::HandleRangefinder, 把carto::sensor::TimedPointCloudData这个数据类型和sensor_id通过trajectory_builder_的AddSensorData把点云类型传递给CollatedTrajectoryBuilder的AddSensorData. 为啥是CollatedTrajectoryBuilder的AddSensorData呢?这块我也想了很久. 咱们先去sensor_bridge.h中看 ::cartographer::mapping::TrajectoryBuilderInterface* const
trajectory_builder_;发现是TrajectoryBuilderInterface, 这明显是个父类啊,没啥意义. 咱们回到SensorBridge的构造函数发现这个trajectory_builder_是SensorBridge构造函数的最后一个参数, 那么这个SensorBridge是在哪构造的呢? 是在map_builder_bridge.cc中, MapBuilderBridge::AddTrajectory的第二步sensor_bridges_[trajectory_id] = absl::make_unique. 我们看到最后一个参数是map_builder_->GetTrajectoryBuilder(trajectory_id)这个map_builder_是MapBuilderBridge构造函数map_builder_(std::move(map_builder)), 而这个map_builder也是父类定义,没啥参考价值std::unique_ptr<:mapping::mapbuilderinterface> map_builder再向前回溯, 看MapBuilderBridge是咋构造的, 发现是Node的构造函数就构造了map_builder_bridge_又要往前回溯, 在node_main.cc中发现map_builder是cartographer::mapping::CreateMapBuilder给的. 咱们再进CreateMapBuilder, 发现其只是一个工厂函数而这个工厂函数实例化了MapBuilder这个类, 所以map_builder_->GetTrajectoryBuilder(trajectory_id)调用的是MapBuilder的GetTrajectoryBuilder. (有一种峰回路转的感觉), 返回trajectory_builders_的轨迹id为trajectory_id的指针,即:而trajectory_builders_在map_builder.cc中被压入absl::make_unique, 如下所以最终SensorBridge的trajectory_builder_实际上是CollatedTrajectoryBuilder的地址, 所以SensorBridge的trajectory_builder_的AddSensorData实际上是把数据添加到了CollatedTrajectoryBuilder里面, 而不是GlobalTrajectoryBuilder或者LocalTrajectoryBuilder(这三个TrajectoryBuilder都继承于TrajectoryBuilderInterface)这块地方难就难在子类可以用父类代替, 搞不清到底是调用的哪个子类的成员函数.既然上面调用的是CollatedTrajectoryBuilder, 那咱们看看CollatedTrajectoryBuilder这个类的AddSensorData再看看sensor::MakeDispatchable, 在dispatchable.h文件中这个函数通过模板, 实现了一个函数处理多个类型, 也就是说可以用一个函数去分发上到激光雷达,下到IMU的数据, 值得学习.CollatedTrajectoryBuilder::AddData又调用sensor_collator_->AddSensorData, 用std::move(data), 把data移动给AddSensorData, 给某个Trajectory加入传感器数据. 而这个sensor_collator_定义如下:sensor::CollatorInterface* const sensor_collator_;又是用父类代替子类, 在CollatedTrajectoryBuilder的构造函数中实现实例化, 这个sensor_collator_实际上是sensor::Collator, 原因是在map_builder.cc中的MapBuilder构造函数中有如下一段程序一般collate_by_trajectory设置为false, 所以是absl::make_uniq免费云主机域名ue<:collator>, 即sensor的Collator咱们进到collator这个类中看看, 发现这个类继承于CollatorInterface, 再看看collator这个类的AddSensorData作用是 向队列中添加传感器数据, 啥是队列?以后将在线程池部分详细说说. 现在简单看看queue_是Cartographer的任务队列, 用于线程池多任务序列的储存与处理.也就是说Collator::AddSensorData负责把data放在任务队列中等待处理并赋一个key, 并不负责处理数据, 所以咱们再往前看看, 看一下OrderedMultiQueue这个类的关于添加数据的成员函数-AddOrderedMultiQueue这个类定义在ordered_multi_queue.cc中, 添加数据是在Add成员函数实现的:可以发现Add就是生产者, 用于生成并传递可用数据.Dispatch()这个成员函数负责数据分发, 将处于数据队列中的数据根据时间依次传入回调函数. 这个后面再看, 咱们先看看it->second.queue.Push(std::move(data));这个部分.it这个变量就是queues_最后一个数据,可以理解为最新的一个数据, 而OrderedMultiQueue的queue_和上一小节提到的Collator是不同的, 在OrderedMultiQueue中的queue_是定义为一个std::map所以it->second就是Queue, 而Queue是个定义在OrderedMultiQueue的结构体Push 也就是把data压入Queue这个结构体中,然后生成map形成个对列. 而这个Push不是push_back, 这个Push是Cartographer自己定义的一种压栈方法. 定义在blocking_queue.h中,如下…发现Push作用相当于阻塞者, 使用了mutex_.Await和锁用来阻塞数据传入. 看看QueueNotFullCondition这个函数就一目了然了. 当队列为无限大或者小于queue_size_的时候返回true.除了阻塞作用, 最大的所用就是把数据压入deque_, 咱们再看看这个deque_发现它是std::deque这个基础类型,类型决定于模板T, GUARDED_BY(mutex_)表示这个数据在使用的时候必须要上锁, 否则就会报错.所以Push的作用就是有阻塞作用的push_back, 负责把data压入OrderedMultiQueue的queue_, 并且在队列满的时候阻塞.咱们再回到OrderedMultiQueue类的Add函数的Dispatch中, 看看Dispatch函数有关数据分发的部分Peek是取出队列最前面的一个数据. callback定义在头文件中, 这个是std::function封装的一个函数而Callback这个函数到底是啥呢? 这个要看OrderedMultiQueue::AddQueue这个成员函数我们看到这个函数把参数传入的callback传入queues_的callback. 那么是谁调用的OrderedMultiQueue的AddQueue这个函数呢? 是Collator的AddTrajectory调用的!我们在回溯到Collator这个类看看Collator的AddTrajectory成员函数我们看到它调用了queue_的AddQueue, 而queue_就是OrderedMultiQueue的实例化 ,所以这里的AddQueue是OrderedMultiQueue的AddQueue. Callback又是个lambda函数这个lambda函数调用的是传入的callback函数, 而这个Collator::AddTrajectory是谁调用的呢?实际上是CollatedTrajectoryBuilder. 在CollatedTrajectoryBuilder的构造函数中就实现了Collator这个类的初始化, 并且调用了AddTrajectory这个函数所以传入的参数是是HandleCollatedSensorData这个函数.这个函数才是真正的消费者. 看一下HandleCollatedSensorData传入sensor data的部分:这个函数的作用是处理按照时间顺序分发的传感器数据, 在进去到Data的AddToTrajectoryBuilder里看看Data这个类又是个基类, 里面有个纯虚函数这个基类只有一个子类: Dispatchable. 进到这个子类中去看看AddToTrajectoryBuilder.所以这里的trajectory_builder指的就是CollatedTrajectoryBuilder::HandleCollatedSensorData中调用的wrapped_trajectory_builder_. 而这个wrapped_trajectory_builder_是啥呢?这又要回溯到CollatedTrajectoryBuilder的初始构造中去, 在map_builder.cc中实现我们看到wrapped_trajectory_builder_实际上是CreateGlobalTrajectoryBuilder2D, 这个在上回也说到就是GlobalTrajectoryBuilder这个类CreateGlobalTrajectoryBuilder2D, 返回Cartographer的前端和后端.到这里, 整个Cartographer的传感器数据传递过程也就明了了.从GlobalTrajectoryBuilder2D开始, 数据才真正走到SLAM的前端与后端部分.到此,相信大家对“C++Cartographer源码中关于Sensor的数据走向分析”有了更深的了解,不妨来实际操作一番吧!这里是百云主机网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: 如何在php中关闭转义字符

这篇“如何在php中关闭转义字符”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何在php中关闭转义字符”文章吧。 关闭转义字符的方法:使用ph…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 06/11 16:59
Next 06/11 17:00

相关推荐