gRPC 非官方教程
一、 简介
gRPC的定义:
- 一个高性能、通用的开源RPC框架
- 主要面向移动应用开发: gRPC提供了一种简单的方法来精确地定义服务和为iOS、Android和后台支持服务自动生成可靠性很强的客户端功能库。
- 基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发
- 支持众多开发语言

二、 简单rpc调用
主要流程:
- 创建maven项目
- 添加grpc依赖,protobuf依赖和插件
- 通过.proto文件定义服务
- 通过protocol buffer compiler插件生成客户端和服务端
- 通过grpc API生成客户端和服务端代码
1. 创建maven项目
添加pom依赖, 包含依赖包和生成基础类的插件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>vip.sunjin</groupId><artifactId>GrpcServer</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><grpc.version>1.36.1</grpc.version><protobuf.version>3.15.6</protobuf.version></properties><dependencies><!-- protobuf --><dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>${protobuf.version}</version></dependency><!-- GRPC --><dependency><groupId>io.grpc</groupId><artifactId>grpc-netty</artifactId><version>${grpc.version}</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-protobuf</artifactId><version>${grpc.version}</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-stub</artifactId><version>${grpc.version}</version></dependency></dependencies><build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.6.2</version></extension></extensions><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.36.0:exe:${os.detected.classifier}</pluginArtifact></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build></project>
2. 定义RPC服务数据结构 proto文件
创建一个文件夹src/main/proto/
创建一个helloworld.proto文件
syntax = "proto3";option java_multiple_files = true;
option java_package = "vip.sunjin.examples.helloworld";
option java_outer_classname = "HelloWorldProto";package helloworld;// The greeting service definition.
service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {}
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings
message HelloReply {string message = 1;
}
3. 生成基础类
使用maven编译项目 生成基础类
生成后的类文件如下:
targetclassesvipsunjinexampleshelloworldGreeterGrpc.javaHelloReply.javaHelloReplyOrBuilder.javaHelloRequest.javaHelloRequestOrBuilder.javaHelloWorldClient.javaHelloWorldProto.javaHelloWorldServer.java
GreeterGrpc封装基本的GRPC功能,后续的客户端和服务端都从这个类引申出来。
4. 创建服务端
服务端只需要指定一个端口号,然后暴露一个服务。
/*** Server that manages startup/shutdown of a {@code Greeter} server.*/
public class HelloWorldServer {private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());private Server server;private void start() throws IOException {/* The port on which the server should run */int port = 50051;server = ServerBuilder.forPort(port).addService(new GreeterImpl()).build().start();logger.info("Server started, listening on " + port);Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {// Use stderr here since the logger may have been reset by its JVM shutdown hook.System.err.println("*** shutting down gRPC server since JVM is shutting down");try {HelloWorldServer.this.stop();} catch (InterruptedException e) {e.printStackTrace(System.err);}System.err.println("*** server shut down");}});}private void stop() throws InterruptedException {if (server != null) {server.shutdown().awaitTermination(30, TimeUnit.SECONDS);}}/*** Await termination on the main thread since the grpc library uses daemon threads.*/private void blockUntilShutdown() throws InterruptedException {if (server != null) {server.awaitTermination();}}/*** Main launches the server from the command line.*/public static void main(String[] args) throws IOException, InterruptedException {final HelloWorldServer server = new HelloWorldServer();server.start();server.blockUntilShutdown();}static class GreeterImpl extends GreeterGrpc.GreeterImplBase {@Overridepublic void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();responseObserver.onNext(reply);responseObserver.onCompleted();}}
}
5. 创建客户端
客户端需要指定调用服务的地址和端口号并且通过调用桩代码调用服务端的服务。
客户端和服务端是直连的。
public class HelloWorldClient {private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());private final GreeterGrpc.GreeterBlockingStub blockingStub;/** Construct client for accessing HelloWorld server using the existing channel. */public HelloWorldClient(Channel channel) {// 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to// shut it down.// Passing Channels to code makes code easier to test and makes it easier to reuse Channels.blockingStub = GreeterGrpc.newBlockingStub(channel);}/** Say hello to server. */public void greet(String name) {logger.info("Will try to greet " + name + " ...");HelloRequest request = HelloRequest.newBuilder().setName(name).build();HelloReply response;try {response = blockingStub.sayHello(request);} catch (StatusRuntimeException e) {logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());return;}logger.info("Greeting Reply: " + response.getMessage());}/*** Greet server. If provided, the first element of {@code args} is the name to use in the* greeting. The second argument is the target server.*/public static void main(String[] args) throws Exception {String user = "neil";// Access a service running on the local machine on port 50051String target = "localhost:50051";// Create a communication channel to the server, known as a Channel. Channels are thread-safe// and reusable. It is common to create channels at the beginning of your application and reuse// them until the application shuts down.ManagedChannel channel = ManagedChannelBuilder.forTarget(target)// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid// needing certificates..usePlaintext().build();try {HelloWorldClient client = new HelloWorldClient(channel);client.greet(user);} finally {// ManagedChannels use resources like threads and TCP connections. To prevent leaking these// resources the channel should be shut down when it will no longer be used. If it may be used// again leave it running.channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);}}
}
6. 测试
先启动服务端代码 HelloWorldServer
然后执行客户端代码 HelloWorldClient
执行结果如下:
2月 18, 2023 9:44:17 上午 vip.sunjin.grpcclient.HelloWorldClient greet
信息: Will try to greet neil ...
2月 18, 2023 9:44:18 上午 vip.sunjin.grpcclient.HelloWorldClient greet
信息: Greeting Reply: I'm Grpc Server , Hello neil
三、 grpc服务端流
一般业务场景下,我们都是使用grpc的simple-rpc模式,也就是每次客户端发起请求,服务端会返回一个响应结果的模式。
但是grpc除了这种一来一往的请求模式外,还有流式模式。
服务端流模式是说客户端发起一次请求后,服务端在接受到请求后,可以以流的方式,使用同一连接,不断的向客户端写回响应结果,客户端则可以源源不断的接受到服务端写回的数据。
下面我们通过简单例子,来说明如何使用,服务端端流。
1. 定义RPC服务数据结构 proto文件
MetricsService.proto
syntax = "proto3";option java_multiple_files = true;
option java_package = "vip.sunjin.examples.helloworld";
option java_outer_classname = "MetricsServiceProto";message Metric {int64 metric = 2;
}message Average {double val = 1;
}service MetricsService {rpc collectServerStream (Metric) returns (stream Average);
}
然后使用maven编译项目 生成基础类
2.创建服务端代码
服务实现类
public class MetricsServiceImpl extends MetricsServiceGrpc.MetricsServiceImplBase {private static final Logger logger = Logger.getLogger(MetricsServiceImpl.class.getName());/*** 服务端流* @param request* @param responseObserver*/@Overridepublic void collectServerStream(Metric request, StreamObserver<Average> responseObserver) {logger.info("received request : " + request.getMetric());for(int i = 0; i < 10; i++){responseObserver.onNext(Average.newBuilder().setVal(new Random(1000).nextDouble()).build());logger.info("send to client");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}responseObserver.onCompleted();}}
服务端Server启动类
public class MetricsServer {private static final Logger logger = Logger.getLogger(MetricsServer.class.getName());public static void main(String[] args) throws IOException, InterruptedException {int port = 50051;
// //启动服务MetricsServiceImpl metricsService = new MetricsServiceImpl();Server server = ServerBuilder.forPort(port).addService(metricsService).build();server.start();logger.info("Server started, listening on " + port);server.awaitTermination();}
}
3.创建客户端代码
通过异步Stub 调用服务
public class MetricsClient {private static final Logger logger = Logger.getLogger(MetricsClient.class.getName());public static void main(String[] args) throws InterruptedException {int port = 50051;
// //获取客户端桩对象ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:" + port).usePlaintext().build();MetricsServiceGrpc.MetricsServiceStub stub = MetricsServiceGrpc.newStub(channel);//发起rpc请求,设置StreamObserver用于监听服务器返回结果stub.collectServerStream(Metric.newBuilder().setMetric(1L).build(), new StreamObserver<Average>() {@Overridepublic void onNext(Average value) {System.out.println(Thread.currentThread().getName() + "Average: " + value.getVal());}@Overridepublic void onError(Throwable t) {System.out.println("error:" + t.getLocalizedMessage());}@Overridepublic void onCompleted() {System.out.println("onCompleted:");}});channel.shutdown().awaitTermination(50, TimeUnit.SECONDS);}
}
代码最后要有等待并且关闭通道的操作。
4.测试
先启动服务端,再启动客户端后,可以看到StreamObserver的onNext方法会源源不断的接受到服务端返回的数据。
5,服务端流使用场景:
- 客户端请求一次,但是需要服务端源源不断的返回大量数据时候,比如大批量数据查询的场景。
- 比如客户端订阅服务端的一个服务数据,服务端发现有新数据时,源源不断的吧数据推送给客户端。
四、 grpc客户端流
客户端流模式是说客户端发起请求与服务端建立链接后,可以使用同一连接,不断的向服务端传送数据,等客户端把全部数据都传送完毕后,服务端才返回一个请求结果。
1. 定义RPC服务数据结构 proto文件
这里修改service的定义,其他不变。 MetricsService.proto
service MetricsService {rpc collectClientStream (stream Metric) returns (Average);
}
2.创建服务端代码
如上rpc方法的入参类型前添加stream标识 是客户端流,然后服务端实现代码如下:
public class MetricsServiceImpl extends MetricsServiceGrpc.MetricsServiceImplBase {private static final Logger logger = Logger.getLogger(MetricsServiceImpl.class.getName());/*** 客户端流** @param responseObserver* @return*/@Overridepublic StreamObserver<Metric> collectClientStream(StreamObserver<Average> responseObserver) {return new StreamObserver<Metric>() {private long sum = 0;private long count = 0;@Overridepublic void onNext(Metric value) {logger.info("value: " + value);sum += value.getMetric();count++;}@Overridepublic void onError(Throwable t) {logger.info("severError:" + t.getLocalizedMessage());responseObserver.onError(t);}@Overridepublic void onCompleted() {responseObserver.onNext(Average.newBuilder().setVal(sum / count).build());logger.info("serverComplete: ");responseObserver.onCompleted();}};}
}
如上代码,服务端使用流式对象的onNext方法不断接受客户端发来的数据,然后等客户端发送结束后,使用onCompleted方法,把响应结果写回客户端。
服务端启动类MetricsServer不需要修改
3.创建客户端代码
客户端调用服务需要使用异步的Stub.
public class MetricsClient2 {private static final Logger logger = Logger.getLogger(MetricsServer.class.getName());public static void main(String[] args) throws InterruptedException {int port = 50051;//1.创建客户端桩ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", port).usePlaintext().build();MetricsServiceGrpc.MetricsServiceStub stub = MetricsServiceGrpc.newStub(channel);//2.发起请求,并设置结果回调监听StreamObserver<Metric> collect = stub.collectClientStream(new StreamObserver<Average>() {@Overridepublic void onNext(Average value) {logger.info(Thread.currentThread().getName() + "Average: " + value.getVal());}@Overridepublic void onError(Throwable t) {logger.info("error:" + t.getLocalizedMessage());}@Overridepublic void onCompleted() {logger.info("onCompleted:");}});//3.使用同一个链接,不断向服务端传送数据Stream.of(1L, 2L, 3L, 4L,5L).map(l -> Metric.newBuilder().setMetric(l).build()).forEach(metric -> {collect.onNext(metric);logger.info("send to server: " + metric.getMetric());});Thread.sleep(3000);collect.onCompleted();channel.shutdown().awaitTermination(50, TimeUnit.SECONDS);}
}
4.测试
先启动服务端,再启动客户端后,可以看到代码3会把数据1,2,3,4,5通过同一个链接发送到服务端, 然后等服务端接收完毕数据后,会计算接受到的数据的平均值,然后把平均值写回客户端。 然后代码2设置的监听器的onNext方法就会被回调,然后打印出服务端返回的平均值3。
5,客户端流使用场景:
- 比如数据批量计算场景:如果只用simple rpc的话,服务端就要一次性收到大量数据,并且在收到全部数据之后才能对数据进行计算处理。如果用客户端流 rpc的话,服务端可以在收到一些记录之后就开始处理,也更有实时性。
五、grpc双向流
双向流意味着客户端向服务端发起请求后,客户端可以源源不断向服务端写入数据的同时,服务端可以源源不断向客户端写入数据。
1. 定义RPC服务数据结构 proto文件
这里修改service的定义,其他不变。 重新生成基础代码。 MetricsService.proto
service MetricsService {rpc collectTwoWayStream (stream Metric) returns (stream Average);
}
如上rpc方法的入参类型前添加stream标识, 返回参数前也添加stream标识 就是双向流,然后服务端实现代码如下:
双向流的代码和客户端流基本一样,只是双向流可以同时支持双向的持续写入。
2.创建服务端代码
将服务实现类进行修改用来测试双向流。
public void onNext(Metric value) {logger.info("value: " + value);sum += value.getMetric();count++;responseObserver.onNext(Average.newBuilder().setVal(sum * 1.0/ count).build());
}
如上代码,服务端使用流式对象的onNext方法不断接受客户端发来的数据, 然后 不断的调用参数中的流对象把响应结果持续的写回客户端。 实现了双向流式调用。
3.创建客户端代码
客户端代码主要是把onCompleted之前的线程等待时间加长,以便等待服务端持续的返回。
Thread.sleep(10000);
collect.onCompleted();
4.测试
先启动服务端,再启动客户端后,可以看到代码3会把数据1,2,3,4,5通过同一个链接发送到服务端, 然后等服务端接收数据后,会实时的计算接受到的数据的平均值,然后把平均值写回客户端。 然后代码2设置的监听器的onNext方法就会被回调,然后打印出服务端返回的平均值。
相关文章:
gRPC 非官方教程
一、 简介 gRPC的定义: 一个高性能、通用的开源RPC框架主要面向移动应用开发: gRPC提供了一种简单的方法来精确地定义服务和为iOS、Android和后台支持服务自动生成可靠性很强的客户端功能库。基于HTTP/2协议标准而设计,基于ProtoBuf(Protoc…...
6.2【人工智能与深度学习】RNN、GRU、远程服务管理、注意力、Seq2 搜索引擎和内存网络
【人工智能与深度学习】RNN、GRU、远程服务管理、注意力、Seq2 搜索引擎和内存网络底层原理介绍 深度学习架构循环神经网络(RNN)循环网络:摊开循环的网络的循环循环神经网络的技巧乘法模组注意模组门控循环单元(GRU)长期短期记忆(Long Short-Term Memory,简称LSTM)序列到序列…...
软件工程复习
软件工程简介 软件: -在执行时提供所需的功能和性能的指令; -使程序能够充分操作信息的数据结构; -描述这些程序的操作和使用情况的文档。 软件定义:计算机程序和相关文档。 软件特点:软件没有质量;它并不…...
将Nginx 核心知识点扒了个底朝天(二)
Nginx 是如何实现高并发的? 如果一个 server 采用一个进程(或者线程)负责一个request的方式,那么进程数就是并发数。那么显而易见的,就是会有很多进程在等待中。等什么?最多的应该是等待网络传输。 而 Nginx 的异步非阻塞工作方…...
【PowerQuery】PowerBI 的PowerQuery支持的数据集成
PowerBI中的各个Power组件已经被深度集成到PowerBI中,不再作为像Excel一样的独立组件而存在。在PowerBI的界面中为了快速导入这些常用的数据,也有相应的快速导入界面。PowerBI的快速导入界面位于主页面中,下图就是PowerBI的快速导入界面。 在PowerBI中的数据导入界面相比Exc…...
scipy spatial transform Rotation库的源代码
前几日研究scipy的旋转,不知道具体里面怎么实现的,因此搜索一番。 发现Rotation在scipy的表达是用四元数的 https://github.com/jgagneastro/coffeegrindsize/edit/master/App/dist/coffeegrindsize.app/Contents/Resources/lib/python3.7/scipy/spatia…...
JAVA文件操作
JAVA文件操作 文章目录JAVA文件操作1.属性2.构造方法3.方法3.1创建文件3.2 文件删除3.3创建目录3.4文件名3.5 文件重命名3.6查看文件的可读性 Java中通过 java.io.file类来对文件(目录)进行抽象的描述。注意, 有File对象时,不代表真实存在该文件。1.属…...
字符串匹配 - 模式预处理:BM 算法 (Boyer-Moore)
各种文本编辑器的"查找"功能(CtrlF),大多采用Boyer-Moore算法,效率非常高。算法简介在 1977 年,Robert S. Boyer (Stanford Research Institute) 和 J Strother Moore (Xerox Palo Alto Research Center) 共…...
RV1126笔记三十:freetype显示矢量字体
若该文为原创文章,转载请注明原文出处。 在前面介绍了使用取模软件,可以自定义OSD,这种做法相对不灵活,也无法变更,适用大部分场景。 如果使用opencv需要移植opencv,芯片资源相对要相比好,而且移植比freetype复杂。 这里记录下如何使用freetype显示矢量字体,使用fre…...
polkit pkexec 本地提权漏洞修复方案
polkit pkexec 本地提权漏洞 漏洞细节,polkit pkexec 中对命令行参数处理有误,导致参数注入,能够导致本地提权。 解决建议 1、无法升级软件修复包的,可使用以下命令删除pkexec的SUID-bit权限来规避漏洞风险: chmod 0…...
es-06聚合查询
聚合查询 概念 聚合(aggs)不同于普通查询,是目前学到的第二种大的查询分类,第一种即“query”,因此在代码中的第一层嵌套由“query”变为了“aggs”。用于进行聚合的字段必须是exact value,分词字段不可进行…...
面试知识点准备与总结——(并发篇)
目录线程有哪些状态线程池的核心参数sleep和wait的区别lock 与 synchronized 的异同volatile能否保证线程安全悲观锁和乐观锁的区别Hashtable 与 ConcurrentHashMap 的区别ConcurrentHashMap1.7和1.8的区别ThreadLocal的理解ThreadLocalMap中的key为何要设置为弱引用线程有哪些…...
Django框架之模型视图-URLconf
URLconf 浏览者通过在浏览器的地址栏中输入网址请求网站对于Django开发的网站,由哪一个视图进行处理请求,是由url匹配找到的 配置URLconf 1.settings.py中 指定url配置 ROOT_URLCONF 项目.urls2.项目中urls.py 匹配成功后,包含到应用的urls…...
操作系统闲谈06——进程管理
操作系统闲谈06——进程管理 一、进程调度 01 时间片轮转 给每一个进程分配一个时间片,然后时间片用完了,把cpu分配给另一个进程 时间片通常设置为 20ms ~ 50ms 02 先来先服务 就是维护了一个就绪队列,每次选择最先进入队列的进程&#…...
DaVinci 偏好设置:用户 - UI 设置
偏好设置 - 用户/ UI 设置Preferences - User/ UI Settings工作区选项Workspace Options语言Language指定 DaVinci Resolve 软件界面所使用的语言。目前支持英语、简体中文、日语、西班牙语、葡萄牙语、法语、俄语、泰语和越南语等等。启动时重新加载上一个工作项目Reload last…...
Nacos超简单-管理配置文件
优点理论什么的就不说了,按照流程开始配配置吧。登录Centos,启动Naocs,使用sh /data/soft/restart.sh将自动启动Nacos。访问:http://192.168.101.65:8848/nacos/账号密码:nacos/nacos分为两部分,第一部分准…...
基于微信小程序的中国各地美食推荐平台小程序
文末联系获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.…...
如何优雅的导出函数
在开发过程中,经常会引用外部函数。方法主要有两种: 方法一:包含头文件并制定lib位置 优点:使用简单缺点:lib和vs版本有关,不同的版本和编译模式可能导致编译失败 方法二:GetProcAddress 优…...
c++多重继承
1.概论多重继承是否有必要吗?这个问题显然是一个哲学问题,正确的解答方式是根据情况来看,有时候需要,有时候不需要,这显然是一句废话,有点像上马克思主义哲学或者中庸思。但是这个问题和那些思想一样&#…...
15_FreeRtos计数信号量优先级翻转互斥信号量
目录 计数型信号量 计数型信号量相关API函数 计数型信号量实验源码 优先级翻转简介 优先级翻转实验源码 互斥信号量 互斥信号量相关API函数 互斥信号量实验源码 计数型信号量 计数型信号量相当于队列长度大于1的队列,因此计数型信号量能够容纳多个资源,这在…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
