好的选择。当Kp 乘以100 时,我们输入的数值就从1.3 变为了130,没有小数点,NXT-G会喜欢这个数的。
但是不要忘记,要对结果进行转换。当完成P控制部分的计算时,要对结果除以100。还记得我们前面定义P控制器的表达式吗?
Turn= Kp*(error)
我们把Kp乘以100,就意味着计算出的 Turn是其实际值的100倍。在使用Turn的值以前,必须要对它除以100。
因此,我们新的、改进过的巡线P控制器虚拟代码如下:
Kp = 1000 !记住,我们用 Kp*100 ,因此这个Kp实际只有10! offset = 45 ! 初始化其它两个变量 Tp = 50
Loop forever
LightValue = read light sensor ! 当前光电传感器的读值
error = LightValue - offset ! 减去offset(补偿量)计算error(误差)
Turn = Kp * error ! ―比例控制部分‖, 我们希望马达的功率值改变多少? Turn = Turn/100 ! 记住消除Kp中因数100的影响! powerA = Tp + Turn ! A马达的功率值 powerC = Tp - Turn ! C马达的功率值
MOTOR A direction=forward power=powerA! 在马达模块中设置这个功率值 MOTOR C direction=forward power=powerC! 设置另一个马达的功率值 end loop forever ! 结束循环,返回,进行下一次循环
等一下,在第一个版本的P控制器中还有一个“微妙的问题”,是什么呢?
在这个例子中,有这样一个问题:在我们计算马达功率值时(如powerC=Tp-Turn),可能会得到一个负的马达功率值,这意味着马达反向转动,但是NXT-G程序中马达模块的数据接口无法“理解”这个数值。马达的功率值是一个在0到100之间的数值,马达的转动方向是由另外一个不同的输入接口控制的。当功率值为负数时,你需要在程序中设置马达的运转方向。方法如下:
If powerA > 0 ! 马达功率值为正值时 MOTOR A direction=forwardpower=powerA else
powerA = powerA * (-1) ! 马达功率值为负值时,要做这一步运算
MOTOR A direction=reverse power=powerA !此时马达功率值为正值,还需要在控制面板上颠倒马达的转向
end If
马达模块通过数据线接收功率值(powerA对应A马达),在马达的参数设置窗口,用复选框设置方向。
对C马达也需要进行相似的程序代码设置。这样,当计算出的马达功率值为负值时,
就可以正确地控制马达了,P控制器就能够实现“零转弯半径转弯”,机器人可根据需要实现原地转弯。
还有其他的“微妙问题”。如果出现计算出的马达功率大于100的情况怎么办?实际上马达会将功率值认定为100。在P控制器(或PID控制器)中出现这种情况,并不十分太好。我们更希望控制器永远不会让马达做超出能力范围的事。如果计算出的马达功率值比100大不了多少(或比-100小不了多少),机器人运行情况还算OK;如果这个计算出的马达功率值比100大很多(或者比-100小很多),这就意味着控制器正经常性的失去控制能力。你需要考虑一下如何处理这种情况!
P控制器概要
希望你已经对P(比例)控制器有了足够的了解,它还是相当简单的。用传感器测量你想控制的东西,将测量结果转换为error(误差)——对于巡线机器人来说,我们通过减掉黑和白光电传感器读值的平均值来实现,将error (误差)乘以一个叫Kp的比例系数,就得到了系统的修正值。在我们的巡线机器人例子中,我们通过加大/减小马达的功率值来应用这个修正值。这个叫Kp的比例系数要用有根据的推测来确定,并通过反复试验进行调整。
P控制器能够处理很多控制问题,不仅仅是用在乐高机器人巡线上。一般来说,在满足条件的情况下,P控制器都能良好工作。
1. 传感器需要有足够宽的动态范围(不幸的是,我们的巡线机器人却不是这样)
2. 被控制的东西(在我们的例子里是马达)也需要有足够宽的动态范围。每个马达在功率值上的宽动态范围应该很接近(NXT马达在这一方面非常好)。
3. 传感器和被控制的东西必须响应迅速。―迅速‖的意思是―比系统内发生的任何变化都快‖。控制马达时,通常不太可能获得马达的―迅速‖响应,因为马达需要一定的时间来改变功率。就是说机器人的动作要比P控制器的命令滞后,这对P控制器的精确控制,会产生一定的困难。
在控制器中加入“I”:PI控制器
(“I”:会给我们带来什么呢?)
为了提高P控制器的响应速度,我们在表达式中加入一个新的部分——积分,PID中的“I”。积分是高等数学中非常重要的内容,在这里,我们只需要直截了当地使用它。
积分用于计算误差的动态求和。
每次我们读取光电传感器的值,并计算error(误差)时,我们将error(误差)加到一个变量中,这个变量我们称之为integral(积分)。
integral(积分)= integral(积分)+ error(误差)
这个表达式不是普通的数学表达方式,它使用了将一系列数值累加的方法,这个方法在编程中经常使用。在计算机程序里,这个表达式有着和数学不相同的含义。(在本文中,会用文字加重的方式来表明这是编程的方式,而不是普通的数学表达式。)这个“=”是赋值的意思,意味着将它右边的计算结果赋值给左边的那个变量名,就是计算机把原有的integral的值加上error的值,将结果赋值给integral。
接下来,同P的部分一样,我们对integral乘以一个比例常数,这是另一个K。因为这个比例常数与积分部分有关,所以我们称其为Ki。与P比例控制部分相同,我们把integral(积分)乘以一个常量,会得到一个修正值。我们要把这个修正值加到Turn变量中去。
Turn= Kp*(error) + Ki*(integral)
上面就是PI控制器的基本表达式。Turn是对马达的修正,Kp*(error) 是比例控制部分, Ki*(integral)是积分控制部分。
积分控制部分有什么作用呢?如果误差在几次循环中都保持同样的值,积分部分就会越来越大。例如,如果我们读取光电传感器值,计算出error为1,很短时间后,我们再次读取光电传感器值,这一次error为2,第三次的error还是为2,那么此时的integral将是1+2+2=5。Integral为5,但这一步的error只为2。在修正量中,积分部分能产生很大的影响,但通常来说,它需要比较长的时间才能发挥作用。
积分控制的另一个作用是能去除小的误差。在巡线过程中,如果光电传感器非常接近线的边缘,但又不是正好在线的边缘上,那么error会很小,只能产生一个很小的修正量。你可以通过改变比例控制中的 Kp来修正这个小的error,但经常会产生机器人的振荡(来回摇摆)。积分控制部分就可以完美地修正小的误差,因为integral(积分)是对errors(误差)的累加,几个连续的小误差可以使integral(积分)大到足以发生作用。
我们可以把积分控制理解为控制器的\(存储器)。Integral(积分)表现的是error(误差)累积的过程,可以持续向控制器提供修正误差的方法。
有关积分的一些微妙问题
我隐藏了一个小问题(其实也不是小问题,但是我们要把它变成小问题)—— 时间。积分计算的其实是 error×(单位时间) 的总和,单位时间(dT)是我们从上一次读取光电传感器值到这一次读取光电传感器值的时间间隔。integral= integral+ error*(dT)
因此,我们每次向integral 中增加的应该是error×dT。测量机器人的dT是相当容易的事。在每次读取光电传感器值时,我们可以读取计时器的值。如果我们从当前时间中减掉上一次的时间,就得到了从上一次读值起的dT。如果不去测量这个dT,不做乘法计算,是不是会更好一些呢?如果这个dT总是相同值呢?如果我们每一次加入到integral中的部分,其dT值都是相同的,我们就能够把因数dT从 error×(dT)中提取出来,只做求和的运算。 integral= integral+ error
实际上只有当我们需要用integral做另外的运算时,我们才需要去乘以dT。因此我们可以把这个时间因数藏起来。在PI控制器中,积分部分的表达式是Ki ×(integral)×dT,其中Ki是一个需要我们进行微调的系数(就像Kp一样),所以
为什么不用一个新的Ki来代替Ki ×dT这一部分呢?这个新的Ki与原来的是不同的,但是因为我们哪个都不知道,所以用哪一个、叫什么,都没有关系。无论它叫什么、代表什么含义,我们都需要通过反复试验来找到相对准确的值。
所以,只要把所有的时间步进值——dT 设定成相同的(或大致相同的)值,我们就可以从积分控制部分中去除时间因素。
积分具有记忆性
关于integral(积分),还有最后一个要注意的细节。通常情况下,integral(积分)只能趋近于0,我们加入到integral(积分)中的error(误差)值绝大多数都是符号相异的,在integral为0 时,它对控制器是不起任何作用的。例如,在经过几次循环后,error(误差)值为1,2,2,3,2,1,相加后得到integral(积分)为11。在最后一点的 error(误差)仅为1,比在那一点的 integral 要小很多。让integral趋近于0 的方法只有一个,就是加入负的error(误差)来平衡早期加入的正的error(误差),来逐渐减少integral。如,下面几个error(误差)是-2,-2,-3,则integral(积分)会从11降到4,还需要加入更多的负 error(误差),才会使积分降到0。此外,integral(积分)期望error(误差)在正负误差之间均匀分布。
如果巡线机器人在线边缘的左侧,而积分部分累积的误差也在线的左侧,那么积分控制部分不仅要把机器人送回线的边缘,还要使机器人越过线,到线的右侧。如果有大的误差持续一段时间,积分会趋向于无穷。这在包含有积分控制的控制器里,会引起一些问题。当积分部分设法修正的误差很大时,积分的这种倾向会引起超调,我们必须在程序上做一些调整以避免出现问题。解决integral(积分)趋向于无穷问题,有两个常见的解决方案:(1)将integral(积分)置零——每次error(误差)为0,或error(误差)改变符号,就将变量integral(积分)设置为0;(2)当计算一个新的integral时,对累积的integral乘以一个小于1的因数来抑制积分:
integral = (2/3)*integral + error
这样,每次循环会把积分值降低1/3。如果你认为积分控制部分是一个控制器的“memory”(存储器),那么这种抑制会在一段“较长”的时间后强迫它变得健忘起来。
PI控制器的虚拟代码
在控制器中加入积分控制部分,我们需要增加新的变量Ki 和integral。别忘了,为了进行整数运算,我们要把Ks乘以100。
Kp = 1000 ! 记住我们用 Kp*100,因此Kp实际为10 Ki = 100 !记住我们用 Ki*100,因此Ki实际为1 offset = 45 ! 初始化变量 Tp = 50
integral = 0 ! 在这个变量里存储积分值 Loopforever
LightValue = read light sensor ! 当前光电传感器读值
error = LightValue - offset !减去offset(补偿量)计算error(误差) integral = integral + error ! 新增的积分控制部分
Turn = Kp*error + Ki*integral ! “比例控制部分”+“积分控制部分”