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

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/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;
}

(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.mzph.cn/news/68312.shtml

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

相关文章

【B树 B+树】B树、B+树理论

目录 引入B树B树定义和性质m阶B树核心特性 B树B树的查找 引入B树 满足上面两个策略就是B树&#xff1a; m 叉查找树中&#xff0c;规定除了根节点外&#xff0c;任何结点至少有 ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉ 个分叉&#xff0c;即至少含有 ⌈ m / 2 ⌉ \lceil m/2 \…

Python爬虫——新手使用代理ip详细教程

Python代理IP爬虫是一种可以让爬虫拥有更多网络访问权限的技术。代理IP的作用是可以为爬虫提供多个IP地址&#xff0c;从而加快其爬取数据的速度&#xff0c;同时也可以避免因为访问频率过高而被网站封禁的问题。本文将介绍如何使用Python实现代理IP的爬取和使用。 一、代理IP的…

RHCA之路---EX280(10)

RHCA之路—EX280(10) 1. 题目 On master.lab.example.com install the OpenShift Mertics component with the following requirements: Use the storage /exports/metrics for cassandra storage. You can use the files on http://materials.example.com/exam280/storage fo…

海康威视二次开发适配安卓电视盒子

收到一个需求&#xff0c;需要在安卓电视盒子上适配海康威视摄像头视频&#xff1a; 1.类似电视家app界面&#xff0c;左边滑动菜单显示通道列表、设置按钮&#xff0c;遥控器呼出菜单。 2.遥控器操作&#xff1a;切换视频通道、云台上下左右控制、缩放等。 3.服务器域名、用…

FPGA输出lvds信号点亮液晶屏

1 概述 该方案用于生成RGB信号&#xff0c;通过lvds接口驱动逻辑输出&#xff0c;点亮并驱动BP101WX-206液晶屏幕。 参考&#xff1a;下面为参考文章&#xff0c;内容非常详细。Xilinx LVDS Output——原语调用_vivado原语_ShareWow丶的博客http://t.csdn.cn/Zy37p 2 功能描述 …

RabbitMQ的安装和配置

将RabbitMQ文件夹传到linux根目录 开启管理界面及配置

【VSCode】文件模板创建及使用.md

背景 最近使用VSCode学习Vue项目比较频繁&#xff0c;每次创建Vue文件都要手动写重复代码&#xff0c;特别麻烦&#xff0c;就上网查找自动生成代码的说明&#xff0c;结果发现VSCode有代码模板&#xff0c;怪怪&#xff0c;感觉发现新大陆了(low!)。 配置 打开配置 方式一&a…

手机电脑scoket通信 手机软件 APP inventor 服务端程序python

python scoket 通信 再帮助同学坐课题的时候接触到了scoket通信&#xff0c;了解到这应该是基层网络通信的原理&#xff0c;于是就导出搜索了一下相关的资料&#xff0c;简单来说scoket通信就是&#xff0c;可以让不同设备在同一个网络环境的条件下&#xff0c;可以实现相互通…

ubuntu20.04+ROS noetic在线运行单USB双目ORB_SLAM

双目摄像头主要有以下几种&#xff0c;各有优缺点。 1.单USB插口&#xff0c;左右图像单独输出2.双USB插口&#xff0c;左右图像单独输出&#xff08;可能存在同步性问题&#xff09;3.双USB插口&#xff0c;左右图像合成输出4.单USB插口&#xff0c;左右图像合成输出 官方版…

[SSM]MyBatisPlus进阶

三、进阶篇 3.1映射 3.1.1自动映射规则 表名和实体类名映射 -> 表名user 实体类名User字段名和实体类属性名映射 -> 字段名name 实体类属性名name字段名下划线命名方式和实体类属性驼峰命名方式映射 -> 字段名 user_email 实体类属性名 userEmail 开启驼峰命名 m…

【Unity3D赛车游戏优化篇】【九】Unity中如何让汽车丝滑漂移?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

CocosCreator3.8研究笔记(三)CocosCreator 项目结构说明及编辑器的简单使用

我们通过Dashboard 创建一个2d项目&#xff0c;来演示CocosCreator 的项目结构。 等待创建完成后&#xff0c;会得到以下项目工程&#xff1a; 一、assets文件夹 assets文件夹&#xff1a;为资源目录&#xff0c;用来存储所有的本地资源&#xff0c;如各种图片&#xff0c;脚本…

webpack(三)loader

定义 loader用于对模块的源代码进行转换&#xff0c;在imporrt或加载模块时预处理文件 webpack做的事情&#xff0c;仅仅是分析出各种模块的依赖关系&#xff0c;然后形成资源列表&#xff0c;最终打包生成到指定文件中。 在webpack内部&#xff0c;任何文件都是模块&#x…

健康舒适的超满意照明体验!SUKER书客SKY护眼台灯测评

健康舒适的超满意照明体验&#xff01;SUKER书客SKY护眼台灯测评 2022年全国儿童青少年总体近视率为53.6%&#xff0c;其中6岁儿童为14.5%&#xff0c;小学生为36%&#xff0c;初中生为71.6%&#xff0c;高中生为81%&#xff0c;近视已成为当下人们遇到的比较普遍的眼健康问题…

qt连接tcp通信和连接数据库

通过数据库实现学生管理系统 widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//判断数据库对象是否包含了自己使用的数据库 Studemt.dbif(!db.co…

线性表之-单向链表(无头)

目录 什么是单向链表 顺序表和链表的区别和联系 顺序表&#xff1a; 链表&#xff1a; 链表表示(单项)和实现 1.1 链表的概念及结构 1.2单链表(无头)的实现 所用文件 将有以下功能&#xff1a; 链表定义 创建新链表元素 尾插 头插 尾删 头删 查找-给一个节点的…

没有软件怎么管理固定资产

在当今数字化的世界中&#xff0c;我们已经习惯了使用各种软件来管理我们的日常生活和工作。然而&#xff0c;当我们面临一个看似简单的问题——如何管理固定资产时&#xff0c;我们可能会感到困惑。那么&#xff0c;如果没有软件&#xff0c;我们该如何进行资产管理呢&#xf…

ClickHouse配置Hdfs存储数据

文章目录 背景配置单机配置HA高可用Hdfs集群性能测试统计trait最多的10个trait term统计性状xxx minValue > 500 0000的数量结论 参考文档 背景 由于公司初始使用Hadoop这一套&#xff0c;所以希望ClickHouse也能使用Hdfs作为存储 看了下ClickHouse的文档&#xff0c;拿Hdf…

【核心复现】基于改进灰狼算法的并网交流微电网经济优化调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

无涯教程-Flutter - 简介

Flutter是一个由谷歌开发的开源移动应用软件开发工具包&#xff0c;用于为Android、iOS、 Windows、Mac、Linux、Google Fuchsia开发应用。 通常&#xff0c;创建移动应用程序是一个非常复杂和具有挑战性的任务。有许多框架可用&#xff0c;它提供了开发移动应用程序的出色函数…