TensorRt(5)动态尺寸输入的分割模型测试

news2024/11/24 18:42:23

文章目录

  • 1、固定输入尺寸逻辑
  • 2、动态输入尺寸
    • 2.1、模型导出
    • 2.2、推理测试
    • 2.3、显存分配问题
    • 2.4、完整代码

这里主要说明使用TensorRT进行加载编译优化后的模型engine进行推理测试,与前面进行目标识别、目标分类的模型的网络输入是固定大小不同,导致输入维度不能直接获取需要自己手动调整的问题。

1、固定输入尺寸逻辑

基本逻辑如下:

  • 读取engine文件到内存
  • 使用TensorRT运行时IRuntime序列化一个引擎ICudaEngine,在创建一个上下文对象IExecutionContext
  • 从引擎中ICudaEngine获取输入、输出的纬度和数据类型,并分配显存
  • 将输入从host内存中拷贝到输入device显存中
  • 利用创建的上下文对象IExecutionContext执行推理
  • 将推理结果从输出device显存拷贝到host内存中

至于显存分配,根据engine是可以获取网络输入输出的尺寸的。以前面 【TensorRt(3)mnist示例中的C++ API】 博客中的简要代码为例说推理代码路程:

int simple2()
{
    /// 2.1  加载engine到内存
	// .... 省略
    std::vector<char> buf(buflen);
    // .... 

    /// 2.2 反序列化
    std::unique_ptr<IRuntime> runtime{ createInferRuntime(sample::gLogger.getTRTLogger()) };
    auto mEngine = std::shared_ptr<nvinfer1::ICudaEngine>(
        runtime->deserializeCudaEngine(buf.data(),buf.size()),[](nvinfer1::ICudaEngine* p) {delete p; });

    // inference上下文
    auto context = std::unique_ptr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());

    // 网络输入、输出信息  
    auto mInputDims = mEngine->getBindingDimensions(0);     // 部署使用 [1,1,28,28]
    auto mOutputDims = mEngine->getBindingDimensions(1);    // 部署使用 [1,10]
    int inputH = mInputDims.d[2];
    int inputW = mInputDims.d[3];

    //----------  整个网络输入只有1个,输出只有1个,且均为float类型,分配cuda显存
    std::vector<void*> bindings(mEngine->getNbBindings());
    for (int i = 0; i < bindings.size(); i++) {
        nvinfer1::DataType type = mEngine->getBindingDataType(i);// 明确为 float
        size_t volume = 
           sizeof(float) * std::accumulate(dims.d,dims.d + dims.nbDims,1,std::multiplies<size_t>());

        CHECK(cudaMalloc(&bindings[i],volume));
    }

    // 加载一个random image
    srand(unsigned(time(nullptr)));
    std::vector<uint8_t> fileData(inputH * inputW);
	// 省略.... 

	//----------  输入host数据类型从uint8_t转换为float, 这里明确知道 1*1*28*28
    std::vector<float> hostDataBuffer(1 * 1 * 28 * 28); 
    for (int i = 0; i < inputH * inputW; i++) {
        hostDataBuffer[i] = 1.0 - float(fileData[i] / 255.0);
    }
    //----------  将图像数据从host空间拷贝到device空间
    CHECK(cudaMemcpy(bindings[0],static_cast<const void*>(hostDataBuffer.data()),hostDataBuffer.size() * sizeof(float), cudaMemcpyHostToDevice));

    //----------  excution执行推理
    bool status = context->executeV2(bindings.data());

    ///  2.3 处理推理结果数据,
    //----------  将推理结果从device空间拷贝到host空间
    std::vector<float> pred(1 * 10); // 这里明确知道 1*10)
    CHECK(cudaMemcpy(static_cast<void*>(pred.data()),bindings[1], pred.size() * sizeof(float),cudaMemcpyDeviceToHost));
    // .... 省略
}

2、动态输入尺寸

以paddle中的语义语义分割模型 OCRNet + backbone HRNet_w18 为例进行说明测试。

2.1、模型导出

默认训练模型导出推理模型是不带softmax和argmax层的,为避免后续实现效率降低,添加这两层之再导出推理模型,使用paddle2onnx导出onnx模型,

paddle2onnx --model_dir saved_inference_model \
            --model_filename model.pdmodel \
            --params_filename model.pdiparams \
            --save_file model.onnx \
            --enable_dev_version True       
            # --opset_version 12 # default 9

使用netron工具查看输入和输出的尺寸信息截图如下
在这里插入图片描述
输入尺寸为 [n, 3, h, w],原始输出为[n,c,h,w],由于使用soft和argmax,输出直接为[1, h, w]。输出和输入图像宽高一致,输出的每个像素位置就是像素分类的类别数。

之后使用 trtexec 将onnx模型编译优化导出为engine类型,由于是动态输入,因此指定了输入尺寸范围和最优尺寸。

trtexec.exe 
	--onnx=model.onnx 
	--explicitBatch --fp16 
	--minShapes=x:1x3x540x960 
	--optShapes=x:1x3x720x1280 
	--maxShapes=x:1x3x1080x1920 
	--saveEngine=model.trt

2.2、推理测试

(0)基本类型数据准备
我们仅使用#include "NvInfer.h" ,来使用TensorRT sdk,定义几个需要的宏和对象

#define CHECK(status)                                                            \
    do                                                                           \
    {                                                                            \
        auto ret = (status);                                                     \
        if (ret != 0)                                                            \
        {                                                                        \
            std::cerr << "Cuda failure: " << ret << std::endl;                   \
            abort();                                                             \
        }                                                                        \
    } while (0)
class Logger : public nvinfer1::ILogger
{
public:
    Logger(Severity severity = Severity::kWARNING) : 
        severity_(severity) {}

    virtual void log(Severity severity, const char* msg) noexcept override
    {
        // suppress info-level messages
        if(severity <= severity_)
            std::cout << msg << std::endl;
    }

    nvinfer1::ILogger& getTRTLogger() noexcept
    {
        return *this;
    }
private:
    Severity severity_;
};
struct InferDeleter
{
    template <typename T>
    void operator()(T* obj) const
    {
        delete obj;
    }
};

template <typename T>
using SampleUniquePtr = std::unique_ptr<T, InferDeleter>;

(1)加载模型


    Logger logger(nvinfer1::ILogger::Severity::kVERBOSE);
    /*
    trtexec.exe --onnx=inference_model\model.onnx --explicitBatch --fp16 --minShapes=x:1x3x540x960 --optShapes=x:1x3x720x1280 --maxShapes=x:1x3x1080x1920 --saveEngine=model.trt
    */

    std::string trtFile = R"(C:\Users\wanggao\Desktop\123\inference_model_0\model.trt)";
    //std::string trtFile = "model.test.trt";

    std::ifstream ifs(trtFile, std::ifstream::binary);
    if(!ifs) {
        return false;
    }
    ifs.seekg(0, std::ios_base::end);
    int size = ifs.tellg();
    ifs.seekg(0, std::ios_base::beg);
    std::unique_ptr<char> pData(new char[size]);
    ifs.read(pData.get(), size);
    ifs.close();

    // engine模型
    //SampleUniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};
    //auto mEngine = std::shared_ptr<nvinfer1::ICudaEngine>(
    //    runtime->deserializeCudaEngine(pData.get(), size), InferDeleter());
    //auto context = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());
    std::shared_ptr<nvinfer1::ICudaEngine> mEngine;
    {
        SampleUniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};
        mEngine = std::shared_ptr<nvinfer1::ICudaEngine>(
            runtime->deserializeCudaEngine(pData.get(), size), InferDeleter());
    }
    auto context = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());

(2)输入
将RGB三通道图像转换为NCHW格式数据,数据类型为float。

    // 输入 前处理
    //cv::Mat img = cv::imread("dog.jpg");
    cv::Mat img = cv::imread(R"(C:\Users\wanggao\Desktop\123\data\test\t.jpg)");
    cv::Mat blob = cv::dnn::blobFromImage(img, 1 / 255., img.size(), {0,0,0}, true, false);
    //blob = blob * 2 - 1;  // 测试使用,可以不归一化

(3)显存分配
不同于固定输入,通过engine获取尺寸并分配显存大小

	// 固定输入尺寸的显存分配方式
    std::vector<void*> bindings(mEngine->getNbBindings());
    for (int i = 0; i < bindings.size(); i++) {
        nvinfer1::DataType type = mEngine->getBindingDataType(i);
        nvinfer1::Dims dims = mEngine->getBindingDimensions(i);
        //size_t volume = 
        //   sizeof(float) * std::accumulate(dims.d,dims.d + dims.nbDims,1,std::multiplies<size_t>());
           
        size_t volume = std::accumulate(dims.d,dims.d + dims.nbDims,1,std::multiplies<size_t>());
        switch (type)
        {
        	case nvinfer1::DataType::kINT32:
        	case nvinfer1::DataType::kFLOAT: volume *= 4; break;  // 明确为类型 float
        	case nvinfer1::DataType::kHALF: volume *= 2; break;
        	case nvinfer1::DataType::kBOOL:
        	case nvinfer1::DataType::kINT8:
        	default:break;
        }
        
        CHECK(cudaMalloc(&bindings[i],volume));
    }

这里通过模型获取的输入类型为float尺寸为[-1,3,1,1]、输出类型为int32尺寸为[-1,-1,-1],即获取不到尺寸信息。所以,只能根据输入的尺寸来分配显存大小。(文后说明在实际推理中应该如何分配)

	// 设置网络的输入尺寸
    context->setBindingDimensions(0, nvinfer1::Dims4{1, 3 , img.rows, img.cols});

	// 分配显存
    std::vector<void*> bindings(mEngine->getNbBindings()); 
    //auto type1 = mEngine->getBindingDataType(0);  // kFLOAT  float
    //auto type2 = mEngine->getBindingDataType(1);  // kINT32  int
    CHECK(cudaMalloc(&bindings[0], sizeof(float) * 1 * 3 * img.rows * img.cols*3));    // n*3*h*w
    CHECK(cudaMalloc(&bindings[1], sizeof(int) * 1 * 1 * img.rows * img.cols*3));   // n*1*h*w

注意,必须通过context->setBindingDimensions()设置网络的输入尺寸,否则网络在推理时报错,即输入维度未指定,导出网络输出无结果。

3: [executionContext.cpp::nvinfer1::rt::ShapeMachineContext::resolveSlots::1541] Error Code 3: API Usage Error (Parameter check failed at: executionContext.cpp::nvinfer1::rt::ShapeMachineContext::resolveSlots::1541, condition: allInputDimensionsSpecified(routine)
)
9: [executionContext.cpp::nvinfer1::rt::ExecutionContext::executeInternal::564] Error Code 9: Internal Error (Could not resolve slots: )

(4)推理
将前处理后的图片数据拷贝到显存中,之后进行推理,之后将推理结果数据从显存拷贝到内存中

    cv::Mat pred(img.size(), CV_32SC1, {255,255,255}); // 用于输出
    //cv::reduceArgMax()  //opencv 4.8.0
    
    //buffers.copyInputToDevice();
    CHECK(cudaMemcpy(bindings[0], static_cast<const void*>(blob.data), 3 * img.rows * img.cols * sizeof(float), cudaMemcpyHostToDevice));
    
    context->executeV2(bindings.data());
    
	// buffers.copyOutputToHost()
    CHECK(cudaMemcpy(static_cast<void*>(pred.data), bindings[1], pred.total() * sizeof(int), cudaMemcpyDeviceToHost));

(5)结果数据展示(后处理)
这里仅显示,运行截图如下
在这里插入图片描述

2.3、显存分配问题

在示例中,我们根据图片大小来分配显存,实际应用将进行多次推理,有多种方案:

  • 1、根据实际输入大小,每次进行动态分配(使用完后释放原已分配显存)
  • 2、在1基础上,如果显存不够再分配(分配前释放原已分配显存)
  • 3、预分配一块较大的显存,程序退出时释放显存

为提高效率,我们可以选择第三种,已知我们动态输入最大尺寸为 --maxShapes=x:1x3x1080x1920,因此我们直接根据网络输入输出类型分配显存

    std::vector<void*> bindings(mEngine->getNbBindings());  // 1个输入,1个输出
    CHECK(cudaMalloc(&bindings[0], sizeof(float) * 1 * 3 * 1280 * 1920)); // n*3*h*w
    CHECK(cudaMalloc(&bindings[1], sizeof(int) * 1 * 1 * * 1280 * 1920)); // n*1*h*w

当输入尺寸超过我们设置的 maxShapes 时,context->setBindingDimensions()将报异常提示,这一种情况就不应该继续执行分配显存,属于输出错误。

2.4、完整代码

#include "opencv2\opencv.hpp"

#include "NvInfer.h"
#include <cuda_runtime_api.h>
#include <random>

//using namespace nvinfer1;
//using samplesCommon::SampleUniquePtr;

#include <fstream>
#include <string>

#define CHECK(status)                                                                \
    do                                                                               \
    {                                                                                \
        auto ret = (status);                                                         \
        if (ret != 0)                                                                \
        {                                                                            \
            std::cerr << "Cuda failure: " << ret << std::endl;                       \
            abort();                                                                 \
        }                                                                            \
    } while (0)

class Logger : public nvinfer1::ILogger
{
public:
    Logger(Severity severity = Severity::kWARNING) : 
        severity_(severity) {}

    virtual void log(Severity severity, const char* msg) noexcept override 
    {
        // suppress info-level messages
        if(severity <= severity_)
            std::cout << msg << std::endl;
    }

    nvinfer1::ILogger& getTRTLogger() noexcept
    {
        return *this;
    }
private:
    Severity severity_;
};


struct InferDeleter
{
    template <typename T>
    void operator()(T* obj) const
    {
        delete obj;
    }
};

template <typename T>
using SampleUniquePtr = std::unique_ptr<T, InferDeleter>;

int inference();

int main(int argc, char** argv)
{
    return inference();
}

int inference()
{
    Logger logger(nvinfer1::ILogger::Severity::kVERBOSE);
 
    /*
    trtexec.exe --onnx=inference_model\model.onnx --explicitBatch --fp16 --minShapes=x:1x3x540x960 --optShapes=x:1x3x720x1280 --maxShapes=x:1x3x1080x1920 --saveEngine=model.trt
    */

    std::string trtFile = R"(C:\Users\wanggao\Desktop\123\inference_model_0\model.trt)";
    //std::string trtFile = "model.test.trt";

    std::ifstream ifs(trtFile, std::ifstream::binary);
    if(!ifs) {
        return false;
    }
    ifs.seekg(0, std::ios_base::end);
    int size = ifs.tellg();
    ifs.seekg(0, std::ios_base::beg);
    std::unique_ptr<char> pData(new char[size]);
    ifs.read(pData.get(), size);
    ifs.close();


    // engine模型
    //SampleUniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};
    //auto mEngine = std::shared_ptr<nvinfer1::ICudaEngine>(
    //    runtime->deserializeCudaEngine(pData.get(), size), InferDeleter());
    //auto context = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());

    std::shared_ptr<nvinfer1::ICudaEngine> mEngine;
    {
        SampleUniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};
        mEngine = std::shared_ptr<nvinfer1::ICudaEngine>(
            runtime->deserializeCudaEngine(pData.get(), size), InferDeleter());
    }
    auto context = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());

    // 输入
    //cv::Mat img = cv::imread("dog.jpg");
    cv::Mat img = cv::imread(R"(C:\Users\wanggao\Desktop\123\data\test\t.jpg)");
    cv::Mat blob = cv::dnn::blobFromImage(img, 1 / 255., img.size(), {0,0,0}, true, false);
    //blob = blob * 2 - 1;

    cv::Mat pred(img.size(), CV_32SC1, {255,255,255});
    //cv::reduceArgMax() //4.8.0
    context->setBindingDimensions(0, nvinfer1::Dims4{1, 3 , img.rows, img.cols});

	// 分配显存
    std::vector<void*> bindings(mEngine->getNbBindings()); 
    //auto type1 = mEngine->getBindingDataType(0);  // kFLOAT  float
    //auto type2 = mEngine->getBindingDataType(1);  // kINT32  int
    CHECK(cudaMalloc(&bindings[0], sizeof(float) * 1 * 3 * img.rows * img.cols*3));    // n*3*h*w
    CHECK(cudaMalloc(&bindings[1], sizeof(int) * 1 * 1 * img.rows * img.cols*3));   // n*1*h*w

    // 推理
    // warmingup ...
    CHECK(cudaMemcpy(bindings[0], static_cast<const void*>(blob.data), 1 * 3 * 640 * 640 * sizeof(float), cudaMemcpyHostToDevice));
    context->executeV2(bindings.data());
    context->executeV2(bindings.data());
    context->executeV2(bindings.data());
    context->executeV2(bindings.data());
    CHECK(cudaMemcpy(static_cast<void*>(pred.data), bindings[1], 1 * 84 * 8400 * sizeof(int), cudaMemcpyDeviceToHost));
    
    auto t1 = cv::getTickCount();
    
    //buffers.copyInputToDevice();
    CHECK(cudaMemcpy(bindings[0], static_cast<const void*>(blob.data), 3 * img.rows * img.cols * sizeof(float), cudaMemcpyHostToDevice));
    context->executeV2(bindings.data());
	// buffers.copyOutputToHost()
    CHECK(cudaMemcpy(static_cast<void*>(pred.data), bindings[1], pred.total() * sizeof(int), cudaMemcpyDeviceToHost));

    auto t2 = cv::getTickCount();

    std::cout << (t2-t1) / cv::getTickFrequency() << std::endl;

	// 资源释放
	cudaFree(bindings[0]);
    cudaFree(bindings[1]);
    
    return 0;
}

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

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

相关文章

Docker中镜像的相关操作

1.辅助操作 docker version&#xff1a;用查看docker客户端引擎和server端引擎版本信息。 docker info&#xff1a;用来查看docker引擎的详细信息。 docker --help&#xff1a;用来查看帮助信息。 2.镜像Image docker images&#xff1a;查看当前本地仓库中存在哪些镜像。 …

Mysql是怎样运行的--下

文章目录 Mysql是怎样运行的--下查询优化explainoptimizer_trace InnoDB的Buffer Pool&#xff08;缓冲池&#xff09;Buffer Pool的存储结构空闲页存储--free链表脏页&#xff08;修改后的数据&#xff09;存储--flush链表 使用Buffer PoolLRU链表的管理 事务ACID事务的状态事…

在CentOS环境下编译GreatSQL RPM包

本文介绍如何在CentOS环境下编译GreatSQL RPM包。 运行环境是docker中的CentOS 8 x86_64&#xff1a; $ docker -v Docker version 20.10.10, build b485636$ docker run -itd --hostname c8 --name c8 centos bash a0a2128591335ef41e6faf46b7e79953c097500e9f033733c3ab37f…

使用curl命令在Linux中进行HTTP请求

在Linux中&#xff0c;curl是一个非常强大的命令行工具&#xff0c;用于发送HTTP请求。它允许用户发送各种类型的HTTP请求&#xff0c;如GET、POST、PUT、DELETE等&#xff0c;并能够处理响应数据。 首先&#xff0c;确保您的Linux系统已经安装了curl。如果未安装&#xff0c;…

Android 13 移除下拉栏中的设置入口

介绍 因为当前项目的设置已被加密&#xff0c;客户不希望通过下拉窗口的设置图标进入设置&#xff0c;决定去掉该图标。 效果展示 分析 这里首先想到在SystemUI寻找这个图标的资源文件&#xff0c;找到资源文件后寻找对应控件调用的地方&#xff0c;根据id寻找控件代码即可。…

2024年Google Ads新手指南——广告运作与类型、工具

谷歌广告投放是出海企业的必备运营动作&#xff0c;但你需要先了解他的运作逻辑、广告类型、投放必备的工具类型&#xff0c;之后可以为你的投放的高速转化做好万全准备&#xff0c;毕竟每一分钱都要花在刀刃上&#xff01;废话不多说&#xff0c;下面开始为新手准备了基础指南…

【LLM】vLLM部署与int8量化

Acceleration & Quantization vLLM vLLM是一个开源的大型语言模型&#xff08;LLM&#xff09;推理和服务库&#xff0c;它通过一个名为PagedAttention的新型注意力算法来解决传统LLM在生产环境中部署时所遇到的高内存消耗和计算成本的挑战。PagedAttention算法能有效管理…

重置 Docker 中 Gitlab 的账号密码

1、首先进入Docker容器 docker exec -it gitlab bash 2、连接到 gitlab 的数据库 需要谨慎操作 gitlab-rails console -e production 等待加载完后会进入控制台 ------------------------------------------------------------------------------------------------------…

Page 251~254 Win32 GUI项目

win32_gui 源代码&#xff1a; #if defined(UNICODE) && !defined(_UNICODE)#define _UNICODE #elif defined(_UNICODE) && !defined(UNICODE)#define UNICODE #endif#include <tchar.h> #include <windows.h>/* Declare Windows procedure */…

知名开发者社区Stack Overflow发布《2023 年开发者调查报告》

Stack Overflow成立于2008年&#xff0c;最知名的是它的公共问答平台&#xff0c;每月有超过 1 亿人访问该平台来提问、学习和分享技术知识。是世界上最受欢迎的开发者社区之一。每年都会发布一份关于开发者的调查报告&#xff0c;来了解不断变化的开发人员现状、正在兴起或衰落…

[机缘参悟-122] :IT人如何认识自己的?自省、面试、考核、咨询?

目录 一、为什么要认识自己 二、认识自己的哪些方面&#xff1f; 三、如何认识自己 3.1 通过自省认识自己 3.2 通过面试认识自己 3.3 通过咨询认识自己 3.4 通过相亲认识自己 3.5 通过一段感情关系认识自己 一、为什么要认识自己 认识自己在人类的成长和心灵发展过程中…

亚马逊实时 AI 编程助手 CodeWhisperer使用体验

文章目录 1&#xff1a;什么是CodeWhisperer &#xff1f;2&#xff1a;试用3&#xff1a;上手体验 1&#xff1a;什么是CodeWhisperer &#xff1f; 最近ChatGPT展现出强大AI能力给我们带来了深刻的影响&#xff0c;AI现在不是一个概念&#xff0c;基于AI的产品一定在各行各业…

Linux网络配置与抓包工具介绍

目录 一、配置命令 1. ifconfig 1.1 概述信息解析 1.2 常用格式 2. ip 2.1 ip link 数据链路层 2.2 ip addr 网络层 2.3 路由 3. hostname 3.1 临时修改主机名 3.2 永久修改主机名 4. route 5. netstat 6. ss 7. ping 8. traceroute 9. nslookup 10. 永久修…

vivado图形化设计篇

一.看懂波形 二.由波形可得真值表 三.可得逻辑表达式 YA(BC) 四. 逻辑框图 五.vivado图形化设计 &#xff08;1&#xff09;创建文件 1.create block desige 2.文件命名&#xff0c;设置文件放置地址 &#xff08;2&#xff09; 添加IP核 1.打开desige&#xff0c;右键&#…

UniRepLKNet实战:使用UniRepLKNet实现图像分类任务(一)

文章目录 摘要安装包安装timm 数据增强Cutout和MixupEMA项目结构计算mean和std生成数据集一些问题 摘要 大核卷积神经网络&#xff08;ConvNets&#xff09;近年来受到广泛关注&#xff0c;但仍存在两个关键问题需要进一步研究。首先&#xff0c;目前的大型卷积神经网络架构大…

如何在企业中实施自适应人工智能?

人工智能不再是企业的选择。很快&#xff0c;它也将不再是一个区分因素。商业中的适应性人工智能正在改变格局。根据最近的统计数据&#xff0c;95%的企业以上都在追求人工智能。 因此&#xff0c;为了确保你拥有竞争优势&#xff0c;你必须期待先进的人工智能选项。适应性就是…

CH341 SPI方式烧录BK7231U

CH341是一个USB总线的转接芯片&#xff0c;通过USB总线提供异步串口、打印口、并口以及常用的2线和4线等同步串行接口。 BK7231U Wi-Fi SOC芯片&#xff0c;内嵌处理器。1. 符合802.11b/g/n 1x1协议 2. 17dBm 输出功率3. 支持20/40 MHz带宽和STBC 4. 支持Wi-Fi STA、AP、…

回归预测 | Matlab基于SO-LSTM蛇群算法优化长短期记忆神经网络的数据多输入单输出回归预测

回归预测 | Matlab基于SO-LSTM蛇群算法优化长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SO-LSTM蛇群算法优化长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SO-LSTM蛇群算法优化…

vivado 工程管理

管理项目 打开项目 当项目打开时&#xff0c;Vivado IDE会从项目已关闭。项目状态包括当前源文件顺序、已禁用和已启用 源文件、活动约束文件和目标约束文件&#xff0c;以及合成、模拟和实现运行。要打开项目&#xff0c;请使用以下方法之一&#xff1a; •在“入门”页面…

使用 STM32 和 DS18B20 温度传感器设计室内温度监测与报警系统

为设计室内温度监测与报警系统&#xff0c;我们将利用STM32微控制器和DS18B20数字温度传感器&#xff0c;以及蜂鸣器实现温度报警功能。在本文中&#xff0c;将介绍如何通过STM32微控制器读取DS18B20传感器的温度数据&#xff0c;并在超出设定范围时触发蜂鸣器报警。 1. 系统概…