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

在这里插入图片描述

前言

因为网上有不少关于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/h
    uint64_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 node
    std::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_speed
    param_client->SetParameter(Parameter("max_speed",180));
    
    //获取修改后的max_speed
    Parameter 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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuan〇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值