释放 OpenAI 和 ESP-BOX 的力量:ChatGPT 与乐鑫 SoC 融合指南

news2024/10/6 6:45:49

当前,我们正见证着一场技术革命,而 OpenAI 正处于这场变革的最前沿。其中最激动人心的创新之一就是ChatGPT,它运用自然语言处理的力量,打造出更加引人入胜、直观的用户体验。而将 OpenAI 的 API 与物联网设备相结合,更是为我们打开了无限可能。  

本文将探索 ChatGPT 与 ESP-BOX 的潜力,这对强大的组合可以将物联网设备提升到一个新的水平。

本文主要分为三个部分,分别介绍本项目的关键内容:

  • 第一部分将详细介绍 ESP-BOX,阐述其功能和特点。
  • 第二部分为案例研究,描述如何从零开始一步步构建项目。
  • 最后一部分为总结,提供了相关资料的来源,便于您巩固和加深对本项目的了解和理解。​​​​​​​

ESP-BOX 简介

ESP-BOX 是新一代 AIoT 开发平台,包含 ESP32-S3-BOX 和 ESP32-S3-BOX-Lite 开发板,二者搭载 ESP32-S3 Wi-Fi + Bluetooth 5 (LE) SoC,为开发集成各种传感器、控制器和网关的 AIoT 应用提供了灵活可定制的解决方案。

ESP32-S3-BOX

ESP-BOX 拥有丰富的功能,使其成为理想的 AIoT 开发平台。 接下来将为您介绍其中一些关键功能:

1. 双麦克风远场语音互动

ESP-BOX 支持双麦克风远场语音互动,实现与设备远距离互动。

2. 离线中英文语音指令识别,识别率高

ESP-BOX 提供离线的中英文语音指令识别,具有高识别率,可轻松开发支持语音的设备。

3. 200+ 中英文语音指令可重新配置

开发人员可根据需求轻松配置 200 多个中英文语音指令。

4. 持续识别和唤醒中断

ESP-BOX 支持持续识别和唤醒中断,确保设备始终做好接收语音指令的准备。

5. 灵活可复用的 GUI 框架

ESP-BOX 配备灵活可复用的 GUI 框架,让开发人员可以为应用程序创建个性化的用户界面。

6. 端到端的 AIoT 开发框架 ESP-RainMaker

ESP-BOX 基于端到端的AIoT 开发框架 ESP-RainMaker,为开发人员提供创建强大智能设备所需的工具。

7. 兼容 Pmod™ 接口,支持外设模块扩展

ESP-BOX 具备兼容 Pmod™ 的接口,可轻松扩展设备功能,与各种外设模块互动无障碍。

案例研究

本案例使用 ESP-BOX 和 OpenAI API 开发一款语音控制的聊天机器人 (chatbot)。

介绍

本案例将介绍如何使用 ESP-BOX 和 OpenAI API 开发一款语音控制的聊天机器人。该系统可以接收用户的语音指令,将其展示在屏幕上,并调用 OpenAI API 进行处理,生成相应的回复。回复将显示在 ESP-BOX 屏幕上,然后播放出来。我们将按照下文中的开发流程,逐步深入了解如何巧妙地融合这些技术,打造出高效的语音控制聊天机器人。

案例研究的开发流程

环境设置

为了避免错误,需设置合适的环境并安装正确的软件版本。

  • ESP-IDF

在本示例中,我们将使用 ESP-IDF 5.0 版本(主分支)。如果你需要关于如何设置 ESP-IDF 的指导,请参考官方《ESP-IDF 编程指南》了解更多信息。

*截至撰写本文时,IDF 提交头为 df9310ada2。

  • ChatGPT API

ChatGPT 是一个基于 GPT-3.5 架构的强大语言模型,要使用 ChatGPT,请先前往 OpenAI 平台上创建账户,获取免费或付费的 API 密钥。通过 API 密钥,可获取很多功能和能力,例如自然语言处理与生成、文本补全以及对话建模等,还可根据自己的需求定义这些功能。更多信息请访问官方 API 参考。

*请务必保障 API 密钥的机密性和安全性,防止未经授权访问您的账户和数据。

赋能离线语音识别

乐鑫开发的创新性语音识别框架 ESP-SR 能够使设备在不依赖外部云服务的情况下识别口语词汇和短语,因此非常适合离线语音识别应用。

ESP-SR 框架包含多个模块,包括音频前端 (AFE)、唤醒词引擎 (WakeNet)、语音指令词识别 (MultiNet) 和语音合成(目前仅支持中文)。请查阅 ESP-SR 官方文档获取更多信息。

集成 OpenAI API

OpenAI API 提供了许多功能,开发人员可以利用这些功能来增强他们的应用程序。我们的项目使用了音频转文本 API 和补全 API,以及基于 ESP-IDF 的 C 语言代码。下文简要介绍我们所使用的代码。

  • 音频转文本

我们使用 HTTPS 和 OpenAI 音频 API 从音频中提取文本,以下为实现代码。

esp_err_t create_whisper_request_from_record(uint8_t *audio, int audio_len)
{
    // Set the authorization headers
    char url[128] = "https://api.openai.com/v1/audio/transcriptions";
    char headers[256];
    snprintf(headers, sizeof(headers), "Bearer %s", OPENAI_API_KEY);
    // Configure the HTTP client
    esp_http_client_config_t config = {
        .url = url,
        .method = HTTP_METHOD_POST,
        .event_handler = response_handler,
        .buffer_size = MAX_HTTP_RECV_BUFFER,
        .timeout_ms = 60000,
        .crt_bundle_attach = esp_crt_bundle_attach,
    };

    // Initialize the HTTP client
    esp_http_client_handle_t client = esp_http_client_init(&config);

    // Set the headers
    esp_http_client_set_header(client, "Authorization", headers);

    // Set the content type and the boundary string
    char boundary[] = "boundary1234567890";
    char content_type[64];
    snprintf(content_type, sizeof(content_type), "multipart/form-data; boundary=%s", boundary);
    esp_http_client_set_header(client, "Content-Type", content_type);

    // Set the file data and size
    char *file_data = NULL;
    size_t file_size;
    file_data = (char *)audio;
    file_size = audio_len;

    // Build the multipart/form-data request
    char *form_data = (char *)malloc(MAX_HTTP_RECV_BUFFER);
    assert(form_data);
    ESP_LOGI(TAG, "Size of form_data buffer: %zu bytes", sizeof(*form_data) * MAX_HTTP_RECV_BUFFER);
    int form_data_len = 0;
    form_data_len += snprintf(form_data + form_data_len, MAX_HTTP_RECV_BUFFER - form_data_len,
                              "--%s\r\n"
                              "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"
                              "Content-Type: application/octet-stream\r\n"
                              "\r\n", boundary, get_file_format(file_type));
    ESP_LOGI(TAG, "form_data_len %d", form_data_len);
    ESP_LOGI(TAG, "form_data %s\n", form_data);

    // Append the audio file contents
    memcpy(form_data + form_data_len, file_data, file_size);
    form_data_len += file_size;
    ESP_LOGI(TAG, "Size of form_data: %zu", form_data_len);

    // Append the rest of the form-data
    form_data_len += snprintf(form_data + form_data_len, MAX_HTTP_RECV_BUFFER - form_data_len,
                              "\r\n"
                              "--%s\r\n"
                              "Content-Disposition: form-data; name=\"model\"\r\n"
                              "\r\n"
                              "whisper-1\r\n"
                              "--%s--\r\n", boundary, boundary);

    // Set the headers and post field
    esp_http_client_set_post_field(client, form_data, form_data_len);

    // Send the request
    esp_err_t err = esp_http_client_perform(client);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "HTTP POST request failed: %s\n", esp_err_to_name(err));
    }

    // Clean up client
    esp_http_client_cleanup(client);

    // Return error code
    return err;
}

这段代码是一个名为 whisper_from_record 的函数,它接受一个指向包含音频数据的缓冲区的指针和一个表示音频数据长度的整数 audio_len。该函数向 OpenAI API 端点发送一个 POST 请求,对给定的音频数据进行转录。

该函数首先初始化 OpenAI API 的 URL,并使用持有者令牌 (bearer token) 的OPENAI_API_KEY 设置授权头。然后,配置并初始化 HTTP 客户端,包括配置 URL、HTTP 方法、事件处理程序、缓冲区大小、超时和 SSL 证书等。

接下来,将内容类型 (content type) 和多部分表单数据 (multipart/form_data) 请求的边界字符串设置为 HTTP 客户端的头部,还设置了文件数据和文件大小,并构建了一个多部分表单数据请求。使用 malloc() 函数分配 form_data 缓冲区,并添加必要的信息,包括音频文件的文件名、内容类型、文件内容以及将用于转录的模型名称。

一旦构建了 form_data,它就被设置为 HTTP 客户端的 POST 字段,并且客户端将 POST 请求发送到 OpenAI API 端点。如果请求过程中出现错误,该函数会记录错误消息。最后,清理 HTTP 客户端,并释放为 form_data 分配的资源。

该函数返回一个 esp_err_t 错误代码,指示 HTTP 请求是否成功。

  • 聊天补全​​​​​​​

我们使用 OpenAI 聊天补全 (Chat Completion) API 发送 HTTPS 请求来进行聊天补全。这个过程使用 create_chatgpt_request 函数,该函数接受一个表示输入文本的 content 参数,并将参数内容输入 GPT-3.5 模型。 

esp_err_t create_chatgpt_request(const char *content)
{
    char url[128] = "https://api.openai.com/v1/chat/completions";
    char model[16] = "gpt-3.5-turbo";
    char headers[256];
    snprintf(headers, sizeof(headers), "Bearer %s", OPENAI_API_KEY);

    esp_http_client_config_t config = {
        .url = url,
        .method = HTTP_METHOD_POST,
        .event_handler = response_handler,
        .buffer_size = MAX_HTTP_RECV_BUFFER,
        .timeout_ms = 30000,
        .cert_pem = esp_crt_bundle_attach,
    };

    // Set the headers
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_http_client_set_header(client, "Content-Type", "application/json");
    esp_http_client_set_header(client, "Authorization", headers);

    // Create JSON payload with model, max tokens, and content
    snprintf(json_payload, sizeof(json_payload), json_fmt, model, MAX_RESPONSE_TOKEN, content);
    esp_http_client_set_post_field(client, json_payload, strlen(json_payload));

    // Send the request
    esp_err_t err = esp_http_client_perform(client);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "HTTP POST request failed: %s\n", esp_err_to_name(err));
    }

    // Clean up client
    esp_http_client_cleanup(client);

    // Return error code
    return err;
}

该函数首先设置了用于 HTTP POST 请求的 URL、模型和头部信息,然后创建一个包含模型、最大 token 数和内容的 JSON 负载。

接下来,函数设置了 HTTP 请求的头部信息,并将 JSON 负载设置为请求的 POST 字段。

使用 esp_http_client_perform() 发送 HTTP POST 请求,如果请求失败,将记录错误消息。

最后,清理 HTTP 客户端并返回错误代码。

  • 处理响应

ESP-IDF HTTP 客户端库使用回调函数 response_handler 来处理在 HTTP 请求/响应交换过程中发生的事件。

esp_err_t response_handler(esp_http_client_event_t *evt)
{
    static char *data = NULL; // Initialize data to NULL
    static int data_len = 0; // Initialize data to NULL

    switch (evt->event_id) {
    case HTTP_EVENT_ERROR:
        ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
        break;

    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;

    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
        break;

    case HTTP_EVENT_ON_HEADER:
        if (evt->data_len) {
            ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
            ESP_LOGI(TAG, "%.*s", evt->data_len, (char *)evt->data);
        }
        break;

    case HTTP_EVENT_ON_DATA:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA (%d +)%d\n", data_len, evt->data_len);
        ESP_LOGI(TAG, "Raw Response: data length: (%d +)%d: %.*s\n", data_len, evt->data_len, evt->data_len, (char *)evt->data);
        
        // Allocate memory for the incoming data        
        data = heap_caps_realloc(data, data_len + evt->data_len + 1,  MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
        if (data == NULL) {
            ESP_LOGE(TAG, "data realloc failed");
            free(data);
            data = NULL;
            break;
        }
        memcpy(data + data_len, (char *)evt->data, evt->data_len);
        data_len += evt->data_len;
        data[data_len] = '\0';
        break;

    case HTTP_EVENT_ON_FINISH:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
        if (data != NULL) {
            // Process the raw data
            parsing_data(data, strlen(data));
            // Free memory
            free(data); 
            data = NULL;
            data_len = 0;
        }
        break;

    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        break;

    default:
        break;
    }
    return ESP_OK;
}

在 HTTP_EVENT_ON_DATA 事件发生时,该函数为接收到的数据分配内存空间,将数据复制到缓冲区,并相应地增加 data_len 变量的值。这样做是为了累积响应数据。

在 HTTP_EVENT_ON_FINISH 事件发生时,该函数打印一条消息,指示 HTTP 交换已完成,然后调用 parsing_data 函数来处理累积的原始数据,释放内存并将数据和 data_len 变量重置为零,释放分配的内存并将缓冲区及其长度重置为零。

最后,该函数返回 ESP_OK,表示操作成功。

  • 解析原始数据

我们使用 JSON 解析器组件解析从 ChatGPT API 和 Whisper AI API 获取的 HTTPS 原始响应。为了完成这个任务,我们使用一个调用解析器组件的函数。有关该工具的更多细节,请参考 GitHub。

void parse_response (const char *data, int len)
{
    jparse_ctx_t jctx;
    int ret = json_parse_start(&jctx, data, len);
    if (ret != OS_SUCCESS) {
        ESP_LOGE(TAG, "Parser failed");
        return;
    }
    printf("\n");
    int num_choices;

    /* Parsing Chat GPT response*/
    if (json_obj_get_array(&jctx, "choices", &num_choices) == OS_SUCCESS) {
        for (int i = 0; i < num_choices; i++) {
            if (json_arr_get_object(&jctx, i) == OS_SUCCESS && json_obj_get_object(&jctx, "message") == OS_SUCCESS &&
                    json_obj_get_string(&jctx, "content", message_content, sizeof(message_content)) == OS_SUCCESS) {
                ESP_LOGI(TAG, "ChatGPT message_content: %s\n", message_content);
            }
            json_arr_leave_object(&jctx);
        }
        json_obj_leave_array(&jctx);
    }

    /* Parsing Whisper AI response*/
    else if (json_obj_get_string(&jctx, "text", message_content, sizeof(message_content)) == OS_SUCCESS) {
        ESP_LOGI(TAG, "Whisper message_content: %s\n", message_content);
    } else if (json_obj_get_object(&jctx, "error") == OS_SUCCESS) {
        if (json_obj_get_string(&jctx, "type", message_content, sizeof(message_content)) == OS_SUCCESS) {
            ESP_LOGE(TAG, "API returns an error: %s", message_content);
        }
    }
}

集成 TTS API

目前,OpenAI 并未公开提供其文本转语音 (TTS) API 的访问权限。然而,市面上有多种其他的 TTS API 可供选择,包括 Voicerss、TTSmaker 和会话精灵 (TalkingGenie)。这些 API 可以根据文本输入生成语音,你可以在它们的网站上找到更多相关信息。

本教程使用的是 TalkingGenie API,它是目前可用的最佳选择之一,可以生成高质量、自然流畅的英文和中文语音。TalkingGenie 的一个特点是它能够无缝地将混合语言文本(如中文和英文)转化为语音。这对于面向全球受众的内容创作来说是一个宝贵的工具。下面的代码将 ChatGPT 生成的文本响应发送给 TalkingGenie API,然后通过 ESP-BOX 播放生成的语音。

esp_err_t text_to_speech_request(const char *message, AUDIO_CODECS_FORMAT code_format)
{
    int j = 0;
    size_t message_len = strlen(message);
    char *encoded_message;
    char *language_format_str, *voice_format_str, *codec_format_str;

    // Encode the message for URL transmission
    encoded_message = heap_caps_malloc((3 * message_len + 1), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    url_encode(message, encoded_message);

    // Determine the audio codec format
    if (AUDIO_CODECS_MP3 == code_format) {
        codec_format_str = "MP3";
    } else {
        codec_format_str = "WAV";
    }

    // Determine the required size of the URL bu
    int url_size = snprintf(NULL, 0, "https://dds.dui.ai/runtime/v1/synthesize?voiceId=%s&text=%s&speed=1&volume=%d&audiotype=%s", \
                            VOICE_ID, \
                            encoded_message, \
                            VOLUME, \
                            codec_format_str);

    // Allocate memory for the URL buffer
    char *url = heap_caps_malloc((url_size + 1), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    if (url == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for URL");
        return ESP_ERR_NO_MEM;
    }

    // Format the URL string
    snprintf(url, url_size + 1, "https://dds.dui.ai/runtime/v1/synthesize?voiceId=%s&text=%s&speed=1&volume=%d&audiotype=%s", \
             VOICE_ID, \
             encoded_message, \
             VOLUME, \
             codec_format_str);

    // Configure the HTTP client
    esp_http_client_config_t config = {
        .url = url,
        .method = HTTP_METHOD_GET,
        .event_handler = http_event_handler,
        .buffer_size = MAX_FILE_SIZE,
        .buffer_size_tx = 4000,
        .timeout_ms = 30000,
        .crt_bundle_attach = esp_crt_bundle_attach,
    };

    // Initialize and perform the HTTP request
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_err_t err = esp_http_client_perform(client);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
    }

    // Free allocated memory and clean up the HT
    heap_caps_free(url);
    heap_caps_free(encoded_message);
    esp_http_client_cleanup(client);

    // Return the result of the function call
    return err;
}

函数 text_to_speech 接受一个消息字符串和 AUDIO_CODECS_FORMAT 参数作为输入。消息字符串是将被合成为语音的文本,而 AUDIO_CODECS_FORMAT 参数指定语音应该以 MP3 还是 WAV 格式进行编码。

该函数首先使用 url_encode 函数对消息字符串进行编码,将一些非有效字符替换为相应的 ASCII 代码,然后将该代码转换为两位十六进制格式。接下来为生成的编码字符串分配内存,检查 AUDIO_CODECS_FORMAT 参数,并设置适当的编解码器格式字符串,用于 url。

然后,函数确定 TalkingGenie API 的 GET 请求需要多大的 url 缓冲区,并分配相应的内存给 url 缓冲区。然后,将适当的参数写入 url 字符串,包括 voiceId(指定要使用的语音)、编码的文本、语音的速度和音量以及音频类型 (MP3 或 WAV)。

接下来,函数使用 url 和其他配置参数设置 esp_http_client_config_t 结构体,并使用该结构体初始化 esp_http_client_handle_t,然后使用 esp_http_client_perform 向 TalkingGenie API 发送 GET 请求。如果请求成功,函数返回 ESP_OK,否则返回错误代码。

最后,函数释放为 url 缓冲区和编码消息分配的内存,清理 esp_http_client_handle_t,并返回错误代码。

  • 处理 TTS 响应

类似地,回调函数 http_event_handler 可用于处理在 HTTP 请求/响应交换过程中发生的事件。

static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id) {
    // Handle errors that occur during the HTTP request
    case HTTP_EVENT_ERROR:
        ESP_LOGE(TAG, "HTTP_EVENT_ERROR");
        break;

    // Handle when the HTTP client is connected
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;

    // Handle when the header of the HTTP request is sent
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
        break;

    // Handle when the header of the HTTP response is received
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
        file_total_len = 0;
        break;

    // Handle when data is received in the HTTP response
    case HTTP_EVENT_ON_DATA:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
        if ((file_total_len + evt->data_len) < MAX_FILE_SIZE) {
            memcpy(record_audio_buffer + file_total_len, (char *)evt->data, evt->data_len);
            file_total_len += evt->data_len;
        }
        break;

    // Handle when the HTTP request finishes
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH:%d, %d K", file_total_len, file_total_len / 1024);
        audio_player_play(record_audio_buffer, file_total_len);
        break;

    // Handle when the HTTP client is disconnected
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        break;

    // Handle when a redirection occurs in the HTTP request
    case HTTP_EVENT_REDIRECT:
        ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT");
        break;
    }
    return ESP_OK;
}  

HTTP_EVENT_ON_DATA 事件用于处理从服务器接收到的音频数据。音频数据存储在名为 record_audio_buffer 的缓冲区中,接收到的音频数据的总长度存储在名为 file_total_len 的变量中。如果接收到的音频数据的总长度小于预定义的 MAX_FILE_SIZE,则将数据复制到 record_audio_buffer 中。

最后,HTTP_EVENT_ON_FINISH 事件用于处理 HTTP 响应的结束。在这种情况下,将 record_audio_buffer 传递给名为 audio_player_play 的函数,用于播放音频。

显示

我们使用 LVGL 实现显示功能。LVGL 是一个开源的嵌入式图形库,因其强大且具有视觉吸引力的特性和低内存占用而日益受到欢迎。LVGL 还发布了一个名为 SquareLine Studio 的可视化拖放式 UI 编辑器。这是一个强大的工具,可帮助你轻松为应用程序创建美观的图形界面。

您可以使用乐鑫提供的官方软件包管理工具将 LVGL 集成到项目中,该工具可直接将 LVGL 和相关的移植组件添加到项目中,极大程度地节省了时间和精力。有关更多信息,请参阅官方博客和文档。


总结

OpenAI 的 ChatGPT 与乐鑫的 ESP-BOX 的完美融合,为创造强大而智能的物联网设备开创了新可能。ESP-BOX 提供灵活且可定制的 AIoT 开发平台,拥有远场语音交互、离线语音命令识别和可复用的 GUI 框架等功能。当这些功能与 OpenAI API 相结合时,开发者们即可打造语音控制的聊天机器人,提升物联网应用的用户体验。​​​​​​​

ESP-BOX 聊天机器人展示

您可以查看乐鑫的 GitHub 仓库,获取更多有关 ESP-IoT-Solution、ESP-SR 和 ESP-BOX 的开源案例。在 ESP-BOX 仓库的 examples 文件夹找到该项目的源代码。未来,我们计划引入一个用于 OpenAI API 的组件,以便为用户提供更多功能。

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

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

相关文章

Spring Cloud Kubernetes配置使用详情

目录 一、 为什么你需要 Spring Cloud Kubernetes&#xff1f; 二、 Starter 三、 用于 Kubernetes 的 DiscoveryClient 四、Kubernetes 原生服务发现&#xff08;service discovery&#xff09; 五、Kubernetes PropertySource 的实现 1、使用 ConfigMap PropertySource …

Unreal Engine 5.1 AI行为树基础入门

ai行为树理解起来其实是npc根据自身一些情况进行一些逻辑执行&#xff0c;而这些逻辑是我们使用ai行为树去实现的。 ai行为树需要一个寻路网格体边界体积&#xff0c;在ue引擎中&#xff0c;体积Actor分为多种&#xff0c;寻路网格体边界体积只是其中的一种。 关于其它的体积&a…

leetcode:448. 找到所有数组中消失的数字(python3解法)

难度&#xff1a;简单 给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字&#xff0c;并以数组的形式返回结果。 示例 1&#xff1a; 输入&#xff1a;nums [4,3,2,7,8,2,3,1] 输出&…

初识网络之https的加密与解密

目录 一、https协议的概念 二、加密的概念 三、为什么需要加密 四、常见的加密方式 1. 对称加密 2. 非对称加密 五、数据摘要&#xff08;数据指纹&#xff09; 六、数据签名 七、加密方案 1. 方案一&#xff1a;只使用对称加密 2. 方案二&#xff1a;只使用非对称加…

院士大咖齐聚蓉城,论道“疑难眼眶病学术论坛”

“全国疑难眼眶病诊疗带教基地”成立&#xff0c;力促“病有所医 ” 大咖云集&#xff0c;这是一场眼科界领军人汇聚一堂&#xff0c;聚焦疑难眼眶病突破性学术成果及前沿技术的高规格论坛; 规模空前&#xff0c;这是一场围绕眼眶病诊疗技术议题深入探讨交流&#xff0c;为我国…

行为型设计模式08-职责链模式

&#x1f9d1;‍&#x1f4bb;作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 职责链模式 1、职责链模式介绍 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;…

seatunnel入门案例,集群模式

目录 安装部署 解压 环境变量 安装plugin 添加资源jar包 SEATUNNEL 配置文件 env&#xff1a;环境设置 source&#xff1a;数据源设置 sink&#xff1a;数据去向设置 transform: 数据转换设置 运行方式 seatunnel 引擎(zeta) 本地模式 集群模式 安装部署 解压 tar…

Centos7在Nginx中配置SSL证书

我的环境 阿里云轻量应用服务器&#xff08;Linux, Centos7.9 x64&#xff09; 一、申请免费SSL证书 一年可以创建20个 下载证书&#xff08;我是Nginx服务器&#xff09; 下载到本地后&#xff0c;打开有两个文件&#xff1a; 二、将SSL证书文件上传至服务器 通过WinScp拖…

图像sensor的特性和驱动解析

1、更换OV9712并且做配置更改和测试 1.1、更改配置脚本 修改Hi3518E_SDK_V1.0.3.0\package\mpp\sample\Makefile.param 1.2、测试运行 运行官方SDK sample的测试版本&#xff08;打包到本地&#xff09; 运行ORTP传输的测试版本&#xff08;RTP实时预览&#xff09; 1.3、更…

如何高效合理规划每天的工作?

如何高效合理规划每天的工作&#xff1f; 〇、基本原则 梳理工作&#xff0c;明确目标。&#xff08;SMART法则&#xff09;轻重缓急&#xff0c;排优先级。&#xff08;四象限管理法则&#xff09;要事第一&#xff0c;尽管去做。&#xff08;GTD 理论&#xff09;限时deadl…

新买的电脑怎么用U盘重装系统?新买的电脑用U盘重装系统教程

新买的电脑怎么用U盘重装系统&#xff1f;用户新买了电脑&#xff0c;想知道怎么用U盘来重装新买的电脑&#xff0c;用U盘来重装电脑其实非常简单&#xff0c;用户需要准备一个U盘&#xff0c;然后完成U盘启动盘的安装&#xff0c;接着按照以下分享的新买的电脑用U盘重装系统教…

[Hadoop安装配置 ]

目录 前言: 执行步骤: 1 创建好目录文件,上传Hadoop版本压缩包,一般都是tar.gz 结尾包 1.1这里压缩包可以直接拖拽到指定虚拟机目录下, 例如xshell连接指定虚拟机, 然后可以拖拽,如果拖拽不了,那就需要设置一下配置, 或者 使用 xftp工具 连接xshell 然后上传文件 2 解压…

pandas---数据合并(concat、append、merge)

1. concat函数 pd.concat([data1, data2], axis1) 按照行或列进行合并&#xff0c;axis0为列索引&#xff0c;axis1为行索引。 df1 make_df([1, 2], [A, B]) df2 make_df([3, 4], [A, B]) display(df1, df2) # 默认上下合并&#xff0c;垂直合并 pd.concat([df1, df2]) …

【机器学习】——学习的基本分类:算法模拟的根本出发点!

目录 引入 一、分类 1、基于学习策略的分类 2、基于所获取知识的表示形式分类 3、按应用领域分类 4、综合分类 二、研究领域 引入 机器学习是继专家系统后人工智能的又一重要研究领域&#xff01;机器学习是研究计算机怎样模拟或实现人类的学习行为&#xff0c;以获取新…

前端面试经验技巧分享

&#x1f469; 个人主页&#xff1a;不爱吃糖的程序媛 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、CSDN内容合伙人&#xff0c;专注于前端各领域技术&#xff0c;成长的路上共同学习共同进步&#xff0c;一起加油呀&#xff01; ✨系列专栏&#xff1a;前端…

NVIDIA的StyleGAN、StyleGAN2、StyleGAN3系列论文解读,梳理基于风格的生成器架构

通俗来讲就是&#xff0c;张三造假币(Generator生成器)&#xff0c;然后用验钞机去验证真假(Discriminator辨别器)&#xff0c;如果是假的就继续提高造假技术&#xff0c;直到验钞机检验不出来为止&#xff0c;也就是说一个造假一个验假(验钞机也需升级)&#xff0c;两者互相学…

Redis集群部署

Redis集群部署 1.单机安装Redis2.Redis主从集群2.1.集群结构2.2.准备实例和配置2.3.启动2.4.开启主从关系2.5.测试 3.搭建哨兵集群3.1.集群结构3.2.准备实例和配置3.3.启动3.4.测试 4.搭建分片集群4.1.集群结构4.2.准备实例和配置4.3.启动4.4.创建集群4.5.测试4.5.测试 本章是基…

安全 --- 内网基础知识(01)

内网基础知识 &#xff08;1&#xff09;概念 内网也称局域网&#xff08;Local Area Network&#xff0c;LAN&#xff09;是指在某一工作区域内由多台计算机互联形成的计算机组&#xff0c;一般是方圆几千米内。局域网可实现文件管理、应用软件共享、打印机共享、工作内的历…

新一代绿色智慧数据中心电气规划设计与常识(一)

绿色智慧数据中心 随着大数据、云计算、人工智能、区块链、ChatGPT等技术加速创新&#xff0c;数字文化产业发展动力强劲&#xff0c;不断解锁新兴业态。近年来&#xff0c;各级政府重要会议中也多次强调“新基建”今后一段时期驱动新一轮产业革命的战略性新兴产业&#xff0c…

软件测试06:软件测试原则和黑盒测试用例设计方法

软件测试原则 所有测试的标准都是建立在用户需求之上软件测试必须基于"质量第一"的思想去开展各项工作&#xff0c;当时间和质量冲突时&#xff0c;时间要服从质量事先定义好产品的质量标准&#xff0c;只有有了质量标准&#xff0c;才嫩如果根据测试的结果&#xf…