使用CyberRT写第一个代码

0. 简介

计算框架是自动驾驶系统中的重中之重,也是整个系统得以高效稳定运行的基础。为了实时地完成感知、决策和执行,系统需要一系列的模块相互紧密配合,高效地执行任务流。由于各种原因,这些模块可能位于不同进程,也可能位于不同机器。这就要求计算框架中具有灵活的、高性能的通信机制。Apollo在3.5版本中推出了Cyber RT替代了原先的ROS。

和ROS & ROS2中类似,Cyber RT中支持两种数据交换模式:一种是Publish-Subscriber模式,常用于数据流处理中节点间通信。即发布者(Publisher)在channel(ROS中对应地称为topic)上发布消息,订阅该channel的订阅者(Subscriber)便会收到消息数据;另一种就是常见的Service-Client模式,常用于客户端与服务端的请求与响应。Node是整个数据拓扑网络中的基本单元。一个Node中可以创建多个读者/写者,服务端/客户端。读者和写者分别对应Reader和Writer,用于Publish-Subscribe模式。服务端和客户端分别对应Service和Client,用于Service-Client模式。

1. CyberRT BUILD文件

Apollo 是优秀的自动驾驶开发框架,出自百度之手,目前的Apollo是基于Cyber RT通信键实现的。Apollo的具体安装可以看Apollo 6.0 安装完全指南。Apollo (或者说CyberRT)使用 Bazel 进行代码构建,Bazel 是由 Google 开源的一款高效的软件构建工具。使用 Bazel 时,我们需要为每个参与构建的目录创建一个 BUILD 文件来定义一些构建规则,BUILD 文件使用类似 Python 的语法。其优点如下:

  • Bazel 仅重建必要的内容。借助高级的本地和分布式缓存,优化的依赖关系分析和并行执行,可以获得快速而增量的构建。
  • 构建和测试 Java、C++、Android、iOS、和其他各种语言平台。Bazel 可以在 Windows、macOS 和 Linux 上运行。
  • Bazel 帮助你扩展你的组织、代码库和持续集成系统。它可以处理任何规模的代码库。

1.1 Bazel项目结构

在构建项目之前,您需要设置其工作区。工作区是一个保存项目源文件和 Bazel 构建输出的目录。它还包含 Bazel 识别为特殊的文件:

  • 该WORKSPACE文件将目录及其内容标识为 Bazel 工作区并位于项目目录结构的根部,
  • 一个或多个BUILD文件,告诉 Bazel 如何构建项目的不同部分。
    其目录结构大致如下:
project
|-- pkg
|   |-- BUILD
|   |-- src.cc
|-- WORKSPACE

1.2 了解BUILD文件

一个BUILD文件包含多种不同类型的 Bazel 指令。最重要的类型是构建规则,它告诉 Bazel 如何构建所需的输出,例如可执行二进制文件或库。文件中构建规则的每个实例BUILD称为目标,并指向一组特定的源文件和依赖项。一个目标也可以指向其他目标。例如:

cc_binary(
    name = "hello_world",
    srcs = ["hello_world.cc"],
)

在示例中,hello-world目标实例化 Bazel 的内置 cc_binary规则。该规则告诉 Bazel 从源文件构建一个独立的可执行二进制文件。

BUILD文件中常见的两种规则:

  • cc_binary:表示要构建对应文件变成二进制文件。
    • name:表示构建完成后的文件名字。
    • srcs:表示要构建的源文件。
    • deps:表示构建该文件所依赖相关的库。
  • cc_library:表示要构建对应文件变成相关依赖库。
    • hdrs:表示的是源文件对应的头文件路径。
    • package(default_visibility = [“//visibility:public”])这段代码则表示该文件是公开的,能被所有对象找到并依赖。
load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "libcommon_component_example.so",
    deps = [":common_component_example_lib"],
    linkopts = ["-shared"],
    linkstatic = False,
)

cc_library(
    name = "common_component_example_lib",
    srcs = [
        "common_component_example.cc",
    ],
    hdrs = [
        "common_component_example.h",
    ],
    deps = [
        "//cyber",
        "//cyber/examples/proto:examples_cc_proto",
    ],
)

cpplint()

2. 算法组件创建(内容引用文档)

Apollo Cyber 运行时框架 (Apollo Cyber RT Framework) 是基于组件概念来构建的。每个组件都是 Cyber 框架的一个构建块,它包括一个特定的算法模块, 此算法模块处理一组输入数椐并产生一组输出数椐。要创建并启动一个算法组件,需要通过以下 4 个步骤:

2.1 初如化组件的文件结构

例如组件的根目录为/apollo/cyber/examples/common_component_example/需要创建以下文件:

Header file: common_component_example.h
Source file: common_component_example.cc
Build file: BUILD
DAG dependency file: common.dag
Launch file: common.launch

2.2 实现组件类

2.2.1 实现组件头文件

如何实现common_component_example.h:

继承 Component 类
定义自己的 Init 和 Proc 函数。Proc 需要指定输入数椐类型。
使用CYBER_REGISTER_COMPONENT宏定义把组件类注册成全局可用。

#include <memory>
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/examples/proto/examples.pb.h"

using apollo::cyber::examples::proto::Driver;
using apollo::cyber::Component;
using apollo::cyber::ComponentBase;

class CommonComponentSample : public Component<Driver, Driver> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<Driver>& msg0,
            const std::shared_ptr<Driver>& msg1) override;
};

CYBER_REGISTER_COMPONENT(CommonComponentSample)
2.2.2 实现组件源文件

对于源文件 common_component_example.ccInit 和 Proc 这两个函数需要实现。

#include "cyber/examples/common_component_example/common_component_example.h"
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"

bool CommonComponentSample::Init() {
  AINFO << "Commontest component init";
  return true;
}

bool CommonComponentSample::Proc(const std::shared_ptr<Driver>& msg0,
                               const std::shared_ptr<Driver>& msg1) {
  AINFO << "Start common component Proc [" << msg0->msg_id() << "] ["
        << msg1->msg_id() << "]";
  return true;
}

2.3 设置配置文件

2.3.1 配置 DAG 依赖文件

在 DAG 依赖配置文件 (例如 common.dag) 中配置下面的项:

  • Channel names: 输入输出数椐的 Channel 名字
  • Library path: 此组件最终编译出的库的名字
  • Class name: 此组件的入口类的名字
# Define all components in DAG streaming.
component_config {
    component_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"
            }
        }
    }
}
2.3.2 配置 launch 启动文件

在 launch 启动文件中 (common.launch), 配置下面的项:

  • 组件的名字
  • 上一步创建的 dag 配置的名字。
  • 组件运行时所在的进程目录。
<cyber>
    <component>
        <name>common</name>
        <dag_conf>/apollo/cyber/examples/common_component_example/common.dag</dag_conf>
        <process_name>common</process_name>
    </component>
</cyber>

2.4 启动组件

通过下面的命令来编译组件:

bash /apollo/apollo.sh build

然后配置环境:

cd /apollo/cyber
source setup.bash

有两种方法来启动组件:

  • 使用 launch 文件来启动 (推荐这种方式)
cyber_launch start /apollo/cyber/examples/common_component_example/common.launch
  • 使用 dag 文件来启动
mainboard -d /apollo/cyber/examples/common_component_example/common.dag

3. 订阅发布与服务客户

3.1 节点 Node

Node 是整个数据拓扑网络中的基本单元。Node 对象会根据需要创建和管理 Writer,Reader,Service 和 Client 对象。Reader 和 Writer,用于发布—订阅模式。Service 和 Client,用于服务—客户模式。

在cyberRT中创建方法如下:

std::unique_ptr<Node> apollo::cyber::CreateNode(const std::string& node_name, const std::string& name_space = "");

参数:

  • node_name:node 的名称,必须保证全局是唯一的,不能重名
  • name_space:node 所在的命名空间。主要用来防止node_name发生冲突。
  • name_space默认为空,加入命名空间后,node的name 变成 /namespace/node_name
  • return value:node的独占智能指针

在cyber::Init()还未执行前,系统还处于未初始化状态,没法创建node,会直接返回nullptr

3.2 发布者

writer 是CyberRT中,用来发布消息最基本的方法。每个writer对应一个特定消息类型的channel。writer可以通过Node类中的 CreateWriter接口创建。具体如下:

template <typename MessageT>
   auto CreateWriter(const std::string& channel_name)
       -> std::shared_ptr<Writer<MessageT>>;
template <typename MessageT>
   auto CreateWriter(const proto::RoleAttributes& role_attr)
       -> std::shared_ptr<Writer<MessageT>>;

参数:

  • channel_name:字面意思,就是该writer对应的channel_name
  • MessageT:写入的消息类型
  • return value:std::shared_ptr<writer></writer

3.3 订阅者

reader是接收消息最基础的方法。reader必须绑定一个回调函数。当channel中的消息到达后,回调会被调用。
reader 可以通过Node类中 CreateReader接口创建。具体如下:

   template <typename MessageT>
   auto CreateReader(const std::string& channel_name, const std::function<void(const std::shared_ptr<MessageT>&)>& reader_func)
       -> std::shared_ptr<Reader<MessageT>>;

   template <typename MessageT>
   auto CreateReader(const ReaderConfig& config,
                     const CallbackFunc<MessageT>& reader_func = nullptr)
       -> std::shared_ptr<cyber::Reader<MessageT>>;

   template <typename MessageT>
   auto CreateReader(const proto::RoleAttributes& role_attr,
                     const CallbackFunc<MessageT>& reader_func = nullptr)
   -> std::shared_ptr<cyber::Reader<MessageT>>;

参数:

  • channel_name:字面意思,就是该reader对应的channel_name
  • MessageT:读取的消息类型
  • reader_func:回调函数
  • return value:std::shared_ptr<reader></reader

3.4 服务端&客户端

除 Reader/Writer 外,Cyber RT 还提供了用于模块通信的 Service/Client 模式。它支持节点之间的双向通信。当对服务发出请求时,客户端节点将收到响应。

// filename: cyber/examples/service.cc
#include "cyber/cyber.h"
#include "cyber/examples/proto/examples.pb.h"

using apollo::cyber::examples::proto::Driver;

int main(int argc, char* argv[]) {
  apollo::cyber::Init(argv[0]);
  std::shared_ptr<apollo::cyber::Node> node(
      apollo::cyber::CreateNode("start_node"));
  auto server = node->CreateService<Driver, Driver>(
      "test_server", [](const std::shared_ptr<Driver>& request,
                        std::shared_ptr<Driver>& response) {
        AINFO << "server: I am driver server";
        static uint64_t id = 0;
        ++id;
        response->set_msg_id(id);
        response->set_timestamp(0);
      });
  auto client = node->CreateClient<Driver, Driver>("test_server");
  auto driver_msg = std::make_shared<Driver>();
  driver_msg->set_msg_id(0);
  driver_msg->set_timestamp(0);
  while (apollo::cyber::OK()) {
    auto res = client->SendRequest(driver_msg);
    if (res != nullptr) {
      AINFO << "client: response: " << res->ShortDebugString();
    } else {
      AINFO << "client: service may not ready.";
    }
    sleep(1);
  }

  apollo::cyber::WaitForShutdown();
  return 0;
}

3.5 参数服务

参数服务被用于节点之间共享的数据,并提供了诸如基本操作setgetlist。参数服务基于Service实现,并包含服务(service)和客户端(client)。

通过 cyber 传递的所有参数都是apollo::cyber::Parameter对象,下表列出了5种受支持的参数类型。

参数类型C++数据类型protobuf数据类型
apollo::cyber::proto::ParamType::INTint64_tint64
apollo::cyber::proto::ParamType::DOUBLEdoubledouble
apollo::cyber::proto::ParamType::BOOLboolbool
apollo::cyber::proto::ParamType::STRINGstd::stringstring
apollo::cyber::proto::ParamType::PROTOBUFstd::stringstring
apollo::cyber::proto::ParamType::NOT_SET--

除了上述5种类型外,Parameter还支持使用protobuf对象作为传入参数的接口。执行序列化后处理该对象,并将其转换为STRING类型以进行传输。具体示例:

#include "cyber/cyber.h"
#include "cyber/parameter/parameter_client.h"
#include "cyber/parameter/parameter_server.h"

using apollo::cyber::Parameter;
using apollo::cyber::ParameterServer;
using apollo::cyber::ParameterClient;

int main(int argc, char** argv) {
  apollo::cyber::Init(*argv);
  std::shared_ptr<apollo::cyber::Node> node =
      apollo::cyber::CreateNode("parameter");
  auto param_server = std::make_shared<ParameterServer>(node);
  auto param_client = std::make_shared<ParameterClient>(node, "parameter");
  param_server->SetParameter(Parameter("int", 1));
  Parameter parameter;
  param_server->GetParameter("int", &parameter);
  AINFO << "int: " << parameter.AsInt64();
  param_client->SetParameter(Parameter("string", "test"));
  param_client->GetParameter("string", &parameter);
  AINFO << "string: " << parameter.AsString();
  param_client->GetParameter("int", &parameter);
  AINFO << "int: " << parameter.AsInt64();
  return 0;
}

3.6 Log API(日志)

3.6.1 日志库

cyber 日志库建立在glog之上。需要包括以下头文件:

#include "cyber/common/log.h"
#include "cyber/init.h"
3.6.2 日志配置

默认全局配置路径:cyber / setup.bash

以下配置可以由devloper修改:

export GLOG_log_dir=/apollo/data/log
export GLOG_alsologtostderr=0
export GLOG_colorlogtostderr=1
export GLOG_minloglevel=0

 点击使用CyberRT写第一个代码 - 古月居可查看全文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513729.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio 打开Logcat界面

在平时调试过程中查看调试日志需要打开 Android Studio Logcat界面。 每次安装AS都会忘记&#xff0c;自己备注一下。 AS->View->Tool Windows->Logcat

Windows12安装Docker

环境及工具&#xff08;文末提供&#xff09; Docker Desktop Installer.exe &#xff08;官网&#xff09; 一、查看windows相关配置 查看是否开启相应的功能&#xff0c;如果没有需要开启&#xff0c;然后重启电脑 打开任务管理器&#xff08;CTRLSHIFTESC&#xff09;-&g…

【Linux】进程控制详解

目录 前言 进程创建 认识fork 写时拷贝 再谈fork 进程终止 进程退出码 用代码来终止进程 常见的进程终止的方式 exit _exit 进程等待 进程等待的必要性 进程等待的方式 wait waitpid 详解status参数 详解option参数 前言 本文适合有一点基础的人看的&#…

【JavaScript】函数 ⑥ ( 使用 arguments 获取所有实参 | arguments 内置对象 | 伪数组概念 )

文章目录 一、使用 arguments 获取所有实参1、arguments 内置对象2、伪数组概念3、arguments 实参遍历4、arguments 代码示例 - 基本使用5、arguments 代码示例 - 遍历实参 一、使用 arguments 获取所有实参 1、arguments 内置对象 在 定义 JavaScript 函数 时 , 有时 不确定 形…

Spring定义Bean对象笔记(二)

前言&#xff1a;上一篇记录了通过XML文件来定义Bean对象&#xff0c;这一篇将记录通过注解和配置类的方式来定义Bean对象。 核心注解&#xff1a; 定义对象&#xff1a;Component,Service,Repository,Controller 依赖注入&#xff1a; 按类型&#xff1a;Autowired 按名称&am…

cesium 加载mapbox底图 黑色主题底图 84底图

cesium提供MapboxStyleImageryProvider&#xff0c;加载mapbox的影像图层&#xff0c;底图是84坐标系。 viewer.imageryLayers.addImageryProvider(new Cesium.MapboxStyleImageryProvider({styleId: dark-v11,accessToken: mapbox的token})); 效果图&#xff1a;加载mapbox黑…

简单使用bootstrap-datepicker日期插件

目录 下载datepicker 方式一&#xff1a; 方式二&#xff1a; 下载依赖 下载bootstarp.js 下载jquery 使用示例 日期选择 单独选择年 单独选择月 单独选择日 设置截止日期 设置默认日期 总结 下载datepicker 方式一&#xff1a; 下载地址 GitHub - uxsolution…

java运行时内存

从jdk1.7以及以后&#xff0c;静态变量和常量池存在堆空间。

关于搭建电商独立站跨境电商接入主流电商平台API商品接口对于商品功能模块的巨大应用

功能设计 首先我们来看下mall项目中商品功能的设计&#xff0c;主要包括商品管理、添加\编辑商品、商品分类、商品类型、品牌管理等功能&#xff0c;这里的功能同时涉及前台商城和后台管理系统。 商品管理【接入主流电商平台商品API接口丰富自建商城商品】 在mall项目的后台管…

JavaScript基础(5)之对象的方法和调用

JavaScript基础5之对象的方法和调用 对象对象使用语法属性和访问方法和调用null遍历对象 内置对象Math属性方法 基本数据类型和引用数据类型堆栈空间分配区别&#xff1a;简单类型的内存分配复杂类型的内存分配 对象 对象是 JavaScript 数据类型的一种&#xff0c;之前已经学习…

填谷式无源PFC电路

目录&#xff1a; 1、概述 2、原理 1、概述 如果不采用PFC&#xff0c;那么典型开关模式电源的功率因数约为0.6&#xff0c;因而会有相当大的奇次谐波失真(第三谐波有时和基本谐波一样大)。令功率因数小于1以及来自峰值负载的谐波减少了运行设备可用的实际功率。为运行这些低…

书生·浦语2.0体系技术报告

前言 本文是书生浦语二期实战营课程视频笔记&#xff0c;如果需要详细视频教程可自行搜索。 InternLM2 InternLM2-Base 高质量和具有很强可塑性的模型基座&#xff0c;是模型进行深度领域适配的高质量起点InternLM2 在Base基础上&#xff0c;在多个能力方向进行了强化&#x…

WSL安装与使用

开启之后&#xff0c;会提示你重启电脑才能使配置生效&#xff0c;我们重启即可。 电脑重启后&#xff0c;打开Microsoft Store搜索WSL&#xff0c;既可以看到支持的操作系统&#xff0c;我们选择Ubuntu即可&#xff0c;我们选择第一个就可以。 随后我们打开&#xff0c;发现报…

提升办公效率,一起了解流程自定义表单优势

提高办公效率&#xff0c;可以一起了解低代码技术平台。对于很多中小型企业而言&#xff0c;低代码技术平台及流程自定义表单优势突出&#xff0c;是助力企业实现流程化办公&#xff0c;实现数字化转型的得力助手。流辰信息是专业研发开发平台、数据治理、数据分析等产品的服务…

软考 系统架构设计师系列知识点之云原生架构设计理论与实践(13)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之云原生架构设计理论与实践&#xff08;12&#xff09; 所属章节&#xff1a; 第14章. 云原生架构设计理论与实践 第3节 云原生架构相关技术 14.3.2 云原生微服务 1. 微服务发展背景 过去开发一个后端应用最为直接的方…

软件架构风格_4.虚拟机体系结构风格

虚拟机体系结构风格的基本思想是人为构建一个运行环境&#xff0c;在这个环境之上&#xff0c;可以解析与运行自定义的一些语言&#xff0c;这样来增加架构的灵活性。虚拟机体系结构风格主要包括解释器风格和规则系统风格。 1.解释器体系结构风格 一个解释器通常包括完成解释工…

Win11 绕过 TPM 或 CPU 检测

方法 1&#xff1a;修改注册表绕过 TPM 或 CPU 检测&#xff08;升级安装&#xff09; 如果你的硬件不完全符合安装 Windows 11 的基本硬件要求&#xff0c;可以通过修改注册表&#xff0c;在至少拥有 TPM 1.2 和不支持的 CPU 上升级安装 Windows 11 系统. 适用场景&#xff…

Nginx是什么?

一、什么是Nginx? Nginx是一个高性能的HTTP和反向代理Web服务器 二、Nginx有什么优点 Nginx稳定性好、资源消耗低、配置简单、功能丰富 1、作为Web服务器&#xff0c;Nginx处理静态文件、索引文件&#xff0c;自动索引的效率非常高 2、作为代理服务器&#xff0c;Nginx可以…

知识产权与标准化

根据希赛相关视频课程汇总整理而成&#xff0c;是个人软考的复习笔记&#xff0c;仅供参考 知识产权概述 知识产权类型&#xff1a; ①著作权&#xff08;版权、文学产权&#xff09; ② 工业产权&#xff08;产业产权&#xff09; 知识产权的特点&#xff1a; 无形性、独占性…

C++的并发世界(五)——线程状态切换

0.线程状态 初始化&#xff1a;该线程正在被创建&#xff1b; 就绪&#xff1a;该线程在列表中就绪&#xff0c;等待CPU调度&#xff1b; 运行&#xff1a;该线程正在运行&#xff1b; 阻塞&#xff1a;该线程被阻塞挂机&#xff0c;Blocked状态包括&#xff1a;pend&#xff…