S1-07 事件组

事件组

在 FreeRTOS 中,事件组(Event Group)是一种用于任务间通信的机制,用于在多个任务之间同步和传递事件。
事件组主要包含一下两个概念:

  1. 事件标志位(Event Flags):每个事件标志位代表一个独立的事件状态。
  2. 事件组控制块(Event Group Control Block,EGCB):事件组的信息结构体,维护了事件标志位的状态等信息。

通过调用 FreeRTOS 提供的 API 函数,任务可以进行如下操作:

  1. 设置指定的事件标志位为已发生。
  2. 查询事件标志位是否已经被设置。
  3. 等待多个事件标志位中的任意一个或全部都被设置。

对于等待事件标志位的任务,可以选择在等待时阻塞自己等待事件的发生,也可以在等待时设置超时时间,如果超时仍然没有事件发生,则任务会自动解除阻塞并返回相应值。

事件组由 EventGroupHandle_t 类型的变量引用。
如果 configUSE_16_BIT_TICKS 如果 configUSE_16_BIT_TICKS 设置为 0,则为 24。configUSE_16_BIT_TICKS 的值取决于 任务内部实现中用于线程本地存储的数据类型。
事件组中的所有事件位都 存储在 EventBits_t 类型的单个无符号整数变量中。 事件位 0 存储在位 0 中, 事件位 1 存储在位1 中,依此类推。
下图表示一个 24 位事件组, 使用 3 个位来保存前面描述的 3 个示例事件。 在图片中,仅设置了 事件位 2。
在这里插入图片描述

第一个餐厅的厨师(作为事件标志位)

回到我们厨子和吃货的世界中,本次出场的只有厨子,另外还有一些服务员,服务员负责给厨子配菜,这时候厨子做一个汉堡需要等待三样东西,分别是面包、肉饼、蔬菜,做蔬菜的服务员等肉饼做好后再做蔬菜,做肉饼的则要等待做面包的,而做面包的需要等待厨子的号令,一切是那么的竟然有序。

代码共享位置:https://wokwi.com/projects/362876432326851585

#define START_FLAG      (1 << 0)
#define BREAD_FLAG      (1 << 1)
#define MEAT_FLAG       (1 << 2)
#define VEGETABLE_FLAG  (1 << 3)
EventGroupHandle_t xEventHamburg = NULL;  // 做汉堡的事件组
// 面包服务员
void waiter_bread_task(void *param_t){EventBits_t uxBits;uxBits = xEventGroupWaitBits(xEventHamburg, // 事件组句柄START_FLAG,     // 等待开始事件pdTRUE,         // 读取后清空标志位pdTRUE,         // ADN关系,1个值无所谓portMAX_DELAY);printf("[BRED] 等到开始事件 : %X\n", uxBits);vTaskDelay(pdMS_TO_TICKS(random(500,2000)));uxBits = xEventGroupSetBits(xEventHamburg, BREAD_FLAG); // 设置面包标志位printf("[BRED] 面包已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 肉饼服务员
void waiter_meat_task(void *param_t){EventBits_t uxBits;uxBits = xEventGroupWaitBits(xEventHamburg, // 事件组句柄BREAD_FLAG,     // 等待面包事件pdFALSE,        // 读取后不清空pdTRUE,         // ADN关系,1个值无所谓portMAX_DELAY);printf("[MEAT] 等到面包做好 : %X\n", uxBits);vTaskDelay(pdMS_TO_TICKS(random(500,2000)));uxBits = xEventGroupSetBits(xEventHamburg, MEAT_FLAG); // 设置肉饼标志位printf("[MEAT] 肉饼已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 蔬菜服务员
void waiter_vegetable_task(void *param_t){EventBits_t uxBits;uxBits = xEventGroupWaitBits(xEventHamburg, // 事件组句柄MEAT_FLAG,      // 等待开始事件pdFALSE,        // 读取后不清空pdTRUE,         // ADN关系,1个值无所谓portMAX_DELAY);printf("[VEGE] 等到肉饼做好 : %X\n", uxBits);vTaskDelay(pdMS_TO_TICKS(random(500,2000)));uxBits = xEventGroupSetBits(xEventHamburg, VEGETABLE_FLAG); // 设置蔬菜标志位printf("[VEGE] 蔬菜已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 厨师线程
void chef_task(void *param_t){pinMode(4, INPUT_PULLUP);while(1){if(digitalRead(4) == LOW){// 开始做汉堡EventBits_t uxBits;  // 设置事件标志位的返回值uxBits = xEventGroupSetBits(xEventHamburg, START_FLAG);  // 这个返回值有可能会清空标志位,具体读文档printf("[CHEF] 开始做汉堡 : %X\n", uxBits);uxBits = xEventGroupWaitBits(xEventHamburg,                            // 事件句柄BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG,    // 等待代表面包、肉饼、蔬菜的标志位pdFALSE,                                    // 是否清空对应标志位pdTRUE,                                     // 等待的Bits判断关系 True为 AND, False为 ORportMAX_DELAY);                             // 等待超时时间printf("[CHEF] 汉堡做完了 : %X\n", uxBits);// 重置事件组xEventGroupClearBits(xEventHamburg, START_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG);uxBits = xEventGroupGetBits(xEventHamburg);printf("[CHEF] 汉堡做好了,我下班了 : %X\n", uxBits);vTaskDelete(NULL);}vTaskDelay(100);}
}
void setup() {Serial.begin(115200);xEventHamburg = xEventGroupCreate();  //初始化事件组// 启动各个线程xTaskCreate(chef_task, "Chef", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_bread_task, "Bread", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_meat_task, "Meat", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_vegetable_task, "Vegetable", 1024*2, NULL, 1, NULL);
}
void loop() {delay(100);
}

代码中首先在setup中通过 xEventGroupCreate 创建了一个事件组。
chef_task 中不断判断是否按下了按钮,当按钮被按下时,首先设置事件组的第一位为1,表示开始信号,此时的3个字节数据为(此时事件组值为1)
在这里插入图片描述

设置完之后,就开始等待第2 3 4位的数据,这里我们用的都是 xEventGroupWaitBits 等待,该函数一共有5个参数,函数原型如下:

EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait );

xEventGroup : 事件组句柄
uxBitsToWaitFor : 指定事件组中要测试的一个或多个事件位的按位值,可以用 | 运算指定多个,例如,等待第0位则为1,等待第二位则为2,等待第三位则为4,等待第四位则为8,如果等待第1位和第三位,则为1|3=5。
xClearOnExit : 是否清除时间位,设置为 pdTRUE, 那么在作为 uxBitsToWaitFor 参数传递的值中设置的任何位 会在 xEventGroupWaitBits() 返回某个值之前在事件组中清除掉, 前提是 xEventGroupWaitBits() 因超时以外的原因而返回值。
xWaitForAllBits
用于创建逻辑与测试 (必须设置所有位)或逻辑或测试(必须设置一个 或多个位),如下所示:如果 xWaitForAllBits 设置为 pdTRUE, 那么当在作为 uxBitsToWaitFor 参数传递的值中设置的所有位 均已在事件组中设置好,或指定的阻塞时间已过期,则 xEventGroupWaitBits() 会返回相应值。如果 xWaitForAllBits 设置为 pdFALSE,那么当在作为 uxBitsToWaitFor 参数传递的值中设置的任何位已在事件组中设置好, 或指定的阻塞时间已过期,则 xEventGroupWaitBits() 会返回相应值。
xTicksToWait : 超时时间。
这里我们等待选择不清空,并且使用同时等待第2、3、4位事件都到达。
xEventGroupSetBits 函数有个返回值,是当前事件组的值,但需要注意的是,因为执行完该函数后,系统调度器会对任务进行一次调度,看是否有任务等到了某个事件,如果有事件被触发,根据任务优先级,会先执行另外的事件,在返回。
因为在 waiter_bread_task 任务中等待该标志位的时候选择了清空标志位,所以这时候获得的返回值为0,其实我们已经设置了 START_FLAG 标志位,可以通过修改 waiter_bread_task 任务中 xEventGroupWaitBits 函数的 xClearOnExit 测试。

待三个事件都到达时,使用 xEventGroupClearBits 清空我们使用过的前四位。

waiter_bread_task 任务中,首先等待 START_FLAG 时间到达,也就是第一位置1,这里的等待函数 xClearOnExit 参数位 pdTRUE,表示当收到这个信号后将立刻清空。
此时任务组数据如下(此时事件组值为0):
在这里插入图片描述

然后通过 xEventGroupSetBits 函数设置第二位为1(此时事件组值为2):
在这里插入图片描述

waiter_meat_task 任务启动后一直等待 BREAD_FLAG 事件到达,当事件到达后,通过 xEventGroupSetBits(xEventHamburg, BREAD_FLAG) 设置事件组为6:
在这里插入图片描述

waiter_vegetable_task 任务启动后一直等待 MEAT_FLAG 事件到达, 当事件到达后,通过 xEventGroupSetBits(xEventHamburg, VEGETABLE_FLAG) 设置事件组为14(也就是E):
在这里插入图片描述

最后回到 chef_task 任务中,等待三个值凑齐,继续往下执行,通过 xEventGroupClearBits(xEventHamburg, START_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG) 清空事件组,此时事件组值再次归零。

标志位的应用方向

事件标志为用于按顺序执行的业务逻辑,举一个智慧物流的例子,机械臂为一辆自动行驶的电动车,装载上货物,电动车开到指定的厂房,中间需要经过一个电动门。机械臂收到指令后触发装货的信号,等装完货之后,机械臂又触发小车的移动信号,等小车移动到门口的时候又触发电动门开闸的信号,然后等电动门开启完毕后,再给小车一个移动的信号,以此类推,直到小车运到下一个厂房机械臂将货物卸下。
这里边用到了多个信号量协同,但是他们之间是有顺序关系的。

二号餐厅的厨师(改进的标志位)

在一号餐厅中,所有服务员必须等上一名服务员完成工作后才会做自己的工作,但本质上面包、肉饼和蔬菜的准备并不是顺序关系,而是并行关系,三个线程完全可以独立运行,他们只需要共同等待一个开始信号,信号到达后各自做自己的工作,而厨师的任务与他们三个是并行的,所以厨师还是需要等待另外三个人工作完成后才能开动。
所以,我们需要对第一个餐厅的代码进行一次修改

代码共享位置:https://wokwi.com/projects/362946353284773889

#define START_FLAG      (1 << 0)
#define BREAD_FLAG      (1 << 1)
#define MEAT_FLAG       (1 << 2)
#define VEGETABLE_FLAG  (1 << 3)
EventGroupHandle_t xEventHamburg = NULL;  // 做汉堡的事件组
// 面包服务员
void waiter_bread_task(void *param_t){EventBits_t uxBits;uxBits = xEventGroupWaitBits(xEventHamburg, // 事件组句柄START_FLAG,     // 等待开始事件pdFALSE,        // 读取后不清空标志位pdTRUE,         // ADN关系,1个值无所谓portMAX_DELAY);printf("[BRED] 等到开始事件 : %X\n", uxBits);vTaskDelay(pdMS_TO_TICKS(random(500,2000)));uxBits = xEventGroupSetBits(xEventHamburg, BREAD_FLAG); // 设置面包标志位printf("[BRED] 面包已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 肉饼服务员
void waiter_meat_task(void *param_t){EventBits_t uxBits;uxBits = xEventGroupWaitBits(xEventHamburg, // 事件组句柄START_FLAG,     // 等待开始事件pdFALSE,        // 读取后不清空标志位pdTRUE,         // ADN关系,1个值无所谓portMAX_DELAY);printf("[MEAT] 等到开始事件 : %X\n", uxBits);vTaskDelay(pdMS_TO_TICKS(random(500,2000)));uxBits = xEventGroupSetBits(xEventHamburg, MEAT_FLAG); // 设置肉饼标志位printf("[MEAT] 肉饼已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 蔬菜服务员
void waiter_vegetable_task(void *param_t){EventBits_t uxBits;uxBits = xEventGroupWaitBits(xEventHamburg, // 事件组句柄START_FLAG,     // 等待开始事件pdFALSE,        // 读取后不清空标志位pdTRUE,         // ADN关系,1个值无所谓portMAX_DELAY);printf("[VEGE] 等到开始事件 : %X\n", uxBits);vTaskDelay(pdMS_TO_TICKS(random(500,2000)));uxBits = xEventGroupSetBits(xEventHamburg, VEGETABLE_FLAG); // 设置蔬菜标志位printf("[VEGE] 蔬菜已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 厨师线程
void chef_task(void *param_t){pinMode(4, INPUT_PULLUP);while(1){if(digitalRead(4) == LOW){// 开始做汉堡EventBits_t uxBits;  // 设置事件标志位的返回值uxBits = xEventGroupSetBits(xEventHamburg, START_FLAG);  // 这个返回值有可能会清空标志位,具体读文档printf("[CHEF] 开始做汉堡 : %X\n", uxBits);uxBits = xEventGroupWaitBits(xEventHamburg,                            // 事件句柄BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG,    // 等待代表面包、肉饼、蔬菜的标志位pdFALSE,                                    // 是否清空对应标志位pdTRUE,                                     // 等待的Bits判断关系 True为 AND, False为 ORportMAX_DELAY);                             // 等待超时时间printf("[CHEF] 汉堡做完了 : %X\n", uxBits);// 重置事件组xEventGroupClearBits(xEventHamburg, START_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG);uxBits = xEventGroupGetBits(xEventHamburg);printf("[CHEF] 汉堡做好了,我下班了 : %X\n", uxBits);vTaskDelete(NULL);}vTaskDelay(100);}
}
void setup() {Serial.begin(115200);xEventHamburg = xEventGroupCreate();  //初始化事件组// 启动各个线程xTaskCreate(chef_task, "Chef", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_bread_task, "Bread", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_meat_task, "Meat", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_vegetable_task, "Vegetable", 1024*2, NULL, 1, NULL);
}
void loop() {delay(100);
}

这个例程基本和第一个例程是一样的,只是我们等待的标志位发生了改变,让所有的服务员都等待 START_FLAG 的信号,而所有人都不得清空这个信号,如果一旦有一个人清空了,那后面有人就会丢失这个信号。

此类表示为的应用方向

还是以智慧物流为例,我们现在的需求是用一辆车运送三个物资到另外的仓库中,当我们下达指令之后,调取物资的机械手并不是顺序运行的,而是各自运行寻找物资,待物资全部装车后车才会启动。这时候就由运货车(或调度台)发送一个“运送”指令,电动车就为等待,三个机械臂分别寻找和装在三种物资到车上,待所有物资都齐全之后,电动车发车。

三号餐厅的厨师(同步)

当这三种配料都准备齐的时候,厨子才开始制作汉堡,而这时候所有的服务员都已经下班走了,厨师就不爽了,凭什么你们做完都走了,我还在工作!
所以,三号餐厅的厨师给老板提了个建议,说要有些服务员的工作太轻松了,下班太早不利于同事间的团结,所以我建议我们必须等到所有人的工作完成之后大家一起下班。
黑心老板也采纳了这个建议,于是,同步就出现了!

代码共享地址:https://wokwi.com/projects/362947317933844481

#define BURG_FLAG       (1 << 0)
#define BREAD_FLAG      (1 << 1)
#define MEAT_FLAG       (1 << 2)
#define VEGETABLE_FLAG  (1 << 3)
EventGroupHandle_t xEventHamburg = NULL;  // 做汉堡的事件组
// 面包服务员
void waiter_bread_task(void *param_t){EventBits_t uxBits;printf("[BRED] 骂骂咧咧的开始烤面包...\n");vTaskDelay(pdMS_TO_TICKS(random(1000,5000)));printf("[BRED] 面包烤好了,我打算设置标志位!\n");uxBits = xEventGroupSync(xEventHamburg,     // 事件句柄BREAD_FLAG,         // 要设置的标志位BURG_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG,  // 同步等待的标志位portMAX_DELAY);     // 超时时间printf("[BRED] 面包已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 肉饼服务员
void waiter_meat_task(void *param_t){EventBits_t uxBits;printf("[MEAT] 叽叽歪歪的开始煎肉饼...\n");vTaskDelay(pdMS_TO_TICKS(random(1000,5000)));printf("[MEAT] 肉饼煎好了,我打算设置标志位!\n");uxBits = xEventGroupSync(xEventHamburg,     // 事件句柄MEAT_FLAG,          // 要设置的标志位BURG_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG,  // 同步等待的标志位portMAX_DELAY);     // 超时时间printf("[MEAT] 肉饼已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 蔬菜服务员
void waiter_vegetable_task(void *param_t){EventBits_t uxBits;printf("[VEGE] 哼哼吱吱的做开始洗蔬菜...\n");vTaskDelay(pdMS_TO_TICKS(random(1000,5000)));printf("[VEGE] 蔬菜洗好了,我打算设置标志位!\n");uxBits = xEventGroupSync(xEventHamburg,     // 事件句柄VEGETABLE_FLAG,     // 要设置的标志位BURG_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG,  // 同步等待的标志位portMAX_DELAY);     // 超时时间printf("[VEGE] 蔬菜已就绪 : %X\n", uxBits);vTaskDelete(NULL);
}
// 厨师线程
void chef_task(void *param_t){pinMode(4, INPUT_PULLUP);EventBits_t uxBits;while(1){if(digitalRead(4) == LOW){// 开始做汉堡printf("[CHEF] 美滋滋的做开始磨洋工...\n");uxBits = xEventGroupSync(xEventHamburg,     // 事件句柄BURG_FLAG,          // 要设置的标志位BURG_FLAG | BREAD_FLAG | MEAT_FLAG | VEGETABLE_FLAG,  // 同步等待的标志位portMAX_DELAY);     // 超时时间printf("[CHEF] 汉堡做好了,大家可以下班了 : %X\n", uxBits);vTaskDelete(NULL);}vTaskDelay(100);}
}
void setup() {Serial.begin(115200);xEventHamburg = xEventGroupCreate();  //初始化事件组// 启动各个线程xTaskCreate(chef_task, "Chef", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_bread_task, "Bread", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_meat_task, "Meat", 1024*2, NULL, 1, NULL);xTaskCreate(waiter_vegetable_task, "Vegetable", 1024*2, NULL, 1, NULL);
}
void loop() {delay(100);
}

在上面的代码中,我们把原来的 START_FLAG 改成了 BURG_FLAG 表示汉堡就绪的标志位,所有线程启动后就开始忙碌自己的工作了,当忙完之后就把自己的标志位设置成1,同时等待其他四位同时的标志位,只有 chef_task 的线程是等待我们操作按钮的。
如果我们启动后不按按钮,那另外三个线程依然会把自己的事情做完,然后开始等待厨子;
但如果启动后马上按动按钮,那么厨子是会提前把自己的汉堡做完,同时等待另外几个同事的(所以这里的例子在实际生活中可能有些不合理)。
例程中我们用到了一个新的函数,同步函数

EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait );

该函数的作用是 将指定标志位设置为1,同时等待其他标志位到达后继续执行。
xEventGroup :事件句柄;
uxBitsToSet :在确定(且可能等待)uxBitsToWait参数指定的所有位 设置完成之前, 要设置事件组中的一个或多个位。
uxBitsToWaitFor:指定事件组中要测试的一个或多个事件位 的按位值。
xTicksToWait:等待时间。
需要注意的是:uxBitsToSet 参数可以是一个位,也可以是是多个位,使用或运算符(|)拼接,但 uxBitsToSet 的值并不一定非得包含在 uxBitsToWaitFor 中。

同步事件组的应用方向

以动车组为例,动车组和普通火车的区别在于,每节车厢都是一个独立的系统,都各自提供动力运行,所以才叫动车组,所以动车组在运行的时候必须等待所有列车就绪的信号才能发车,这样才能最大的节省能量提高效率。
动车组在开车前,先要装在每节车厢的乘客,然后各自关门,当1号车厢的门关闭后,发出一个同步信号,并等待其他车厢就绪,陆续所有车厢的门关闭之后,大家一同出发。
需要注意的是,每节车厢关门就绪的时间是不同的,但最终所有车厢是需要在同一时间发车的,所有车厢是并行的,如果再用标志位的方式,就无法实现了(或者实现起来很啰嗦),所以就用到了 同步 的概念。

关于事件组的所有API,可以参考:https://www.freertos.org/zh-cn-cmn-s/event-groups-API.html

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

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

相关文章

任务类型划分

以下内容来自于ChatGPT内存密集型应用和IO密集型应用是两种不同类型的计算应用&#xff0c;它们在资源需求和性能特点上有所不同。 内存密集型应用&#xff08;Memory-Intensive Applications&#xff09;&#xff1a; 特点&#xff1a; 这类应用主要依赖大量的内存资源来执行任…

动态规划篇-02:杨辉三角

118、杨辉三角 状态转移方程 我们还是老套路起手&#xff1a;先列出状态转移方程。 base case 每行的第一个和最后一个数字都是1 明确状态 “原问题或子问题中变化的变量” 此处的“状态”就是某一位置的数字大小 在此题中&#xff0c;每一个数的“状态” 是由其左上方…

计数排序(Java语言)

视频讲解地址&#xff1a;【手把手带你写十大排序】9.计数排序&#xff08;Java语言&#xff09;_哔哩哔哩_bilibili 代码&#xff1a; public class CountSort {public void sortFucntion(int[] array) {int max Integer.MIN_VALUE;for (int i : array) {max Math.max(max…

测试案例实例

一、hello hello.go package mainconst (spanish "Spanish"french "French"englishHelloPrefix "Hello, "spanishHelloPrefix "Hola, "frenchHelloPrefix "Bonjour, " )func main() { } fun…

Ensp AR/WLAN设备启动失败问题 错误代码41 解决方案

现象描述 启动AR设备之后&#xff0c;设备命令行无法接收输入&#xff0c;在长时间等待后一直输出“####”。启动AR/WLAN设备时&#xff0c;提示“…错误代码40…”。 检查虚拟网卡设置。 检查安装eNSP的PC上是否存在名为“VirtualBox Host-Only Network”的虚拟网卡。 - 如果…

【conda】pip安装报错,网络延时问题解决记录(亲测有效)

【conda】pip安装报错&#xff0c;网络延时问题解决记录 1. pip install 报错如下所示2. 解决方案&#xff1a; 1. pip install 报错如下所示 pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(hostfiles.pythonhosted.org, port443): Read timed out.…

AI文本生图模型Stable Diffusion部分模型叠加效果

兄弟们,最近有个烦恼,就是找图有点费事,干脆自己部署个文本生图模型Stable Diffusion,虽然机器性能慢,但是效果还可以,先和大家截图分享下,后面将映射ai.shenjian.online供大家免费使用 1. 效果预览 2. 主模型及插件安装 下载模型icerealistic_v21.safetensors放到./models/S…

Leetcode447. 回旋镖的数量

Every day a Leetcode 题目来源&#xff1a;447. 回旋镖的数量 解法1&#xff1a;枚举 哈希 题目所描述的回旋镖可以视作一个 V 型的折线。我们可以枚举每个 points[i]&#xff0c;将其当作 V\texttt{V}V 型的拐点。设 points 中有 m 个点到 points[i] 的距离均相等&#…

【笔记ing】Helm-3 主题-1 Chart

Chart Helm使用的包格式称为chart。chart就是一个描述Kubernetes相关资源的文件集合。单个chart可以用来部署一些简单的&#xff0c;类似于memcache pod&#xff0c;或者某些复杂的HTTP服务器以及web全栈应用、数据库、缓存等等。 chart是作为特定目录布局的文件被创建的。它们…

学习Java API(一):基础知识点一文通✅

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读API文档注释String类创建字符串拼接字符串格式化字符串String方法substring(…

Qt之QByteArray数值转换和输出

尽管QByteArray类是一个集合&#xff0c;但也可以作为一个特殊形式的数值来用&#xff0c;其灵活的转换格式可大大一方便各种格式数据转换与显示的需求&#xff0c;如显示二进制和十六进制、显示科学记数和指定小数位的数值。QByteArray类的公有静态函数number可以完成这些功能…

Vue.observable详解(细到原码)

文章目录 一、Observable 是什么二、使用场景三、原理分析参考文献 一、Observable 是什么 Observable 翻译过来我们可以理解成可观察的 我们先来看一下其在Vue中的定义 Vue.observable&#xff0c;让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象 返回…

项目进度管理

7过程 计划过程组6项&#xff1a;规划进度管理&#xff0c;定义活动&#xff0c;排列活动顺序&#xff0c;估算活动资源&#xff0c;估算活动持续时间&#xff0c;制定进度计划&#xff0c; 监控过程组1项&#xff1a;控制进度 1、规划进度管理&#xff0c; 对项目过程中管理…

定时任务-理论基础

什么是小顶堆 小顶堆&#xff08;Min Heap&#xff09;是一种特殊的二叉堆&#xff0c;它满足以下条件&#xff1a; 它是一个完全二叉树&#xff0c;即除了最后一层外&#xff0c;其他层的节点数都是满的&#xff0c;并且最后一层的节点从左到右依次排列。树中的每个节点的…

物联网智能控制器—福建蜂窝物联网科技有限公司

什么是物联网智能控制器&#xff1f; 物联网智能控制器是蜂窝物联自主研发的一种远程测控设备(RTU)&#xff0c;负责对现场信号、工业设备的监测和控制。本质上是一个模块化封装的微型计算机设备&#xff0c;将相应的一些功能进行了封装&#xff0c;无需进行电路设计和硬件程序…

Java多线程并发篇----第十二篇

系列文章目录 文章目录 系列文章目录前言一、ReentrantLock二、Condition 类和 Object 类锁方法区别区别三、tryLock 和 lock 和 lockInterruptibly 的区别前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章…

Python 网络爬虫入门详解

什么是网络爬虫 网络爬虫又称网络蜘蛛,是指按照某种规则在网络上爬取所需内容的脚本程序。众所周知,每个网页通常包含其他网页的入口,网络爬虫则通过一个网址依次进入其他网址获取所需内容。 优先申明:我们使用的python编译环境为PyCharm 一、首先一个网络爬虫的组成结构…

大括号内两行公式中,如何左对齐公式的条件

1. 先建立一个大括号&#xff0c;中间设置一个二维矩阵如下&#xff1a; 2. 选中整个矩阵&#xff0c;不要选外面的括号&#xff0c;进行如下操作 3. 选择左侧对齐 即可。

C++ std::string使用+=后追加变成了覆盖

背景 在进行OCR的rec的时候&#xff0c;读取了一个本地文件表加载到一个std::vector<std::string> labelList&#xff0c;然后循环把推理的结果转为为idx&#xff0c;使用labelList[i]获取当前字符的结果&#xff0c;再用一个外层的std::string resStr进行连接得到最后的…

微信小程序 - 模板与配置 介绍

文章目录 模板与配置一、WXML模板语法1、WXML模板语法 - 数据绑定2、WXML模板语法 - 事件绑定3、WXML模板语法 - 条件渲染4、WXML模板语法 - 列表渲染 二、WXSS模板样式1、WXSS模板样式 - rpx1.1 什么是 rpx 尺寸单位1.2 rpx 的实现原理1.3 rpx 与 px 之间的单位换算 2、WXSS模…