当前位置: 首页 > article >正文

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

前言在前面的系列文章中我们已经介绍了移远 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 全栈实现原创不易如果本文对你有帮助欢迎点赞、收藏、关注三连有任何问题都可以在评论区留言我会及时回复。

相关文章:

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

前言 在前面的系列文章中,我们已经介绍了移远 QuecOpen 开发环境搭建、基础 API 使用以及 GPIO、UART 等外设驱动开发。今天我们将深入 QuecOpen 开发的核心 ——FreeRTOS 多任务编程。 移远 QuecOpen 平台基于 FreeRTOS 实时操作系统构建,所有的应用程…...

.NET 9 + Docker一键上线:从零构建高可用API容器的5步极简工作流

更多请点击: https://intelliparadigm.com 第一章:.NET 9 Docker一键上线:从零构建高可用API容器的5步极简工作流 .NET 9 带来了原生AOT编译、性能增强的HTTP/3支持以及更轻量的运行时镜像,结合Docker可实现真正意义上的“开箱即…...

【Hung-yi Lee】《Introduction to Generative Artificial Intelligence》(12)

Introduction to Generative AI 2024 Spring 文章目录第17講:有關影像的生成式AI (上) — AI 如何產生圖片和影片 (Sora 背後可能用的原理)(24.05.31)video or image to contentcondition to video/imagetalking headLAION datasetsTextual I…...

扣子(coze+image2)实战:香,Coze 一键生成英语场景卡片,家长、老师必备神器

大家好,我是专注于AI的咕咕姐。你还在对着单词书死记硬背?记了忘、忘了记,一到真实场景还是张口就懵?最近小红书上的英语场景卡片记忆,流量很好且有趣。今天,我结合目前生图效果比较好的image2 Coze 一键生…...

用快马ai快速构建mos管工作原理交互演示原型,直观理解电压控制奥秘

今天想和大家分享一个用InsCode(快马)平台快速搭建MOS管工作原理演示工具的经历。作为电子爱好者,我经常需要向学弟学妹解释这个基础但重要的元器件,但单纯用PPT讲解效果总是不理想。直到发现这个平台,终于找到了可视化演示的捷径。 项目构思…...

快速搭建集成hermes引擎的react native项目原型

最近在尝试为React Native项目集成Hermes引擎时,发现手动配置的过程相当繁琐。经过一番摸索,我总结出一套快速搭建原型的方法,特别适合需要快速验证想法的场景。这里分享我的实践过程,希望能帮到同样想尝试Hermes的开发者。 为什么…...

新手福音:快马AI辅助生成零基础龙虾安装教程,带你轻松上手

最近在帮朋友搭建一个数据分析项目时,需要用到一个叫"龙虾"的数据库(LobsterDB)。作为一个刚接触数据库的新手,我发现手动安装配置的过程真是让人头大。好在发现了InsCode(快马)平台,用它的AI辅助功能&#…...

Rust + PostgreSQL 极简技术栈应用开发

文章目录Rust PostgreSQL 极简技术栈应用开发核心思路环境准备初始化项目与依赖PostgreSQL 扩展安装初始化代码模块一:替代缓存新建业务表与物化视图缓存刷新Axum 接口调用缓存模块二:替代消息队列队列表设计生产者:发送消息消费者&#xff…...

NI数据采集避坑指南:搞懂NI MAX里仿真和真实设备的这5个关键区别

NI数据采集避坑指南:搞懂NI MAX里仿真和真实设备的5个关键区别 在工业自动化测试和实验室数据采集领域,NI(National Instruments)的数据采集设备因其稳定性和灵活性而广受工程师青睐。然而,许多开发者在从仿真环境切换…...

AI智能体记忆守护进程:构建持久化语义记忆系统的架构与实践

1. 项目概述:一个为AI智能体设计的记忆守护进程最近在折腾AI智能体(Agent)项目时,我遇到了一个几乎所有开发者都会头疼的经典问题:记忆管理。当你的智能体需要处理长时间、多轮次的复杂对话或任务时,如何让…...

新手也能上手的ASO关键词优化完整实操(下篇)

上期我们重点讲解了ASO关键词库搭建的全套方法,相信大家已经掌握了关键词基础属性、词库建立的核心步骤。本期承接上篇内容,继续深入讲解关键词优化进阶实操,手把手教大家精准筛选、优化关键词,零基础也能轻松落地,高效…...

开源工作流引擎Conductor:微服务任务编排与自动化实践指南

1. 项目概述与核心价值最近在折腾一个自动化任务编排的项目,发现了一个挺有意思的开源工具——Dragoon0x/conductor。这名字听起来就很有“指挥家”的范儿,事实上,它的定位也确实如此:一个轻量级、高性能的工作流编排引擎。如果你…...

扩散语言模型超参数优化与工程实践指南

1. 项目背景与核心价值去年在部署一个百亿参数规模的文本生成系统时,我们团队遇到了一个典型困境:模型在测试集上表现优异,但实际部署后生成质量却出现明显波动。经过三周的排查才发现,问题出在训练阶段未被充分优化的学习率衰减策…...

开源AI对话平台Stellar-Chat:自托管部署与多模型接入实战

1. 项目概述:一个面向开发者的开源AI对话平台最近在GitHub上看到一个挺有意思的项目,叫“Stellar-Chat”。第一眼看到这个名字,我以为是某个新的即时通讯工具,但点进去才发现,这是一个完全开源的、可自托管的AI对话应用…...

栈和队列实践多项式加法与乘法

本次来记录栈和队列进行实战,即来编写多项式的加法与乘法,首先我们先把题目列出来。P1067 [NOIP 2009 普及组] 多项式输出 - 洛谷。为了方便大家阅读,我把题目copy过来。 对于多项式而言,他分为系数和指数两个部分,我们…...

Seg-ReSearch:动态搜索增强的图像分割技术解析

1. 项目背景与核心价值在计算机视觉领域,图像分割技术一直是研究热点。传统分割模型往往面临两个关键瓶颈:一是面对未见过的物体类别时表现不佳,二是对复杂场景的细节分割精度有限。Seg-ReSearch创新性地将外部搜索机制引入分割推理过程&…...

端到端GUI智能体UI-Venus-1.5:革新自动化测试与RPA

1. 项目概述:当GUI智能体遇上端到端革命在自动化测试和RPA(机器人流程自动化)领域,我们正见证着从传统脚本录制到智能交互的技术跃迁。UI-Venus-1.5作为新一代端到端GUI智能体框架,彻底改变了人机交互自动化的实现方式…...

Hugging Face模型加载超快

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 Hugging Face模型加载加速:从技术瓶颈到边缘智能的跃迁目录Hugging Face模型加载加速:从技术瓶颈到边缘智…...

PCIe协议学习-浅谈SR-IOV

转载:(13 封私信 / 81 条消息) PCIe协议学习-浅谈SR-IOV - 知乎 1:背景和概述: SR-IOV,全称叫single root I/O virtualization and sharing,顾名思义,这是一种虚拟化技术,目的是让多个终端或者…...

CI/CD——使用Jenkins实现自动化部署与持续集成之jenkins的安装部署

DevOps详解与监控方法论https://blog.csdn.net/xiaochenxihua/article/details/157059743 Git实践——GitLab服务器的部署与使用https://blog.csdn.net/xiaochenXIHUA/article/details/160722357 一、CI/CD与Jenkins介绍 1.1、CI/CD是什么 CI/CD(持续集成/持续交付…...

第1章 Nginx 简介与架构【20260503】-002篇-Nginx日志切割

文章目录 ✅ Nginx 日志切割(生产级实操) 一、为什么要做日志切割(SRE 视角) 二、推荐方案对比 三、标准实操(DevOps 交付级) 1️⃣ logrotate 配置文件(重点) 2️⃣ 手动验证(SRE 必会) 四、故障场景(SRE 面试/考核高频) ❌ 故障 1:磁盘爆满 ❌ 故障 2:reload 后…...

第1章 Nginx 简介与架构【20260503】-001篇

文章目录 1.2 Nginx 进程模型(Master + Worker) 进程职责 课后习题(升级版) ✅ 实操考核(强烈建议纳入上岗考核) 实操 1:进程模型验证(SRE) 实操 2:热重载为何不中断?(面试/考核高频) 执行流程(重点) 实操 3:配置即代码(DevOps) 实操 4:交付标准(Delivery …...

扩散模型推理加速:SenCache动态缓存技术解析

1. 项目概述:当扩散模型遇上推理加速在生成式AI领域,扩散模型(Diffusion Models)已经成为图像生成的主流架构之一。然而这类模型在推理阶段需要多次迭代计算的特点,使得其推理速度成为实际应用中的主要瓶颈。SenCache正…...

FastClaw:一键在Mac上创建预装OpenClaw的Linux虚拟机

1. 项目概述:为什么要在Mac上运行Linux虚拟机来使用OpenClaw? 如果你是一位Mac用户,同时又需要用到一些只能在Linux环境下稳定运行或性能更优的特定工具,比如OpenClaw,那你可能正面临一个经典的“平台鸿沟”问题。直接…...

超导神经元原理与生物神经元模拟技术解析

1. 超导神经元的基础原理与生物神经元模拟超导神经元是一种利用超导材料特性模拟生物神经元行为的硬件实现。其核心工作机制建立在超导体特有的量子现象之上,特别是约瑟夫森效应和磁通量子化原理。当超导体被冷却至临界温度以下时,电子会形成库珀对&…...

保姆级教程:在CentOS 7上用Docker Compose一键部署EdgeX Foundry 3.1(含虚拟设备服务)

保姆级教程:在CentOS 7上用Docker Compose一键部署EdgeX Foundry 3.1(含虚拟设备服务) EdgeX Foundry作为开源物联网边缘计算框架,正成为工业4.0和智能家居领域的基础设施。本教程将带您从零开始,在CentOS 7系统上完成…...

点云遮挡检测实战:用PCL和Open3D复现HPR算法(附完整C++/Python代码)

点云遮挡检测实战:用PCL和Open3D复现HPR算法(附完整C/Python代码) 在三维视觉和机器人领域,点云遮挡检测是一个基础但至关重要的任务。想象一下,当机器人试图在复杂环境中导航时,准确识别哪些物体表面可见、…...

从零构建个人ChatGPT:基于Llama与LoRA的SFT与RLHF全流程实战

1. 从零到一:构建你自己的个人ChatGPT全流程拆解想不想拥有一个像ChatGPT那样能说会道、善解人意的AI伙伴,但它只属于你,能记住你的习惯,理解你的偏好,甚至用你喜欢的风格和你聊天?这听起来像是科幻电影里的…...

XFCE 桌面环境组件详解:从面板到剪贴板管理

文章目录1. XFCE 简介2. 核心组件架构3. xfce4-panel:面板系统3.1 功能概述3.2 关键命令3.3 插件生态3.4 配置文件位置4. xfce4-keyboard-settings:键盘与快捷键管理4.1 功能概述4.2 启动方式4.3 快捷键配置结构4.4 底层存储机制5. xfce4-clipman&#x…...

RDD API 学习

📊 RDD vs DataFrame 对比特性RDDDataFrameAPI 风格函数式(Scala/Java)声明式(SQL)性能较慢更快(Catalyst 优化)类型安全编译时运行时内存管理手动(JVM)自动(…...