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

当前,我们正见证着一场技术革命,而 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 的组件,以便为用户提供更多功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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.测试 本章是基…

java SSM 摄影作品网站myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 摄影作品网站系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

计算机网络-网络体系结构

目录 计算机网络的基本概念计算机网络的定义组成与功能计算机网络的分类按照网络的作用范围进行分类按照网络的使用者进行分类 计算机网络主要性能指标 计算机网络体系结构计算机网络协议、接口、服务等概念ISO/OSI 参考模型和 TCP/IP 模型OSI七层模型TCP/IP 模型封装与分用 计…

攻防渗透第四章(谷歌语法)

一、常用谷歌黑客语法 制定网站的URL site: 包含特定字符的URL inurl: 网页标题中包含特定字符 intitle: 正文中指定字符 intext: 指定类型文件 filetype 开发语言判断 site:163.com filetype:php site:163.com filetype:jsp site:163.com filetype:asp site:163.com filetype…

【裸机开发】内核时钟 PLL1 配置实验(一)—— 寄存器分析篇

本章主要会回答以下问题 &#xff1f; imx6u 的时钟源来自于哪 &#xff1f;为什么一个起始时钟源&#xff0c;最终分成了多路&#xff1f;不同的时钟源是如何与外设对应起来的&#xff1f;&#xff08;时钟树&#xff09;要配置内核时钟频率 有哪些步骤 &#xff1f;涉及到哪…

基于Java购物商城系统设计与实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

今天面了个35k字节跳动出来,真是砂纸擦屁股,给我露了一手...

​2023年春招已经结束&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c;他山之石…

深入篇【C++】string类的常用接口介绍:标准库中的string类 【万字总结】

深入篇【C】string类的常用接口介绍&#xff1a;标准库中的string类 Ⅰ.string类介绍Ⅱ.string类的常用接口①.string类对象的常用构造1.string()2.string(const char*ch)3.string(const string& str)4.string(size_t n,char c)5.string(const string& str,size_t pos,…

第四章 Linux网络编程 4.1 网络结构模式 4.2MAC地址、IP地址、端口

第四章 Linux网络编程 4.1 网络结构模式 C/S结构 简介 服务器 - 客户机&#xff0c;即 Client - Server&#xff08;C/S&#xff09;结构。C/S 结构通常采取两层结构。服务器负责数据的管理&#xff0c;客户机负责完成与用户的交互任务。客户机是因特网上访问别人信息的机器…

Seata客户端的启动过程 学习记录

Seata客户端的启动过程 1.自动装配4个配置类 将在SpringBoot启动时往容器中添加4个类 1. 自动配置类 SeataAutoConfiguration SeataAutoConfiguration将会往容器中添加两个bean failureHandler 事务处理失败执行器globalTransactionScanner failureHandler failureHandle…

DBA 抓包神器 tshark 测评

想窥探神秘的网络世界的奥秘&#xff0c;tshark 助你一臂之力&#xff01; 作者&#xff1a;赵黎明 爱可生 MySQL DBA 团队成员&#xff0c;熟悉 Oracle、MySQL 等数据库&#xff0c;擅长数据库性能问题诊断、事务与锁问题的分析等&#xff0c;负责处理客户 MySQL 及我司自研 D…

计算机组成原理(六)指令系统

一、指令的基本格式 1.1机器指令的相关概念 指令集(Instruction Set) 某机器所有机器指令的集合 *定长指令集 指令集中的所有指令长度均相同!取指令控制简单*不定长指令集 指令集中的所有指令长度有长、有短 操作码 (1)长度固定 用于指令字长较长的情况RISC 如IBM370操作码8位…

2023 年前端 Web 发展趋势

虽然就个人观点&#xff0c;我觉得 Web 开发在最近几年都没什么进展&#xff08;2016 年至 2021 年&#xff09;&#xff0c;但在刚刚过去的 2022 年中确实又出现了一些新的技术。在本文中&#xff0c;我想跟大家聊聊自己看到的最新 Web 开发的发展趋势。相信这波浪潮会继续激发…

RHCE shell 作业一

1. 设置邮箱 [rootserver ~]# yum install s-nail -y [rootserver ~]# vim /etc/s-nail.rc 编写脚本 [rootserver ~]# vim homework1.sh 设置定时任务 [rootserver ~]# vim /etc/crontab 2. [rootserver ~]# vim homework2.sh 测试&#xff1a; 3. [rootserve…

一文让你了解appium自动化的工作原理

目录 前言&#xff1a; 一、Appium加载的过程图解 二、初步认识appium工作过程 三、bootstrap介绍 四、所使用的技术 五、Capabilities 六、自我理解的工作原理 前言&#xff1a; Appium是一个流行的开源自动化测试框架&#xff0c;支持移动应用程序的自动化测试。 一…

进程管道:父进程和子进程

在接下来的对pipe调用的研究中&#xff0c;我们将学习如何在子进程中运行一个与其父进程完全不同的另外一个程序&#xff0c;而不是仅仅运行一个相同程序。我们用exec调用来完成这一工作。这里的一个难点是&#xff0c;通过exec调用的进程需要知道应该访问哪个文件描述符。在前…

MMPretrain

title: mmpretrain实战 date: 2023-06-07 16:04:01 tags: [image classification,mmlab] mmpretrain实战 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ccTl9bOl-1686129437336)(null)] 主要讲解了安装,还有使用教程.安装教程直接参考官网.下面讲…

pikachu靶场-../../(目录遍历)

目录遍历, 也叫路径遍历, 由于web服务器或者web应用程序对用户输入的文件名称的安全性验证不足而导致的一种安全漏洞&#xff0c;使得攻击者通过利用一些特殊字符就可以绕过服务器的安全限制&#xff0c;访问任意的文件 (可以是web根目录以外的文件&#xff09;&#xff0c;甚至…

pytorch深度学习框架—torch.nn模块(二)

pytorch深度学习框架—torch.nn模块&#xff08;二&#xff09; 激活函数 pytorch中提供了十几种激活函数&#xff0c;常见的激活函数通常为S形激活函数&#xff08;Sigmoid&#xff09;双曲正切激活函数(Tanh) 和线性修正单元&#xff08;ReLu&#xff09;激活函数等 层对应的…

Linux笔记

版本用的是CentOS7最min版 安装JDK&#xff1a;安装上传工具包&#xff1a;自动安装 yum install lrzsz -y 上传本地文件&#xff1a; rz -be 解压jdk&#xff1a; tar -zxvf jdk-8u371-linux-x64.tar.gz -z 用gzip来压缩/解压缩文件&#xff0c;加上该选项后可以将档案…