Cyber RT学习---5.Cyber RT通信机制解析与实践

news2024/10/7 9:23:58

5.Cyber RT通信机制解析与实践

5.1 通信机制简介

5.1.1 话题通信

在这里插入图片描述模式:
以发布订阅的方式实现不同节点之间数据交互的通信模式。
如图1-1所示,Listener-Talker通信首先创建了两个Node,分别是Talker Node和 Listener Node。
每个Node实例化Writer类和Reader类对Channel进行消息的读写。
Writer和Reader通过Topic连接,对同一块共享内存(Channel)进行读写处理。
Talker Node 为了实现其“诉说”的功能,实例化Writer,通过Writer来对Channel进行消息写操作。
Listener Node为了实现其“聆听”功能,实例化reader类,通过Reader来对channel进行读操作。
场景:
话题通信方式适合于持续性通信的应用场景,比如雷达信号,摄像头图像信息这类数据的传输。
使用:
Listener-Talker通信一方主动送消息,一方被动接收。
我们想要一直获取车的速度,该需求不需要向发送方返回什么消息,也不需要发送方对消息进行进一步处理。所以我们选择了Listener-Talker通信方式实现该功能。
数据定义
话题通信中用的的数据格式的定义car message 定义在car.proto中。

5.1.2 服务通信

在这里插入图片描述模式:
以请求响应的方式实现不同节点之间数据交互的通信模式。
如图1-2所示,Server-Client通信可以在客户端发出消息请求时,服务端才进⾏请求回应,并将客户端所需的数据返回给客户端。
场景:
我们想要获得⼩⻋的详细信息,⽐如⻋牌这些,但是⼜不需要⼀直获得该信息,想要在需要知道这些信息的时候请求⼀下就好,于是考虑⽤Server- Client通信实现该功能。

使用:
该通信模式适合临时的消息传输,适⽤于不需要持续性发送数据的场景。
数据定义
其传输的数据定义依然在对应的proto⽂件中。

5.1.3 参数通信

在这里插入图片描述模式:
以共享的方式实现不同节点之间数据交互的通信模式。
参数服务器是基于服务实现的,包含客户端和服务器端,服务端节点可以存储数据,客户端节点可以访问服务端节点操作数据,这个过程虽然基于请求响应的,但是无需自己实现请求与响应,此过程已经被封装,调用者只需要通过比较简单友好的API就可以实现参数操作。
场景:
自动驾驶场景中有一些参数比如该车的最高限速、最多乘客以及是否自动驾驶等需要被各个模块使用数据,比如是否自动驾驶这个参数可能同时影响这很多模块,也可能被很多模块运行时所更改。
这些数据如何实现在不同模块之间的共享呢?
使用:
类似于“全局变量”的方式来存储这些参数,并定义一些自定义参数来进行使用。
数据定义:
Cyber中设计了全局参数服务器来实现这个功能,其通信基于RTPS协议。该通信方式服务端和客户端都可以设置参数和更改参数。

5.2 数据通信基础Protobuf

5.2.1 Protobuf简介

Protobuf 是 Google 公司开发的一种跨语言和平台的序列化数据结构的方式,是一个灵活的、高效的用于序列化数据的协议,与 XML 和 JSON 格式相比,Protobuf 更小、更快、更便捷。
Protobuf 是跨语言的,并且自带一个编译器( protoc ),只需要用protoc进行编译,就可以编译成 Java、Python、C++、C#、Go 等多种语言代码,然后可以直接使用,不需要再写其它代码,自带有解析的代码。只需要将要被序列化的结构化数据定义一次(在 .proto 文件定义),便可以使用特别生成的源代码(使用protobuf提供的生成工具)轻松的使用不同的数据流完成对结构数据的读写操作。甚至可以更新 .proto 文件中对数据结构的定义而不会破坏依赖旧格式编译出来的程序。其优点如下:

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

5.2.2 Protobuf文件编写

为了方便讲解,使用cyber/examples/proto/examples.proto文件来讲解Protobuf的结构:

Protobuf有几个部分构成:
(1)syntax :表示使用Protobuf的版本,目前Protobuf支持proto3,但在Apollo中使用的是proto2;
(2)package: 表示该文件的路径;
(3) message:表示一种数据结构,message后面跟的是数据结构名字,括号里的字段定义格式为:字段规则 数据类型 字段名称 字段编号。

字段规则主要有三种:
(1)required:调用时必须提供该字段的值,否则该消息被视为“未初始化”,官方不建议使用,当把字段规则改为其他规则会存在兼容性问题。
(2)optional:该字段的值可以设置也可以不设置,会根据数据类型生成一个默认的值。
(3)repeated:类似于动态数组,可以存储多个同类型的数据。

# examples.proto
syntax = "proto2";
package apollo.cyber.examples.proto;
message SamplesTest1 {
  optional string class_name = 1;
  optional string case_name = 2;
};
message Chatter {
  optional uint64 timestamp = 1;
  optional uint64 lidar_timestamp = 2;
  optional uint64 seq = 3;
  optional bytes content = 4;
};
message Driver {
  optional string content = 1;
  optional uint64 msg_id = 2;
  optional uint64 timestamp = 3;
};

5.2.3 Protobuf编译

Protobuf的编译要分为两个步骤:
(1)首先要根据.proto文件生成proto库;
(2)然后再根据生产的proto库生成C++相关的源文件。

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

package(default_visibility = ["//visibility:public"])
#1、生成proto库
proto_library(
    name = "examples_proto",
    srcs = ["examples.proto"],
)
#2、生成源文件
cc_proto_library(
    name = "examples_cc_proto",
    deps = [
        ":examples_proto",
    ],
)

代码解析:

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

5.2.4 小案例

目的:使用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

<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",
    ]
)

编写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>

<3> 编写proto源文件及BUILD文件

编写proto文件,文件中定义了车辆的信息:
proto/car_msg.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"],
)

<4> 编写主代码及BUILD文件

通过car.cc输出车辆基本信息:

#include "test/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;
}

注意
本段代码中内容需要进行

#include "test/proto/car_msg.pb.h"

--------------------需要替换为---------------------------

#include "cyber_demo/cyber_03/proto/car_msg.pb.h"

编辑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/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 = "*",
)

注意
本段BUILD代码中内容需要进行修改

cc_binary(
    name = "car",
    srcs = ["car.cc"],
    deps = ["//test/proto:car_msg_cc_proto"], 
)

--------------------需要替换为---------------------------

cc_binary(
    name = "car",
    srcs = ["car.cc"],
    deps = ["//cyber_demo/cyber_03/proto:car_msg_cc_proto"],
)

<5> 编译代码

cd /apollo_workspace
buildtool build -p cyber_demo/

编译结果如图所示:
在这里插入图片描述

编译完成后会在 /opt/apollo/neo/bin/的目录下生成可执行文件car,如下图所示:
在这里插入图片描述

<6> 运行可执行文件

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

运行结果如下所示:
在这里插入图片描述

5.3 话题通信实例

5.3.1 创建目录

接来下我们就开始用Cyber实现这个小demo,并一一解答以上疑问。该案例我们创建在/apollo/workspace/test文件下,命名为communication整体目录结构如下:

/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  //小车数据定义的文件ß

5.3.2 编写

(1)proto文件编写

通过第一节内容,我们知道了proto文件的使用方法,那么这一章,我们来自己编写一个proto文件,来实现我们“车”的变量定义,在后续的三种通信方式的案例中都是用这一数据定义。
对car.proto文件进行编写,其内容如下:

// 定义proto使用的版本
syntax = "proto2";

//定义包名,在cc文件中调用
package apollo.cyber.test.proto;

//定义一个车的消息,车的型号,车主,车的车牌号,已跑公里数,车速
message Car{
    optional string plate = 1; 
    optional string type = 2;
    optional string owner = 3;
    optional uint64 kilometers = 4;
    optional uint64 speed = 5;
};

(2)talker.cc 文件编写

  • 我们来编写一个talker.cc来实现主动对话。
//头文件引用
#include "test/proto/car.pb.h"
#include "cyber/cyber.h"
#include "cyber/time/rate.h"

//car数据定义的引用,可以看出其定义来源于一个proto
using apollo::cyber::examples::cyber_test_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;
}

(3) listener.cc 文件编写

  • 编写一个listener来实现对talker发送过来的内容进行接收。
#include "test/proto/car.pb.h"
#include "cyber/cyber.h"

using apollo::cyber::examples::cyber_test_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;
}

5.3.3 运行与测试

(1) bazel 编译文件编写

编写bazel编译文件(编写在cyber/examples/cyber_test/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/proto:car_cc_proto",
    ],
    linkstatic = True,
)

cc_binary(
    name = "listener",
    srcs = ["listener.cc"],
    deps = [
        "//cyber",
        "//test/proto:car_cc_proto",
    ],
    linkstatic = True,
)

(2) 编译:

使用apollo 包管理开发方式提供的buildtool工具

buildtool build -p test/communication/

如下图所示,则编译成功。
在这里插入图片描述

(3)运行

首先,先将输出方法改为控制台输出。

export GLOG_alsologtostderr=1

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

./bazel-bin/test/communication/talker

结果显示:
在这里插入图片描述

./bazel-bin/test/communication/listener

在这里插入图片描述

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

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

相关文章

精通期权短线交易的技巧,从此成为投资高手!

投资期权最常见的两种交易方式就是长线交易和短线交易&#xff0c;每一种交易方式都有自己的特点和优势&#xff0c;适合不同类型和目的的投资者。下面给大家分享一些精通期权短线交易的技巧&#xff0c;从此成为投资高手&#xff01;希望能帮到大家。本文来自&#xff1a;期权…

中国人保为天能电力器具承保产品责任险,为消费者保驾护航!

战略达成&#xff0c;保驾护航 2023年7月&#xff0c;河北天能电力器具制造有限公司与世界五百强之一的综合性保险公司——中国人民财产保险股份有限公司达成战略合作&#xff0c;签署产品安全责任险保单&#xff0c;携手为消费者保驾护航&#xff01; 产品质量是当下企业生存发…

Unity 中 Pivot 与 Center,重置模型物体的轴心为中心

文章目录 1. 概念2. 动态计算正确的模型中心点Center3. 重置模型物体的轴心为中心 1. 概念 在 Unity 面板的左上角有两个按钮&#xff0c;在本文中主要研究 Pivot/Center Pivot&#xff08;轴心&#xff09;&#xff1a;模型的真实位置&#xff0c;是由建模软件设定的可在模…

mysql trace

optimizer_trace 它可以跟踪优化器做出的各种决策&#xff08;比如访问表的方法、各种开销计算、各种转换等&#xff09;&#xff0c;并将跟踪结果记录到 information_schema 数据库中的 optimizer_trace 表中。 mysql 默认是关闭 追踪的 开启、并设置格式为 josn,同时设置trac…

光环云出席Enjoy出海AIGC主题研讨会,助力企业迎接AI时代机遇与挑战

AIGC的崛起&#xff0c;为2023年的全球化突围之路拓展了想象空间。 从年初至今&#xff0c;OpenAI和ChatGPT高举高打&#xff0c;很大程度上起到了教育市场的作用&#xff1b;此外&#xff0c;Meta推出大模型&#xff0c;Snapchat、Soul、字节等大厂或上线或内测聊天机器人&…

正中优配:纺织服装行业景气向上 多因素支撑稀土价格上涨

昨日&#xff0c;沪深两市股指盘中放量拉升&#xff0c;沪指、深成指涨超1%&#xff0c;上证50指数涨近2%。截至收盘&#xff0c;沪指涨1.4%报3177.06点&#xff0c;深成指涨1.41%报10611.74点&#xff0c;创业板指涨0.85%报2118.03点&#xff0c;上证50指数涨1.79%&#xff1b…

递归算法学习——有效的数独,解数独

一&#xff0c;有效的数独 1.题意 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#x…

【LeetCode】83. 删除排序链表中的重复元素

83. 删除排序链表中的重复元素&#xff08;简单&#xff09; 方法&#xff1a;一次遍历 思路 由于给定的链表是排好序的&#xff0c;因此重复的元素在链表中出现的位置是连续的&#xff0c;因此我们只需要对链表进行一次遍历&#xff0c;就可以删除重复的元素。 从指针 cur 指…

MATLAB旋转动图的绘制

MATLAB旋转动图的绘制 文章目录 MATLAB旋转动图的绘制1、动图效果2、matlab代码 利用matlab实现三维旋转动图的绘制。 1、动图效果 2、matlab代码 close all clear clcf(x,y,z)(x.^2 (9./4).*y.^2 z.^2 - 1).^3 - x.^2.*z.^3 - (9./80).*y.^2.*z.^3; [x,y,z]meshgrid(linspac…

STM32 SPI对存储芯片发送写是能命令后一直忙等待

我采用CUBE配置的SPI外设&#xff0c;对NSS引脚选择了硬件输出&#xff0c;这种方式对读取命令没有影响&#xff0c;但是对写命令有&#xff0c;当我发送写是能命令后&#xff0c;读取状态寄存器的值一直都是忙&#xff0c;我猜测这可能是硬件控制NSS引脚后&#xff0c;对于HAL…

Java迷宫问题

迷宫问题&#xff1a; 问题&#xff1a; /*迷宫问题 &#xff1a; 让小球从左上角开始 到达右上角 绕过障碍物求最短路径这里*号是障碍物的意思* * * * * * ** ** * ** * * ** ** ** ** * * * * * *思路 &#xff…

Linux安装JDK并配置环境变量

1.下载jdk 可以去官网直接下载JDK&#xff0c;下载时选择好需要的版本和操作系统位数 2.安装jdk &#xff08;1&#xff09;选择合适的位置新建安装目录 mkdir -p /home/local/java&#xff08;2&#xff09;进入目录 cd /home/local/java&#xff08;3&#xff09;上传j…

2023年9月CDGA/CDGP数据治理认证考试报名,当然弘博创新

据DAMA中国官方网站消息&#xff0c;2023年度第三期DAMA中国CDGA和CDGP认证考试定于2023年9月23日举行。 报名通道现已开启&#xff0c;相关事宜通知如下&#xff1a; 考试科目: 数据治理工程师(CertifiedDataGovernanceAssociate,CDGA) 数据治理专家(CertifiedDataGovernanc…

电子半导体行业电能质量监测与治理系统解决方案 安科瑞 许敏

摘要&#xff1a;在国家鼓励半导体材料国产化的政策导向下&#xff0c;本土半导体材料厂商不断提升半导体产品技术水平和研发能力&#xff0c;逐渐打破了国外半导体厂商的垄断格局&#xff0c;推进中国半导体材料国产化进程&#xff0c;促进中国半导体行业的发展。半导体产品的…

如何让insert程序速度快,可以试试联合SQL(insert 和 select 一起使用)?

查询添加可选择SQL执行&#xff0c;速度远超程序执行 insert 和 select案例 insert into 表1(列1,列2,列3,...) select 列1,列2,列3,...from表2(GROUP BY 列)116511 条数据 耗时45秒&#xff0c; 如果是程序查询然后再insert&#xff0c;则需要30分钟左右&#xff01;&#x…

docker安装mysql8之lower_case_table_names参数

docker安装mysql8之lower_case_table_names参数 前言发现问题找到问题解决方案不挂载外部文件挂载外部文件 前言 既然使用docker安装mysql&#xff0c;看重的就是一键拉取和一键启动&#xff0c;然后在安装mysql容器的过程中&#xff0c;出现了新问题&#xff0c;关于lower_ca…

Python Asyncio 之网络编程方法详解

https://so1n.me/2023/08/29/python_asyncio_lib_network/

CS420 课程笔记 P8 - 如何编辑汇编代码 (最终篇)

文章目录 IntroHardwareReturn to virtual memoryCompilerFinding assembly codeExample!Assembly languageRegisters Assembly ExampleIncDecAddSubMov!NegMulDiv Assembly problem恭喜 Congratulations! Intro 大多数人在走到这里之前就退出了&#xff0c;这里是 game hacki…

nginx--技术文档--基本概念--《十分钟快速扫盲》

nginx是什么&#xff1f; Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。它具有高并发性、稳定性和灵活性&#xff0c;并且使用事件驱动的方式处理请求&#xff0c;能够有效地处理大量并发连接。此外&#xff0c;Nginx还具有高效的内…

总结开发中一些数据处理方法的封装

摘要&#xff1a; 开发中经常会遇到一些组件需要的特定数据结构&#xff0c;后端不一定会返回你需要的数据结构的&#xff0c;所以还是要前端来处理的&#xff01;这里来总结一下平常开发中遇到的需要处理结构的方法&#xff0c;下次遇到直接拿来用就可以了&#xff01; 目录概…