PID:从入门到放弃

发布时间 2023-07-20 18:25:46作者: SymPny

PID:从入门到放弃

前言

​ 前段时间参加了智能小车的比赛,为了方便和快速性,我们采用了四个麦克纳姆轮结构的小车,并用openmv做视觉导航定位。由于这个项目是第一次做,而且没有学长学姐的指导,不过好歹跌跌撞撞的走过来了。现在,横跨在我们面前的是“如何使小车启动后直走”这一个大魔王。为了战胜它,我们决定去寻找传说中的“pid调节器”圣剑……

什么是模拟量

​ 前情回顾:

​ 首先,单片机的一般GPIO口输入输出量为0和1,也就是对应的0V和5V(或3.3V),但是现在的STM32系列的单片机不仅可以输入输出0V和5V,同时也可以输入输出05V之间的电压。但由于05V电压变化范围很小,并且电压值属于一个物理量,这个量并不直观,并且这个量并不能由单片机直接输入输出或处理。我们可以把这个物理量转变为更加直观的数字量,这个数字量不仅可以表现出电压的相对高低,而且这个数字量可以直接给单片机进行数据处理。我们把05这一段区域等分成256等份(这里的256是单片机标准库里所提供的对于模拟量精度的设置,当然用户也可以自己去写一个精度更高的模拟量输出函数),那么05V电压我们就可以用0~255这些整数来表示。这样用一个直观的数字量去表示一个测量量(电压、电流、频率、温度等),这个数字量就是称为模拟量。

​ ————by 李杰大佬

​ 什么是PWM:

​ 我们普通的控制,只需要给其高电平,或者是低电平就可以进行控制了,如果想让其在中间某个值进行控制,就需要使用pwm来控制,也就是快速的对该器件进行开关动作。一个比较常用的pwm控制情景就是,来调节灯的亮度,根据占空比的不同,就可以完成灯亮度的不同控制。pwm的占空比,就是指高电平保持的时间,与该pwm时钟周期时间之比。一般用单片机来产生pwm,有以下几种方法,第一种就是用指令来形成pwm,这种方法可以产生,分辨率很高的pwm,但是这样会影响其他任务的运行,一般使用在比较简单的控制中。另外一种就是用,定时器来产生pwm,还有的单片机本身具有pwm模块功能。

​ ————by 度娘

​ 怎么测量直流电机的转速:

电机测速

什么是PID

​ 要了解什么是PID,我们得先弄清楚为什么要PID。众所周知,我们的电机驱动模型在假设输入量确定(为目标转速),输出量确定(PWM一定)时,系统的传递函数模型可以用一个表达式进行表征(这里看不懂的同学可以翻开控制工程看一下),并且该传递函数只和系统本身的特性有关。就好比输入x,传递函数就是y=2x,输出的自然就是2x了。但是,实际情况下输入量和系统都会有一个干扰量,输入电压也会波动,并且输入输出也并不完全成线性关系,这就导致了我们无法实际准确的表达传递函数,这会导致两点后果:第一,哪怕是同一型号规格,两个不同的电机在同PWM输出的时候转速是不同的;第二,同一个电机同PWM在不同路况下会受到干扰的影响,速度也是波动的。

​ 这就意味着我们用恒定PWM输出控制小车走直线的愿望破灭了。为了改善速度曲线,经典控制理论*惯使用添加串联校正或者反馈校正,但是刚才提到,我们的实际电机模型是未知的,明确的环节校正是比较困难的。我们联想,在数学中对于一条曲线,在某点附*可以用泰勒展开变成用一个多项式表述一段曲线,而在控制中也是类似,只不过这里的“多项式”变成了PID,即:比例环节,积分环节和微分环节(之所以选择这几个环节的原因参考传递函数的数学表达模型,同样参考控制工程)。

​ 在找宝物“pid调节圣器”之前,需要向当地的村民问路是一种常识,百度村的村民们都很热情,提供的资料多种多样,不过因为时间原因,村民们表述不清,个人能力不够等原因,很多资料都是过时的,错误的,甚至无法理解的。作为勇者的我们没有办法,只好拿着钱包抵……抱歉,串台了,只好拿着资料一点点学*对照,终于锁定了若干条有价值的情报:

  1. PID算法讲解

  2. PID控制器

  3. 第八届飞思卡尔智能车北科摄像头一组技术报告

​ 经过简单的情报分析,我们对PID有了一个大概的理解,也坚定了我们使用PID调节圣器打倒魔王的决心。那么接下来的就很清晰了:我们现在对每个电机都有一个霍尔传感器,通过这些传感器我们可以测得电机转速,我们假设经过PID调节后四个轮子转速一样,小车不就可以走直线了吗?自以为找到问题关键的我们踏上了寻找PID调节器的不归路。

​ 不过,为了能尽快的找到PID圣器,我们偷偷开启了勇者金手指,打算看一下有没有游戏攻略。于是我们特地找到度娘,经过百般恳求,终于发现了arduino单片机居然有现成的,封装好的库!这真是太棒了!老师无数次的告诉我们,不要重复的造轮子,但也一定要有造轮子的能力。为了践行老师的要求,我们特别深入PID库源码,辅助着度娘免费送给我们的这条珍贵的经验:使用PID库,轻松搞定PID(上),终于弄清了事情的真相,跃跃欲试只等动手……

PID调试和出现的问题

​ 很早的时候就听闻PID调试是一个难点,不过这并不是我们放弃的理由。决定采用arduino中封装好的PID库后,问题已经变得简单很多了。观察PID源码,需要我们设定的值有:

  • Setpoint:目标值

  • Kp,Ki,Kd: pid参数

  • OutputLimits:输出极限

  • SampleTime:采样时间

​ 一共6个值,Setpoint 相对是最容易理解的,它就是我们想达到的目标值,我们初步设定空载速度为2.00rad/s,尽量慢一点保证摄像头能一次性捕捉到二维码。但是下地跑了一下发现前后运动虽然启动有点困难但是还可以,左右却完全启动不了,排除机械故障和代码问题后,我们认为初始速度过小,左右运动需要速度为4.00rad/s或者以上方可以。

​ 接下来是OutputLimits,是输出的极限范围。我们观察代码公式,得知该库是用的位置式PID。关于两者的区别详见:PID控制及位置式与增量式区别,所以我们输出的范围应该是0-255.

​ SampleTime的确有点不确定,arduino库例程给的倒立摆的例子是5ms,而一些参考文档则说温度等采样时间是1-5s。

​ 我们的第一个问题就是,采样时间如何取呢

​ 我们分别设置采样时间5ms,50ms,100ms和200ms设置对照时间,发现采样时间对PID参数的选取有着比较明显的影响,每换一个都要重新调整PID系数。最后,我们参考电机编码器的采样时间,暂时设置成了100ms。

​ 接下里就是最复杂的PID调整。一般而言PI控制器就已经能很好的实现速度控制,所以整体调整以PI为主。**如何调整PID呢?**网上关于PID调整的技巧很多,还有口诀什么的,但是对于第一次接触调整的我们来说……恩,这个吗。

​ 我们先看一段口诀:

​ 参数整定找最佳, 从小到大顺序查。
​ 先是比例后积分, 最后再把微分加。
​ 曲线振荡很频繁, 比例度盘要放大。
​ 曲线漂浮绕大弯, 比例度盘往小扳。
​ 曲线偏离回复慢, 积分时间往下降。
​ 曲线波动周期长, 积分时间再加长。
​ 曲线振荡频率快, 先把微分降下来。
​ 动差大来波动慢, 微分时间应加长。
​ 理想曲线两个波, 前高后低四比一。
​ 一看二调多分析, 调节质量不会低。

​ 喵喵喵?什么算震荡频繁?什么叫绕大弯,积分系数我明白,积分时间又是什么鬼?什么叫周期长?动差多少算大,0.05rad/s算吗?还有下面的图,请麻烦告诉我你用什么软件做出来的啊?不知道波形我分析个鬼啊?
在这里插入图片描述

​ 好在天无绝人之路,经过一两天的折磨,大致调整思路差不多整理出来了:

  • 工欲善其事必先利其器,先写一个波形分析程序再说

  • 先将I、D两个系数置为0,只调节P,从1开始,向大调整(如果不震荡),直到P出现较大的震荡为止,然后将P回调一点(70%左右)。调整后可能出现静差,属于正常现象

  • 再调整I,从1开始逐渐放大(如果不震荡),观察,当I较小的时候并且存在静态误差时候,会经过很长很长时间速度才会逐渐到达标准值,增大I会减小反应时间,而如果I过大,则会产生明显的超调现象。我们需要在不改变P的情况将I调整到不出现震荡,然后再回调一点。

  • 同时放大P和I到尽量大,同时保证图像不震荡。

  • 如果和震荡周期有关,则调整P,如果和超调及反应速度有关,则调整I(I放大好像就把波拉长了一样)

  • 如果震荡过于频繁,则可少量的加入D使图像稍微平滑一点,但是尽量不要加。微分环节在传递函数中表示超前量,及对当前运动趋势(求导,斜率)进行估计从而提前反应,

    ​ 单一串口波形分析python程序如下,用matplotlib和numpy库:

    import matplotlib.pyplot as plt
    import numpy as np
    

    setpoint = 20
    items = []
    fr = open('data.txt','r')
    points = fr.readlines()
    for point in points:
    # print(point)
    items.append(float(point))

    length = len(items)

    minX = 0
    maxX = len(items)*100
    X = np.arange(minX,maxX,100)

    # 图像中显示
    plt.plot(X, items, color = 'red')
    plt.plot(X, items,'ro', color = 'blue')
    plt.xlabel('Time (s)')
    plt.ylabel('n (rad/s)')
    plt.show

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    ​ 但是!这绝对不是结束,而才刚刚开始……

    PID优化与选择

    ​ 观察整个速度曲线感觉仍然不是很满意,思考一下,问题出在那里呢?

    猜想:是不是启动时候获得的pwm太大,以至于不容易下来出现超调呢?

    实验:如果利用两个轮子的速度差作为输入会不会好一点呢?

    过程:问题出现在了最后输出上,现在我们将两个电机作为一个大的系统,怎么处理最后得到的Output呢?

    方案:输出分别为 pwm[i]+Output 和 pwm[i]-Output

    结果:四个轮子极难调节,放弃该方案

    猜想:启动时,PWM调整初始值为目标值附*会不会提高快速性?

    结果:影响不大。

    猜想:初始值放大,能提高快速性吗?

    结果:影响不大,第一个Output很快会改变初始PWM

    猜想:输入值大小会不会对波动产生影响?比如输入2.00和20.00会有影响吗?

    实验:分别将输入和目标值同时放大1、10、100倍

    结果:PID发生改变,但是性能没有优化太多。

    ​ 到这里我已经连续建立了3个不同的PID模型了,但是连续失败让我突然有了一点疑惑,难道位置PID不适合速度调节吗?我决定先以当前最优的模型来跑一下,于是又突然发现了一个严重的问题:

    ​ **四个轮子从开始到到达目标值附*的波动速率是不一致的。**即,小车在刚开始会出现转向,到最后才会走直线。这完全不符合比赛的要求,我希望它在启动后能直接走直线。这个发现促使我放弃了位置式PID,因为在实际调试中我发现位置式PID的波动很大,我希望能减小这种波动,所以我更换了增量式PID。模仿PID库,我重写了PID类,将其改成了增量式,以后的工作都是在增量式调节的背景下进行的。

    ​ 增量式与位置式的一个不同在于,它是有初始值的。同时,为了解决启动不走直线的问题,我们决定放大初始值的作用,设置一个滤波器,当当前速度与目标速度相差大于常量error后,PID才真正启动。初始值可以用开环调试的方法测出来,保证启动时尽量沿直线启动。而error呢……慢慢试吧。需要调整的参数不过从6个上升为8个而已,没什么大不了的,没什么大……哇的一声哭了出来。

    ​ 或许因为调整的次数比较多,熟练了,增量式的调整明显快了起来。最后1轮的速度曲线可以看出来还不错:

在这里插入图片描述

​ 接下来四个轮子:
在这里插入图片描述

​ 总之,差不多了,可以下地跑一跑了。

​ !!!

​ 发生了什么?为什么它还是跑歪!

​ 仔细复盘中…………………………

猜想:霍尔传感器的精度不够

实验:恒定PWM=100输出,输出转速曲线

结果:
在这里插入图片描述

改进:加入滤波器,并且采用 (double)( (int)转速 ) 的方法降低精度

效果:好了一些,但是还是不行

猜想:是不是因为没有悬挂底盘导致车轮没法完全接触地面导致打滑之类的现象

实验:将小车放入柔软的地面实验

结果:不是这个原因

猜想:传感器精度、安装误差、摩擦角非完全45度导致的累积误差

实验:没法实验

结果:………………

​ 这时候我又看到了放在前面的北京科技大学飞思卡尔智能车摄像头一组的技术报告,发现他们同时使用了速度PID和……方向PID!我们的目标不是控制方向吗?既然速度不行,那么方向可不可以呢?

猜想:用方向PID取代速度PID

实验:仔细研究他们的方向PID,发现一个很绝望的事情,他们的方向控制完全是由转向一个舵机控制的,而麦克纳姆轮呢……抱歉打扰了;并且,方向PID的输入将不会是转速,可以是视觉,也可以是陀螺仪。前者由于由openmv单独处理,和主控通讯进行调控代价太大,而后者……不会,需要学*,临*考研的学*代价更大。

结果:放弃PID

结论与未验证猜想

​ 咳咳,英雄不问出处,既然没有或者PID调节器这个圣器,但是我们还有开环控制这个板砖能用吗!不是说板砖在手,天下我有吗!用开环控制战胜“走直线大魔王“也是可以的吗!

​ 所以,总结一下,历时3个星期的学*和调试,进度为零,经过了从入门、学*、钻研、到放弃的全过程,呼应题目,非常完美。

​ 其实我私以为PID应该还是可以继续调整的,但是时间的确不多了,所以无奈之下选择放弃,到最后,仍然有几个猜想没有试验,留待小学弟们解决了:

  1. 减速比大的电机是不是转速输出更加平稳
  2. 目标速度对速度曲线性能的影响

结语

​ 这可能是我,还有我的小伙伴们,郑鸿和李杰两位大佬们在大学期间参加的最后一场比赛了吧,这次比赛之后我们即将准备考研,然后各奔西东。我们大胆地选择了一个没有人做过的项目,试着挑战一下极限,作为自己的大学里青春的结束。谢谢两位愿意陪我尝试,即使花了大把时间也极有可能拿不到奖项的比赛,也谢谢几位老师的指导和帮助。不管怎么样,祝我们成功吧!

后记

现在再来看这篇文章,真是简单和天真的。简单的讲一下我现在的看法:我们当时做的搬运小车使用的是减速电机, 而减速电机需要PID进行调节的,使用它的原因不在于控制方向,而是平稳速度。因为减速电机不像步进电机一样速度是可控的,它的速度是有上升和波动的,PID的目的在于平稳速度曲线和降低上升的反应时间,不需要同步,甚至说每个轮子的目标PWM都是不一样的才对(小车设计的负载不一样,每个轮子在负载不同的情况下要跑出相同的速度必须要不同的PWM),方向的控制可以用陀螺仪,或者像我之前一样用视觉,甚至可以直接盲走。这个和飞思卡尔方向PID不一样,飞思卡尔智能车转向通过舵机控制,所以可以做方向PID,而对于麦轮驱动的搬运小车而言,其实没有必要,速度PID足够了。
其实比赛的时候更多的人用的是步进电机。步进电机有它的优点,它的速度比较容易控制,但是相比减速电机,它的驱动负载能力相对较弱。如果你的车子又大又重,还是建议减速电机比较合适。减速电机记得一定要选择一个合适的减速比,在速度和稳定性方向做一个平衡。