资讯动态

基于 OpenMV 的色块追踪与 STM32 双轴舵机云台 PID 跟随实战

发布时间:2026/9/4 7:38:01 来源:尧图企业网站定制
文章目录一、为什么需要闭环视觉追踪二、系统架构与数据流2.1 为什么不让 OpenMV 直接驱动舵机三、OpenMV 端色块识别与坐标解算3.1 摄像头与阈值3.2 找最大色块并解算中心3.3 串口发送二进制帧而非字符串四、通信协议与 STM32 接收4.1 帧格式4.2 用 DMAIDLE 中断零等待接收4.3 状态机解析五、双轴 PID 跟随控制5.1 坐标系与控制量5.2 角度到 PWM 的映射理论对照5.3 PID 调参记录失败路径六、硬件接线七、测试验证7.1 追踪性能目标在视场内匀速横移7.2 不同光照下的识别率7.3 P 控制 vs PID 控制对比八、故障排查九、总结版本备注参考资料摘要智能视觉追踪是嵌入式 AI 的入门必经之路。传统开环舵机云台要么打不准、要么在目标移动时来回抖动根本原因是缺少看到目标→计算偏差→闭环修正的反馈链路。本文以 OpenMV Cam H7 为视觉前端、STM32F103C8T6 为控制核心搭建一套双轴舵机云台OpenMV 负责 LAB 阈值色块识别并解算目标中心坐标通过自定义二进制串口协议发给 STM32STM32 用 DMAIDLE 中断零等待接收运行位置式 PID 分别驱动偏航、俯仰两路舵机对准目标。实测目标在视场内匀速移动时追踪延迟约 110ms、稳态指向误差 1.5°、超调量 5%弱光下识别率仍保持在 82% 以上。全文给出接线表、OpenMV/STM32 双侧工程代码与完整 PID 调参记录。一、为什么需要闭环视觉追踪第一次做视觉小车时我图省事直接把 OpenMV 识别到的色块中心 X 坐标线性映射到舵机角度写完代码满心欢喜通电——结果云台对着目标疯狂抽搐目标一移动就追不上像帕金森病人盯着苍蝇。问题出在开环舵机只是被动地摆到一个由当前坐标算出的角度它既不知道目标相对视野中心偏了多少也无法在目标移动时平滑补偿。一旦坐标被噪声抖动光照变化、色块边缘锯齿舵机就跟着抖。真正可用的追踪必须构成闭环摄像头看到目标 → 算出相对视野中心的偏差 → PID 把偏差收敛到 0 → 舵机带动摄像头重新对准 → 下一帧再测偏差。本文要做的就是这条链路从硬件到算法的完整落地。相关阅读【OpenMVSTM32】PID算法调优与二维云台色块追踪实战 — 看别人踩过的 PID 调参坑能少走很多弯路。本文完整工程代码可在 CSDN 下载频道 获取VIP 免费。二、系统架构与数据流整套系统是一个典型的视觉前端 运动控制双 MCU 分工架构。OpenMV 擅长 Python 图像处理但不擅长实时 PWM 控制STM32 反之二者通过串口解耦LAB阈值色块识别二进制串口帧 UART3DMAIDLE 零等待接收偏差 e160-cx, 120-cyTIMx PWM 50HzTIMx PWM 50Hz带动摄像头转向带动摄像头俯仰摄像头 sensorOpenMV Cam H7解算目标中心 cx,cySTM32F103C8T6 USART1状态机解析坐标位置式 PID ×2轴偏航舵机 Yaw俯仰舵机 Pitch闭环就在最后那两步舵机转动改变摄像头朝向下一帧 OpenMV 看到的目标坐标随之变化偏差被持续修正直到收敛。2.1 为什么不让 OpenMV 直接驱动舵机OpenMV 本身能用pyb.Servo()输出 50Hz PWM但实测有两个坑一是 OpenMV 的 3.3V 引脚驱动舵机时电流倒灌会拉低核心电压导致掉帧二是把控制逻辑塞进图像线程会让帧率从 30fps 掉到 15fps 以下。把看和动分给两个芯片各自跑满性能是这种双 MCU 架构最朴素的理由。三、OpenMV 端色块识别与坐标解算3.1 摄像头与阈值OpenMV 初始化时我踩过第一个坑默认分辨率 RGB565 全分辨率跑识别帧率只有 12fps追踪慢半拍。改成 QVGA320×240后帧率回到 30fps识别精度对色块追踪完全够用。importsensor,image,time,pyb,ustruct sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.QVGA)# 320x240平衡速度与精度sensor.set_auto_gain(False)# 必须关自动增益否则阈值会漂移sensor.set_auto_whitebal(False)# 必须关自动白平衡否则颜色随光变sensor.skip_frames(time2000)# LAB 阈值用 OpenMV IDE 的阈值编辑器在目标环境下实时取不要抄网上的值RED_THRESHOLD(30,70,20,60,0,45)设计决策为什么关掉自动增益/白平衡。这两个智能功能会根据画面整体亮度自动调整导致同一个红色色块在阴天和晴天解算出的 LAB 值完全不同阈值就失效了。色块追踪的前提是颜色空间稳定所以必须手动锁死。3.2 找最大色块并解算中心deffind_target(img):blobsimg.find_blobs([RED_THRESHOLD],pixels_threshold200,area_threshold200,mergeTrue)ifnotblobs:returnNone# 选面积最大的过滤掉小噪点bmax(blobs,keylambdax:x.area())return(b.cx(),b.cy(),b.w(),b.h())坐标以图像左上角为原点中心是 (160, 120)。我们发给 STM32 的是相对中心的偏差而不是绝对坐标——这样 STM32 端的 PID 设定值永远是 0逻辑更干净。3.3 串口发送二进制帧而非字符串uartpyb.UART(3,115200,timeout_char1000)defsend_frame(cx,cy,found):# 偏差归一化到图像中心dxcx-160dycy-120# 帧头 0xAA55 dx(16bit) dy(16bit) 状态 校验和 帧尾 0x0D0Abufbytearray()bufustruct.pack(H,0xAA55)bufustruct.pack(h,dx)# 有符号目标偏左为负bufustruct.pack(h,dy)bufbytearray([1iffoundelse0])checksum(sum(buf[2:])0xFF)bufbytearray([checksum])bufbytearray([0x0D,0x0A])uart.write(buf)设计决策为什么用二进制而不是 “x,y\n” 字符串。我第一版就用的字符串结果目标丢失瞬间坐标跳到 (0,0)云台猛地甩到边界而且字符串解析要用split(,)int()每帧都吃 CPU。二进制帧带独立状态字节和校验和STM32 端状态机收到丢失标志就保持上一角度进入搜索模式数据错一字节也会被校验和挡掉。实测同样 115200 波特率下二进制协议比字符串解析效率提升约 8 倍参考二进制CRC 方案实测结论。四、通信协议与 STM32 接收4.1 帧格式字节内容说明0~10xAA 0x55双字节帧头降低单字节误触发概率2~3dx(int16, little)水平偏差目标偏左为负4~5dy(int16, little)垂直偏差目标偏下为负6found(0/1)目标是否检测到7checksum前 6 字节累加和取低 8 位8~90x0D 0x0A帧尾4.2 用 DMAIDLE 中断零等待接收最初我用轮询 软件串口PA2/PA3在 115200 下丢包率高达 15%云台时不时失忆。改用 USART1 硬件串口 DMA IDLE 中断后丢包归零。IDLE 中断的好处是一帧发完总线空闲即触发不用自己数字节数。// CubeMX: USART1 115200-8-N-1, DMA1_Channel5 Rx, 开启 USART1 全局中断#defineFRAME_LEN10uint8_tg_uart_rx[FRAME_LEN];volatileuint8_tg_frame_ready0;voidMX_USART1_UART_Init(void){// ...HAL 配置省略__HAL_UART_ENABLE_IT(huart1,UART_IT_IDLE);HAL_UART_Receive_DMA(huart1,g_uart_rx,FRAME_LEN);}// IDLE 中断在 stm32f1xx_it.c 的 USART1_IRQHandler 中调用voidUSART1_IDLE_Callback(UART_HandleTypeDef*huart){if(__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE)){__HAL_UART_CLEAR_IDLEFLAG(huart);HAL_UART_DMAStop(huart);uint16_tlenFRAME_LEN-__HAL_DMA_GET_COUNTER(huart-hdmarx);if(lenFRAME_LEN)g_frame_ready1;HAL_UART_Receive_DMA(huart,g_uart_rx,FRAME_LEN);// 重启 DMA}}相关阅读OpenMV与STM32通信及视觉循迹实战 — 里面也用了帧头数据帧尾的固定格式可对照理解。4.3 状态机解析收到整帧后做帧头/校验校验二者任一不符直接丢弃避免脏数据驱动舵机int16_tg_dx0,g_dy0;uint8_tg_found0;voidparse_frame(uint8_t*f){if(f[0]!0xAA||f[1]!0x55)return;if(f[8]!0x0D||f[9]!0x0A)return;uint8_tsum(f[2]f[3]f[4]f[5]f[6])0xFF;if(sum!f[7])return;// 校验失败丢弃g_dx(int16_t)(f[2]|(f[3]8));g_dy(int16_t)(f[4]|(f[5]8));g_foundf[6];}五、双轴 PID 跟随控制5.1 坐标系与控制量图像中心 (160,120) 是设定值目标偏差e_x 160 - cx即-dx、e_y 120 - cy是 PID 输入。PID 输出的是舵机目标角度增量叠加到当前角度上。这里用位置式 PIDtypedefstruct{floatKp,Ki,Kd;floatintegral;floatprev_err;floati_limit;// 积分限幅关键}PID_t;floatpid_calc(PID_t*p,floaterr){p-integralerr;if(p-integralp-i_limit)p-integralp-i_limit;if(p-integral-p-i_limit)p-integral-p-i_limit;floatdererr-p-prev_err;p-prev_errerr;returnp-Kp*errp-Ki*p-integralp-Kd*der;}设计决策为什么用位置式而非增量式。增量式 PID 输出的是本次应该调整多少适合步进电机这种相对运动而舵机本身就是位置伺服我需要的是目标角度绝对值位置式直接输出目标角更直观也方便在目标丢失时冻结当前角度。代价是积分饱和风险所以用i_limit限幅。5.2 角度到 PWM 的映射理论对照舵机标准信号是 50Hz周期 20ms脉宽 0.5ms~2.5ms 对应 0°~180°。STM32 定时器时钟 72MHzPSC71 分频得 1MHz 计数ARR19999 得 20ms 周期。则角度 θ 对应的比较值CCR (0.5 θ/180 × 2.0) ms / 20ms × 20000 500 θ × 11.11角度 θ理论脉宽理论 CCR0°0.5 ms50090°1.5 ms1500180°2.5 ms2500理论 vs 实测的偏差我的两台 SG90 实测机械中点在 92° 附近而非 90°导致云台静止时始终偏 2~3°。这是舵机个体零点误差。解决办法不是改公式而是给俯仰轴单独加一个pitch_bias 2并用 PID 的积分项慢慢吃掉这个静态误差——实测加 I 后 3 秒内静态偏差收敛到 0.5°。5.3 PID 调参记录失败路径调参是最能拉开文章深度的部分我如实记录踩坑只加 PKp0.08舵机转向快但到目标位置来回振荡像刹车太急的车前后晃。稳定时间约 800ms。加 DKd0.017振荡被显著抑制稳定时间从 800ms 缩到 300ms超调减少约 62%。D 本质是按误差变化率提前刹车。⚠️加 IKi0.002但无限幅积分项会持续累积把输出推到舵机限位外舵机啪地撞上机械极限甚至打坏齿轮。务必加i_limit限幅我取 30加上后不再出现。目标移动快时永远慢半拍这是系统固有延时采集识别串口控制 ≈ 110ms。单纯加 P 会让振荡更凶正确做法是适当增大 Kd 用微分预测趋势进阶可上卡尔曼滤波预测目标下一帧位置。相关阅读STM32定时器PWM驱动舵机从原理到代码实现与调试 — 讲清了 PWM 脉宽与角度的换算以及双轴云台协同。六、硬件接线信号OpenMVSTM32电压/类型说明TXP4 (UART3)PA10 (USART1_RX)3.3V TTLOpenMV 发 → STM32 收RXP5 (UART3)PA9 (USART1_TX)3.3V TTL备用指令通道GNDGNDGND—必须共地否则串口乱码Yaw PWM—PA6 (TIM3_CH1)5V 信号偏航舵机Pitch PWM—PA7 (TIM3_CH2)5V 信号俯仰舵机舵机电源—外部 5V 稳压5V/≥1A独立供电⚠️风险提示舵机启动电流可达 500mA~1A绝对不要从 OpenMV 的 3.3V 或 STM32 的 5V 引脚取电否则电流倒灌会拉垮核心电压导致 OpenMV 掉帧甚至复位。务必用独立 5V 稳压模块且信号地与电源地共到一起。PWM 频率务必设为 50Hz误设成 5%~25% 占空比对应错误脉宽区间会直接把舵机卡死在极限位。七、测试验证7.1 追踪性能目标在视场内匀速横移测试环境OpenMV QVGA 30fpsSTM32 控制周期 33msPID 参数 Kp0.08/Ki0.002/Kd0.017。目标速度追踪延迟稳态指向误差超调量慢速5°/s95 ms0.8°2%中速20°/s110 ms1.3°4%快速50°/s140 ms2.6°9%可见速度越快固有延时带来的滞后越大。快速段超调接近 10% 是因为 Kd 不足以预测高速运动后续可加前馈或卡尔曼滤波改善。7.2 不同光照下的识别率光照条件识别率现象强光正午窗边76%反光致阈值漂移需加偏振片正常室内98%稳定追踪弱光阴天台灯84%偶发丢帧靠搜索模式兜底失败路径弱光下第一版直接找不到色块→坐标跳 0→云台抽搐。修复方式是目标丢失时found0STM32 端保持上一角度并启动小范围扫描搜索丢帧期间云台不抽搐重新捕获后无缝接续。7.3 P 控制 vs PID 控制对比控制器稳定时间超调稳态误差纯 P (Kp0.08)800 ms振荡不收敛±3°机械零点PID (加 D/I)300 ms5%0.5°这组对照说明 D 抑制振荡、I 消除静态误差是 PID 三件套价值最直观的证据。八、故障排查1. 舵机只抖不动或卡死在极限位现象上电后舵机剧烈抖动或纹丝不动。最常见原因是 PWM 脉宽范围错设占空比 5%~25% 而非 2.5%~12.5%。排查用示波器量 PA6/PA7 波形确认高电平宽度在 0.5~2.5ms检查 ARR/PSC 是否凑出 20ms 周期。2. 追踪方向反了现象目标向左移云台却向右转。排查误差符号e_x 160 - cx还是cx - 160舵机安装朝向偏航轴正向定义。改一个符号位即可不要同时改两处。3. 坐标跳变、云台抽搐现象目标一丢失云台就猛甩。排查是否发了丢失标志并校验STM32 是否在found0时冻结角度。加上状态字节与校验和后基本消除。4. 串口大量丢包现象追踪时断时续。最常见原因有三用软件串口改硬件 USART1、未共地补一根 GND、波特率两端不一致都设 115200。我实测软串口 115200 丢包 15%换硬件串口后归零。5. 弱光下识别率骤降现象阴天或台灯下频繁丢目标。排查是否关了自动白平衡开着阈值会漂LAB 阈值是否在该光照下重新用 IDE 标定过。建议做 3 档光照各标一组阈值。6. 目标移动快时永远慢半拍现象快速横移追不上。根因是系统固有延时约 110ms。排查先确认帧率是否掉到 30fps 以下掉帧会放大延时再适当增大 Kd 或用运动预测。不要盲目加 P否则振荡更凶。九、总结核心要点回顾视觉追踪必须闭环偏差反馈 PID 收敛开环映射只会抽搐。双 MCU 分工OpenMV 专看、STM32 专动串口二进制协议解耦二者。PID 三件套缺一不可P 给速度、D 抑振荡、I 消静态误差且积分必须限幅。工程细节决定成败舵机独立供电、串口共地、阈值按光照标定。适用边界本文方案适合单色块、视场受限320×240、对成本敏感的教学/竞赛场景。若目标颜色多变或需多目标需要上 AprilTag 或轻量神经网络。局限性与已知问题无深度信息纯靠二维偏差无法估计目标距离快速运动下固有延时导致滞后单一 LAB 阈值在强反光环境会失效。扩展方向① 加卡尔曼滤波做目标运动预测改善快速追踪② 多色块时用面积/置信度做优先级队列③ 把色块追踪升级为 AprilTag获得位姿而非仅中心④ 双轴升级为三轴 激光测距做完整指向系统。如需获取本文完整代码和更多实战项目可开通 CSDN 技术会员。版本备注版本备注硬件平台OpenMV Cam H7 STM32F103C8T6 2×SG90 舵机 双轴云台 外部 5V/2A 稳压软件版本OpenMV IDE 4.5.1固件 v4.5.1 STM32CubeIDE 1.16.1 HAL 库 L4/F1 同款 UART/DMA 驱动兼容说明STM32F1/F4 系列 USARTDMAIDLE 写法通用OpenMV Cam M7/H7 均适用若换 MG996R 等大扭矩舵机需将供电提升到 6V 并重新标定脉宽中点。参考资料别再手动调参了用STM32F407OpenMV实现PID自动追踪色块 — 看 F407 方案的关键参数对照表电赛E题实战基于STM32与OpenMV的运动目标视觉追踪系统全解析 — 前后台架构与 33ms 控制周期的工程实践OpenMV 官方文档find_blobs / UART 接口说明STM32 参考手册 RM0008USART 的 IDLE 标志与 DMA 配合机制

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价