【QuecOpen 实战-006】FreeRTOS 多任务编程实战

news2026/5/5 7:22:31
前言在前面的系列文章中我们已经介绍了移远 QuecOpen 开发环境搭建、基础 API 使用以及 GPIO、UART 等外设驱动开发。今天我们将深入 QuecOpen 开发的核心 ——FreeRTOS 多任务编程。移远 QuecOpen 平台基于 FreeRTOS 实时操作系统构建所有的应用程序都运行在 FreeRTOS 之上。掌握多任务编程是开发稳定、高效的 QuecOpen 应用的必备技能。本文将从基础概念入手结合大量实战代码详细讲解 QuecOpen 中 FreeRTOS 多任务的创建、管理、通信与同步以及实际开发中常见的坑点与解决方案。本文基于移远 EC200U 模块QuecOpen SDK V3.3.0 版本编写其他移远模块如 EC600U、EC800M、BG95 等的 FreeRTOS 接口基本一致可直接参考。一、QuecOpen 与 FreeRTOS 的关系1.1 QuecOpen 平台架构移远 QuecOpen 是基于高通 / 展锐芯片平台的开放式开发环境它允许开发者直接在模块上编写和运行 C 语言应用程序无需额外的 MCU。其架构如下┌─────────────────────────────────┐ │ 用户应用程序 (C语言) │ ├─────────────────────────────────┤ │ QuecOpen API 接口层 │ ├─────────────────────────────────┤ │ FreeRTOS 实时操作系统 │ ├─────────────────────────────────┤ │ 芯片硬件抽象层 (HAL) │ ├─────────────────────────────────┤ │ 硬件平台 │ └─────────────────────────────────┘1.2 FreeRTOS 在 QuecOpen 中的特点抢占式调度高优先级任务可以抢占低优先级任务的 CPU 使用权任务优先级QuecOpen 中 FreeRTOS 的优先级范围是0~310 是最低优先级31 是最高优先级系统任务QuecOpen 已经创建了多个系统任务如 AT 命令处理、网络协议栈、硬件驱动等用户任务优先级建议设置在 5~20 之间避免影响系统任务运行内存管理QuecOpen 使用 FreeRTOS 的 heap_4 内存管理算法支持动态内存分配与释放系统节拍默认系统节拍频率为1000Hz即每个 tick 为 1ms二、FreeRTOS 多任务基础2.1 任务的基本概念任务是 FreeRTOS 中最小的执行单元每个任务都有自己的栈空间、程序计数器和上下文。任务的状态主要有以下几种运行态任务正在占用 CPU 执行就绪态任务已经准备好可以被调度器调度运行阻塞态任务正在等待某个事件如延时、信号量、队列消息等挂起态任务被挂起不会被调度器调度直到被恢复2.2 任务函数的格式QuecOpen 中 FreeRTOS 任务函数的标准格式如下/** * brief FreeRTOS任务函数模板 * param pvParameters 任务创建时传入的参数 */ void vTaskFunction(void *pvParameters) { // 任务初始化代码只执行一次 for(;;) // 任务主体循环必须是死循环 { // 任务执行的代码 // 必须有阻塞调用否则会占用100%CPU vTaskDelay(pdMS_TO_TICKS(100)); } // 任务函数不能返回如果需要结束任务调用vTaskDelete(NULL) vTaskDelete(NULL); }重要注意事项任务函数必须是无返回值的函数任务函数必须包含死循环循环内部必须有阻塞调用如 vTaskDelay、xQueueReceive 等否则会导致 CPU 使用率 100%系统卡死任务函数的参数是void *类型可以传递任意类型的数据三、任务的创建与管理实战3.1 动态创建任务QuecOpen 中推荐使用xTaskCreate函数动态创建任务函数原型如下BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 任务函数指针 const char * const pcName, // 任务名称用于调试 const uint16_t usStackDepth, // 任务栈大小单位字即4字节 void * const pvParameters, // 传递给任务函数的参数 UBaseType_t uxPriority, // 任务优先级 TaskHandle_t * const pxCreatedTask // 任务句柄用于后续管理任务 );返回值pdPASS任务创建成功errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY内存不足任务创建失败实战代码创建两个简单任务#include ql_application.h #include ql_freertos.h #include ql_uart.h // 任务句柄 static TaskHandle_t s_task1_handle NULL; static TaskHandle_t s_task2_handle NULL; /** * brief 任务1每秒打印一次信息 */ void vTask1(void *pvParameters) { uint32_t count 0; QL_LOG_INFO(Task1 started, parameter: %d, (int)pvParameters); for(;;) { QL_LOG_INFO(Task1 running, count: %d, count); vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1秒 } } /** * brief 任务2每500ms打印一次信息 */ void vTask2(void *pvParameters) { uint32_t count 0; QL_LOG_INFO(Task2 started, parameter: %s, (char *)pvParameters); for(;;) { QL_LOG_INFO(Task2 running, count: %d, count); vTaskDelay(pdMS_TO_TICKS(500)); // 延时500ms } } /** * brief 应用入口函数 */ void ql_main(void *param) { BaseType_t ret; QL_LOG_INFO(QuecOpen FreeRTOS Demo started); // 创建任务1优先级5栈大小2048字8KB传递参数123 ret xTaskCreate(vTask1, Task1, 2048, (void *)123, 5, s_task1_handle); if(ret ! pdPASS) { QL_LOG_ERROR(Create Task1 failed, ret: %d, ret); } // 创建任务2优先级6栈大小2048字传递字符串参数 ret xTaskCreate(vTask2, Task2, 2048, Hello QuecOpen, 6, s_task2_handle); if(ret ! pdPASS) { QL_LOG_ERROR(Create Task2 failed, ret: %d, ret); } // 主任务可以删除自己 vTaskDelete(NULL); }运行结果[INFO] QuecOpen FreeRTOS Demo started [INFO] Task1 started, parameter: 123 [INFO] Task2 started, parameter: Hello QuecOpen [INFO] Task2 running, count: 0 [INFO] Task1 running, count: 0 [INFO] Task2 running, count: 1 [INFO] Task2 running, count: 2 [INFO] Task1 running, count: 1 [INFO] Task2 running, count: 3 [INFO] Task2 running, count: 4 [INFO] Task1 running, count: 2 ...可以看到优先级更高的 Task2 会更频繁地执行。3.2 任务栈大小的设置任务栈大小是 QuecOpen 开发中非常重要的参数设置过小会导致栈溢出系统崩溃设置过大会浪费内存。QuecOpen 中任务栈大小的建议值简单任务只做打印、延时等1024~2048 字4~8KB中等复杂度任务包含串口通信、数据处理2048~4096 字8~16KB复杂任务包含网络通信、文件操作4096~8192 字16~32KB栈溢出检测QuecOpen 提供了栈溢出检测功能可以通过以下函数获取任务的剩余栈空间UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask);实战代码检测任务栈使用情况void vTaskStackCheck(void *pvParameters) { UBaseType_t stack_remaining; for(;;) { // 获取当前任务的剩余栈空间 stack_remaining uxTaskGetStackHighWaterMark(NULL); QL_LOG_INFO(Task stack remaining: %d words, stack_remaining); vTaskDelay(pdMS_TO_TICKS(5000)); } }3.3 任务的挂起与恢复// 挂起任务 void vTaskSuspend(TaskHandle_t xTaskToSuspend); // 恢复任务 void vTaskResume(TaskHandle_t xTaskToResume); // 在中断服务函数中恢复任务 BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);实战代码任务挂起与恢复演示void vTaskControl(void *pvParameters) { uint32_t count 0; for(;;) { count; if(count 5) { QL_LOG_INFO(Suspend Task1); vTaskSuspend(s_task1_handle); // 挂起任务1 } else if(count 10) { QL_LOG_INFO(Resume Task1); vTaskResume(s_task1_handle); // 恢复任务1 count 0; } vTaskDelay(pdMS_TO_TICKS(1000)); } }3.4 删除任务// 删除任务 void vTaskDelete(TaskHandle_t xTaskToDelete);注意如果参数为NULL则删除当前任务删除任务时系统会自动回收任务的栈空间和 TCB任务控制块内存不要在中断服务函数中调用vTaskDelete四、任务间通信与同步实战在实际开发中多个任务之间经常需要交换数据和同步执行。FreeRTOS 提供了多种任务间通信与同步机制包括队列、信号量、互斥量和事件组。4.1 队列Queue队列是 FreeRTOS 中最常用的任务间通信机制它可以在任务之间传递任意类型的数据。队列基本操作函数// 创建队列 QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize); // 向队列发送数据 BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait); // 从队列接收数据 BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait); // 删除队列 void vQueueDelete(QueueHandle_t xQueue);实战代码使用队列传递数据// 队列句柄 static QueueHandle_t s_data_queue NULL; // 数据结构体 typedef struct { uint32_t id; char message[32]; } DataPacket_t; /** * brief 发送任务向队列发送数据 */ void vSenderTask(void *pvParameters) { DataPacket_t packet; BaseType_t ret; uint32_t count 0; for(;;) { // 构造数据包 packet.id count; snprintf(packet.message, sizeof(packet.message), Hello from sender, count: %d, count); // 向队列发送数据等待最多100ms ret xQueueSend(s_data_queue, packet, pdMS_TO_TICKS(100)); if(ret pdPASS) { QL_LOG_INFO(Sent packet: id%d, message%s, packet.id, packet.message); } else { QL_LOG_WARN(Send packet failed, queue is full); } vTaskDelay(pdMS_TO_TICKS(1000)); } } /** * brief 接收任务从队列接收数据 */ void vReceiverTask(void *pvParameters) { DataPacket_t packet; BaseType_t ret; for(;;) { // 从队列接收数据无限等待 ret xQueueReceive(s_data_queue, packet, portMAX_DELAY); if(ret pdPASS) { QL_LOG_INFO(Received packet: id%d, message%s, packet.id, packet.message); } } } // 在ql_main中创建队列和任务 void ql_main(void *param) { // 创建队列最多存储5个数据包每个数据包大小为DataPacket_t s_data_queue xQueueCreate(5, sizeof(DataPacket_t)); if(s_data_queue NULL) { QL_LOG_ERROR(Create data queue failed); return; } // 创建发送任务和接收任务 xTaskCreate(vSenderTask, Sender, 2048, NULL, 5, NULL); xTaskCreate(vReceiverTask, Receiver, 2048, NULL, 6, NULL); vTaskDelete(NULL); }4.2 二进制信号量Binary Semaphore二进制信号量主要用于任务间的同步和中断与任务间的同步。二进制信号量基本操作函数// 创建二进制信号量 SemaphoreHandle_t xSemaphoreCreateBinary(void); // 获取信号量 BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait); // 释放信号量 BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore); // 在中断服务函数中释放信号量 BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken); // 删除信号量 void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);实战代码使用二进制信号量同步任务// 二进制信号量句柄 static SemaphoreHandle_t s_sync_sem NULL; /** * brief 等待任务等待信号量 */ void vWaitingTask(void *pvParameters) { BaseType_t ret; for(;;) { QL_LOG_INFO(Waiting for semaphore...); // 获取信号量无限等待 ret xSemaphoreTake(s_sync_sem, portMAX_DELAY); if(ret pdPASS) { QL_LOG_INFO(Got semaphore, doing work...); // 模拟处理工作 vTaskDelay(pdMS_TO_TICKS(500)); QL_LOG_INFO(Work done); } } } /** * brief 触发任务释放信号量 */ void vTriggerTask(void *pvParameters) { uint32_t count 0; for(;;) { vTaskDelay(pdMS_TO_TICKS(3000)); QL_LOG_INFO(Triggering semaphore, count: %d, count); xSemaphoreGive(s_sync_sem); // 释放信号量 } } // 在ql_main中创建信号量和任务 void ql_main(void *param) { // 创建二进制信号量初始状态为未获取 s_sync_sem xSemaphoreCreateBinary(); if(s_sync_sem NULL) { QL_LOG_ERROR(Create semaphore failed); return; } // 创建等待任务和触发任务 xTaskCreate(vWaitingTask, Waiting, 2048, NULL, 5, NULL); xTaskCreate(vTriggerTask, Trigger, 2048, NULL, 6, NULL); vTaskDelete(NULL); }4.3 互斥量Mutex互斥量用于保护共享资源防止多个任务同时访问同一个资源导致数据不一致。互斥量基本操作函数// 创建互斥量 SemaphoreHandle_t xSemaphoreCreateMutex(void); // 获取互斥量 BaseType_t xSemaphoreTake(SemaphoreHandle_t xMutex, TickType_t xTicksToWait); // 释放互斥量 BaseType_t xSemaphoreGive(SemaphoreHandle_t xMutex); // 删除互斥量 void vSemaphoreDelete(SemaphoreHandle_t xMutex);实战代码使用互斥量保护共享资源// 互斥量句柄 static SemaphoreHandle_t s_uart_mutex NULL; // 共享资源UART打印函数 void uart_print_safe(const char *str) { // 获取互斥量 if(xSemaphoreTake(s_uart_mutex, pdMS_TO_TICKS(100)) pdPASS) { // 访问共享资源 QL_LOG_INFO(%s, str); // 释放互斥量 xSemaphoreGive(s_uart_mutex); } else { QL_LOG_WARN(Get uart mutex timeout); } } /** * brief 任务A打印信息 */ void vTaskA(void *pvParameters) { char buf[64]; uint32_t count 0; for(;;) { snprintf(buf, sizeof(buf), TaskA: count%d, count); uart_print_safe(buf); vTaskDelay(pdMS_TO_TICKS(500)); } } /** * brief 任务B打印信息 */ void vTaskB(void *pvParameters) { char buf[64]; uint32_t count 0; for(;;) { snprintf(buf, sizeof(buf), TaskB: count%d, count); uart_print_safe(buf); vTaskDelay(pdMS_TO_TICKS(700)); } } // 在ql_main中创建互斥量和任务 void ql_main(void *param) { // 创建互斥量 s_uart_mutex xSemaphoreCreateMutex(); if(s_uart_mutex NULL) { QL_LOG_ERROR(Create mutex failed); return; } // 创建任务A和任务B xTaskCreate(vTaskA, TaskA, 2048, NULL, 5, NULL); xTaskCreate(vTaskB, TaskB, 2048, NULL, 5, NULL); vTaskDelete(NULL); }五、QuecOpen FreeRTOS 开发常见坑点与解决方案5.1 任务栈溢出现象系统突然重启或者出现奇怪的崩溃现象原因任务栈大小设置过小解决方案使用uxTaskGetStackHighWaterMark函数检测任务栈使用情况适当增大任务栈大小避免在任务函数中定义过大的局部变量建议使用动态内存分配5.2 CPU 使用率 100%现象系统运行缓慢响应迟钝原因某个任务的循环中没有阻塞调用一直占用 CPU解决方案检查所有任务函数确保循环内部有阻塞调用如 vTaskDelay、xQueueReceive 等使用ulTaskGetRunTimeStats函数查看各个任务的 CPU 使用率5.3 优先级反转现象高优先级任务被低优先级任务阻塞导致系统实时性下降原因低优先级任务持有互斥量高优先级任务等待互斥量而中间优先级的任务抢占了低优先级任务的 CPU解决方案使用互斥量而不是二进制信号量来保护共享资源互斥量具有优先级继承机制合理设置任务优先级5.4 内存泄漏现象系统运行一段时间后内存不足无法创建新的任务或队列原因动态分配的内存没有被释放解决方案确保每个malloc都有对应的free使用xPortGetFreeHeapSize函数查看剩余堆内存避免频繁地创建和删除任务、队列等对象5.5 在中断服务函数中调用不允许的函数现象系统崩溃原因在中断服务函数中调用了非中断安全的 FreeRTOS 函数解决方案中断服务函数中只能调用以FromISR结尾的 FreeRTOS 函数复杂的处理逻辑应该交给任务来完成中断服务函数只负责触发任务六、实战项目多任务数据采集与处理系统为了让大家更好地理解 FreeRTOS 多任务编程我们来实现一个简单的多任务数据采集与处理系统。该系统包含以下三个任务数据采集任务模拟采集传感器数据数据处理任务对采集到的数据进行处理数据显示任务显示处理后的数据任务之间通过队列进行通信。完整代码#include ql_application.h #include ql_freertos.h #include ql_uart.h #include stdlib.h #include string.h // 队列句柄 static QueueHandle_t s_raw_data_queue NULL; static QueueHandle_t s_processed_data_queue NULL; // 原始数据结构体 typedef struct { uint32_t timestamp; float temperature; float humidity; } RawData_t; // 处理后的数据结构体 typedef struct { uint32_t timestamp; float avg_temperature; float avg_humidity; uint32_t sample_count; } ProcessedData_t; /** * brief 数据采集任务模拟采集传感器数据 */ void vDataAcquisitionTask(void *pvParameters) { RawData_t raw_data; BaseType_t ret; QL_LOG_INFO(Data Acquisition Task started); for(;;) { // 模拟采集数据 raw_data.timestamp xTaskGetTickCount(); raw_data.temperature 25.0f (rand() % 100) / 10.0f; // 25.0~35.0℃ raw_data.humidity 50.0f (rand() % 500) / 10.0f; // 50.0~100.0%RH // 将原始数据发送到队列 ret xQueueSend(s_raw_data_queue, raw_data, pdMS_TO_TICKS(100)); if(ret ! pdPASS) { QL_LOG_WARN(Raw data queue is full, dropping data); } // 每100ms采集一次数据 vTaskDelay(pdMS_TO_TICKS(100)); } } /** * brief 数据处理任务对原始数据进行平均处理 */ void vDataProcessingTask(void *pvParameters) { RawData_t raw_data; ProcessedData_t processed_data; BaseType_t ret; float temp_sum 0.0f; float humi_sum 0.0f; uint32_t count 0; QL_LOG_INFO(Data Processing Task started); for(;;) { // 从队列接收原始数据 ret xQueueReceive(s_raw_data_queue, raw_data, portMAX_DELAY); if(ret ! pdPASS) { continue; } // 累加数据 temp_sum raw_data.temperature; humi_sum raw_data.humidity; count; // 每10个数据计算一次平均值 if(count 10) { processed_data.timestamp raw_data.timestamp; processed_data.avg_temperature temp_sum / count; processed_data.avg_humidity humi_sum / count; processed_data.sample_count count; // 将处理后的数据发送到队列 ret xQueueSend(s_processed_data_queue, processed_data, pdMS_TO_TICKS(100)); if(ret ! pdPASS) { QL_LOG_WARN(Processed data queue is full, dropping data); } // 重置累加器 temp_sum 0.0f; humi_sum 0.0f; count 0; } } } /** * brief 数据显示任务显示处理后的数据 */ void vDataDisplayTask(void *pvParameters) { ProcessedData_t processed_data; BaseType_t ret; QL_LOG_INFO(Data Display Task started); for(;;) { // 从队列接收处理后的数据 ret xQueueReceive(s_processed_data_queue, processed_data, portMAX_DELAY); if(ret ! pdPASS) { continue; } // 显示数据 QL_LOG_INFO(); QL_LOG_INFO(Timestamp: %lu ms, processed_data.timestamp); QL_LOG_INFO(Average Temperature: %.2f ℃, processed_data.avg_temperature); QL_LOG_INFO(Average Humidity: %.2f %%RH, processed_data.avg_humidity); QL_LOG_INFO(Sample Count: %lu, processed_data.sample_count); QL_LOG_INFO(); } } /** * brief 应用入口函数 */ void ql_main(void *param) { QL_LOG_INFO(Multi-task Data Acquisition System started); // 创建原始数据队列最多存储20个数据 s_raw_data_queue xQueueCreate(20, sizeof(RawData_t)); if(s_raw_data_queue NULL) { QL_LOG_ERROR(Create raw data queue failed); return; } // 创建处理后的数据队列最多存储5个数据 s_processed_data_queue xQueueCreate(5, sizeof(ProcessedData_t)); if(s_processed_data_queue NULL) { QL_LOG_ERROR(Create processed data queue failed); vQueueDelete(s_raw_data_queue); return; } // 创建任务 xTaskCreate(vDataAcquisitionTask, DataAcq, 2048, NULL, 7, NULL); xTaskCreate(vDataProcessingTask, DataProc, 2048, NULL, 6, NULL); xTaskCreate(vDataDisplayTask, DataDisp, 2048, NULL, 5, NULL); // 主任务删除自己 vTaskDelete(NULL); }运行结果[INFO] Multi-task Data Acquisition System started [INFO] Data Acquisition Task started [INFO] Data Processing Task started [INFO] Data Display Task started [INFO] [INFO] Timestamp: 1000 ms [INFO] Average Temperature: 29.85 ℃ [INFO] Average Humidity: 74.20 %RH [INFO] Sample Count: 10 [INFO] [INFO] [INFO] Timestamp: 2000 ms [INFO] Average Temperature: 28.62 ℃ [INFO] Average Humidity: 71.35 %RH [INFO] Sample Count: 10 [INFO] ...七、总结本文详细讲解了移远 QuecOpen 平台上 FreeRTOS 多任务编程的基础知识和实战技巧包括任务的创建与管理、任务间通信与同步以及实际开发中常见的坑点与解决方案。最后通过一个完整的多任务数据采集与处理系统展示了 FreeRTOS 在实际项目中的应用。掌握 FreeRTOS 多任务编程是开发高质量 QuecOpen 应用的关键。在实际开发中我们应该合理设计任务划分每个任务只负责一个功能正确设置任务优先级和栈大小使用合适的任务间通信与同步机制注意避免常见的坑点如栈溢出、CPU 使用率 100%、优先级反转等后续预告下一篇文章【QuecOpen 实战-007】移远 4G QuecOpen C SDK 工业级开发实战彻底告别 AT 指令TCP/UDP 全栈实现原创不易如果本文对你有帮助欢迎点赞、收藏、关注三连有任何问题都可以在评论区留言我会及时回复。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…