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

小白学习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.dtcms.com/a/328423.html

相关文章:

  • 杰里平台7083G 如何支持4M flash
  • 【oracle闪回查询】记录字段短时间被修改的记录
  • MyBatis-Plus核心内容
  • AAC音频编码器技术详解:原理、应用与发展
  • Java数组排序
  • 嵌入式系统分层开发:架构模式与工程实践(四)(状态机的应用和面向对象的编程)
  • redis认识缓存击穿
  • 特征工程--机器学习
  • [ 数据结构 ] 时间和空间复杂度
  • Linux中Apache与Web之虚拟主机配置指南
  • 栈和队列:数据结构中的基础与应用​
  • GaussDB 数据库架构师修炼(十三)安全管理(2)-数据库权限管理
  • 专题:2025城市NOA智能驾驶研究报告|附70+份报告PDF 汇总下载
  • Spring MVC 处理请求的完整流程详解
  • Kubernetes1.28-单Master集群部署
  • 【Vue中key属性的技术分析】
  • 智能装配线cad【8张】三维图+设计说明书
  • 安卓Fragmnet的生命周期
  • 【5】Transformers快速入门:Transformer 是啥?
  • 【接口自动化】-11-接口加密签名 全局设置封装
  • Android领域驱动设计与分层架构实践
  • TF-IDF:信息检索与文本挖掘的统计权重基石
  • 开源生态认证体系介绍
  • 当 GitHub 宕机时,我们如何协作?
  • 机器学习-集成学习(EnsembleLearning)
  • Linux 可执行程序核心知识笔记:ELF、加载、虚拟地址与动态库
  • MLOps(机器学习运维)LLMOps(大语言模型运维)介绍(通过自动化、标准化和协作优化模型的开发、部署、监控和维护流程)
  • Ubuntu与Rocky系统安装Java全指南
  • 【门诊进销存出入库管理系统】佳易王医疗器械零售进销存软件:门诊进销存怎么操作?系统实操教程 #医药系统进销存
  • 湖北手机基站数据分享