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

从按键消抖到多任务通信:手把手教你用STM32CubeMX和FreeRTOS搭建一个‘智能’按键响应系统

从按键消抖到多任务通信手把手教你用STM32CubeMX和FreeRTOS搭建一个‘智能’按键响应系统在嵌入式开发中按键处理看似简单实则暗藏玄机。当你的项目从简单的单任务裸机系统升级到多任务实时操作系统时按键处理会面临全新的挑战如何确保按键消抖的可靠性如何在不同任务间传递按键事件如何防止按键状态被多个任务同时修改这些问题如果处理不当轻则导致按键响应不灵敏重则引发系统死锁。本文将带你从零开始使用STM32CubeMX和FreeRTOS构建一个完整的智能按键响应系统。这个系统不仅能准确识别短按、长按和组合键还能通过事件标志、互斥锁和信号量等机制实现按键事件在多任务间的安全传递和处理。无论你是刚接触FreeRTOS的新手还是想提升嵌入式系统设计能力的中级开发者这个项目都能让你对RTOS的任务调度和进程间通信有更深入的理解。1. 系统设计与环境搭建1.1 硬件平台选择与配置我们选用STM32F4 Discovery开发板作为硬件平台它内置了四个用户按键和多个LED指示灯非常适合演示按键响应系统。在STM32CubeMX中新建工程时需要做以下关键配置时钟配置将系统主频设置为168MHz确保足够的处理能力GPIO配置按键引脚设置为输入模式启用内部上拉电阻LED引脚设置为输出模式初始状态为高电平LED灭FreeRTOS配置启用USE_MUTEXES和USE_COUNTING_SEMAPHORES设置合适的堆栈大小建议每个任务至少128字调整系统时钟节拍为1ms默认值// 示例按键GPIO初始化代码由CubeMX生成 static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); /* 配置用户按键引脚 */ GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); /* 配置LED引脚 */ GPIO_InitStruct.Pin GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOE, GPIO_InitStruct); }1.2 软件架构设计我们的智能按键系统采用分层设计各模块职责明确硬件抽象层处理GPIO读写和基本消抖事件检测层识别短按、长按和组合键任务通信层使用FreeRTOS机制传递按键事件应用层根据按键事件执行相应操作系统包含以下几个关键任务任务名称优先级功能描述KeyScan3扫描按键状态检测按键事件LEDCtrl2根据按键事件控制LED效果AppTask1处理按键触发的应用逻辑2. 按键消抖与事件检测2.1 可靠的消抖算法实现机械按键的抖动问题不容忽视。我们采用状态机方式实现消抖比简单的延时更可靠typedef enum { KEY_STATE_RELEASED, // 按键释放状态 KEY_STATE_DEBOUNCE, // 消抖确认状态 KEY_STATE_PRESSED, // 按键按下状态 KEY_STATE_LONG_PRESS // 长按状态 } KeyState; void KeyScanTask(void const * argument) { KeyState keyState KEY_STATE_RELEASED; uint32_t pressStartTime 0; for(;;) { GPIO_PinState keyValue HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin); switch(keyState) { case KEY_STATE_RELEASED: if(keyValue GPIO_PIN_RESET) { keyState KEY_STATE_DEBOUNCE; pressStartTime osKernelSysTick(); } break; case KEY_STATE_DEBOUNCE: if((osKernelSysTick() - pressStartTime) 5) { if(keyValue GPIO_PIN_RESET) { keyState KEY_STATE_PRESSED; // 发送短按事件 osSignalSet(LEDTaskHandle, KEY_SHORT_PRESS_EVENT); } else { keyState KEY_STATE_RELEASED; } } break; case KEY_STATE_PRESSED: if(keyValue GPIO_PIN_SET) { keyState KEY_STATE_RELEASED; } else if((osKernelSysTick() - pressStartTime) 1000) { keyState KEY_STATE_LONG_PRESS; // 发送长按事件 osSignalSet(LEDTaskHandle, KEY_LONG_PRESS_EVENT); } break; case KEY_STATE_LONG_PRESS: if(keyValue GPIO_PIN_SET) { keyState KEY_STATE_RELEASED; } break; } osDelay(1); // 每1ms检测一次按键状态 } }2.2 组合键检测策略组合键检测需要考虑时序和按键释放顺序。我们使用位掩码记录当前按下的按键#define KEY_COMBO_MASK (KEY1_MASK | KEY2_MASK) uint8_t currentKeys 0; uint32_t comboStartTime 0; void detectCombo() { if((currentKeys KEY_COMBO_MASK) KEY_COMBO_MASK) { if((osKernelSysTick() - comboStartTime) 50) { // 组合键持续50ms以上 osSignalSet(AppTaskHandle, KEY_COMBO_EVENT); currentKeys ~KEY_COMBO_MASK; // 清除已处理的组合键 } } else { comboStartTime osKernelSysTick(); } }3. FreeRTOS任务间通信实战3.1 事件标志组的灵活应用事件标志组非常适合传递按键事件因为它可以同时传递多个事件且不丢失// 定义事件标志 #define KEY1_SHORT_PRESS (1 0) #define KEY1_LONG_PRESS (1 1) #define KEY2_SHORT_PRESS (1 2) #define KEY_COMBO_EVENT (1 3) void LEDControlTask(void const * argument) { osEvent event; for(;;) { // 等待任何按键事件最多等待500ms event osSignalWait(0xFF, 500); if(event.status osEventSignal) { if(event.value.signals KEY1_SHORT_PRESS) { // 单次闪烁LED1 blinkLED(LED1, 1); } else if(event.value.signals KEY1_LONG_PRESS) { // 三次快速闪烁LED1 blinkLED(LED1, 3); } else if(event.value.signals KEY_COMBO_EVENT) { // 流水灯效果 ledMarqueeEffect(); } } } }3.2 互斥锁保护共享资源当多个任务需要访问共享的按键状态缓冲区时必须使用互斥锁osMutexDef(keyMutex); osMutexId keyMutexId; typedef struct { uint8_t keyStates; uint32_t pressDuration[4]; } KeyStatus; KeyStatus sharedKeyStatus; void KeyReadTask(void const * argument) { keyMutexId osMutexCreate(osMutex(keyMutex)); for(;;) { if(osMutexWait(keyMutexId, 100) osOK) { // 安全读取按键状态 uint8_t states sharedKeyStatus.keyStates; osMutexRelease(keyMutexId); // 处理按键状态... } osDelay(10); } }3.3 信号量实现资源管理信号量非常适合限制同时执行的按键动作数量osSemaphoreDef(actionSem); osSemaphoreId actionSemId; void initActionSlots() { // 最多允许3个按键动作同时执行 actionSemId osSemaphoreCreate(osSemaphore(actionSem), 3); } void executeAction(uint8_t actionType) { if(osSemaphoreWait(actionSemId, 200) osOK) { // 执行耗时动作... playSoundEffect(actionType); osSemaphoreRelease(actionSemId); } }4. 高级功能实现与优化4.1 按键配置表驱动设计为了提高系统的可配置性我们采用表驱动方式管理按键行为typedef struct { uint8_t keyMask; uint32_t shortPressEvent; uint32_t longPressEvent; void (*shortPressAction)(void); void (*longPressAction)(void); } KeyConfig; const KeyConfig keyConfigTable[] { {KEY1_MASK, KEY1_SHORT_EVENT, KEY1_LONG_EVENT, actionVolumeUp, actionPowerOff}, {KEY2_MASK, KEY2_SHORT_EVENT, KEY2_LONG_EVENT, actionVolumeDown, actionMute}, // ...更多按键配置 }; void handleKeyEvent(uint8_t keyIndex, bool isLongPress) { if(keyIndex sizeof(keyConfigTable)/sizeof(KeyConfig)) { if(isLongPress keyConfigTable[keyIndex].longPressAction) { keyConfigTable[keyIndex].longPressAction(); } else if(!isLongPress keyConfigTable[keyIndex].shortPressAction) { keyConfigTable[keyIndex].shortPressAction(); } } }4.2 低功耗优化技巧在电池供电设备中按键扫描任务可以动态调整运行频率void KeyScanTask(void const * argument) { uint32_t lastActivityTime osKernelSysTick(); uint8_t sleepLevel 0; for(;;) { bool keyActive scanKeys(); if(keyActive) { lastActivityTime osKernelSysTick(); sleepLevel 0; osDelay(1); // 活跃时快速扫描 } else { sleepLevel (sleepLevel 5) ? sleepLevel 1 : 5; osDelay(1 sleepLevel); // 休眠时逐渐降低扫描频率 } } }4.3 按键日志与调试技巧添加按键事件日志功能方便调试复杂问题#define KEY_LOG_SIZE 32 typedef struct { uint32_t timestamp; uint8_t keyState; uint8_t eventType; } KeyLogEntry; KeyLogEntry keyLog[KEY_LOG_SIZE]; uint8_t logIndex 0; osMutexDef(logMutex); osMutexId logMutexId; void logKeyEvent(uint8_t keyState, uint8_t eventType) { if(osMutexWait(logMutexId, 50) osOK) { keyLog[logIndex].timestamp osKernelSysTick(); keyLog[logIndex].keyState keyState; keyLog[logIndex].eventType eventType; logIndex (logIndex 1) % KEY_LOG_SIZE; osMutexRelease(logMutexId); } }5. 常见问题与解决方案在实际项目中我们可能会遇到各种按键相关的问题。以下是几个典型场景及其解决方案问题1按键响应延迟或不灵敏可能原因任务优先级设置不合理按键扫描任务被高优先级任务阻塞系统负载过高导致任务调度延迟消抖时间设置过长解决方案// 调整任务优先级 osThreadDef(keyTask, KeyScanTask, osPriorityAboveNormal, 0, 128); // 优化消抖算法 #define DYNAMIC_DEBOUNCE_TIME(key) \ (key.lastStableState KEY_RELEASED ? 10 : 5) // 按下消抖10ms释放消抖5ms问题2长按事件误触发可能原因长按检测阈值设置不合理没有考虑按键抖动对长按检测的影响解决方案// 改进长按检测逻辑 if(keyState KEY_STATE_PRESSED) { uint32_t stableTime osKernelSysTick() - pressStartTime; if(stableTime LONG_PRESS_THRESHOLD !isBouncing()) { // 添加抖动检测 triggerLongPressEvent(); } }问题3多任务同时处理按键导致系统卡顿可能原因多个任务直接读取按键状态导致资源竞争没有限制按键动作的并发执行数量解决方案// 使用消息队列统一分发按键事件 osMessageQDef(keyQueue, 10, KeyEvent); osMessageQId keyQueueId; void KeyDispatchTask(void const * argument) { KeyEvent event; for(;;) { if(osMessageGet(keyQueueId, event, 100) osOK) { // 根据事件类型分发给不同任务 if(event.type SHORT_PRESS) { osSignalSet(shortPressTask, EVENT_MASK); } // ...其他事件分发 } } }6. 性能测试与优化建议一个健壮的按键系统需要经过严格的测试。以下是关键的测试场景和优化指标测试用例设计快速连续按键测试以100ms间隔快速按键验证系统是否能准确记录每次按键预期结果无丢失按键无重复触发长按边界测试在长按阈值附近如设置1秒长按测试0.9-1.1秒范围预期结果能稳定区分短按和长按组合键时序测试两个按键按下时间差从0ms到100ms变化预期结果能正确识别组合键性能指标测量指标测量方法优化目标按键扫描延迟从物理按键变化到任务响应的最大时间5ms事件处理吞吐量每秒能处理的按键事件数量100 events/s内存占用测量按键相关数据结构的内存使用512 bytes优化建议关键路径优化// 使用内联函数减少函数调用开销 __inline void updateKeyState(KeyState* state) { // 快速状态更新逻辑 }内存访问优化// 将频繁访问的按键状态放入快速内存区域 #pragma location .fastram volatile uint8_t keyStatusFlags;任务调度优化// 调整任务优先级确保实时性 osThreadDef(keyTask, KeyScanTask, osPriorityRealtime, 0, 128); osThreadDef(appTask, AppTask, osPriorityNormal, 0, 256);7. 扩展功能与进阶设计对于更复杂的应用场景可以考虑实现以下高级功能7.1 按键宏与自定义脚本typedef struct { uint8_t keySequence[10]; // 按键序列 uint16_t timeIntervals[9]; // 按键间隔时间 void (*macroAction)(void); // 宏命令对应的动作 } KeyMacro; void executeKeyMacro(uint8_t macroIndex) { if(macroIndex MAX_MACROS) { KeyMacro* macro keyMacros[macroIndex]; for(int i 0; i macro.length; i) { simulateKeyPress(macro.keySequence[i]); osDelay(macro.timeIntervals[i]); } } }7.2 自适应按键灵敏度typedef struct { uint16_t debounceTime; uint16_t longPressTime; uint8_t sensitivity; // 0-100 } KeyProfile; void adaptKeySensitivity(uint8_t usagePattern) { // 根据使用习惯动态调整按键参数 if(usagePattern 80) { // 频繁使用 currentProfile.debounceTime 5; currentProfile.longPressTime 800; } else { currentProfile.debounceTime 10; currentProfile.longPressTime 1000; } }7.3 按键固件在线升级void handleKeyFirmwareUpdate(uint8_t* newFirmware, uint32_t size) { if(verifyFirmware(newFirmware, size)) { osMutexWait(flashMutexId, osWaitForever); flashErase(KEY_FIRMWARE_SECTION); flashProgram(KEY_FIRMWARE_SECTION, newFirmware, size); osMutexRelease(flashMutexId); // 重置按键控制器 resetKeyProcessor(); } }在实际项目中我发现最实用的技巧是将按键处理逻辑模块化通过良好的接口设计隔离硬件相关部分。这样当需要更换硬件平台时只需重写底层的GPIO操作部分而上层的按键事件检测和处理逻辑可以完全复用。

相关文章:

从按键消抖到多任务通信:手把手教你用STM32CubeMX和FreeRTOS搭建一个‘智能’按键响应系统

从按键消抖到多任务通信:手把手教你用STM32CubeMX和FreeRTOS搭建一个‘智能’按键响应系统 在嵌入式开发中,按键处理看似简单,实则暗藏玄机。当你的项目从简单的单任务裸机系统升级到多任务实时操作系统时,按键处理会面临全新的挑…...

Chandra OCR效果对比:领先GPT-4o,实测识别精度展示

Chandra OCR效果对比:领先GPT-4o,实测识别精度展示 1. 为什么选择Chandra OCR:布局感知的革命性突破 在文档数字化领域,传统OCR技术长期面临一个核心痛点:它们只能识别文字内容,却丢失了文档的排版结构信…...

OFA模型企业级部署方案:基于Docker和Kubernetes的高可用架构

OFA模型企业级部署方案:基于Docker和Kubernetes的高可用架构 1. 引言 想象一下这样的场景:你的电商平台每天需要处理数百万张商品图片和对应的英文描述,人工审核图文一致性几乎是不可能完成的任务。这时候,OFA(One-F…...

XUnity.AutoTranslator技术深度解析:Unity游戏实时翻译引擎的架构设计与实现原理

XUnity.AutoTranslator技术深度解析:Unity游戏实时翻译引擎的架构设计与实现原理 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator是一款基于运行时hook技术的Unity游戏实…...

百度网盘提取码智能获取:3秒解锁资源的完整指南

百度网盘提取码智能获取:3秒解锁资源的完整指南 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘分享链接的提取码而烦恼吗?每次遇到需要密码的资源,都要花费大量时间在各种网…...

Python的__init__方法调用父类初始化与多重继承中的参数传递问题

Python作为一门灵活且强大的编程语言,其面向对象特性中的继承机制尤为关键。其中,__init__方法的初始化调用以及多重继承中的参数传递问题,常常让开发者感到困惑。理解这些细节不仅能避免潜在的错误,还能提升代码的可维护性。本文…...

别再只跑demo了!用Python实战CWRU轴承数据集,从数据清洗到模型部署的完整避坑指南

从数据到部署:Python实战CWRU轴承故障诊断全流程指南 如果你已经跑过几个轴承故障诊断的demo,却对如何将CWRU数据集真正应用到自己的项目中感到迷茫,这篇文章正是为你准备的。我们将从原始数据解压开始,一步步构建一个健壮的数据处…...

毫米波雷达中CAPON算法的性能优化与实现

1. CAPON算法在毫米波雷达中的核心原理 第一次接触CAPON算法时,我也被那些数学公式吓到过。但后来发现,它的核心思想其实特别像我们用手电筒找东西——普通DBF算法就像打开手电筒直接照射,而CAPON算法则是能自动调节光圈,让想找的…...

Open Images数据集工具包完全指南:分类器、下载器与瓶颈计算深度剖析

Open Images数据集工具包完全指南:分类器、下载器与瓶颈计算深度剖析 【免费下载链接】dataset The Open Images dataset 项目地址: https://gitcode.com/gh_mirrors/dat/dataset Open Images数据集是一个广泛使用的计算机视觉资源,包含数百万张带…...

通义千问2.5-7B-Instruct部署优化:量化模型仅4GB显存占用

通义千问2.5-7B-Instruct部署优化:量化模型仅4GB显存占用 1. 引言 在本地部署大语言模型时,显存占用一直是开发者面临的主要挑战之一。传统70亿参数模型通常需要12GB以上显存,而通义千问2.5-7B-Instruct通过量化技术实现了突破性优化&#…...

Multibit技术解析:从低功耗设计到面积优化的实践指南

1. Multibit技术入门:为什么我们需要它? 第一次接触Multibit技术时,我和很多工程师一样充满疑问:为什么要在设计中引入这种看似复杂的结构?直到在实际项目中遇到面积和功耗的双重挑战,才真正体会到它的价值…...

文墨共鸣模型自动化作业批改应用:针对编程与文本作业的智能评估

文墨共鸣模型自动化作业批改应用:针对编程与文本作业的智能评估 最近和几位当老师的朋友聊天,他们都在感慨,批改作业真是个体力活,尤其是编程作业和文科的问答题。编程题要一行行看逻辑、查错误,文科题要逐字逐句分析…...

GLM-ASR-Nano-2512入门必看:如何微调模型适配垂直领域术语(医疗/法律)

GLM-ASR-Nano-2512入门必看:如何微调模型适配垂直领域术语(医疗/法律) 1. 为什么需要微调语音识别模型 语音识别技术在通用场景下已经相当成熟,但一到专业领域就容易"听不懂话"。想象一下,医生在手术室里说…...

Qwen3.5推理模型应用:打造你的个人学习辅助与解题分析工具

Qwen3.5推理模型应用:打造你的个人学习辅助与解题分析工具 1. 模型介绍与核心能力 Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF是一个专为推理任务优化的轻量级AI模型。基于Qwen3.5-4B架构,通过蒸馏技术强化了其逻辑分析和分步骤解答能力。这…...

从零开始:在树莓派5上部署WuliArt Qwen-Image Turbo生成高清图

从零开始:在树莓派5上部署WuliArt Qwen-Image Turbo生成高清图 1. 项目概述与技术亮点 WuliArt Qwen-Image Turbo是一款专为个人GPU优化的轻量级文生图系统,基于阿里通义千问Qwen-Image-2512模型,融合了Wuli-Art Turbo LoRA微调技术。这个项…...

终极Font Face Observer错误处理指南:从超时检测到优雅降级的完整方案

终极Font Face Observer错误处理指南:从超时检测到优雅降级的完整方案 【免费下载链接】fontfaceobserver Webfont loading. Simple, small, and efficient. 项目地址: https://gitcode.com/gh_mirrors/fo/fontfaceobserver 在现代Web开发中,Web字…...

使用StructBERT构建Reddit社区情感监测系统

使用StructBERT构建Reddit社区情感监测系统 社区讨论中的负面情绪就像煤矿中的金丝雀,及早发现能避免很多潜在问题 1. 引言 你有没有遇到过这样的情况:一个原本活跃的在线社区,突然间讨论氛围变得消极,用户流失严重,等…...

深入CAPL引擎盖下:从‘回调函数’本质理解on事件,告别信号监听的那些坑

深入CAPL引擎盖下:从‘回调函数’本质理解on事件,告别信号监听的那些坑 在CANoe仿真环境中,CAPL脚本的on事件机制就像汽车引擎盖下的精密齿轮组——表面看是简单的语法结构,实则暗藏精妙的事件驱动哲学。许多开发者能熟练编写on m…...

NaViL-9B部署案例解析:上海AI实验室原生多模态模型生产实践

NaViL-9B部署案例解析:原生多模态模型生产实践 1. 平台概述 NaViL-9B是一款原生多模态大语言模型,具备同时处理文本和图像的能力。该模型支持纯文本问答和图片理解两大核心功能,能够实现: 传统文本对话交互图片内容识别与分析图…...

基于Node.js的Qwen3-ForcedAligner-0.6B云服务接口开发

基于Node.js的Qwen3-ForcedAligner-0.6B云服务接口开发 最近在折腾一个音频处理的项目,需要给大量的音频文件生成精确到词级别的时间戳。手动对齐?那简直是噩梦。找了一圈,发现通义千问开源的Qwen3-ForcedAligner-0.6B模型正好能解决这个问题…...

Blueprint —— 蓝图技术指南

目录 一,蓝图函数库 二,蓝图编译器 术语 编译过程 三,向蓝图公开游戏元素 使类可蓝图化 可读和可写属性 可执行和可覆盖函数 四,将C暴露给蓝图 速度 复杂度 范例 创建蓝图 API:提示和技巧 蓝图 是UE4中引…...

STM32开发文档智能检索:Lychee-Rerank助力嵌入式工程师

STM32开发文档智能检索:Lychee-Rerank助力嵌入式工程师 你是不是也遇到过这样的场景?正在调试一个STM32的USART通信,突然想不起来某个中断标志位的具体含义,或者某个库函数的参数该怎么配置。于是,你不得不放下手头的…...

【GESP C++八级考试考点详细解读】

GESP C 八级考试考点详细解读及洛谷练习题单 1. 计数原理(加法原理、乘法原理) 重要性:组合数学基础,用于分解复杂问题为独立事件。常见题型:统计路径数、事件组合可能性、分阶段计数问题。洛谷练习题: [P…...

【Git】TortiseGit设置过滤上传文件

一、Git忽略文件机制概述 Git通过.gitignore文件管理版本控制中的忽略规则,决定哪些文件不应被跟踪和提交。TortoiseGit作为Windows平台常用的Git图形化客户端,提供了便捷的界面操作来配置这些规则。合理设置文件过滤对于保持仓库整洁、避免提交敏感信息…...

Qwen3.5-9B助力VSCode Codex风格编程:个性化AI助手配置指南

Qwen3.5-9B助力VSCode Codex风格编程:个性化AI助手配置指南 1. 为什么选择Qwen3.5-9B作为你的编程助手 如果你是一名开发者,可能已经体验过GitHub Copilot这类AI编程助手的便利。但商业化的解决方案往往存在隐私顾虑、定制化程度低等问题。Qwen3.5-9B作…...

Gartner Magic Quadrant for Data Center Switching 2025 | Gartner 数据中心交换魔力象限 2025

Gartner Magic Quadrant for Data Center Switching 2025 Gartner 魔力象限:数据中心网络交换机 2025 请访问原文链接:https://sysin.org/blog/gartner-magic-quadrant-data-center-switching-2025/ 查看最新版。原创作品,转载请保留出处。…...

Lingbot-Depth-Pretrain-ViTL-14模型精调教程:基于自定义数据集的迁移学习

Lingbot-Depth-Pretrain-ViTL-14模型精调教程:基于自定义数据集的迁移学习 想把手头那个强大的Lingbot深度估计模型,调教得更懂你的专业领域吗?比如,让它从看普通的街景,变成能精准分析医疗影像的层厚,或者…...

小白也能搞定的人脸检测:MogFace本地部署+可视化界面详解

小白也能搞定的人脸检测:MogFace本地部署可视化界面详解 你是不是觉得人脸检测这种技术听起来很高深,需要写很多代码、配置复杂环境才能用?或者你试过一些在线工具,但担心隐私问题,或者觉得效果不够理想? …...

ccmusic-database实战教程:结合plot.py可视化训练曲线与混淆矩阵

ccmusic-database实战教程:结合plot.py可视化训练曲线与混淆矩阵 1. 引言:为什么需要可视化? 当你训练一个音乐流派分类模型时,最让人头疼的是什么?是漫长的等待,还是看着一堆冰冷的数字,却不…...

图形学面试题

仅用于个人学习记录 主要参考乐书和这篇:https://zhuanlan.zhihu.com/p/430541328 还有这个网站:https://learnopengl-cn.github.io/,这个写的真的非常好 数学方面 点乘/点积/内积 ab axbx ayby azbz |ab|cosθ 几何意义:1…...