Apollo:如何为apollo创建一个组件Component

本文的介绍主要基于cyber/examples/common_component_example中的例子.

目录 说明
README.md 介绍了如何编译运行该例子
common.launch 启动文件
common.dag 配置
common_component_example.cc 定义了一个组件
common_component_example.h
channel_test_writer.cc
channel_prediction_writer.cc
BUILD

README.md

CyberRT框架的通用组件示例

如何编译

  • 方法一:
cd /apollo
./apollo.sh build cyber
  • 方法二:
bazel build //cyber/examples/common_component_example/...

如何运行

配置环境

  • 打开cyber/setup.bash文件, 修改GLOG_alsologtostderr1 ,日志将会输出到stderr
  • 运行 source cyber/setup.bash

启动组件

  • 方法一(推荐):使用 Launch 文件启动
cyber_launch start cyber/examples/common_component_example/common.launch
  • 方法二:使用 DAG 文件启动
mainboard -d cyber/examples/common_component_example/common.dag

使用 DAG 文件启动

再打开两个终端,分别运行以下命令。

source cyber/setup.bash
export GLOG_alsologtostderr=1
/apollo/bazel-bin/cyber/examples/common_component_example/channel_test_writer

source cyber/setup.bash
export GLOG_alsologtostderr=1
/apollo/bazel-bin/cyber/examples/common_component_example/channel_prediction_writer

这时,如果成功,你会看到第一个终端有如下的输出:

I0331 16:49:34.736016 1774773 common_component_example.cc:25] [mainboard]Start common component Proc [1094] [766]
I0331 16:49:35.069005 1774775 common_component_example.cc:25] [mainboard]Start common component Proc [1095] [767]
I0331 16:49:35.402289 1774783 common_component_example.cc:25] [mainboard]Start common component Proc [1096] [768]

实现

common_component_example是一个简单的module,仅包含一个component CommonComponentSample, 定义在common_component_example.cc中。初始化的时候Init函数会被框架调用,Proc是消息回调函数,客户端发送的消息到达的时候会被框架自动调用。

common.launch

这是一个launch启动文件,可以配合cyber_launch 一起使用

  • name:组件的名字
  • dag_conf:配置dag文件的路径
  • process_name:运行组件时的进程名
<cyber>
    <module>
        <name>common</name>
        <dag_conf>/apollo/cyber/examples/common_component_example/common.dag</dag_conf>
        <process_name>common</process_name>
    </module>
</cyber>

指定了一个配置文件

common.dag

  • 内容如下:
# Define all coms in DAG streaming.
    module_config {
    
    
    module_library : "/apollo/bazel-bin/cyber/examples/common_component_example/libcommon_component_example.so"
    components {
    
    
        class_name : "CommonComponentSample"
        config {
    
    
            name : "common"
            readers {
    
    
                channel: "/apollo/prediction"
            }
            readers {
    
    
                channel: "/apollo/test"
            }
        }
      }
    }

  • 从下面common_component_example.cpp可以看到,common_component_example.cc中并没有main函数。没错,它并不是一个完整的可执行程序,编译出来的成果物是动态库libcommon_component_example.so。 通过下面的命令可以启动该动态库:
mainbooard -d cyber/examples/common_component_example/common.dag
  • Dag文件是module的配置文件,由protocolbuffers文件cyber/proto/dag_conf.proto定义,每个module都要有一个配套的dag文件,启动本module时使用。这里:module_library指明了要加载的动态库。

common_component_example.h

如何实现

  • 继承 Component 类
    在这里插入图片描述
  • 定义自己的 Init 和 Proc 函数。Proc 需要指定输入数椐类型。
    在这里插入图片描述
  • 使用CYBER_REGISTER_COMPONENT宏定义把组件类注册成全局可用。

在这里插入图片描述

在这里要特别强调的是:

  • 你创建的component是继承其他模块的,比如这边写的是Component
  • 那么这个Proc的参数类型也要一一对应各个通道的定义类型。
    在这里插入图片描述
  • 具体对应通道是哪里来的,则要看 apollo/cyber/examples/common_component_example/common.dag,这里面的两个readers 就代表着输入的两个通道信息

在这里插入图片描述

再比如PlanningComponent 组件

在这里插入图片描述

  • 那么component中的内容怎么来的呢,首先是根据你当前模块的驱动源,比如你是图像感知处理,没有图像你这模块就不能工作,那么其中就必须有drivers驱动层中camera

  • 另外,这个变量要怎么写呢,这得到你通道定义变量的proto中去看。比如你传的是类型,那你先找到对应的proto,即/apollo/modules/prediction/proto/prediction_obstacle.proto

  • 进入你可以看到:

syntax = "proto2";
package apollo.prediction;
message PredictionObstacles {
    
    
 ···
}
  • 看到package是apollo.prediction,那么你写的时候就是< prediction::PredictionObstacles>

  • 当然,你如果已经包含了proto的头文件,理论上你只要写< PredictionObstacles>,但是如果你包含头文件中有同样的变量,这时候就需要具体指定了。

common_component_example.cc

实现了 Init 和 Proc

在这里插入图片描述

原理

Cyber RT由dag指定配置文件

Cyber RT的模块加载机制主要分为两部分:
- 编译期进行模块注册
- 运行期加载模块并初始化

编译期工作

common_component_example.h中调用CYBER_REGISTER_COMPONENT(CommonComponentSample)注册component。

将宏CYBER_REGISTER_COMPONENT逐步展开后的代码如下(cyber\class_loader\class_loader_register_macro.h),其中:

  • 模板参数Derived的值为CommonComponentSample,即本component对应的类名
  • 模板参数Base的值为apollo::cyber::ComponentBase,是CommonComponentSample的间接基类
  • UniqueID是一个唯一的整数,由编译期宏__COUNTER__实现

在这里插入图片描述
上面的宏定义了一个名为ProxyType##UniqueID的结构体,UniqueID保证了该结构体是全局唯一的,然后用这个结构体定义了一个静态全局变量

运行期工作

运行期主要负责加载动态库libcommon_component_example.so,然后创建类CommonComponentSample的实例对象并初始化

可执行程序mainboard通过dag文件加载对应的component,入口为cyber/mainboard/mainboard.cc中的main函数。类ModuleController负责加载动态库并初始化各component的实例。

bool ModuleController::LoadModule(const DagConfig& dag_config) {
    
    
  const std::string work_root = common::WorkRoot();

  for (auto module_config : dag_config.module_config()) {
    
    
    ...
    // 1. 加载动态库libcommon_component_example.so
    class_loader_manager_.LoadLibrary(load_path);

    // 2. 根据配置信息初始化各个component实例
    for (auto& component : module_config.components()) {
    
    
      const std::string& class_name = component.class_name();
      std::shared_ptr<ComponentBase> base =
          class_loader_manager_.CreateClassObj<ComponentBase>(class_name);
      if (base == nullptr || !base->Initialize(component.config())) {
    
    
        return false;
      }
      component_list_.emplace_back(std::move(base));
    }
    ... 
  }
  
  return true;
}

参考

猜你喜欢

转载自blog.csdn.net/zhizhengguan/article/details/129370558
今日推荐