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

news2024/11/23 8:42:27

在这里插入图片描述

文章目录

  • 前言
  • 基本概念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]

基本概念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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

electron dialog.showMessageBox使用案例

electron 版本&#xff1a;25.3.1 index.html <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Hello World!</title><meta http-equiv"Content-Security-Policy" content"script-src self unsa…

在linux中进行arm交叉编译体验tiny6410裸机程序开发流程

在某鱼上找了一个友善之臂的Tiny6410开发板用来体验一下嵌入式开发。这次先体验一下裸机程序的开发流程&#xff0c;由于这个开发板比较老旧了&#xff0c;官方文档有很多过期的内容&#xff0c;所以记录一下整个过程。 1. 交叉编译器安装 按照光盘A中的文档《04- Tiny6410 L…

C语言非常道 c0609.c 练习6.8

结构类型处理复杂的数据 结构声明位于源文件的开头&#xff0c;相当于结构类型的全局声明 注意&#xff1a;读取或者写入某个文件&#xff0c;都要先打开文件 写入读出函数的参数类型&#xff0c;实参和形参的类型要对应&#xff1b; 上述&#xff1a; & emp 得到一个指向…

Appium+python自动化(二十一)- 让猴子按你指令大闹手机,让我们都成为耍猴高手(超详解)

耍猴第一式 - 隐藏命令 monkey隐藏的两个命令&#xff1a; 1 –pck-blacklist-file<黑名单文件><br><br>–pck-whitelist-file<白名单文件> monkey还有一个隐藏的命令那就是&#xff1a; 1 –f<脚本文件>:可以指定monkey的自定义脚本 一般…

批处理下载视频

一、安装annie Github 上 annie 下载神器的安装及使用教程: https://blog.csdn.net/qq_41780295/article/details/119795152 二、视频下载 安装了annie之后&#xff0c;我们就可以使用命令行下载视频文件了&#xff1b; 例如&#xff0c;打开B站&#xff0c;随便点开一个视频…

能耗监测管理系统

能耗监测管理系统是一种用于监测和管理能源消耗的软件系统&#xff0c;可以帮助企业、机构或个人实现对能源消耗的实时监控、分析和管理。随着能源问题的日益凸显&#xff0c;能耗监测管理系统的重要性也越来越受到人们的关注。本文将从以下几个方面介绍能耗监测管理系统。 一、…

springboot 项目启动不打印spring 启动日志

今天项目遇到一个很奇怪的问题&#xff0c;服务在启动时&#xff0c;不打印spring 的启动日志。经过排查发现是因为其他的依赖引入了 log4j 的依赖&#xff0c;因为我们的项目用的是logback&#xff0c;所以项目中没有log4j 的相关配置&#xff0c;所以干扰到了日志的打印 原因…

使用IntelliJ Idea开发Flink应用程序

使用IntelliJ Idea开发Flink应用程序 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 启动IntelliJ Idea并创建flink项目5.2 编写flink代码5.2.1 准备工作5.2.2 批处理5.2.3 有界流处理5.2.4 无界流处理 ⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何…

剑指Offer第一章——整数

1. 整数 1.1 整数的基础知识 整数是一种基本的数据类型。编程语言可能会提供占据不同内存 空间的整数类型&#xff0c;每种类型能表示的整数的范围也不相同。例如&#xff0c;Java中有4种不同的整数类型&#xff0c;分别为8位的byte&#xff08;-&#xff5e;-1&#xff09;、…

Minecraft 1.20.x Forge模组开发 04.动画效果物品

我们本次实现一个具有动画效果的流星锤: 效果演示 效果演示 效果演示 首先,请确保你的开发包中引入了geckolib依赖,相关教程请参考:Minecraft 1.20.x Forge模组开发 03.动画生物实体 1.首先我们要使用geckolib制作一个物品和对应的动画: 在blockbench中新建一个

Android TelephonyManager双卡获取数据开启状态异常的可能原因

背景 应用内不指定subId获取数据状态可能会错误&#xff0c;因为可能拿到voice的能力&#xff0c;而非data。 代码逻辑 1、通过TelephonyManager的isDataEnabled()没有指定subId时&#xff0c;调用内部方法isDataEnabledForReason&#xff0c;传入getId()参数以指定subid&am…

设备监测诊断与维护:优化工业生产效率的关键措施

在现代工业生产中&#xff0c;设备的稳定运行对于保障生产效率至关重要。设备监测、诊断和维护作为关键措施&#xff0c;能够帮助企业及时发现设备问题、诊断故障原因&#xff0c;并采取有效维护措施&#xff0c;从而降低生产中断风险&#xff0c;提高生产效率。本文将深入探讨…

【二叉树】利用前序和中序遍历结果生成二叉树并输出其后序和层序遍历结果

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; ⭐️往期关于树的文章&#xff1a; 【哈夫曼树】基本概念、构建过程及C代码 【线索二叉树】C代码及线索化过程详解 欢迎阅读&#xff01; 实验内容 根据二叉树先序和中序遍历的结果,生成该二叉树。并…

pytest自动化测试框架tep环境变量、fixtures、用例三者之间的关系

tep是一款测试工具&#xff0c;在pytest测试框架基础上集成了第三方包&#xff0c;提供项目脚手架&#xff0c;帮助以写Python代码方式&#xff0c;快速实现自动化项目落地。 在tep项目中&#xff0c;自动化测试用例都是放到tests目录下的&#xff0c;每个.py文件相互独立&…

低代码治好了CIO们的 “精神内耗”

目录 一、前言 二、低代码在企业数字化转型中能“C位出道”&#xff0c;凭什么&#xff1f; 三、低代码的数字化构建能力 四、结束语 一、前言 近两年&#xff0c;我发现身边的CIO/CTO朋友都得了一种“病”——“数字化焦虑”症。 他们经常皱着眉头问我&#xff1a;“老板对数字…

gradio初体验

背景 近期随着很多开源大模型的出现&#xff0c;对于其如何落地&#xff0c;或者说充分地去挖掘其实际应用领域和商业价值变得格外重要。于是乎&#xff0c;对于不懂技术的前方市场或销售人员&#xff0c;如何在没有形成AI产品之前向其展示算法模型效果呢&#xff1f;这时候gr…

【严重】Citrix ADC 和 Citrix Gateway 远程代码执行漏洞(PoC)

漏洞描述 Citrix ADC是应用程序交付和负载平衡解决方案&#xff0c;Citrix Gateway是一套安全的远程接入解决方案&#xff0c;常用于提供虚拟桌面和远程桌面服务&#xff0c;此外&#xff0c;Citrix ADC还被广泛用作Windows堡垒机。 在 Citrix ADC 和 Citrix Gateway 受影响版…

《我为什么要听你的 如何与强势的人相处》读书笔记二

目录 反驳例子 勇敢反击的益处 一些重要的自我保护法 不要再讲述你生活中的细节 别答应那些表意不明的请求 记录下一切 第一时间告知亲友 拒绝成为中间人 区别对待强势者 谨慎应对奉承话 想方设法快点跑 反驳例子 例子 例子&#xff1a; 例子&#xff1a; 例子&am…

什么是UE像素流送,像素流推流是什么原理?

游戏开发者通常在运行游戏逻辑时会将游戏渲染到屏幕的同一台设备上来运行虚幻引擎应用&#xff0c;多人联网游戏可能会在应用程序的多个实例之间分发部分游戏逻辑&#xff0c;但每个单独的实例仍然会为自己的玩家在本地渲染游戏。即使是使用 HTML5 部署选项创建可以在 Web 浏览…

CountDownLatch 一个神奇的计数器,您了解吗

一、CountDownLatch基础概念及案例 1.CountDownLatch是java.util.concurrent 包下提供一个同步工具类&#xff0c;它允许一个或多个线程一直等待&#xff0c;直到其他线程执行完成再执行。其本质就是一个计数器&#xff0c;传入一个初始的值&#xff0c;调用await 方法会阻塞当…