场景
无人车上的传感器数据可能需要被融合,比如在车辆上安装了多颗雷达,不同雷达由于安装位置与自身参数差异,可探测的角度、范围、距离等都是不尽相同的,现在需要将不同雷达感知到的数据融合在一起以建立车辆所处的完整环境,可以使用组件实现数据融合。
概念
组件是 Cyber RT 提供的用于构建应用程序模块的基类,
作用
可用于实现数据过滤、融合。
实例1
需求:启动之前的消息发布节点,对订阅到的数据进行过滤,只获取其中的姓名和年龄信息并输出到终端
准备:保证之前话题通信中的发布方可以正常运行;在 demo_cc 目录下新建文件夹:component_common01,并在 component_common01 下新建BUILD文件。
实现流程
上述需求可以通过组件实现只要包括如下步骤:
1.自定义类继承 Component类,并重写其 Init()与 Proc() 函数;
2.编写 dag 文件声 launch 文件;
3.编辑BUILD文件;
4.编译执行。
1.继承并重写 Component 类
component_common01 目录下新建 C++ 文件 common_cpt.h,输入如下内容:
/*
编写一个Student学生信息过滤组件
实现:
1 包含被依赖的其他头文件
2 自定义继承 compoenent
3 在 cyberRT 中 注册组建
*/
//1 包含被依赖的其他头文件
#include "cyber/component/component.h"
#include "cyber/demo_base_proto/student.pb.h"
using apollo::cyber::Component;
using apollo::cyber::demo_base_proto::Student;
//2 自定义继承 compoenent
/*
1. Component 模板需要与处理的消息类型一致;
2.最多支持四种通道数据
*/
class CommonCpt : public Component<Student> {
public:
bool Init() override;
bool Proc(const std::shared_ptr<Student> & stu) override;
};
//3 在 cyberRT 中 注册组建
CYBER_REGISTER_COMPONENT(CommonCpt)
component_common01 目录下新建 C++ 文件 common_cpt.cc,输入如下内容:
#include "cyber/demo_cc/component_common01/common_cpt.h"
//初始化函数
bool CommonCpt::Init(){
AINFO<<"-----------Init-------------";
return true;
}
//消息处理,订阅到 Student 消息时,会调用该函数
bool CommonCpt::Proc(const std::shared_ptr<Student> & stu){
//处理逻辑
AINFO<<"name = "<<stu->name()<<"; age = "<<stu->age();
return true;
}
BUILD 文件
cc_library(
name="common_cpt_lib",
srcs=["common_cpt.cc"],
hdrs=["common_cpt.h"],
deps=[
"//cyber",
"//cyber/demo_base_proto:student_cc"
]
)
cc_binary(
name = "lib_common_cpt.so",
deps = [":common_cpt_lib"],
linkshared = True,
linkstatic = False,
)
使用的模板与订阅的消息类型一致,
Init()函数是用于初始化的,只会执行一次。
proc()函数在订阅到消息时就会被回调执行。
2.编写 dag 文件与 launch 文件
在 component_common01 目录下创建 cpt.dag 文件,内容如下:
module_config{
module library: "/apollo/bazel-bin/cyber/demo_cc/component_common01/lib_common_cpt.so"
components {
class_name:"CommonCpt"
config{
name : "my_common"
readers{
channel: "chatter"
}
}
}
}
在 component_common01 目录下创建 cpt.launch 文件,内容如下:
<cyber>
<module>
<name>my_module</name>
<dag_config>/apollo/cyber/demo_cc/component_common01/cpt.dag</dag_config>
<process_name>my_cpt</process_name>
</module>
</cyber>
实例2
需求:现无人车前后各安装了一颗雷达,雷达可以发布障碍物消息,开要将两颗雷达发送的消息进行融合
准备:编写两个节点,分别横拟前后雷达发送障碍物消息:在 demo_cc 目录下新建文件夹:component_common02,并在 component_common02 下新里BUILD文件。
模拟雷达消息发布实现如下:
1.定义雷达消息
demo_base_proto 目录下新建 laser.proto,输入如下内容:
// 1 声明版本
syntax = "proto2";
// 2 声明包
package apollo.cyber.demo_base_proto;
// 3 创建消息
message Laser {
//雷达消息编号
optional int64 seq = 1;
//障碍物个数
optional int64 count = 2;
}
配置文件添加如下内容:
#############组件
proto_library(
name="laser_proto",
srcs=["laser.proto"]
)
cc_proto_library(
name = "laser_cc",
deps = [":laser_proto"],
)
然后编译,生成 laser.pb.cc 等文件。
2.分别编写前后雷达消息发送节点
component_common2 目录下新建C++文件 front_laser.cc,输入如下内容:
/*
需求:模拟雷达发布节点,发布laser 消息
实现:
1 包含头文件;
2 初始化 cyber 框架;
3 创建节点
4 创建发布方
5 组织并发布数据
6 等待关闭,释放资源
*/
#include "cyber/cyber.h"
#include "cyber/demo_base_proto/laser.pb.h"
using apollo::cyber::demo_base_proto::Laser;
int main(int argc, char const *argv[])
{
/* code */
apollo::cyber::Init(argv[0]);
AINFO<<"前雷达节点———";
auto front_laser_node = apollo::cyber::CreateNode("front_bode");
auto front_laser = front_laser_node->CreateWriter<Laser>("/front/laser");
apollo::cyber::Rate rate(0.5);
uint64_t seq=0;
while(apollo::cyber::OK()){
seq++;
auto laser_ptr=std::make_shared<Laser>();
laser_ptr->set_seq(seq);
laser_ptr->set_count(2);
front_laser->Write(laser_ptr);
rate.Sleep();
}
apollo::cyber::WaitForShutdown();
return 0;
}
BUILD 文件:
cc_binary(
name = "front_laser",
srcs= ["front_laser.cc"],
deps = [
"//cyber",
"//cyber/demo_base_proto:laser_cc"
],
)
编译执行
back_laser.cc 内容与 front_laser.cc 类似。
3.重写组件
laser_cpt.h:
/*
组件相关的头文件
1 包含其他相关头文件
2 自定义继承组件,重写 Init() 和 Proc() 函数
3 在 cyber 中注册组件
*/
#include "cyber/component/component.h"
#include "cyber/demo_base_proto/laser.pb.h"
using apollo::cyber::Component;
using apollo::cyber::demo_base_proto::Laser;
class LaserCpt: public Component<Laser,Laser>{
public:
bool Init() override;
bool Proc(const std::shared_ptr<Laser>& front, const std::shared_ptr<Laser>& back) override;
private:
std::shared_ptr<apollo::cyber::Writer<Laser>> writer = nullptr;
uint64_t seq;
};
CYBER_REGISTER_COMPONENT(LaserCpt)
laser_cpt.cc:
#include "cyber/demo_cc/component_common02/laser_cpt.h"
bool LaserCpt::Init(){
AINFO<<"-----------------初始化发布方-----------------";
//初始化一个发布者
//this->node_->CreateWriter
writer = this->node_->CreateWriter<Laser>("laser");
seq=0;
return true;
}
bool LaserCpt::Proc(const std::shared_ptr<Laser>& front, const std::shared_ptr<Laser>& back){
seq++;
//数据融合
//解析被融合数据
uint64_t front_seq = front->seq();
uint64_t front_count= front->count();
uint64_t back_seq = back->seq();
uint64_t back_count = back->count();
uint64_t sum = front_count + back_count;
AINFO << "front_seq="<<front_seq <<" ---- back_seq="<<back_seq;
AINFO<<"sum = "<<sum;
//数据写出
auto laser_ptr = std::make_shared<Laser>();
laser_ptr->set_count(sum);
laser_ptr->set_seq(seq);
writer->Write(laser_ptr);
return true;
}
4.dag 文件与launch 文件
dag:
module_config{
module library: "/apollo/bazel-bin/cyber/demo_cc/component_common02/liblaser_cpt.so"
components {
class_name:"LaserCpt"
config{
name : "my_laser"
readers{
channel: "/front/laser"
}
readers{
channel: "/back/laser"
}
}
}
}
launch:
<cyber>
<module>
<name>my_laser</name>
<dag_config>/apollo/cyber/demo_cc/component_common02/cpt2.dag</dag_config>
<process_name>my_laser</process_name>
</module>
</cyber>
5.编译运行
实例3
需求:周期性的执行某种操作
准备:在demo_cc 目录下新建文件夹:component_timer下新建BUILD文件
实现流程:
上述需求可以通过定时器组件实现,只需包括以下步骤:
1.自定义类继承 TimerComponent 类,并重写其 Init() 与 Proc() 函数;
2.编写 dag 文件与 launch文件;
3.编辑 BUILD 文件;
4.编译执行。
1.继承并重写 TimerComponent 类
component_timer 目录下新建 C++ 文件 timer_cpt.h,输入如下内容:
/*
需求:实现定时器,循环控制台打印出语句
实现:
1 包含头文件
2 自定义继承 TimerComponent 重写 Init() 与 Proc()
3 在 Cyber 中注册组件
*/
#include "cyber/component/timer_component.h"
#include "cyber/component/component.h"
using apollo::cyber::TimerComponent;
class MyTimer: public TimerComponent{
public:
bool Init() override;
bool Proc() override;
private:
uint64_t seq;
};
CYBER_REGISTER_COMPONENT(MyTimer)
component_timer 目录下新建 C++ 文件 timer_cpt.cc,输入如下内容:
#include "cyber/demo_cc/component_timer/timer_cpt.h"
bool MyTimer::Init(){
AINFO <<"-----------------------timer Component Init";
seq=0;
return true;
}
bool MyTimer::Proc(){
seq++;
AINFO<<"-------------------"<<seq;
return true;
}
新建 BUILD 文件,输入如下内容:
# load("//tools/install:install.bzl","install")
# 配合组件
cc_library(
name = "timer_cpt_lib",
deps = [
"//cyber",
],
srcs = ["timer_cpt.cc"],
hdrs = ["timer_cpt.h"],
)
cc_binary(
name = "libtimer_cpt.so",
linkshared = True,
linkstatic = False,
deps = [":libtimer_cpt"],
)
# 将 dag 与 launch 划分在 同一文件组
filegroup(
name="conf",
srcs=[
":timer.dag",
":timer.launch",
],
)
# 安装函数安装
install(
name="install",
data=[
":conf",
],
runtime_dest="cyber/demo_cc/component_timer",
targets=[
":libtimer_cpt.so",
],
)
2.dag 文件 与 launch 文件:
dag:
module_config{
module library: "/apollo/bazel-bin/cyber/demo_cc/component_timer/libtimer_cpt.so"
components {
class_name:"MyTimer"
config{
name : "timer"
interval: 10
}
}
}
launch:
<cyber>
<module>
<name>MyTimer</name>
<dag_config>/apollo/cyber/demo_cc/component_timer/timer.dag</dag_config>
<process_name>MyTimer</process_name>
</module>
</cyber>