【ESP32】ESP-IDF开发 | 低功耗蓝牙开发 | GATT规范和ATT属性协议 + 电池电量服务例程

news2025/5/18 19:59:46

1. 简介

        低功耗蓝牙中最为核心的部分当属 GATT(Generic Attribute Profile,全称通用属性配置文件。而 GATT 又是建立在 ATT 协议(属性协议)的基础之上,为 ATT 协议传输和存储的数据建立了通用操作和框架。GATT 和 ATT 并不针对特定的传输协议,可在经典蓝牙和低功耗蓝牙中使用。不过,它们是必须在低功耗蓝牙中实施的,因为低功耗蓝牙用于发现服务。

        GATT 定义了两种角色: 服务器(Server)客户端(Client)。GATT 服务器存储通过ATT 协议传输的数据,并接受来自 GATT 客户端的 ATT 协议请求、命令和确认。GATT 服务器发送对请求的响应,并在 GATT 服务器发生指定事件时,根据配置向 GATT 客户端异步发送指示和通知。

1.1 ATT协议

        低功耗蓝牙里面的数据以属性 (Attribute) 的方式存在,每条属性由四个元素组成——属性句柄(Attribute Handle)、属性类型(Attribute UUID)、属性值(Attribute Value)和属性许可(Attribute Permissions)

  • 属性句柄:可以通俗理解为属性的序号,例如第⼀个属性的句柄是0x0001,第二个属性的句柄是 0x0002,以此类推,最大可以到 0xFFFF;
  • 属性类型:每个数据有自己需要代表的意思,例如温度、发射功率、电池等等。这个类型是以UUID的形式来表示的,有16bit和128bit两种长度,蓝牙组织(Bluetooth SIG)负责对常用的一些数据类型进行归类;
  • 属性值:这个部分就是数据本身,长度可以是固定的,也可以是可变的;
  • 属性许可:每个属性对各自的属性值有相应的访问限制,比如有些属性是可读的、有些是可写的、有些是可读又可写的等等。拥有数据的一方可以通过属性许可,控制本地数据的可读写属性。

1.2 GATT规范

        ATT 属性协议规定了在低功耗蓝牙中的最小数据存储单位,而 GATT 规范则定义了如何用特性值和描述符表示一个数据,如何把相似的数据聚合成服务(Service),以及如何发现对端设备拥有哪些服务和数据。

        GATT 规范引进了特性(Charateristic)的概念。这是由于在某些时候,一个数据可能并不只是单纯的数值,还会带有一些额外的信息。比如,这个数据的单位、这个数值的名称等等。

        一个特性包含特性声明(Characteristic Declaration)、特性数值(Characteristic Value)和描述符(Descriptor)

  • 特性声明:用于告诉对方此声明后面跟的内容为特性数值。
  • 特征数值:用于承载特性的真正内容。
  • 描述符:对特性进行进⼀步的描述,每个特性可以有多个描述符,也可以没有描述符。

2. 例程

        这个例程会建立一个标准的 GATT 和 ATT 通信环境,然后注册一个电池电量服务。使能ESP32作为服务器,启动后不断广播让周围的设备连接自己;然后我们使用手机上的测试APP连接ESP32,并读取电池电量服务中的数据。

2.1 menuconfig

        ESP32是默认不使能蓝牙功能的,因此要配置menuconfig来使能。首先,使能蓝牙控制器为仅低功耗蓝牙。

        然后,使能协议栈为NimBLE。

        保存配置后建议清除原有的CMake缓存,重新配置,不然静态语言检查可能会不生效。 

2.2 代码

#include <stdint.h>
#include <string.h>
#include <inttypes.h>
#include <stdbool.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#include "nvs.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "esp_log.h"

#include "host/ble_gap.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "services/bas/ble_svc_bas.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"

#define TAG "app"
#define OWN_NAME "ESP32"

#define BDASTR "%02X:%02X:%02X:%02X:%02X:%02X"
#define BDA2STR(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5]

static uint8_t ble_addr_type;
static TaskHandle_t bas_task_handle;
static uint8_t device_name[] = OWN_NAME;

static void ble_advertise(void);
static int ble_gap_event(struct ble_gap_event *event, void *arg);
static void ble_on_reset(int reason);
static void ble_on_sync(void);
static void ble_host_task(void *param);
static void bas_task(void* args);


static void ble_advertise(void)
{    
    int rc;

    struct ble_hs_adv_fields fields;
    memset(&fields, 0, sizeof(fields));
    /* 标志位 */
    fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
    /* 发射功率 */
    fields.tx_pwr_lvl_is_present = 1;
    fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
    /* 设备名 */
    fields.name = device_name;
    fields.name_len = strlen(OWN_NAME);
    fields.name_is_complete = 1;
    /* UUID */
    fields.uuids16 = (ble_uuid16_t[]) {
        BLE_UUID16_INIT(BLE_SVC_BAS_UUID16)
    };
    fields.num_uuids16 = 1;
    fields.uuids16_is_complete = 1;

    rc = ble_gap_adv_set_fields(&fields);
    if (rc != 0) {
        ESP_LOGE(TAG, "error setting advertisement data; rc=%d", rc);
        return;
    }

    /* 开始广播 */
    struct ble_gap_adv_params adv_params;
    memset(&adv_params, 0, sizeof(adv_params));

    adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
    adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
    rc = ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_gap_event, NULL);
    if (rc != 0) {
        ESP_LOGE(TAG, "error enabling advertisement, rc=%d", rc);
        return;
    }
}

static int ble_gap_event(struct ble_gap_event *event, void *arg)
{
    int rc = 0;

    switch (event->type) {
        /* 连接建立事件 */
        case BLE_GAP_EVENT_LINK_ESTAB:
            ESP_LOGI(TAG, "connection %s, status=%d",
                     event->connect.status == 0 ? "established" : "failed",
                     event->connect.status);

            if (event->connect.status != 0) {
                ble_advertise();
            } else {
                xTaskCreate(bas_task, "bas_task", 2048, NULL, 5, &bas_task_handle);
            }
            break;

        /* 连接断开事件 */
        case BLE_GAP_EVENT_DISCONNECT:
            ESP_LOGI(TAG, "disconnect, reason=%d", event->disconnect.reason);
            if (bas_task_handle != NULL) {
                vTaskDelete(bas_task_handle);
            }
            ble_advertise();
            break;

        /* 广播完成事件 */
        case BLE_GAP_EVENT_ADV_COMPLETE:
            ESP_LOGI(TAG, "advertise complete");
            ble_advertise();
            break;

        /* 订阅事件 */
        case BLE_GAP_EVENT_SUBSCRIBE:
            ESP_LOGI(TAG, "subscribe event, cur_notify=%d, val_handle=%d",
                     event->subscribe.cur_notify,
                     event->subscribe.attr_handle);
            break;

        /* MTU更新事件 */
        case BLE_GAP_EVENT_MTU:
            ESP_LOGI(TAG, "mtu updated, conn_handle=%d mtu=%d",
                     event->mtu.conn_handle,
                     event->mtu.value);
            break;

        default:
            break;
    }

    return rc;
}

static void ble_on_reset(int reason)
{
    ESP_LOGE(TAG, "Resetting state; reason=%d", reason);
}

static void ble_on_sync(void)
{
    int ret;

    /* 获取最佳地址类型 */
    ret = ble_hs_id_infer_auto(0, &ble_addr_type);
    if (ret) {
        ESP_LOGE(TAG, "ble_hs_id_infer_auto failed, err: %d", ret);
        return;
    }

    /* 获取地址 */
    uint8_t addr_val[6] = {0};
    ret = ble_hs_id_copy_addr(ble_addr_type, addr_val, NULL);
    if (ret) {
        ESP_LOGE(TAG, "ble_hs_id_copy_addr failed, err: %d", ret);
        return;
    }
    ESP_LOGI(TAG, "Device address: " BDASTR, BDA2STR(addr_val));

    /* 使能广播 */
    ble_advertise();
}

static void ble_host_task(void *param)
{
    nimble_port_run();
    nimble_port_freertos_deinit();
}

static void bas_task(void* args)
{
    while (1) {
        for (uint8_t i = 0; i <= 10; i++) {
            ble_svc_bas_battery_level_set(i * 10);
            ESP_LOGI(TAG, "set battery level to %d", i * 10);
            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }
    }
}

int app_main()
{
    /* 初始化NVS */
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    /* 初始化控制器和NimBLE协议栈 */
    ret = nimble_port_init();
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to init nimble %d ", ret);
        return -1;
    }

    /* 使能电池电量服务 */
    ble_svc_bas_init();

    /* 配置主机 */
    ble_hs_cfg.reset_cb = ble_on_reset;
    ble_hs_cfg.sync_cb = ble_on_sync;
    ble_hs_cfg.store_status_cb = ble_store_util_status_rr;

    /* 使能绑定 */
    ble_hs_cfg.sm_bonding = 1;
    ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
    ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
    ble_hs_cfg.sm_sc = 1;
    ble_hs_cfg.sm_mitm = 1;

    /* 设置设备名 */
    if (ble_svc_gap_device_name_set(OWN_NAME) != 0) {
        ESP_LOGE(TAG, "set device name failed");
        return -1;
    }

    /* 使能NimBLE协议栈 */
    nimble_port_freertos_init(ble_host_task);

    return 0;
}

在上一篇文章中已经介绍了低功耗蓝牙 GAP 协议相关的代码编写,所以下面就不再赘述。

        在初始化蓝牙控制器和NimBLE协议栈后,调用 ble_svc_bas_init 函数即可初始化电池电量服务,NimBLE协议栈封装了大部分常用的服务接口,基本上可以即用。

        接着配置相关的回调函数,一个是 reset_cb ,这个是在设备复位时会调用,这里就是简单打印一个log;另一个是 sync_cb ,这个是协议栈刚启动和复位时会调用的,这里面首先会调用 ble_hs_id_infer_auto 函数去获取当前设备的最佳地址类型,然后调用 ble_advertise 去使能广播。

        使能广播前需要配置一个结构体,定义如下:

struct ble_hs_adv_fields {
    /*** 0x01 - Flags. */
    uint8_t flags;

    /*** 0x02,0x03 - 16-bit service class UUIDs. */
    const ble_uuid16_t *uuids16;
    uint8_t num_uuids16;
    unsigned uuids16_is_complete:1;

    /*** 0x04,0x05 - 32-bit service class UUIDs. */
    const ble_uuid32_t *uuids32;
    uint8_t num_uuids32;
    unsigned uuids32_is_complete:1;

    /*** 0x06,0x07 - 128-bit service class UUIDs. */
    const ble_uuid128_t *uuids128;
    uint8_t num_uuids128;
    unsigned uuids128_is_complete:1;

    /*** 0x08,0x09 - Local name. */
    const uint8_t *name;
    uint8_t name_len;
    unsigned name_is_complete:1;

    /*** 0x0a - Tx power level. */
    int8_t tx_pwr_lvl;
    unsigned tx_pwr_lvl_is_present:1;

    /*** 0x0d - Slave connection interval range. */
    const uint8_t *slave_itvl_range;

    /*** 0x10 - Security Manager TK value */
    const uint8_t *sm_tk_value;
    unsigned sm_tk_value_is_present:1;

    /*** 0x11 - Security Manager OOB flag */
    uint8_t sm_oob_flag;
    unsigned sm_oob_flag_is_present:1;

    /*** 0x14 - 16-bit service soliciation list. */
    const ble_uuid16_t *sol_uuids16;
    uint8_t sol_num_uuids16;

    /*** 0x15 - 128-bit service solicitation UUIDs. */
    const ble_uuid128_t *sol_uuids128;
    uint8_t sol_num_uuids128;

    /*** 0x16 - Service data - 16-bit UUID. */
    const uint8_t *svc_data_uuid16;
    uint8_t svc_data_uuid16_len;

    /*** 0x17 - Public target address. */
    const uint8_t *public_tgt_addr;
    uint8_t num_public_tgt_addrs;

    /*** 0x18 - Random target address. */
    const uint8_t *random_tgt_addr;
    uint8_t num_random_tgt_addrs;

    /*** 0x19 - Appearance. */
    uint16_t appearance;
    unsigned appearance_is_present:1;

    /*** 0x1a - Advertising interval. */
    uint16_t adv_itvl;
    unsigned adv_itvl_is_present:1;

    /*** 0x1b - LE Bluetooth device address */
    const uint8_t *device_addr;
    unsigned device_addr_type;
    unsigned device_addr_is_present:1;

    /*** 0xF1 - 32-bit service solicitation UUIDs */
    const ble_uuid32_t *sol_uuids32;
    uint8_t sol_num_uuids32;

    /*** 0x1c - LE Role */
    uint8_t le_role;
    unsigned le_role_is_present:1;

    /*** 0x20 - Service data - 32-bit UUID. */
    const uint8_t *svc_data_uuid32;
    uint8_t svc_data_uuid32_len;

    /*** 0x21 - Service data - 128-bit UUID. */
    const uint8_t *svc_data_uuid128;
    uint8_t svc_data_uuid128_len;

    /*** 0x24 - URI. */
    const uint8_t *uri;
    uint8_t uri_len;

    /*** 0xff - Manufacturer specific data. */
    const uint8_t *mfg_data;
    uint8_t mfg_data_len;
};

        这个结构体非常长,这个例程里面我们只需关注标志位(Flags)、UUID、本地名称(Local Name)和发射功率这几个部分。

        标志位目前支持以下几个:

#define BLE_HS_ADV_F_DISC_LTD                   0x01  // 限制性发现模式
#define BLE_HS_ADV_F_DISC_GEN                   0x02  // 普通发现模式
#define BLE_HS_ADV_F_BREDR_UNSUP                0x04  // 禁用经典蓝牙

        UUID是跟使能的服务类型有关的,例程里面的电池电量服务使用的是16bit的UUID,UUID号可以在对应的头文件中找到。

        本地名称就是广播时附带的设备名,设置字符串数组和其长度即可。

        发射功率一般来说填BLE_HS_ADV_TX_PWR_LVL_AUTO,来让协议栈决定。

        配置完结构体后调用 ble_gap_adv_set_fields 写入配置。调用 ble_gap_adv_start 来使能广播;参数一为地址类型;参数二为直接地址,用于定向广播时使用;参数三为广播时长,填BLE_HS_FOREVER表示无限时间;参数四为配置参数,结构体定义如下:

struct ble_gap_adv_params {
    /** Advertising mode. Can be one of following constants:
     *  - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2).
     *  - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3).
     *  - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4).
     */
    uint8_t conn_mode;
    /** Discoverable mode. Can be one of following constants:
     *  - BLE_GAP_DISC_MODE_NON  (non-discoverable; 3.C.9.2.2).
     *  - BLE_GAP_DISC_MODE_LTD (limited-discoverable; 3.C.9.2.3).
     *  - BLE_GAP_DISC_MODE_GEN (general-discoverable; 3.C.9.2.4).
     */
    uint8_t disc_mode;

    /** Minimum advertising interval, if 0 stack use sane defaults */
    uint16_t itvl_min;
    /** Maximum advertising interval, if 0 stack use sane defaults */
    uint16_t itvl_max;
    /** Advertising channel map , if 0 stack use sane defaults */
    uint8_t channel_map;

    /** Advertising  Filter policy */
    uint8_t filter_policy;

    /** If do High Duty cycle for Directed Advertising */
    uint8_t high_duty_cycle:1;
};
  • conn_mode:连接模式,BLE_GAP_CONN_MODE_NON(不可连接)、BLE_GAP_CONN_MODE_DIR(直接连接)、BLE_GAP_CONN_MODE_UND(非直接连接)
  • disc_mode:发现模式,BLE_GAP_DISC_MODE_NON(不可发现)、BLE_GAP_DISC_MODE_LTD(限制性发现)、BLE_GAP_DISC_MODE_GEN(普通发现)
  • itvl_min:最小广播间隔,0为协议栈决定;
  • itvl_max:最大广播间隔,0为协议栈决定;
  • channel_map:通道遮罩,一共有3个通道,填BLE_GAP_ADV_DFLT_CHANNEL_MAP为全开;0为协议栈决定;
  • filter_policy:过滤策略;
  • high_duty_cycle:高占空比(仅直接广播)。

         参数五为回调函数,参数六为回调函数的用户参数。

        上面的回调函数是重点,主要处理以下几个GAP事件:

1. 广播完成事件(BLE_GAP_EVENT_ADV_COMPLETE)

        当广播程序执行完后触发,理论上我们这里是不会触发的,因为代码里面设置了永不超时的广播。

2. 连接建立事件(BLE_GAP_EVENT_LINK_ESTAB)

        当有子节点与广播者建立连接时触发,这里会创建一个电池电量的任务,任务主函数里面就是定时地调用 ble_svc_bas_battery_level_set 来设置电池电量百分比。

3. 连接断开事件(BLE_GAP_EVENT_DISCONNECT)

        当有子节点断开连接时触发,这里会删除电池电量任务,并重新使能广播。

4. 订阅事件(BLE_GAP_EVENT_SUBSCRIBE)

        当有子节点的订阅状态改变时触发,这里只是打印log。

5. MTU更新事件(BLE_GAP_EVENT_MTU)

        当L2CAP协议的某通道MTU长度改变时触发,这里只是打印log。

        回到 main 函数,下面继续设置一些安全配对相关的配置。sm_bonding,使能设备绑定功能;sm_our_key_dist,本地键值对遮罩;sm_their_key_dist,远程键值对遮罩;sm_mitm,使能MITM保护协议;sm_sc,使能安全连接。

        调用 ble_svc_gap_device_name_set 设置本机的设备名。最后调用 nimble_port_freertos_init 使能NimBLE协议栈。

2.3 运行

        下面是部分的运行log。

        打开手机的调试上位机,找到我们的ESP32。

         连接设备后,可以在“服务”这一栏看到电池电量服务的相关信息。

         点击该服务卡片,接收服务的数据,可以看到下面的就会显示ESP32发送过来的服务数据了。

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

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

相关文章

2025 年九江市第二十三届中职学校技能大赛 (网络安全)赛项竞赛样题

2025 年九江市第二十三届中职学校技能大赛 &#xff08;网络安全&#xff09;赛项竞赛样题 &#xff08;二&#xff09;A 模块基础设施设置/安全加固&#xff08;200 分&#xff09;A-1 任务一登录安全加固&#xff08;Windows,Linux&#xff09;A-2 任务二 Nginx 安全策略&…

【记录】Windows|竖屏怎么调整分辨率使横竖双屏互动鼠标丝滑

本文版本&#xff1a;Windows11&#xff0c;记录一下&#xff0c;我最后调整的比较舒适的分辨率是800*1280。 文章目录 第一步 回到桌面第二步 右键桌面第三步 设置横屏为主显示器第四步 调整分辨率使之符合你的需求第五步 勾选轻松在显示器之间移动光标第六步 拖动屏幕符合物理…

开源项目实战学习之YOLO11:12.2 ultralytics-models-sam-decoders.py源码分析

👉 点击关注不迷路 👉 点击关注不迷路 👉 另外,前些天发现了一个巨牛的AI人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。感兴趣的可以点击相关跳转链接。 点击跳转到网站。 ultralytics-models-sam 1.sam-modules-decoders.pyblocks.py: 定义模型中的各…

数据结构*优先级队列(堆)

什么是优先级队列(堆) 优先级队列一般通过堆&#xff08;Heap&#xff09;这种数据结构来实现&#xff0c;堆是一种特殊的完全二叉树&#xff0c;其每个节点都满足堆的性质。如下图所示就是一个堆&#xff1a; 堆的存储方式 由于堆是一棵完全二叉树&#xff0c;所以也满足二…

汽车Wafer连接器:工业设备神经网络的隐形革命者

汽车Wafer连接器正在突破传统车载场景的边界&#xff0c;以毫米级精密结构重构工业设备的连接范式。这款厚度不足3毫米的超薄连接器&#xff0c;在新能源电池模组中承载200A持续电流的同时&#xff0c;仍能保持85℃温升的稳定表现&#xff0c;其每平方厘米高达120针的触点密度&…

微信小程序:封装表格组件并引用

一、效果 封装表格组件,在父页面中展示表格组件并显示数据 二、表格组件 1、创建页面 创建一个components文件夹,专门用于存储组件的文件夹 创建Table表格组件 2、视图层 (1)表头数据 这里会从父组件中传递表头数据,这里为columns,后续会讲解数据由来 循环表头数组,…

Conda 完全指南:从环境管理到工具集成

Conda 完全指南&#xff1a;从环境管理到工具集成 在数据科学、机器学习和 Python 开发领域&#xff0c;环境管理一直是令人头疼的问题。不同项目依赖的库版本冲突、Python 解释器版本不兼容等问题频繁出现&#xff0c;而 Conda 的出现彻底解决了这些痛点。作为目前最流行的跨…

安卓中0dp和match_parent区别

安卓中的 0dp 和 match_parent 的区别&#xff1f; 第一章 前言 有段时间&#xff0c;看到同事在编写代码的时候&#xff0c;写到的是 0dp 有时候自己写代码的时候&#xff0c;编写的是 match_parent 发现有时候效果很类似。 后来通过一个需求案例&#xff0c;才发现两者有着…

信贷风控笔记4——贷前策略之额度、定价(面试准备12)

1.贷前模型的策略应用 分类&#xff1a;审批准入&#xff08;对头尾部区分度要求高&#xff09;&#xff1a;单一规则&#xff08;找lift>3的分数做规则&#xff09;&#xff1b;二维交叉&#xff1b;拒绝回捞 额度定价&#xff08;对排序性要求高&#xff09;&am…

A级、B级弱电机房数据中心建设运营汇报方案

该方案围绕A 级、B 级弱电机房数据中心建设与运营展开,依据《数据中心设计规范》等标准,施工范围涵盖 10 类机房及配套设施,采用专业化施工团队与物资调配体系,强调标签规范、线缆隐藏等细节管理。运营阶段建立三方协同运维模式,针对三级故障制定30 分钟至 1 小时响应机制…

Linux中的域名解析服务器

一、DNS&#xff08;域名系统&#xff09;详解 1. 核心功能与特点 特性说明核心作用将域名&#xff08;如 www.example.com&#xff09;转换为 IP 地址&#xff08;如 192.168.1.1&#xff09;&#xff0c;实现人类可读地址与机器可读地址的映射。端口与协议- 默认端口&#…

CycleISP: Real Image Restoration via Improved Data Synthesis通过改进数据合成实现真实图像恢复

摘要 大规模数据集的可用性极大释放了深度卷积神经网络(CNN)的潜力。然而,针对单图像去噪问题,获取真实数据集成本高昂且流程繁琐。因此,图像去噪算法主要基于合成数据开发与评估,这些数据通常通过广泛假设的加性高斯白噪声(AWGN)生成。尽管CNN在合成数据集上表现优异…

Day28 Python打卡训练营

知识点回顾&#xff1a; 1. 类的定义 2. pass占位语句 3. 类的初始化方法 4. 类的普通方法 5. 类的继承&#xff1a;属性的继承、方法的继承 作业 题目1&#xff1a;定义圆&#xff08;Circle&#xff09;类 要求&#xff1a; 1. 包含属性&#xff1a;半径 radius。 2. …

Linux之Nginx安装及配置原理篇(一)

Nginx安装及配置 前情回顾 首先针对Nginx进程模型&#xff0c;我们回顾一下它的原理机制&#xff0c;我们知道它是通过Master通过fork分发任务节点给予work节点&#xff0c;然后work节点触发了event事件&#xff0c;之后通过一个access_muttex互斥锁&#xff0c;来单线程调用我…

【Linux网络】NAT和代理服务

NAT 之前我们讨论了&#xff0c;IPv4协议中&#xff0c;IP地址数量不充足的问题。 原始报文途径路由器WAN口时&#xff0c;对报文中的源IP进行替换的过程&#xff0c;叫做NAT。 NAT技术当前解决IP地址不够用的主要手段&#xff0c;是路由器的一个重要功能&#xff1a; NAT能…

中药药效成分群的合成生物学研究进展-文献精读130

Advances in synthetic biology for producing potent pharmaceutical ingredients of traditional Chinese medicine 中药药效成分群的合成生物学研究进展 摘要 中药是中华民族的文化瑰宝&#xff0c;也是我国在新药创制领域的重要驱动力。许多中药材来源于稀缺物种&#xf…

【消息队列】RabbitMQ基本认识

目录 一、基本概念 1. 生产者&#xff08;Producer&#xff09; 2. 消费者&#xff08;Consumer&#xff09; 3. 队列&#xff08;Queue&#xff09; 4. 交换器&#xff08;Exchange&#xff09; 5. 绑定&#xff08;Binding&#xff09; 6. 路由键&#xff08;Routing …

1T 服务器租用价格解析

服务器作为数据存储与处理的核心设备&#xff0c;对于企业和个人开发者而言至关重要。当涉及到租用 1T 服务器时&#xff0c;价格是大家很为关注的要点。然而&#xff0c;1T 服务器租用一个月的费用并非固定不变&#xff0c;而是受到诸多因素的综合影响。​ 影响 1T 服务器租用…

【JavaWeb】Maven(下)

1 依赖管理 1.1 依赖配置 1.1.1 基本配置 依赖&#xff1a;指当前项目运行所需要的jar包。 一个项目中可以引入多个依赖&#xff1a; 例如&#xff1a;在当前工程中&#xff0c;我们需要用到logback来记录日志&#xff0c;此时就可以在maven工程的pom.xml文件中&#xff0c;引…

openEuler24.03 LTS下安装MySQL8.0.42

目录 前提步骤 删除原有mysql及maridb数据库 安装MySQL 启动MySQL 启动查看MySQL状态 设置MySQL开机自启动 查看登录密码 登录MySQL 修改密码及支持远程连接 远程连接MySQL 前提步骤 拥有openEuler24.03 LTS环境&#xff0c;可参考&#xff1a;Vmware下安装openEule…