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

从OpenMV到执行器:当PID算法开始“调教”舵机

如果到现在还不会驱动舵机——朋友,电赛的元器件清单每年都在对你“明示”,二维云台都快成祖传考题了!补课?现在!立刻!(当然,如果你脸皮够厚,也可以私信骚扰作者,但建议先自罚三杯咖啡熬夜恶补)。

好了,收起你的悔恨泪水,接下来才是硬核环节:STM32如何优雅地“吃掉”OpenMV的串口数据,用PID“驯服”舵机,最后让色块追得像初恋一样死心塌地。从协议解析到算法调参,全程无废话(这句是假的,但代码是真的)。

目标:让你从“能跑就行”进化到“跑得嚣张”。现在,系好安全带,你的代码即将起飞——(如果崩溃了,记得Ctrl+S)。

文章封面

欢迎关注QQ频道:电赛工坊

文章目录

  • 1. 串口数据解析:如何让STM32“听懂”OpenMV的“加密通话”
  • 2. 舵机控制:如何让云台“指哪打哪”(或者疯狂摇头)
  • 3. PID算法:从“帕金森”到“德芙级丝滑”的终极奥义

1. 串口数据解析:如何让STM32“听懂”OpenMV的“加密通话”

OpenMV发来的数据不是随便甩几个字节就能糊弄过去的——帧头、帧尾、校验和,少一个都算“通信事故”。(别问为什么这么严格,问就是被电赛现场的血泪史毒打过。)

为了让大家少走弯路,笔者含泪把帧解析模块解耦成独立.c/.h文件(复用性拉满,夸我!)。如果你的协议类似,直接CV大法好,省下的时间够你多调三次PID(然后发现还是调不好,笑)。

核心代码解析(附赠“人话”注释版)

/*** @brief  解析一帧数据* @param  frame: 帧数据指针* @param  len: 帧长度* @param  blob: 解析结果存储结构体* @retval 解析是否成功(0:失败, 1:成功)*/
u8 Protocol_ParseFrame (u8 *frame, u16 len, BlobData *blob)
{/* 检查最小长度 */if(len < FRAME_MIN_LEN){return 0;}/* 检查帧头 */if(frame[0] != FRAME_HEADER){return 0;}/* 检查帧尾 */if(frame[len-2] != '\r' || frame[len-1] != '\n'){return 0;}/* 提取数据部分(4字节) */u8 *data = &frame[1];if(data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF)   // 无目标情况{blob->has_target = 0;return 1;}/* 校验和检查 */u8 checksum = Protocol_CalChecksum(data, 4);if(checksum != frame[5]){return 0;}/* 解析dx和dy */blob->dx = (short)((data[0] << 8) | data[1]);blob->dy = (short)((data[2] << 8) | data[3]);blob->has_target = 1;return 1;
}

2. 舵机控制:如何让云台“指哪打哪”(或者疯狂摇头)

舵机控制,说白了就是PWM占空比的数字游戏——但如果你连定时器配置都搞不定……(电赛评委的凝视.jpg)。

笔者用的二维云台(某趣科技出品),水平270° + 垂直180°,两个舵机组成“摇头晃脑”二人组。

角度 ↔ 占空比 の 神秘公式

// 180°舵机:角度 → 占空比(0.5ms~2.5ms对应0°~180°)
duty = (angle * 1000) / 180 + 250;  // 250=0.5ms(基准值), 1000=2ms范围// 270°舵机:同理,但分母变成270(数学老师欣慰地笑了)
duty = (angle * 1000) / 270 + 250;  // 注意别让角度超限,否则舵机会“嘎嘣”一声

注:duty的单位是TIM的计数值,具体取决于你的时钟配置(不会算的速翻STM32参考手册第987页,假的)。

PWM操作:一句代码让舵机“扭起来”

初始化定时器(TIMx)和PWM通道后,只需调用库函数修改比较值:

TIM_SetCompare1(TIM2, duty_horizontal); // 水平舵机(TIM2通道1)
TIM_SetCompare2(TIM2, duty_vertical);   // 垂直舵机(TIM2通道2)

警告:

  • 直接TIM_SetCompare可能会让舵机“抽风”,建议渐变角度(比如每次变化≤10°)。
  • 270°舵机别给到angle=271°,否则它会用“齿轮打齿声”抗议你的数学能力。

下一幕:PID算法即将登场——
“当你以为调参是科学,其实全是玄学。”(手动狗头)

3. PID算法:从“帕金森”到“德芙级丝滑”的终极奥义

欢迎来到PID调参现场——这里没有科学,只有玄学、耐心和亿点点运气。你的云台要么优雅追踪,要么抽风摇头,全看这一趴!(友情提示:备好咖啡,调参前深呼吸三次。)

(一)PIDの灵魂拷问:方向别搞反!

在写代码前,先解决哲学问题

水平舵机(dx)

  • dx > 0(色块偏右)→ 舵机该往右转(占空比↑还是↓?)
  • dx < 0(色块偏左)→ 舵机该往左转(占空比?)

垂直舵机(dy):同理,但方向可能相反(取决于云台机械结构)。

验证方法

手动给dx=100,观察舵机转向是否符合预期。如果反向——要么改代码符号,要么改云台安装方向(物理调参法,简单粗暴)。

(二)“稳态误差”的暴击:为什么你的舵机中途摆烂?

笔者血泪史:当误差dx=50时,舵机竟然不动了!原因:

  • P值太小误差×Kp < 舵机死区阈值,输出力不足,舵机:“懒得动了。”
  • 解决方案加大Kp(可能引发震荡),引入Ki(积分项专治“摆烂”,用累积误差逼舵机动起来)

(三)PID代码实现(附“人话”注释)

/*** @brief  PID计算:让误差“社会性死亡”* @param  pid:   PID参数结构体(含Kp/Ki/Kd)* @param  actual: 当前误差(来自OpenMV的dx/dy)* @retval 控制量(直接喂给舵机)*/
short Pid_Calculate(PID_TypeDef *pid, short actual) 
{// 1. 计算当前误差(目标值通常是0,即对准中心)short error = pid->target - actual;  // 2. 【P项】当前误差的即时惩罚(Kp是下手狠度)float p_out = pid->kp * error;// 3. 【I项】历史误差的“秋后算账”(专治稳态误差)pid->integral += error;pid->integral = MAX(MIN(pid->integral, 1000), -1000); // 积分限幅防饱和float i_out = pid->ki * pid->integral;// 4. 【D项】预见未来:抑制过冲(Kd是刹车力度)float d_out = 0;if (pid->kd != 0) {d_out = pid->kd * (error - pid->last_error);pid->last_error = error; // 记录本次误差,下次算微分}// 5. 三路输出合体!(注意限制输出范围)return (short)(p_out + i_out + d_out);
}

关键操作解析

  • 积分限幅(±1000):防止长时间误差累积导致“积分饱和”(比如目标丢失时积分项爆炸)。
  • 微分项条件判断:如果Kd=0则跳过计算,变身PI控制器

调参口诀(默念三遍)

  1. 先Kp,后Ki,最后Kd(别一上来就三个一起调,会疯)。
  2. Kp从0.1开始,逐步加大,直到舵机开始高频抖动(然后回调20%)。
  3. Ki取Kp的1/10~1/100,慢慢加,直到稳态误差消失(但别让系统变“迟钝”)。
  4. Kd谨慎加,一般不超过Kp的1/10,否则系统会“过度紧张”。

经典翻车现场

  • “舵机蹦迪”(震荡严重)→ 降低Kp或增加Kd。
  • “反应迟钝”(跟踪慢)→ 增加Kp或Ki。
  • “抽风式微调”(高频抖动)→ 降低Kd或检查机械结构。
http://www.dtcms.com/a/275688.html

相关文章:

  • 计算机视觉与深度学习 | 基于Matlab的多特征融合可视化指纹识别系统(附完整代码)
  • 迅为RK3588开发板Android13系统super.img的解包和重新组包
  • 【C++】封装红黑树模拟实现set和map
  • GESP2025年6月认证C++三级( 第三部分编程题(2)分糖果)
  • MIG_IP核的时钟系统
  • 《硬件产品经理》第七章:产品开发流程之验证
  • 【6.1.3 漫画分布式锁】
  • 【web站点安全开发】任务1:html基础表单和表格
  • C# 接口(派生成员作为实现)
  • Leaflet面试题及答案(41-60)
  • OneCode 3.0架构深度剖析:工程化模块管理与自治UI系统的设计与实现
  • 20250712-3-Kubernetes 应用程序生命周期管理-服务编排(YAML)及编写技巧_笔记
  • 【Elasticsearch】function_score与rescore
  • 【云端深度学习训练与部署平台】AutoDL连接VSCode运行深度学习项目的全流程
  • Qt窗口:菜单栏
  • 人工智能之数学基础:多元逻辑回归算法的矩阵参数求导
  • 回归(多项式回归)
  • 电商订单数据分析全流程:从数据处理到可视化洞察
  • AI Agents时代,数据分析将彻底被颠覆
  • 数据分析库 Pandas
  • 每日一SQL 【销售分析 III】
  • 【Modern C++ Part10】Prefer-scoped-enum-to-unscoped-enums
  • 开源 python 应用 开发(五)python opencv之目标检测
  • (C++)STL标准库(vector动态数组)(list列表)(set集合)(map键值对)相关对比,基础教程
  • React - createPortal
  • React useState原理解密:从源码到实战
  • Java 泛型 (Generics)
  • bp使用爆破模块破解pikachu的登陆密码
  • 第34次CCF-CSP认证第4题,货物调度
  • 分析与展望