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

小白学习pid环控制-实现篇

小白学习pid环控制-实现篇

文章目录

  • 小白学习pid环控制-实现篇
    • 一、前言
    • 二、从网页仿真开始认识PID
    • 三、理论转实际
      • 3.1 先看看网页作者的实现
      • 3.2 在Pico中实现CascadePid
      • 3.3 开始调参
    • 四、一些辅助知识
      • 4.1 如何知道电机转动一圈前进多少距离
      • 4.2 为什么我编码器的数值统计出来跟厂家不一样
      • 4.3 我要如何使用PID的输出
    • 五、总结

一、前言

  • 上一篇文章的硬件平台搭建链接: 小白学习pid环控制-概览篇
  • 这个说是实现,只能说让树莓派Pico能够控制电机来回动了,并没有去适配G-Code以及上位机,但有概率一直🕊了还是先写了记录一下

二、从网页仿真开始认识PID

在这里插入图片描述

  • 在线仿真地址: 兰博文/PID-Simulator-Web,详细的内容我觉的可以打开这个链接,并点开引导教程来继续(毕竟纯理论的东西还是挺难讲的),我只能说一说我的大致理解
  • PID是一个闭环控制算法,他的启动只需要设定三个基础参数(先别管),然后你周期性的把一个测量你需要的目标数值,输入到算法中,他会不断地给出一个数值去逼近
    • 不太负责的说法就是,我们只要不断的读取传感器,并调用此算法,就能实现目标
    • 然后可以适用于你想稳定某个变量的时候,就可以使用

请添加图片描述

  • 上图就可以实现对于一个目标(当作目标值)的跟踪了,我们假定给定一些连续G-Code点,电机上的终端执行器就可以在有限的步内靠近目标了(理论上)
  • 当然上面的理论虽然很美好,但是要用在实际上还是比较难的,比如你想控制电机到这个目标,是不是要设定pwm来驱动运动,但设定这些后续强相关参量又没什么参考,我认为多级环就是为了解决这个问题
    • 还记得吗,只要是一个用于稳定物理量的参数就可以尝试使用pid算法
    • 我们可以直观的看到,需要根据的目标是一个物理距离,并且我们希望调整pwm(速度),来让电机自动的靠近目标,这就形成了二级环的基础,三级以后后续环也是同理

请添加图片描述

  • 至此,理论成立。我需要在驱动代码中实现一个两组pid算法,分别去追踪目标,并调整电机转速,然后只要传感器接收到的距离跟目标距离很接近,就停止电机即可

三、理论转实际

3.1 先看看网页作者的实现

  • https://gitee.com/skythinker/pid-simulator-web/blob/master/scripts/pid.js#
  • 直接写总也是懵逼的,我们先看看作者是怎么实现CascadePid的
  • 下列代码中可以清晰的看到,我们将物理的距离作为后面一个pid输出速度的参考,所以代码可以简单的认为是这样子执行的:
    • 当距离太远,我每次调用都输出一个速度的增量,让pwm变大。当距离减小后,速度部分也会跟随着减小(CascadePID),然后用户需要判断不在调用此算法的时机即可(达到目标精度了)
class PID{constructor(p,i,d,mi,mo){this.kp=p;this.ki=i;this.kd=d;this.maxInt=mi;this.maxOut=mo;this.error=0;this.lastError=0;this.integral=0;this.output=0;}limit(value,min,max){return (value<min)?min:(value>max?max:value);}calc(ref,fdb){this.lastError=this.error;this.error=ref-fdb;var pErr=this.error*this.kp;var dErr=(this.error-this.lastError)*this.kd;this.integral+=this.ki*this.error;this.integral=this.limit(this.integral,-this.maxInt,this.maxInt);var sumErr=pErr+dErr+this.integral;this.output=this.limit(sumErr,-this.maxOut,this.maxOut);}clear(){this.error=this.lastError=this.integral=this.output=0;}
}class CascadePID{constructor(inParams,outParams){this.inner=new PID(inParams[0],inParams[1],inParams[2],inParams[3],inParams[4]);this.outer=new PID(outParams[0],outParams[1],outParams[2],outParams[3],outParams[4])this.output=0;}calc(outRef,outFdb,inFdb){this.outer.calc(outRef,outFdb);this.inner.calc(this.outer.output,inFdb);this.output=this.inner.output;}clear(){this.inner.clear();this.outer.clear();}
}

3.2 在Pico中实现CascadePid

  • 跟原作者的JavaScript写法没啥区别,只不过拆分成了固定的全局变量以及辅助函数
pid_i = {}
pid_o = {}def init_pid_d(pid_d, p, i, d, mi, mo):pid_d["kp"] = ppid_d["ki"] = ipid_d["kd"] = dpid_d["maxInt"] = mipid_d["maxOut"] = mopid_d["error"] = 0.0pid_d["lastError"] = 0.0pid_d["integral"] = 0.0pid_d["output"] = 0.0def pid_limit(value, min_value, max_value):if value < min_value:return min_valueif value > max_value:return max_valuereturn valuedef pid_calc(pid_d, ref, fdb):pid_d["lastError"] = pid_d["error"]pid_d["error"] = ref - fdbpErr = pid_d["error"] * pid_d["kp"]dErr = (pid_d["error"] - pid_d["lastError"]) * pid_d["kd"]pid_d["integral"] += pid_d["ki"] * pid_d["error"]pid_d["integral"] = pid_limit(pid_d["integral"], -pid_d["maxInt"], pid_d["maxInt"])sumErr = pErr + pid_d["integral"] + dErrpid_d["output"] = pid_limit(sumErr, -pid_d["maxOut"], pid_d["maxOut"])def pid_clear(pid_d):pid_d["error"] = 0.0pid_d["lastError"] = 0.0pid_d["integral"] = 0.0pid_d["output"] = 0.0def cascade_pid_init(value_a, value_b):global pid_iglobal pid_oinit_pid_d(pid_i, value_a[0], value_a[1], value_a[2], value_a[3], value_a[4])init_pid_d(pid_o, value_b[0], value_b[1], value_b[2], value_b[3], value_b[4])def cascade_pid_calc(out_ref, out_fdb, in_fdb):global pid_iglobal pid_opid_calc(pid_o, out_ref, out_fdb)pid_calc(pid_i, pid_o["output"], in_fdb)return pid_i["output"]def cascade_pid_clear():global pid_iglobal pid_opid_clear(pid_o)pid_clear(pid_i)

3.3 开始调参

这一步开始前需要自己根据上一篇博客,实现丝杠距离测量后继续,必要的知识点在第四章有些

  • 和网页上给出的参数没有什么变化,我调整了输出的幅度数值,让他更加贴合我现在的电机运动
  • 比如外层的是6400.0,我希望在距离太远的情况下pwm能自动加速到6400, 并且考虑电机的承载能力,我希望每次算法调用的时候只增加400的pwm(或者最大减小400)
  • 最总的采样波形如下图所示了,得到的跟踪精度是0.01mm,还算不错
  • 至于要做到工业的那种快速定位、重复精度控制啥的,就需要对算法的进一步了解,并搭配其他知识进行了
value_i = [1.0, 0.0, 0.0, 0.0, 400.0]
value_o = [1.0, 0.0, 3.0, 0.0, 6400.0]
cascade_pid_init(value_i, value_o)

请添加图片描述

四、一些辅助知识

4.1 如何知道电机转动一圈前进多少距离

  • 【教程】关于丝杆旋转一周前进的距离
    在这里插入图片描述
  • 螺距:沿螺旋线方向量得的,相邻两螺纹之间的距离。一般指在螺纹螺距中螺纹上相邻两牙在中径线上对应两点间的轴向距离。
  • 导程:是螺纹上任意一点沿同一条螺旋线转一周所移动的轴向距离。
  • 导程(丝杆旋转一周螺母座前进的距离) = 螺距 * 头数
  • 纯理论的知识链接放在上面了,实际上笨办法就是你把东西固定上去,转一圈量一下就好了

4.2 为什么我编码器的数值统计出来跟厂家不一样

  • 编码器四倍频

在这里插入图片描述

  • 跟编码器的信号解码方式有关,我的上一篇博客采用了四倍频解码(一个信号统计四次)用于提高可靠性

4.3 我要如何使用PID的输出

  • 以下就是我使用CascadePID的核心代码,是不是很简单(调参很难)
differ_pwm = cascade_pid_calc(encode_value, dst_value, now_pwm)
now_pwm += differ_pwm

五、总结

  • 整体来说缺少了大量的后续适配操作,但从硬件平台搭建,到此下位机算法的资料搜集以及各种物理参量的转换并实现一定的效果后,真是佩服前人是怎么把这些知识连接起来的
  • 有个机会能从超底层开始学习,真是让人受益匪浅,希望这些文章能对后来者有所帮助
  • 这次的代码就让我卖个关子放VIP资源里了: 小白学习pid环控制-实现篇 核心代码 ,如果有人拿到了不会限制分享
http://www.xdnf.cn/news/17740.html

相关文章:

  • 知名车企门户漏洞或致攻击者远程解锁汽车并窃取数据
  • ENCOPIM, S.L. 参展 AUTO TECH China 2025 广州国际汽车技术展览会
  • SSH浅析
  • 【C#】正则表达式
  • Emscripten 指南:概念与使用
  • 科研人如何挖出SCI级创新选题?
  • [激光原理与应用-253]:理论 - 几何光学 - 变焦镜头的组成原理及图示解析
  • 《算法导论》第 21 章-用于不相交集合的数据结构
  • JavaWeb从入门到精通!第二天!(Servlet)
  • HTTPS服务
  • 小黑课堂计算机一级WPSOffice题库安装包1.44_Win中文_计算机一级考试_安装教程
  • 系统架构设计师备考之架构设计实践知识
  • Kafka跨机房双活方案中MM1与MM2
  • 新型Windows RPC攻击可劫持服务并完全攻陷Active Directory,PoC已公开
  • 开发npm包【详细教程】
  • 测试匠谈 | AI语音合成之大模型性能优化实践
  • 【c++】反向赋值:颠覆传统的数据交互范式
  • HeidiSQL 连接 MySQL 报错 10061
  • 工业相机终极指南:驱动现代智能制造的核心“慧眼”
  • mimiconda+vscode
  • 基恩士FSN10系列数字光纤传感器全新型光纤放大器 FSN11CP
  • 後端開發技術教學(五) 魔術方法、類、序列化
  • Day02——Docker
  • Avalonia实例实战六:控件的拖放
  • 前端面试:promise...then与asnyc ...await
  • 【算法训练营Day23】贪心算法part1
  • 前端工程化:pinia
  • 分享一款基于STC8H8K32U-45I-LQFP48单片机的4路数字量输入输出模块
  • 当AI重塑世界:普通人如何成为“主动进化者”?
  • 第16届蓝桥杯Python青少组_省赛_中/高级组_2025年5月真题