FreeRTOS源码分析-12 低功耗管理

目录

1 STM32低功耗管理概念及应用

1.1睡眠模式      

1.2 停止模式

1.3 待机模式      

2 Tickless低功耗管理

2.1 Tickless低功耗模式介绍

2.2 FreeRTOS低功耗模式配置

2.3 FreeRTOS低功耗模式应用 

3 低功耗管理实际项目开发

3.1 低功耗设计必须要掌握的硬件知识

3.2 开发板电路低功耗分析

3.3 HAL库低功耗处理相关接口

4 低功耗实现原理

4.1 空闲任务详解

4.2 任务删除自身详解 

4.3 Tickless业务流程

4.4 休眠处理详解


1 STM32低功耗管理概念及应用

低功耗产品案例

ADC是模拟电路,调压电路时数字电路,模数分离所以ADC是单独电路。

核心功耗在调压供电电路,关闭外设时钟即能关闭外设。

1.1睡眠模式      

在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM4核心的外设全都还照常运行。      

有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是WFI(wait for interrupt)和WFE(wait for event,也可以由中断唤醒,但是不会去处理中断),即由等待“中断”唤醒和由“事件”唤醒。睡眠模式的各种特性见下表

1.2 停止模式

在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其1.2V区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息。      

所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒。在停止模式中可以选择电压调节器为开模式或低功耗模式,可选择内部FLASH工作在正常模式或掉电模式。(唤醒后,要重新初始化外设时钟

一般都把FLASH掉电模式和、FLASH掉电模式都设置为处于关闭状态。唤醒后会有延迟,需要产品对延迟容忍度的考虑。

1.3 待机模式      

待机模式,它除了关闭所有的时钟,还把1.2V区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测boot条件,从头开始执行程序。它有四种唤醒方式,分别是WKUP(PA0)引脚的上升沿,RTC闹钟事件,NRST引脚的复位和IWDG(独立看门狗)复位。

内存掉电了,程序是错乱的,所以只有上述几种复位

2 Tickless低功耗管理

2.1 Tickless低功耗模式介绍

Idle task 任务中会调用tickless,当休眠时间>10ms那么会进入低功耗模式,可选择3种休眠模式 

再分析多任务调度源码的时候,有个UnblockTime,我们可以把这个值传给tickless,来计算下个任务需要调度的时间。

2.2 FreeRTOS低功耗模式配置

问:为什么要大于2个tick值?

进入休眠模式,需要进行很多判断处理一些外设等。

2.3 FreeRTOS低功耗模式应用 

CubeMX

生成代码后Freertos.c中会多两个代码

#if configUSE_TICKLESS_IDLE == 1 
#define configPRE_SLEEP_PROCESSING                        PreSleepProcessing
#define configPOST_SLEEP_PROCESSING                       PostSleepProcessing
#endif /* configUSE_TICKLESS_IDLE == 1 */void PreSleepProcessing(uint32_t *ulExpectedIdleTime);
void PostSleepProcessing(uint32_t *ulExpectedIdleTime);

自写休眠函数

__weak void PreSleepProcessing(uint32_t *ulExpectedIdleTime)
{/* place for user code */ printf("input sleep mode!\r\n,ulExpectedIdleTime = %u\r\n",*ulExpectedIdleTime);//休眠时间tick值打印HAL_SuspendTick();   //先挂起SystickHAL_PWREx_EnableFlashPowerDown();__HAL_RCC_GPIOA_CLK_DISABLE(); __HAL_RCC_GPIOB_CLK_DISABLE();  __HAL_RCC_GPIOC_CLK_DISABLE();  __HAL_RCC_GPIOH_CLK_DISABLE();  __HAL_RCC_GPIOI_CLK_DISABLE();__HAL_RCC_GPIOF_CLK_DISABLE();
}__weak void PostSleepProcessing(uint32_t *ulExpectedIdleTime)
{/* place for user code */HAL_PWREx_DisableFlashPowerDown();printf("output sleep mode!\r\n");__HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOI_CLK_ENABLE();__HAL_RCC_GPIOF_CLK_ENABLE();__HAL_RCC_GPIOH_CLK_ENABLE(); //HAL_ResumeTick();   //不能再这恢复tick值,否则会不断唤醒
}

如果不挂起Systick和不注释TIM6,会不断触发中断唤醒,还需要查看每个任务中osDelay,不能设太小。

3 低功耗管理实际项目开发

不启用Tickless,烧写程序,直接接电流表测试功耗

启用后在测试电流

程序               TimerTask     Tickless    L去除LCD  关闭GPIO外设时钟&Flash DOWN
工作电流        441mA        395mA       193mA       192mA
休眠程序减少 46mA 

3.1 低功耗设计必须要掌握的硬件知识

主要从四个方面控制功耗:

  • 主控芯片
  • 电源管理
  • 外设模块
  • 外设接口 

 

 

3.2 开发板电路低功耗分析

根据原理图找芯片手册www.alldatasheet.com

电源芯片手册,静态电流5ma 

3.3 HAL库低功耗处理相关接口

4 低功耗实现原理

4.1 空闲任务详解

idleTack

void vTaskStartScheduler( void )//空闲任务创建xReturn = xTaskCreate(	prvIdleTask,"IDLE", configMINIMAL_STACK_SIZE,( void * ) NULL,( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),&xIdleTaskHandle ); /*全局搜索IDLE,发现portTASK_FUNCTION,再次搜索这个宏是方便用户开发,其他语言也能实        现,相当于
*/
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )/*空闲任务,宏定义实际相当于void prvIdleTask( void *pvParameters );*/
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{/* Stop warnings. */( void ) pvParameters;/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THESCHEDULER IS STARTED. **/for( ;; ){/*检查任务删除自身处理 */prvCheckTasksWaitingTermination();/*判断调度器工作模式是否开启了优先级抢占模式*/#if ( configUSE_PREEMPTION == 0 ){ /* 1、触发了上下文切换2、让调度器判断是否有其他任务,处于了就绪态,然后进行调度*/taskYIELD();}#endif /* configUSE_PREEMPTION *///调度器使能抢占式#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ){/*列表项检查:1、和空闲任务处于同一优先级的任务,处于就绪态2、进行上下文切换3、高于空闲任务优先级的任务,有调度器进行处理*/if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ){taskYIELD();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) *//*钩子函数,主要让用户自己填充代码*/#if ( configUSE_IDLE_HOOK == 1 ){extern void vApplicationIdleHook( void );/* 用户自己实现,比如检测外部信息 */vApplicationIdleHook();}#endif /* configUSE_IDLE_HOOK *//* 低功耗处理功能 */#if ( configUSE_TICKLESS_IDLE != 0 ){TickType_t xExpectedIdleTime;/* 获取系统的最小时间片 */xExpectedIdleTime = prvGetExpectedIdleTime();//判断是否大于休眠空闲处理的最小间隔=2tickif( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){//挂起调度器vTaskSuspendAll();{/* 又一次获取获取系统的最小时间片,防止挂起调度器期间又有任务更新*/xExpectedIdleTime = prvGetExpectedIdleTime();//再次判断if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){//进入了休眠处理,传入系统的最小时间片portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );}else{mtCOVERAGE_TEST_MARKER();}}//恢复调度器( void ) xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TICKLESS_IDLE */}
}

低功耗处理慢点分析,先查看一下任务删除自身的源码分析。

4.2 任务删除自身详解 

 结合之前任务删除分析,uxDeletedTasksWaitingCleanUp 在vTaskDelete进行加1处理

/*任务删除自身*/
static void prvCheckTasksWaitingTermination( void )
{/** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/#if ( INCLUDE_vTaskDelete == 1 ){BaseType_t xListIsEmpty;/* 遍历将要删除的任务uxDeletedTasksWaitingCleanUp 在vTaskDelete进行加1处理*/while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ){//挂起了调度器vTaskSuspendAll();{//读取删除任务自身列表里任务状态是否为空xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );}//开启调度器( void ) xTaskResumeAll();if( xListIsEmpty == pdFALSE ){//删除任务TCB_t *pxTCB;//进入临界段taskENTER_CRITICAL();{/*1、获取任务控制块2、从任务列表项移除任务3、任务总计数减一4、等待删除计数减一*/pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );( void ) uxListRemove( &( pxTCB->xStateListItem ) );--uxCurrentNumberOfTasks;--uxDeletedTasksWaitingCleanUp;}//退出临界段taskEXIT_CRITICAL();//释放了任务控制块prvDeleteTCB( pxTCB );}else{mtCOVERAGE_TEST_MARKER();}}}#endif /* INCLUDE_vTaskDelete */
}

问:为什么挂起调度器?

不需要别的任务调度,以免影响读取删除任务列表状态。 

4.3 Tickless业务流程

大于2个TICK才有意义去处理休眠

先跳转在4.1中低功耗处理分析代码,将要获取的空闲时间xExpectedIdleTime = prvGetExpectedIdleTime()。

最后进入portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime )

4.4 休眠处理详解

计算systick装载值

Systick最大值为24bit

/*获取最小系统时间片*/static TickType_t prvGetExpectedIdleTime( void ){TickType_t xReturn;UBaseType_t uxHigherPriorityReadyTasks = pdFALSE;/* */#if( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ){//就绪态的任务优先级高于空闲if( uxTopReadyPriority > tskIDLE_PRIORITY ){uxHigherPriorityReadyTasks = pdTRUE;}}#else{const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01;/* 就绪态的任务优先级高于空闲 */if( uxTopReadyPriority > uxLeastSignificantBit ){uxHigherPriorityReadyTasks = pdTRUE;}}#endif//当前任务优先级高于空闲任务if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ){xReturn = 0;}//与空闲任务优先级相同的其他任务处于就绪态else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ){xReturn = 0;}//高优先级任务处于就绪态else if( uxHigherPriorityReadyTasks != pdFALSE ){/* There are tasks in the Ready state that have a priority above theidle priority.  This path can only be reached ifconfigUSE_PREEMPTION is 0. */xReturn = 0;}else //空闲任务优先级最高,才计算{//系统解锁时间-系统tick计数值== 就是当前系统的最小时间片xReturn = xNextTaskUnblockTime - xTickCount;}return xReturn;}

进入低功耗模式

 恢复systick

 

portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
//这个函数,是需要用户自己实现,但是STM32FreeRTOS已经帮我们实现
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime )
#endif
/*低功耗实际处理函数1、传入系统的最小时间片*/
__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ){uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL;TickType_t xModifiableIdleTime;/* 判断系统最小时间片是否大于systick的最大装载周期  单位都tick*/if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ){//系统最小时间片=systick最大装载周期//如果获取系统最小时间片很大,但是systick休眠周期的最大值就是最大装载值//为什么这样设计????  1、systick定时器受限制(定时周期)2、保证systick精度问题xExpectedIdleTime = xMaximumPossibleSuppressedTicks;}/* 关闭systick定时器 */portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;/*systick重载值= 当前的systick计数值+单次系统tick装载值*(系统最小时间片-1)*/ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );//装载值是否大于补偿周期 之后减去补偿周期//最终计算出,systick重载值if( ulReloadValue > ulStoppedTimerCompensation ){ulReloadValue -= ulStoppedTimerCompensation;}/* 关闭中断 关闭所有中断 和 进入临界段不一样 虽然关闭了中断,但是可以唤醒CPU,不进行中断处理*/__disable_irq();__dsb( portSY_FULL_READ_WRITE );__isb( portSY_FULL_READ_WRITE );/* 是否有其他任务,进入了就绪态 */if( eTaskConfirmSleepModeStatus() == eAbortSleep ){//终止休眠/* 当前的systick计数值,放到systick装载寄存器中 */portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;/* 启动systick */portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;/* 重新赋值装载寄存器值为一个系统的tick周期. */portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;/* 开启中断 */__enable_irq();}else{/* 装载休眠systick装载值 */portNVIC_SYSTICK_LOAD_REG = ulReloadValue;/* 清除systick当前计数值 */portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;/* 启动systick定时器*/portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;/*  */xModifiableIdleTime = xExpectedIdleTime;//这个就是给我用户提供的接口,让我自己实现休眠处理,其实就是进一步降低功耗configPRE_SLEEP_PROCESSING( &xModifiableIdleTime );if( xModifiableIdleTime > 0 ){//让CPU休眠__dsb( portSY_FULL_READ_WRITE );__wfi();__isb( portSY_FULL_READ_WRITE );}//退出处理configPOST_SLEEP_PROCESSING( &xExpectedIdleTime );/* 停止systick定时器 */ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG;portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT );/* 使能中断 */__enable_irq();//判断是否为systick唤醒的if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ){uint32_t ulCalculatedLoadValue;/*systick恢复值= 单个tick周期值- (休眠装载值-当前systick计数值)*/ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );/* 这是一个保护处理1、装载值很小,就赋值为1个tick周期2、装载很大,也赋值为1个tick周期*/if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ){ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );}//装载恢复systick装载值portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;/* 休眠周期的补偿值,单位为tick  也就是1ms单位  */ulCompleteTickPeriods = xExpectedIdleTime - 1UL;}else{/* 休眠运行装载值= 休眠装载值-当前systick计数值)*/ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;/* 休眠运行周期,单位为tick值 */ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;//装载恢复systick装载值portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;}/* 清除systick计数值*/portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;portENTER_CRITICAL();{/*1、使能了systick2、补偿系统的tick周期值,也是说,tick运行了多长时间(tick值)为什么这样做?在调度器恢复的时候,会根据tick值,进行遍历的,保证实时性3、恢复systick周期为1个tick值*/portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;vTaskStepTick( ulCompleteTickPeriods );portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;}portEXIT_CRITICAL();}}

这些逻辑都是在调试中发现的,主要去理解先计算Systick装载值,在进入休眠,在恢复三个不住。

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

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

相关文章

Debian11 Crontab

Crontab用户命令 可执行文件 crontab命令的可执行文件在哪儿&#xff1f; $ which -a crontab /usr/bin/crontab /bin/crontabcrontab命令的可执行文件有2个&#xff1a;/usr/bin/crontab 和 /bin/crontab $ diff /usr/bin/crontab /bin/crontab $diff 发现这两个文件并无区…

一、docker及mysql基本语法

文章目录 一、docker相关命令二、mysql相关命令 一、docker相关命令 &#xff08;1&#xff09;拉取镜像&#xff1a;docker pull <镜像ID/image> &#xff08;2&#xff09;查看当前docker中的镜像&#xff1a;docker images &#xff08;3&#xff09;删除镜像&#x…

JavaScript 快速入门手册

本篇文章学习&#xff1a; 菜鸟教程、尚硅谷。 JavaScript 快速入门手册 &#x1f4af; 前言&#xff1a; 本人目前算是一个Java程序员&#xff0c;但是目前环境… ε(ο&#xff40;*))) 一言难尽啊&#xff0c;blog也好久好久没有更新了&#xff0c;一部分工作原因吧(外包真…

分组与引用:用正则实现更复杂的查找和替换操作

括号在正则中的功能就是用于分组。简单来理解就是&#xff0c;由多个元字符组成某个部分&#xff0c;应该被看成一个整体的时候&#xff0c;可以用括号括起来表示一个整体&#xff0c;这是括号的一个重要功能。其实用括号括起来还有另外一个作用&#xff0c;那就是“复用”。 …

sass笔记

声明变量 通过$标识符进行命名及引用混合器 类似vue中的函数 通过 mixin标识定义 include 标识调用& 父选择器标识extend 进行继承可嵌套可导入 通过 import 文件位置’ 、进行导入 <style> //1 声明变量 $name: 15px; $color: skyblue;mixin border-radius($num) {/…

iOS textView支持超链接跳转

将某些文字变成高量可以点击的超链接核心功能代码 attri.addAttribute(NSAttributedString.Key.link, value:NSURL.init(string: "dctt:p/userPrivacy.html")!, range: NSRange.init(location: s.count - 4, length: 4) )textView.linkTextAttributes [NSAttributed…

CSS3基础

CSS3在CSS2的基础上增加了很多功能&#xff0c;如圆角、多背景、透明度、阴影等&#xff0c;以帮助开发人员解决一些实际问题。 1、初次使用CSS 与HTML5一样&#xff0c;CSS3也是一种标识语言&#xff0c;可以使用任意文本编辑器编写代码。下面简单介绍CSS3的基本用法。 1.1…

pdf 转 word

pdf 转 word 一、思路 直接调用LibreOffice 命令进行文档转换的命令行工具 使用的前系统中必须已经安装了 libreofficelibreoffice已翻译的用户界面语言包: 中文 (简体)libreoffice离线帮助文档: 中文 (简体)上传字体 重点&#xff1a;重点&#xff1a;重点&#xff1a; 亲…

flutter:webview_flutter和flutter_inappwebview的简单使用

前言 最近在研究如何在应用程序中嵌入Web视图&#xff0c;发现有两个库不错。 一个是官方维护、一个是第三方维护。因为没说特别的需求&#xff0c;就使用了官方库&#xff0c;实现一些简单功能是完全ok的 webview_flutter 不建议使用&#xff0c;因为效果不怎么样&#xf…

网站老域名跳转到新域名有哪些方法?内网穿透内网主机让外网访问

在网站服务器变更及本地主机搭建时&#xff0c;我们经常会遇到老域名地址跳转到新URL的配置&#xff0c;一些朋友还会面对无公网IP让外网访问的问题。今天我们来了解下网站老域名跳转到新域名有哪些方法&#xff0c;以及如何通过内网穿透实现内网主机让外网访问。 网站老域名跳…

js ajax 国内快速 映像

ajax 快速 映像 https://www.bootcdn.cn/ axios入门和axios基本请求方式 https://blog.csdn.net/m0_68997646/article/details/127438174 使用 jsDelivr CDN: <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>因为我们国…

关于vant2 组件van-dropdown-item,在IOS手机上,特定条件下无法点击问题的探讨

情景重现 先贴有问题的代码 <template><div :class"showBar ? homeContain : homeContain-nobar"><div class"contant" id"content"><van-dialog v-model"loading" :before-close"onBeforeClose" :…

git merge规则

参考文档&#xff1a;https://juejin.cn/post/7129333439299321887 丹尼尔&#xff1a;Hi&#xff0c;蛋兄&#xff0c;周杰伦都出新专辑了&#xff0c;你咋还不更新啊&#xff0c;真的打算半年一更啊&#xff1f; 蛋先生&#xff1a;好像确实是这样&#xff0c;要不&#xff0…

不要着急购买iPhone 15,先看看这5点再做决定吧!

人们对下个月可能推出的iPhone 15感到兴奋,这是有充分理由的——有传言称,新机型正在做出一些重大改变,尤其是在iPhone 15 Pro机型方面。从四款新iPhone都采用USB-C,到iPhone 15 Pro Max采用潜望镜式长焦镜头以实现更好的变焦,听起来有很多功能值得兴奋。 当然,除非你没…

OpenCV图片校正

OpenCV图片校正 背景几种校正方法1.傅里叶变换 霍夫变换 直线 角度 旋转3.四点透视 角度 旋转4.检测矩形轮廓 角度 旋转参考 背景 遇到偏的图片想要校正成水平或者垂直的。 几种校正方法 对于倾斜的图片通过矫正可以得到水平的图片。一般有如下几种基于opencv的组合方…

探索Chevereto图床:使用Docker Compose快速搭建个人图床

家人们!图片在今天的社交媒体、博客和论坛中扮演着至关重要的角色。然而&#xff0c;随着图片数量的增加&#xff0c;寻找一个可靠的图片托管解决方案变得越来越重要。Chevereto图床是一个备受赞誉的解决方案&#xff0c;而使用Docker Compose搭建它更是一种高效、可维护的方法…

UE4中关于利用粒子系统做轨迹描绘导致系统流畅性下降的问题

UE4中关于利用粒子系统做轨迹描绘导致系统流畅性下降的问题 文章目录 UE4中关于利用粒子系统做轨迹描绘导致系统流畅性下降的问题前言假设及验证1. 过多的粒子发射器影响仿真系统2. 粒子数目太多&#xff0c;降低粒子发射频率&#xff0c;同时增大粒子显示范围3. 把信息输出到屏…

matlab 点云最小二乘拟合空间直线(方法一)

目录 一、算法原理1、空间直线2、最小二乘法拟合二、代码实现三、结果展示四、可视化参考本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、空间直线 x

基于 Vercel TiDB Serverless 的 chatbot

作者&#xff1a; shiyuhang0 原文来源&#xff1a; https://tidb.net/blog/7b5fcdc9 # 前言 TiDB Serverless 去年就有和 Vercel 的集成了&#xff0c;同时还有一个 bookstore template 方便大家体验。但个人感觉 bookstore 不够炫酷&#xff0c;借 2023 TiDB hackthon 的…

07_缓存预热缓存雪崩缓存击穿缓存穿透

缓存预热&缓存雪崩&缓存击穿&缓存穿透 一、缓存预热 提前将数据从数据库同步到redis。 在程序启动的时候&#xff0c;直接将数据刷新到redis懒加载&#xff0c;用户访问的时候&#xff0c;第一次查询数据库&#xff0c;然后将数据写入redis 二、缓存雪崩 发生情…