【Apollo星火计划】—— Cyber基础概念|通信机制

2023-11-05 21:59

本文主要是介绍【Apollo星火计划】—— Cyber基础概念|通信机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

文章目录

  • 前言
    • 相关代码整理
  • 基本概念1
    • Cyber简介
    • 通信构成
    • Bazel简介
  • TEST1. 构建单包工程
  • TEST2. 构建多包工程
  • 基本概念2
    • 话题通信
    • 服务通信
    • 参数通信
    • 数据通信基础Protobuf
      • Protobuf简介
      • Protobuf 文件编写
      • Protobuf编译
  • TEST3. protobuf实验
  • TEST4.C++话题通信实践案例
  • TEST5.C++服务通信实践案例
  • TEST6.C++参数服务器通信实践案例
  • 参考

前言

因为网上有不少关于cyber基础介绍的文章了,本文便不加赘述。本文主要关注cyber基础以及通信相关例子的实现.
课程地址: https://apollo.baidu.com/community/course/outline/329?activeId=10200
更多还请参考:
[1] Apollo星火计划学习笔记——第三讲(Apollo Cyber RT 模块详解与实战)https://blog.csdn.net/sinat_52032317/article/details/126924375
[2] 第一章:Cyber RT基础入门与实践https://apollo.baidu.com/community/article/1093 [TEST1 & TEST2]
[3] 第二章:Cyber RT通信机制解析与实践https://apollo.baidu.com/community/article/1094 [TEST3 & TEST4 & TEST5 & TEST6]

相关代码整理

链接: https://pan.baidu.com/s/1ENgXE4yQ1v4nJRjcfZtd8w?pwd=ht4c 提取码: ht4c

基本概念1

这部分更多内容请见 第一章:Cyber RT基础入门与实践https://apollo.baidu.com/community/article/1093

Cyber简介

Apollo Cyber是首个专为自动驾驶定制的高性能且开源的实时通信框架,它主要解决了自动驾驶系统的高并发低延迟高吞吐任务调度等问题,同时还提供了多种通信机制和用户级的协程,在资源有限的情况下会根据任务的优先级来进行调度处理。

通信构成

Node是Cyber的基础构建,每一个模块都会包含一个Node节点,模块之间通过Node节点来进行通信。Node之间的通信可以设定不同的模式,有Reader/WriterService/Client

Cyber采用的是分布式系统Node是通过Topology来管理的,每个Node都是这个拓扑图的顶点,其中每个Node顶点是通过Channel或者Service来连接的。Node节点是去中心化的,可以动态监控节点的增加和删除。Channel可以理解为一块共享内存,采用共享内存的通信方式,可以大大提高通信效率。

Bazel简介

Apollo采用Bazel进行编译.Bazel是Google研发的一款开源构建和测试工具,也是一种简单、易读的构建工具。其优点如下:

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

Bazel项目结构如下所示:

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

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

  • cc_binary:表示要构建对应文件变成二进制文件。

    • name:表示构建完成后的文件名字。
    • srcs:表示要构建的源文件。
    • deps:表示构建该文件所依赖相关的库。
  • cc_library:表示要构建对应文件变成相关依赖库。

    • hdrs:表示的是源文件对应的头文件路径。
    • package(default_visibility = [“//visibility:public”])这段代码则表示该文件是公开的,能被所有对象找到并依赖。

TEST1. 构建单包工程

流程

<1> 创建本节实验工程目录;
<2> 编写包管理相关的BUILD和cyberfile.xml文件文件
<3> 编写源文件及BUILD文件;
<4> 编译代码目录;
<5> 运行可执行文件。

  1. 创建本节实验工程目录(learning_cyber), 创建完成后如下
.
├── BUILD
├── cyberfile.xml
├── learning_cyber.BUILD //内容为空
└── test01└── demo01├── BUILD└── demo01.cc
  1. 编写包管理相关的BUILD和cyberfile.xml文件文件
    BUILD
load("//tools/install:install.bzl", "install", "install_src_files")install(name = "install",data = ["learning_cyber.BUILD","cyberfile.xml",],deps = ["//learning_cyber/test01/demo01:install",],
)install_src_files(name = "install_src",src_dir = ["."],dest = "learning_cyber/src",filter = "*",deps = ["//learning_cyber/test01/demo01:install_src",]
)

PS:
<1>install 规则可以将模块 BUILD 文件中定义的 target安装到本地仓库;
<2>install_src规则根据特定规则直接安装文件到本地仓库,并保留源码目录结构;
<3>当要新增模块时(例如增加一个demo02),则要对BUILD文件中的deps进行相应修改。

cyberfile.xml

<package><name>learning_cyber</name><version>1.0.0</version><description>learning_cyber</description><maintainer email="AD-platform">AD-platform@baidu.com</maintainer><type>module</type><src_path>//learning_cyber</src_path><license>BSD</license><author>Apollo</author><depend type="binary"  repo_name="cyber">cyber-dev</depend><builder>bazel</builder>
</package>
  1. 编写源文件及BUILD文件;
    demo01.cc
#include<cyber/cyber.h>int main(int argc, char const *argv[])
{apollo::cyber::Init(argv[0]);AINFO << "hello Apollo";AWARN << "hello Apollo";AERROR << "hello Apollo";AFATAL << "hello Apollo";return 0;
}

BUILD

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")
package(default_visibility = ["//visibility:public"])cc_binary(
name = "demo01",
srcs = ["demo01.cc"],
deps = ["//cyber"], 
)install(
name = "install",
runtime_dest = "learning_cyber/bin",
targets = [
":demo01"],
)install_src_files(
name = "install_src",
src_dir = ["."],
dest = "learning_cyber/src/cyberatest",
filter = "*",
)

cc_binary的编译规则可见https://docs.bazel.build/versions/5.1.0/be/c-cpp.html#cc_binary

  1. 编译代码目录
    在apollo_workspace目录下执行buildtool编译指令。
buildtool build -p learning_cyber/

为了能够看到结果,通过以下命令将输出结果打印到窗口,命令如下:

export GLOG_alsologtostderr=1
  1. 运行可执行文件
cd /opt/apollo/neo/bin

/opt/apollo/neo/bin目录下,执行以下命令:

./demo01

执行完成后,可以看到命令行窗口打印出了"hello Apollo"的内容。

在这里插入图片描述

单个源码的工程可以满足小型项目的需要,但在实际使用中,我们常希望将较大的项目拆分为多个包,以允许快速的增量构建(即仅重建更改的内容)接下来,我们介绍使用多个包来管理你的工程。

TEST2. 构建多包工程

  1. 按如下目录结构进行手动创建:
.
├── BUILD
├── cyberfile.xml
├── test_bazel
│   ├── demo_lib
│   │   ├── BUILD
│   │   ├── getName.cc
│   │   └── getName.h
│   └── demo_main
│       ├── BUILD
│       └── main.cc
└── test.BUILD
  1. 编写包管理BUILD文件和cyberfile.xml
    BUILD文件内容如下所示:
load("//tools/install:install.bzl", "install", "install_src_files")install(name = "install",data = ["test.BUILD","cyberfile.xml",],deps = ["//test/test_bazel/demo_main:install",],
)install_src_files(name = "install_src",src_dir = ["."],dest = "test/src",filter = "*",deps = ["//test/test_bazel/demo_main:install_src",]
)

cyberfile.xml

<package><name>test</name><version>1.0.0</version><description>test component</description><maintainer email="AD-platform">AD-platform@baidu.com</maintainer><type>module</type><src_path>//test</src_path><license>BSD</license><author>Apollo</author><depend type="binary" repo_name="cyber">cyber-dev</depend><builder>bazel</builder>
</package>
  1. 编写源文件及BUILD文件
    编写demo_lib库实现get_name功能,获取字符串的输入并与"Hello" 拼接。

getName.h

#pragma once
#include <string>
using namespace std;string get_name(const string& name);

getName.cc

#include "getName.h"
string get_name(const string& name){return "Hello" + name;
}

源码编写完成后,通过cc_library规则将getName.cc源码构建为库文件getName_lib,hdrs表示的是源文件对应的头文件路径,demo_lib的BUILD文件内容如下:

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
package(default_visibility = ["//visibility:public"])cc_library(name = "getName_lib",srcs = ["getName.cc"],hdrs = ["getName.h"]
)

main.cc

#include "test/test_bazel/demo_lib/getName.h"
#include <iostream>int main()
{for (int i = 0; i< 5; ++i){std::cout << get_name(" Apollo ") << std::endl;}return 0;
}

demo_main的BUILD文件
通过cc_binary配置将main.cc编译构建可执行文件main,deps表示构建该文件所依赖相关的库。

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")package(default_visibility = ["//visibility:public"])cc_binary(name = "main",srcs = ["main.cc"],deps = ["//test/test_bazel/demo_lib:getName_lib"], 
)install(name = "install",runtime_dest = "test/bin",targets = [":main"],
)install_src_files(name = "install_src",src_dir = ["."],dest = "test/src/cyberatest",filter = "*",
)
  1. 编译代码目录
    在apollo_workspace目录下执行buildtool编译指令。
buildtool build -p cyber_demo
  1. 运行可执行文件
cd /opt/apollo/neo/bin
export GLOG_alsologtostderr=1
./main

也可以cd到bazel-bin去查看

在这里插入图片描述

在这里插入图片描述

基本概念2

这部分更多内容请见 第二章:Cyber RT通信机制解析与实践https://apollo.baidu.com/community/article/1094

话题通信

在这里插入图片描述

  1. Listener-Talker通信首先创建了两个Node,分别是Talker Node和 Listener Node。
  2. 每个Node实例化Writer类和Reader类对Channel进行消息的读写。
  3. Writer和Reader通过Topic连接,对同一块共享内存(Channel)进行读写处理。

话题通信方式适合于持续性通信的应用场景,比如雷达信号,摄像头图像信息这类数据的传输。

服务通信

在这里插入图片描述
Server-Client通信可以在客户端发出消息请求时,服务端才进⾏请求回应,并将客户端所需的数据返回给客户端。该通信模式适合临时的消息传输,适⽤于不需要持续性发送数据的场景。

参数通信

在这里插入图片描述
共享的方式实现不同节点之间数据交互的通信模式。参数服务器是基于服务实现的,包含客户端和服务器端,服务端节点可以存储数据,客户端节点可以访问服务端节点操作数据,这个过程虽然基于请求响应的,但是无需自己实现请求与响应,此过程已经被封装,调用者只需要通过比较简单友好的API就可以实现参数操作。类似于“全局变量”的方式来存储这些参数,并定义一些自定义参数来进行使用。

数据通信基础Protobuf

Protobuf简介

Protobuf 是 Google 公司开发的一种跨语言和平台的序列化数据结构的方式,是一个灵活的、高效的用于序列化数据的协议,与 XML 和 JSON 格式相比,Protobuf 更小、更快、更便捷。其优点如下:

  • 性能效率高:序列化后字节占用空间比 XML 少3-10倍,序列化的时间效率比 XML 快20-100倍。
  • 使用便捷便捷:将对结构化数据的操作封装成一个类,便于使用。
  • 兼容性高:通信两方使用同一数据协议,当有一方修改了数据结构,不会影响另一方的使用。
  • 跨语言:支持 Java,C++,Python、Go、Ruby 等多种语言。

Protobuf 文件编写

Protobuf有几个部分构成:

  • syntax :表示使用Protobuf的版本,目前Protobuf支持proto3,但在Apollo中使用的是proto2;
  • package: 表示该文件的路径;
  • message:表示一种数据结构,message后面跟的是数据结构名字,括号里的字段定义格式为:字段规则 数据类型 字段名称 字段编号。
    字段规则主要有三种
  • required:调用时必须提供该字段的值,否则该消息被视为“未初始化”,官方不建议使用,当把字段规则改为其他规则会存在兼容性问题。
  • optional:该字段的值可以设置也可以不设置,会根据数据类型生成一个默认的值。
  • repeated:类似于动态数组,可以存储多个同类型的数据。

Protobuf编译

Protobuf的编译要分为两个步骤:

  • 首先要根据.proto文件生成proto库;
  • 然后再根据生产的proto库生成C++相关的源文件。
    这个源文件是C++语言自动编写的,可以被C++程序自动识别。每一个message会被解析生一个类,里面的字段就相当于这个类的属性。在源文件中也会根据属性生成额外的成员,如获取和设置属性的函数。

TEST3. protobuf实验

使用Protobuf来定义数据格式,在main程序中设置数据值并输出。

<1> 创建本节实验工程目录
<2> 编写Apollo包管理相关的BUILD和cyberfile.xml文件文件
<3> 编写proto文件及BUILD文件;
<4> 编写主代码及BUILD文件:
<5> 编译代码目录
<6> 运行可执行文件

<1> 创建本节实验工程目录:

cyber_demo
|-- cyber_03|-- proto|-- BUILD|-- car_msg.proto|-- test_proto|-- BUILD|-- car.cc
|--BUILD
|--cyberfile.xml
|--cyber_demo.BUILD

PS: 也可以直接在之前创建过的文件夹中进行手动创建cyber03文件夹以及其下文件并对cyberfile.xml和BUILD文件进行修改.

<2> 编写Apollo包管理相关的BUILD和cyberfile.xml文件

BUILD文件内容:

load("//tools/install:install.bzl", "install", "install_src_files")

install(name = "install",data = ["cyber_demo.BUILD","cyberfile.xml",],deps = ["//cyber_demo/cyber_03/test_proto:install",],
)

install_src_files(name = "install_src",src_dir = ["."],dest = "cyber_demo/src",filter = "*",deps = ["//cyber_demo/cyber_03/test_proto:install_src",]
)

若在之前创建的文件夹进行添加,则只需对install和install_src_files中的deps进行修改与添加.

编写cyberfile文件:

<package><name>cyber_demo</name><version>1.0.0</version><description>cyber_demo</description><maintainer email="AD-platform">AD-platform@baidu.com</maintainer><type>module</type><src_path>//cyber_demo</src_path><license>BSD</license><author>Apollo</author><depend type="binary" src_path="//cyber" repo_name="cyber">cyber-dev</depend><depend lib_names="protobuf" repo_name="com_google_protobuf">3rd-protobuf-dev</depend><builder>bazel</builder>
</package>

与之前的实验相比多了一句 <depend lib_names="protobuf" repo_name="com_google_protobuf">3rd-protobuf-dev</depend>,添加protobuf相关依赖.

<3> 编写proto源文件及BUILD文件;
编写proto文件,文件中定义了车辆的信息:

syntax = "proto2";package apollo.cyber.test.proto;
message CarMsg {required string owner = 1;optional string license_plate = 2;optional uint64 max_passenger = 3;repeated string car_info = 4;
}

编写proto的BUILD文件:

load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//tools:python_rules.bzl", "py_proto_library")package(default_visibility = ["//visibility:public"])proto_library(name = "car_msg_proto",srcs = ["car_msg.proto"],
)cc_proto_library(name = "car_msg_cc_proto",deps = [":car_msg_proto"],
)

代码解析:

  • 我们使用Bazel构建系统的BUILD文件,用于生成proto库和相关的源文件。
  • 首先,通过proto_library规则定义了一个名为"car_msg_proto"的proto库,它使用"car_msg.proto"作为源文件。
  • 然后,通过cc_proto_library规则定义了一个名为"car_msg_cc_proto"的源文件生成规则。它依赖于"car_msg_proto"库,并使用该库生成相关的C++源文件。
  • 这些规则中的名称是任意定义的,您可以根据需要进行更改。

<4> 编写主代码及BUILD文件
通过car.cc输出车辆基本信息:

#include "test/cyber_03/proto/car_msg.pb.h" // 填写自己的路径
using namespace std;

int main()
{apollo::cyber::test::proto::CarMsg car;
car.set_owner("apollo");car.set_license_plate("京A88888");car.set_max_passenger(6);car.add_car_info("SUV"); //车型car.add_car_info("Red"); //车身颜色car.add_car_info("electric"); //电动
string owner = car.owner();string license_plate = car.license_plate();uint64_t max_passenger = car.max_passenger();cout << "owner:" << owner << endl;cout << "license_plate:" << license_plate << endl;cout << "max_passenger:" << max_passenger << endl;
for (int i = 0; i < car.car_info_size(); ++i){string info = car.car_info(i);cout << info << " ";}cout << endl;return 0;
}

编辑car.cc的BUILD文件:

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")package(default_visibility = ["//visibility:public"])cc_binary(name = "car",srcs = ["car.cc"],deps = ["//test/cyber_03/proto:car_msg_cc_proto"], 
)install(name = "install",runtime_dest = "test/bin",targets = [":car"],
)install_src_files(name = "install_src",src_dir = ["."],dest = "test/src/cyberatest",filter = "*",
)

修改cc_binary中的deps路径

<5> 编译代码

cd /apollo_workspace
buildtool build -p test

<6> 运行可执行文件

cd /opt/apollo/neo/bin/
./car

结果

owner:apollo
license_plate:京A88888
max_passenger:6
SUV Red electric 

TEST4.C++话题通信实践案例

文件结构参照文档第二章:Cyber RT通信机制解析与实践https://apollo.baidu.com/community/article/1094

/apollo_workspace/|--test|   |--communication|   |  |--BUILD //cyber_test编译文件|  |--talker.cc //talker-listener通信实现|  |--listener.cc|  |--server.cc //server-client通信实现|  |--client.cc|  |--param_server.cc //parameter server-client通信实现|  |--param_client.cc |--proto |--BUILD //car.proto 编译文件|--car.proto  //小车数据定义的文件ß

<1> proto文件编写

// 定义proto使用的版本
syntax = "proto2";//定义包名,在cc文件中调用(重名需更改)
package apollo.cyber.example.proto;//定义一个车的消息,车的型号,车主,车的车牌号,已跑公里数,车速
message Car{optional string plate = 1; optional string type = 2;optional string owner = 3;optional uint64 kilometers = 4;optional uint64 speed = 5;
};

BUILD文件

load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//tools:python_rules.bzl", "py_proto_library")package(default_visibility = ["//visibility:public"])proto_library(name = "car_proto",srcs = ["car.proto"],
)cc_proto_library(name = "car_cc_proto",deps = [":car_proto"],
)

<2> 源文件以及BUILD文件编写
talker.cc

//头文件引用
#include "test/communication/proto/car.pb.h" //注意路径
#include "cyber/cyber.h"
#include "cyber/time/rate.h"//car数据定义的引用,可以看出其定义来源于一个proto
using apollo::cyber::example::proto::Car;int main(int argc, char *argv[]) {// 初始化一个cyber框架apollo::cyber::Init(argv[0]);// 创建talker节点auto talker_node = apollo::cyber::CreateNode("talker");// 从节点创建一个Topic,来实现对车速的查看auto talker = talker_node->CreateWriter<Car>("car_speed");AINFO << "I'll start telling you the current speed of the car.";//设置初始速度为0,然后速度每秒增加5km/huint64_t speed = 0;while (apollo::cyber::OK()) {auto msg = std::make_shared<Car>();msg->set_speed(speed);//假设车速持续增加speed += 5;talker->Write(msg);sleep(1);}return 0;
}

listener.cc

#include "test/communication/proto/car.pb.h" //注意路径
#include "cyber/cyber.h"//car数据定义的引用,可以看出其定义来源于一个proto
using apollo::cyber::example::proto::Car;//接收到消息后的响应函数
void message_callback(const std::shared_ptr<Car>& msg) {AINFO << "now speed is: " << msg->speed();
}int main(int argc, char* argv[]) {//初始化cyber框架apollo::cyber::Init(argv[0]);//创建监听节点auto listener_node = apollo::cyber::CreateNode("listener");//创建监听响应进行消息读取auto listener = listener_node->CreateReader<Car>("car_speed", message_callback);apollo::cyber::WaitForShutdown();return 0;
}

BUILD
注意路径的更改

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")package(default_visibility = ["//visibility:public"])cc_binary(name = "talker",srcs = ["talker.cc"],deps = ["//cyber","//test/communication/proto:car_cc_proto",],linkstatic = True,
)cc_binary(name = "listener",srcs = ["listener.cc"],deps = ["//cyber","//test/communication/proto:car_cc_proto",],linkstatic = True,
)install(name = "install",runtime_dest = "test/bin",targets = [":talker",":listener"],
)install_src_files(name = "install_src",src_dir = ["."],dest = "test/src/cyberatest",filter = "*",
)

<3> 包管理的BUILD文件deps修改,和之前的步骤一致.
<4> 编译

buildtool build -p test/

<5>运行
首先,先将输出方法改为控制台输出。

export GLOG_alsologtostderr=1

打开两个终端,都进入Apollo的docker环境,一个终端运行talker,另一个运行listener,会发现listener运行后开始接收talker发送的小车速度的消息。

# 运行talker./bazel-bin/test/communication/cyber_test/talker# 运行listener./bazel-bin/test/communication/cyber_test/listener

输出结果:
在这里插入图片描述

TEST5.C++服务通信实践案例

<1> proto文件编写
TEST4中已经写过了,不用再写.
<2> 源文件以及BUILD文件编写
server.cc

#include "test/communication/proto/car.pb.h" //注意路径
#include "cyber/cyber.h"//car数据定义的引用,可以看出其定义来源于一个proto
using apollo::cyber::example::proto::Car;int main(int argc, char* argv[]) {apollo::cyber::Init(argv[0]);std::shared_ptr<apollo::cyber::Node> node(apollo::cyber::CreateNode("server"));//创建server node并设置topic名称,定义响应函数auto server = node->CreateService<Car, Car>("service_client", [](const std::shared_ptr<Car>& request,std::shared_ptr<Car>& response) {AINFO << "Hi, I am your car's server.";response->set_type("apollo");response->set_plate("京A8888888");response->set_owner("xxx");response->set_kilometers(5);});apollo::cyber::WaitForShutdown();return 0;
}

client.cc

#include "test/communication/proto/car.pb.h" //注意路径
#include "cyber/cyber.h"//car数据定义的引用,可以看出其定义来源于一个proto
using apollo::cyber::example::proto::Car;int main(int argc, char* argv[]){//初始化cyber框架apollo::cyber::Init(argv[0]);//创建client nodestd::shared_ptr<apollo::cyber::Node> node(apollo::cyber::CreateNode("client"));auto client = node->CreateClient<Car, Car>("service_client");AINFO << "Hi server, I want to know car's information";//创建一个请求auto request = std::make_shared<Car>();//发送请求并获得回应数据auto response = client->SendRequest(request);if(apollo::cyber::OK){if (response != nullptr) {AINFO << "this car owner is : " << response->owner();AINFO << "this car plate is : " << response->plate();AINFO << "this car type is : " << response->type();AINFO << "this car has run : " << response->kilometers()<<"kilometers";} else {AINFO << "service may not ready.";}}apollo::cyber::WaitForShutdown();return 0;
}

BUILD文件

// 加上server 和 client
cc_binary(name = "server",srcs = ["server.cc"],deps = ["//cyber","//test/communication/proto:car_cc_proto",],linkstatic = True,
)cc_binary(name = "client",srcs = ["client.cc"],deps = ["//cyber","//test/communication/proto:car_cc_proto",],linkstatic = True,
)
// 加上 server和client
install(name = "install",runtime_dest = "test/bin",targets = [":talker",":listener",":client",":server",],
)

<3> 包管理的BUILD文件deps修改,和之前的步骤一致(此处若已经进行过TEST4则无需更改).
<4> 编译

buildtool build -p test/

<5>运行
首先,先将输出方法改为控制台输出。

export GLOG_alsologtostderr=1

分别打开两个终端,运行server和client

# 运行server
./bazel-bin/test/communication/cyber_test/server
# 运行client
./bazel-bin/test/communication/cyber_test/client

输出结果:
在这里插入图片描述

TEST6.C++参数服务器通信实践案例

<1>编写源文件和BUILD文件
param_client.cc

#include "cyber/cyber.h"
#include "cyber/parameter/parameter_client.h"using apollo::cyber::Parameter;
using apollo::cyber::ParameterClient;using apollo::cyber::Parameter;
using apollo::cyber::ParameterClient;int main(int argc, char** argv){apollo::cyber::Init(*argv);std::shared_ptr<apollo::cyber::Node> client_node =apollo::cyber::CreateNode("parameters_client");auto param_client = std::make_shared<ParameterClient>(client_node,"parameters_server");AINFO<<"Hi, I'm car's parameters client!";//获取所有参数,用vector获取所有参数std::vector<Parameter> car_parameters;param_client->ListParameters(&car_parameters);//遍历获取的参数,显示参数的名字以及参数的类型for(auto &&parameter : car_parameters){AINFO << parameter.Name() <<" is " << parameter.TypeName();}//客户端选择一个参数进行修改,比如修改max_speedparam_client->SetParameter(Parameter("max_speed",180));//获取修改后的max_speedParameter car_parameter;param_client->GetParameter("max_speed", &car_parameter);AINFO << "now max_speed " << car_parameter.AsInt64();apollo::cyber::WaitForShutdown();return 0;
}

param_server.cc

#include "cyber/cyber.h"
#include "cyber/parameter/parameter_client.h"
#include "cyber/parameter/parameter_server.h"using apollo::cyber::Parameter;
using apollo::cyber::ParameterServer;int main(int argc, char** argv) {//初始化cyber框架//创建一个node,该node的名字和Topic名字同名apollo::cyber::Init(*argv);std::shared_ptr<apollo::cyber::Node> server_node =apollo::cyber::CreateNode("parameters_server");AINFO << "Hi,I'am your car's parameter server.";//从该node创建参数服务器auto param_server = std::make_shared<ParameterServer>(server_node);//服务端进行参数设置,对小车的最高速度、最大人数、是否自动驾驶等参数进行了设置param_server->SetParameter(Parameter("max_speed", 120));param_server->SetParameter(Parameter("max_people", 5));param_server->SetParameter(Parameter("if_auto", true));//尝试客户端修改一个参数,如max_speed,查看服务端目前的值Parameter car_parameter;param_server->GetParameter("max_speed", &car_parameter);AINFO << "origin max_speed " << car_parameter.AsInt64();apollo::cyber::WaitForShutdown();return 0;
}

BUILD
在原有基础上,添加如下:

cc_binary(name = "param_server",srcs = ["param_server.cc"],deps = ["//cyber","//cyber/parameter",],linkstatic = True,
)cc_binary(name = "param_client",srcs = ["param_client.cc"],deps = ["//cyber","//cyber/parameter",],linkstatic = True,
)install(name = "install",runtime_dest = "test/bin",targets = [":talker",":listener",":client",":server",":param_client",":param_server",],
)

<2> 包管理BUILD(无需更改)
<3> 编译

buildtool build -p test/

<4>运行
首先,先将输出方法改为控制台输出。

export GLOG_alsologtostderr=1

分别在两个终端运行param_server和param_client

./bazel-bin/test/communication/cer_test/param_server
./bazel-bin/test/communication/ber_test/param_client

输出结果:
在这里插入图片描述

参考

[1] Apollo星火计划学习笔记——第三讲(Apollo Cyber RT 模块详解与实战)https://blog.csdn.net/sinat_52032317/article/details/126924375
[2] Cyber RT基础入门与实践https://apollo.baidu.com/community/article/1093
[3] 第二章:Cyber RT通信机制解析与实践https://apollo.baidu.com/community/article/1094

这篇关于【Apollo星火计划】—— Cyber基础概念|通信机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/352754

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

【Tools】大模型中的自注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 自注意力机制(Self-Attention)是一种在Transformer等大模型中经常使用的注意力机制。该机制通过对输入序列中的每个元素计算与其他元素之间的相似性,