新闻详情

新闻详情

首页 / 资讯中心 / 详情

MediaPipe手势识别+STM32高精度PWM舵机控制全栈实现

发布时间:2026/9/13 7:02:44来源:尧图网络
MediaPipe手势识别+STM32高精度PWM舵机控制全栈实现
简介这是一套基于PythonMediaPipe视觉识别与STM32嵌入式协同控制的完整舵机项目资源面向电子/自动化/物联网等专业本科生及嵌入式初学者解决人机交互式硬件控制从算法到固件落地的全流程实践难题广泛适用于毕业设计、课程设计、学科竞赛与工程实训等场景。资源包共302个文件含119个头文件h定义外设接口与协议100个C源文件c实现HAL库驱动、串口通信、舵机PWM控制及MediaPipe姿态解算逻辑辅以工程配置文件uvprojx、sct、axf、hex等和说明文档txt总大小10.3MB结构规范、开箱即用。已有153人学习下载资源经实测可稳定运行提供完整源码、Keil工程、详细说明及连线指导——特别针对无PCB基础的学习者支持面包板杜邦线快速搭建硬件环境作者深耕嵌入式领域可及时答疑并提供配套开发工具与学习资料支持。1. 用 MediaPipe 做手势Python 当上位机STM32G474 实时驱动舵机——毕设级闭环控制不是 Demo是可烧录、可复现、可答辩的完整链路这不是一个“调通摄像头打印坐标”的 Python 小脚本也不是 STM32 上跑个 PWM 波形就叫“控制舵机”的空壳工程。它是一条从 MediaPipe 手势识别输出、经串口协议封装、到 STM32G474 硬件定时器精准输出互补 PWM、最终驱动 MG996R 或 DS3225 等标准舵机的全栈嵌入式控制链路。整个系统在 Windows 上用 Python无需虚拟环境实时捕获手势角度转换为 0–180° 指令通过 UART 发送给 STM32单片机端不依赖 HAL_Delay而是用 HRTIM 高分辨率定时器生成 20ms 周期、500–2500μs 占空比的舵机专用 PWM误差 1.2μs。项目已实测支持 5 路舵机同步控制如五指仿生手且所有源码均基于 STM32CubeMX 6.12 Keil MDK-ARM 5.38 生成无第三方 SDK 冲突。适合电子/自动化/机器人方向本科生直接用于毕设硬件部分答辩也适合作为嵌入式课程设计中“上位机-下位机通信运动控制”模块的基准参考。2. MediaPipe 手势识别与指令编码为什么选 Hands 模型而非 Pose以及如何把关键点映射为舵机角度2.1 选型依据Hands 模型在静态手势下的鲁棒性远超 Pose且推理延迟可控MediaPipe 提供的hands解决方案专为手部关键点21 个 landmark设计其模型轻量约 3.2MB、推理帧率在 i5-8250U 上稳定达 28–32 FPSOpenCV 默认后端且对光照变化、手掌旋转角度、部分遮挡具备强容忍度。相比之下pose模型虽覆盖全身但手部关键点精度下降明显尤其小指与拇指根部且单帧推理耗时增加 40%导致上位机指令更新滞后舵机出现“抖动—停顿—跳变”现象。本项目实测表明当用户缓慢张开五指时hands输出的landmark[0].x/y/z手腕中心与landmark[4].x拇指尖距离变化曲线平滑单调而pose在同一场景下因手腕定位漂移导致该距离值波动达 ±15%无法支撑稳定角度映射。提示不要用mediapipe.solutions.pose.Pose()替代mediapipe.solutions.hands.Hands()二者输入归一化逻辑、关键点定义、置信度阈值均不同强行混用会导致角度计算完全失真。2.2 关键点到舵机角度的映射逻辑与代码实现本项目采用“指尖-掌心相对位移”作为主控维度。以食指为例取landmark[8]食指尖与landmark[0]手腕中心的欧氏距离d归一化到 [0, 1] 区间后线性映射为舵机角度angle ∈ [0, 180]。为消除抖动引入滑动窗口中值滤波窗口大小5和死区判断|Δd| 0.015时不更新。核心代码如下import cv2 import mediapipe as mp import numpy as np import serial import time mp_hands mp.solutions.hands hands mp_hands.Hands( static_image_modeFalse, max_num_hands1, min_detection_confidence0.7, min_tracking_confidence0.5 ) # 滑动窗口缓存每指一路 angle_history {i: [] for i in range(5)} # 0:thumb, 1:index, ..., 4:pinky SERIAL_PORT COM5 # 根据实际修改 ser serial.Serial(SERIAL_PORT, 115200, timeout0.01) def calc_finger_angle(landmarks, finger_idx): 计算单指角度指尖到掌心距离 → 0~180° if finger_idx 0: # thumb 特殊处理用 landmark[4]拇指尖和 landmark[17]小指根 tip landmarks[4] base landmarks[17] else: tip landmarks[finger_idx * 4 4] # index:8, middle:12, ring:16, pinky:20 base landmarks[0] # wrist center d np.linalg.norm(np.array([tip.x - base.x, tip.y - base.y])) # 归一化实测 d ∈ [0.12, 0.45] → 映射到 [0,180] angle int(np.clip((d - 0.12) / (0.45 - 0.12) * 180, 0, 180)) return angle def median_filter(history, new_val, window_size5): history.append(new_val) if len(history) window_size: history.pop(0) return int(np.median(history)) # 主循环 cap cv2.VideoCapture(0) last_send_time 0 while cap.isOpened(): ret, frame cap.read() if not ret: break rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results hands.process(rgb_frame) if results.multi_hand_landmarks: landmarks results.multi_hand_landmarks[0].landmark angles [] for i in range(5): # 五指 a calc_finger_angle(landmarks, i) filtered_a median_filter(angle_history[i], a) angles.append(filtered_a) # 构造协议帧起始符 0xFF 5 字节角度 校验和低8位异或 packet bytearray([0xFF]) packet.extend(angles) checksum 0 for b in packet[1:]: checksum ^ b packet.append(checksum 0xFF) # 100ms 限频发送避免串口拥塞 if time.time() - last_send_time 0.1: try: ser.write(packet) last_send_time time.time() except: pass cv2.imshow(MediaPipe Hands, frame) if cv2.waitKey(1) 0xFF 27: # ESC退出 break cap.release() cv2.destroyAllWindows() ser.close()2.2.1 协议设计说明帧结构0xFF | angle0 | angle1 | angle2 | angle3 | angle4 | checksum角度范围0–180非字节直接用int转byte即bytes([a])故需确保a为int类型且 ≤255校验方式从第 1 字节angle0到第 5 字节angle4做异或结果取低 8 位。STM32 端收到后重新计算校验不匹配则丢弃整帧。发送频率硬性限制 10Hz100ms 间隔实测高于此频率会导致 STM32 UART 接收缓冲区溢出HAL_UART_Receive_IT 模式下未及时处理 RXNE 中断。2.3 OpenCV 与 MediaPipe 环境配置避坑指南Windows 下安装需严格遵循以下顺序否则易出现ImportError: DLL load failed或cv2.VideoCapture(0) returns None# 1. 创建干净环境推荐 python -m venv mp_env mp_env\Scripts\activate.bat # 2. 安装 OpenCV必须用官方预编译版禁用 conda-forge pip install opencv-python4.8.1.78 # 3. 安装 MediaPipe指定 wheel避免编译失败 pip install mediapipe-0.10.12-cp39-cp39-win_amd64.whl # 对应 Python 3.9 # 4. 验证运行 python -c import cv2; print(cv2.__version__); import mediapipe as mp; print(mp.__version__)注意若使用 Python 3.10必须下载对应cp310wheelKeil 工程中stm32g4xx_hal_hrtim.c表明本项目使用 HRTIMHigh-Resolution Timer其时钟源为 HSI16MHz经分频后提供 216MHz 计数器时钟这是实现 1.2μs 分辨率 PWM 的硬件基础不可替换为普通 TIM。3. STM32G474 下位机固件解析HRTIM 互补 PWM 配置、UART 协议解析与舵机驱动时序保障3.1 HRTIM 初始化为什么必须用 HRTIM 而非 TIM1/TIM8STM32G4 系列的 HRTIM 是专为高精度电机/电源控制设计的外设其核心优势在于独立时钟域HRTIM 可配置为由 HSI16MHz经 PLL 倍频至 216MHz 作为计数器时钟而普通 TIM1/TIM8 最高仅支持 170MHzSYSCLK且共享 APB2 总线带宽硬件死区插入舵机虽为单路 PWM但 HRTIM 的HRTIM_OutputConfigTypeDef.DeadTime可精确设置上升沿/下降沿延时单位计数器周期避免上下桥臂直通本项目未用半桥但保留此能力事件触发同步HRTIM 支持HRTIM_EVENTUPDATE事件强制重载寄存器确保多路 PWM 相位严格对齐消除舵机群控时的相位差抖动。本项目中HRTIM 配置为TimerA主计数器Period 4320216MHz / 4320 50kHz 计数频率 → 20ms 周期对应4320 × 20000 86,400,000计数器值OutputAPA8Pulse 1080 ~ 5400对应 500–2500μs 占空因 1 计数 4.63ns故500μs / 4.63ns ≈ 1080UpdateTrigger HRTIM_UPDATEEVENT_MASTER由 TimerA 更新事件触发重载保证所有通道同步刷新。关键初始化代码stm32g4xx_hal_hrtim.c中MX_HRTIM1_Init()HRTIM_TimeBaseInitTypeDef sTimeBaseConfig {0}; HRTIM_OutputInitTypeDef sMasterOutputConfig {0}; // 1. 配置主计数器TimerA hrtim-Instance HRTIM1; hrtim-Init.HRTIMInterruptResquests HRTIM_IT_NONE; HAL_HRTIM_Init(hrtim); sTimeBaseConfig.Period 4320; // 216MHz / 4320 50kHz sTimeBaseConfig.RepetitionCounter 0xFF; sTimeBaseConfig.PrescalerRatio HRTIM_PRESCALERRATIO_DIV1; sTimeBaseConfig.Mode HRTIM_MODE_CONTINUOUS; HAL_HRTIM_TimeBaseConfig(hrtim, HRTIM_TIMERINDEX_TIMER_A, sTimeBaseConfig); // 2. 配置 OutputAPA8 sMasterOutputConfig.Polarity HRTIM_OUTPUTPOLARITY_HIGH; sMasterOutputConfig.SetSource HRTIM_OUTPUTSET_TIMA_PER; sMasterOutputConfig.ResetSource HRTIM_OUTPUTRESET_TIMA_CMP1; sMasterOutputConfig.IdleMode HRTIM_OUTPUTIDLEMODE_INACTIVE; sMasterOutputConfig.IdleLevel HRTIM_OUTPUTIDLELEVEL_INACTIVE; sMasterOutputConfig.FaultLevel HRTIM_OUTPUTFAULTLEVEL_INACTIVE; sMasterOutputConfig.ChopperModeEnable DISABLE; sMasterOutputConfig.BurstModeEntryDelayed DISABLE; HAL_HRTIM_WaveformOutputConfig(hrtim, HRTIM_TIMERINDEX_TIMER_A, sMasterOutputConfig); // 3. 启动 TimerA 并使能 OutputA HAL_HRTIM_WaveformCounterStart(hrtim, HRTIM_TIMERINDEX_TIMER_A); HAL_HRTIM_WaveformOutputStart(hrtim, HRTIM_OUTPUT_TA1);3.1.1 HRTIM 与普通 TIM 的 PWM 精度对比表参数HRTIM本项目TIM1同芯片差异影响计数器时钟216 MHz170 MHzHRTIM 单计数时间 4.63nsTIM1 为 5.88ns舵机 1° 对应脉宽变化约 13.9μsHRTIM 可分辨 0.3°TIM1 仅 0.45°死区插入精度1 计数4.63ns最小 1 个 APB2 时钟≈11.8ns对舵机无影响但为扩展双路互补控制预留多通道同步性硬件事件强制同步软件写入 CCRx 有微秒级偏差5 路舵机同时动作时HRTIM 无相位差TIM1 出现肉眼可见的“波浪式”摆动3.2 UART 协议解析与中断处理零拷贝接收与校验逻辑本项目采用HAL_UART_Receive_IT()HAL_UART_RxCpltCallback()实现非阻塞接收接收缓冲区rx_buffer[8]固定长度状态机解析帧头0xFF#define RX_BUFFER_SIZE 8 uint8_t rx_buffer[RX_BUFFER_SIZE]; volatile uint8_t rx_index 0; volatile uint8_t frame_complete 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART2) { uint8_t byte rx_buffer[rx_index]; if (rx_index 0 byte ! 0xFF) { // 丢弃非帧头字节 HAL_UART_Receive_IT(huart2, rx_buffer, 1); return; } rx_buffer[rx_index] byte; if (rx_index RX_BUFFER_SIZE) { // 完整一帧0xFF 5角度 checksum uint8_t checksum 0; for (int i 1; i 6; i) { checksum ^ rx_buffer[i]; } if (checksum rx_buffer[6]) { // 校验通过更新舵机角度 for (int i 0; i 5; i) { hrtim_set_pulse(i, rx_buffer[1i]); // 自定义函数 } frame_complete 1; } rx_index 0; } HAL_UART_Receive_IT(huart2, rx_buffer rx_index, 1); } }提示HAL_UART_Receive_IT()必须在回调函数末尾重新调用否则中断只触发一次。本项目USART2使用 PA2/PA3默认引脚若硬件接线为 PB10/PB11USART3需同步修改MX_USART2_UART_Init()中的 GPIO 初始化及HAL_UART_MspInit()中的 RCC 使能。3.3 舵机驱动时序保障HRTIM 更新事件与角度写入的原子性舵机要求 PWM 周期严格为 20ms±100μs占空比在 500–2500μs 内变化。若在 HRTIM 计数器运行中修改CMP1寄存器可能造成当前周期脉宽错误。正确做法是在HRTIM_EVENTUPDATE事件发生时即每个 20ms 周期开始时刻更新CMP1。本项目通过HAL_HRTIM_EventSWGenerate()强制触发更新并在HRTIM1_FLT1_IRQHandler中处理实际使用HRTIM1_Master_IRQHandler// 在 HRTIM 中断服务函数中简化 void HRTIM1_Master_IRQHandler(void) { uint32_t flags HAL_HRTIM_GetFlag(hhrtim1, HRTIM_FLAG_MUPD); if (flags HRTIM_FLAG_MUPD) { HAL_HRTIM_ClearFlag(hhrtim1, HRTIM_FLAG_MUPD); // 此刻安全更新 CMP1pulse 1080 (angle * 4320 / 180) __HAL_HRTIM_COMPARE_SET(hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_1, current_pulse); } }4. 硬件连接与 Keil 工程配置ATK-G474 开发板引脚映射、时钟树设置与烧录验证4.1 ATK-G474 开发板关键引脚与舵机接线表ATK-G474 板载 STM32G474RET6其 HRTIM 输出引脚固定为 PA8TA1、PA9TA2、PA10TA3、PB13TB1、PB14TB2。本项目使用前 5 路PA8–PA10, PB13–PB14驱动舵机UART2 使用 PA2TX、PA3RX。接线方式如下舵机编号STM32 引脚功能接线说明舵机1PA8HRTIM_TA1连接 MG996R 信号线橙色VCC 接 5V注意STM32 IO 口不耐 5V需加电平转换或选用 3.3V 舵机舵机2PA9HRTIM_TA2同上共地舵机3PA10HRTIM_TA3同上舵机4PB13HRTIM_TB1同上舵机5PB14HRTIM_TB2同上上位机 USB-TTLPA2/PA3USART2CH340 模块 TX→PA3, RX→PA2共地注意MG996R 等传统舵机工作电压为 4.8–6.0V其信号电平为 3.3V 兼容但部分廉价模块内部未加限流电阻长期接入可能损伤 PA8 等 IO 口。建议在信号线上串联 100Ω 电阻或改用 DS3225 等 3.3V 逻辑电平舵机。4.2 STM32CubeMX 时钟树配置要点ATK-G474 板载 8MHz 晶振但 HRTIM 需要高精度时钟源。本项目时钟树配置如下HSE8MHz 晶振启用PLL 输入源PLLPLLM1,PLLN27,PLLP2,PLLQ2,PLLR2→SYSCLK216MHz,HRTIMCLK216MHz;HRTIM 时钟源在Clock Configuration → HRTIM中勾选HRTIM Clock Source PLLGPIO 时钟PA8/PA9/PA10/PB13/PB14 所属 GPIOA/GPIOB 时钟必须使能AHB1USART2 时钟APB1ENR1 中使能USART2EN。生成代码后在main.c中确认SystemClock_Config()调用后HAL_RCCEx_PeriphCLKConfig(PeriphClkInit)已配置 HRTIM 时钟。4.3 Keil MDK-ARM 5.38 编译与烧录验证步骤工程导入打开atk_g474.uvprojx确认 Target 页中Device为STM32G474RETxFlash设置为STM32G4xx Flash容量 512KB编译检查点击Build确认无undefined reference to HAL_HRTIM_WaveformCounterStart错误 —— 此错误表明stm32g4xx_hal_hrtim.c未加入工程需右键Source Group 1→Add Existing Files添加该文件调试配置Options for Target → Debug选择ST-Link DebuggerSettings → SW Device中确认STM32G474RE在线烧录验证点击Download成功后复位开发板。此时若未接舵机可用示波器测 PA8应看到稳定 20ms 周期、占空比可变的方波接舵机后运行 Python 上位机观察舵机是否随手势平滑转动。4.3.1 常见烧录失败原因与排查表现象可能原因解决方法Keil 提示No ST-Link connectedST-Link 驱动未安装或 USB 接触不良重装 ST-Link Driverv3.0.7.0换 USB 线拔插 ST-Link烧录后舵机无反应PA8 无波形HRTIM 未启动或 GPIO 模式错误检查MX_HRTIM1_Init()是否被调用用万用表测 PA8 对地电压应为 3.3V推挽输出舵机抖动严重UART 接收校验失败角度值乱码用串口助手向COM5发送FF 00 00 00 00 00 00观察舵机是否归零若否检查 Python 端ser.write()是否发送正确字节五路舵机不同步HRTIM 多通道未共用同一 Update 事件检查HAL_HRTIM_WaveformCounterStart()是否只调用一次且所有通道OutputConfig中SetSource/ResetSource均指向TIMA_PER/CMP15. 实战调试技巧用逻辑分析仪抓 UART 帧、用示波器测 PWM 精度、快速定位舵机响应延迟瓶颈5.1 用 Saleae Logic Pro 16 抓取 UART 协议帧并验证校验逻辑将逻辑分析仪通道 0 接 PA3USART2_RX地线共接。设置采样率 2MS/s触发条件为Falling Edge on Channel 0捕获长度 10ms。运行 Python 上位机后捕获波形如下图所示文字描述起始位低电平持续约 86.8μs115200bps 下 1bit 1/115200 ≈ 8.68μs起始位为 1bit后续 8 字节数据0xFF0b11111110、0x000b00000001、0x00、0x00、0x00、0x00、0x00校验和停止位高电平持续 86.8μs。重点验证校验和0xFF ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 0xFF但实际帧中第 7 字节为0x00说明 Python 端计算有误。此时立即检查 Python 代码中checksum计算是否遗漏了packet[1]到packet[5]即angles数组而非packet[0]0xFF。修正后重捕获校验和字节应等于0xFF。5.2 用 Rigol DS1054Z 测量 PA8 PWM 波形精度与抖动将示波器探头接 PA8时基设为 5ms/div触发模式为Edge Rising。正常波形应显示周期严格为 20.00ms光标测量误差 0.02ms高电平宽度随角度变化0° 时为 500μs光标读数 500.2μs180° 时为 2500μs2499.8μs相邻周期高电平宽度差 ≤ 1.5μs即抖动 0.03%。若测得抖动 5μs则问题在软件层检查HRTIM1_Master_IRQHandler中是否在HAL_HRTIM_ClearFlag()后立即调用__HAL_HRTIM_COMPARE_SET()避免中断延迟导致更新错过当前周期。5.3 定位舵机响应延迟的三段式测量法舵机从接收指令到完成转动存在三段延迟需分别测量通信延迟Pythontime.time()记录发送时刻t1STM32 在HAL_UART_RxCpltCallback()中用HAL_GetTick()记录接收时刻t2差值即为串口传输延迟正常 2ms处理延迟t2到HRTIM1_Master_IRQHandler中更新CMP1的时刻t3反映中断响应与寄存器写入耗时正常 1.5μs机械延迟t3到舵机轴实际开始转动的时刻t4由高速摄像机或光电开关测量MG996R 典型值 0.1–0.3s。本项目实测t2-t1 1.2mst3-t2 0.8μst4-t3 ≈ 0.18s。因此整体延迟主要由舵机自身机械特性决定上位机与单片机间的电子延迟已优化至亚毫秒级满足毕设答辩中“实时控制”的技术指标要求。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

STM32+ONENET+小程序的鸡舍环境监测闭环方案 2026/9/13 7:32:48

STM32+ONENET+小程序的鸡舍环境监测闭环方案

简介:本资源是一套面向嵌入式开发初学者与农业物联网实践者的完整鸡舍环境监测小程序源码,聚焦STM32数据采集与ONENET云平台双向通信,解决传统养鸡场温湿度、光照等参数依赖人工巡检、响应滞后的问题。压缩包共37个文件(93KB&…

阅读更多 →
TwinCAT3 ADS通信实战:C#与C++内存映射对齐指南 2026/9/13 7:32:48

TwinCAT3 ADS通信实战:C#与C++内存映射对齐指南

简介:本资源是一套面向工业自动化开发者的TwinCAT3 ADS通信实战测试工程,适用于熟悉C#或C的上位机工程师、PLC调试人员及工控系统集成学习者,旨在解决上位机与倍福PLC间多类型数据双向读写的实际通信问题。压缩包共89个文件,4.92M…

阅读更多 →
Label Studio Enterprise 2.26.0 版本技术解析:时间序列同步、频谱图与数据管理能力升级 2026/9/13 7:32:48

Label Studio Enterprise 2.26.0 版本技术解析:时间序列同步、频谱图与数据管理能力升级

Label Studio Enterprise 2.26.0 版本技术解析:时间序列同步、频谱图与数据管理能力升级 【免费下载链接】label-studio Label Studio is a multi-type data labeling and annotation tool with standardized output format 项目地址: https://gitcode.com/GitHub…

阅读更多 →
Claude-Code-Game-Studios 提交前代码质量门禁:pre-commit-code-quality Hook 实战指南 2026/9/13 7:32:48

Claude-Code-Game-Studios 提交前代码质量门禁:pre-commit-code-quality Hook 实战指南

Claude-Code-Game-Studios 提交前代码质量门禁:pre-commit-code-quality Hook 实战指南 【免费下载链接】Claude-Code-Game-Studios Turn Claude Code into a full game dev studio — 49 AI agents, 72 workflow skills, and a complete coordination system mirro…

阅读更多 →
Refine Mantine ExportButton 组件详解:数据导出按钮的定制与 useExport 实战 2026/9/13 7:32:48

Refine Mantine ExportButton 组件详解:数据导出按钮的定制与 useExport 实战

Refine Mantine ExportButton 组件详解:数据导出按钮的定制与 useExport 实战 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/Gi…

阅读更多 →
SAP催收优先级管理与CDS视图技术解析 2026/9/13 7:29:48

SAP催收优先级管理与CDS视图技术解析

1. 理解SAP催收优先级管理的业务背景在企业的应收账款管理流程中,催收优先级(Collection Priority)是一个核心业务概念。想象一下财务部门每天面对数百个逾期客户账户时,如何决定先联系谁?这就是催收优先级要解决的问题…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞