Blogs

如何构建一个实用的固定点PI控制器:第二部分

杰森萨赫斯 2012年3月24日 2评论

第I部分 我们讨论了离散时间比例 - 积分(PI)控制器周围的一些问题:

  • 各种形式以及是否使用针对z变换的规范形式(不要这样做!)
  • 在整数项中操作顺序:是否缩放,然后集成(我的推荐),或集成,然后缩放。
  • 饱和和抗风

在这一部分中,我们将讨论PI控制器周围的解决方案实现问题。首先让我们回顾概念结构和我的“首选实施”进行浮点。

对于没有饱和度的PID控制器:

本文以PDF格式提供,便于打印

(从图中的图 wikipedia在pid控制器上输入。执行器信号=右侧的求和块的输出=输入到工厂/过程的输入未标记,但对应于x(t)。)

对于具有饱和度的PI控制器,使用浮点算术:

if ((sat < 0 && e < 0) || (sat > 0 && e > 0))
;
/* do nothing if there is saturation, and error is in the same direction;
* if you're careful you can implement as "if (sat*e > 0)"
*/
else
x_integral = x_integral + Ki2*e;
(x_integral,sat) = satlimit(x_integral, x_minimum, x_maximum);
x = limit(Kp*e + x_integral, x_minimum, x_maximum);

/* satlimit(x, min, max) does the following:
* if x is between min and max, return (x,0)
* if x < min, return (min, -1)
* if x > max, return (max, +1)
*
* limit(x, min, max) does the following:
* if x is between min and max, return x
* if x < min, return min
* if x > max, return max
*/

这里 e 是错误,还有 x 是输出,还有 kp. Ki2 是比例和积分的收益,其中ki2 = ki * timestep dt。

固定点基础

在固定点算术中,我们通常使用16位或32位数量(很少8位或64位)来表示工程量。由于物理量有单位,因此您被迫对缩放因子作出决定,该缩放因子与软件中存储的整数相关给物理量。这可能听起来摘要,所以让我们使用一个具体的例子:

假设我有一个12位模数转换器测量从0到3.0V的模拟输入电压,输入电压来自20:1分压器。这变得相当简单:ADC上的0计数代表0V和4096计数(嗯,真的4095计数但现在忽略)表示60V。这是一个缩放因子为14.65mV / count。

在固定点算术中,出于概念目的,我们经常想象缩放2的二进制点(类似于小数点)Q 对于一些数字问答。在上面的示例中,Q = 12的选择是方便:4096 Counts = 2 12 * 1.0表示某种全量程ADC缩放因子的1.0倍,在这种情况下是60V。该“Q”号码用于指的是定点表示:Q12意味着浮点数缩放为212 要表示为整数,以及工程单位的进一步缩放因子。该工程编码的完整规范是60V Q12。要从整数转换为工程值,我们将划分4096并乘以60V;要从工程值转换,我们将60V分为60V并将4096乘以到最接近的整数。

如果我测量了一些电压V1,并且我想乘以增益k,其中k = 1.237,我也可以通过Q12号代表k。 1.237 * 4096 =约5067,因此5067计数代表Q12编码中的1.237。

一个浮点示例(以及这里我将使用{}表示浮点数):{v1} = 38.2V,{k} = 1.237,{v2} = {v1} * {k} = 47.25 V.很简单的。

要在REDIOP点中执行此操作,我们将启动V1 = {V1} / 60V * 4096 = 2608计数,并且k = {k} * 4096 = 5067计数。

为了获得{v2} = {v1} * {k},我们替换:

{v2} = v2 * 60v / 4096
{v1} = v1 * 60v / 4096
{k} = k / 4096

V2 * 60V / 4096 =(V1 * 60V / 4096)*(k / 4096)

并简化:

v2 = v1 * k / 4096

对于我们的示例,V1 = 2608计数和K = 5067计数,因此V2 = V1 * K / 4096 = 3226计数。让我们看看这是有意义的,当我们翻回到工程单位时:

{V2} = V2 * 60/4096 = 3226 * 60/4096 = 47.26V

由此产生的计算几乎匹配浮点案例;差异是由于定点的有限分辨率。

一个重要的备注:请在固定点数学中理解,除非我们需要将它们转换为人类的有意义数字,否则浮点数{v1} = 38.2v,{v2} = 47.26v和{k} = 1.237或其他数据的消费者;相反,我们只处理V1 = 2608,V2 = 3226,k = 5067的固定点数量。

这种乘法的概括

{c} = {a} * {b}

有定点表示

{a} = a * a_u / 2QA
{b} = b * b_u / 2QB
{C} = C * C_U / 2QC

可以简化如下。注意Q数字可能都不同,因此工程单位缩放因子A_U,B_U,C_U。

C * C_U / 2 QC = (A * A_U / 2QA )*(b * b_u / 2 QB)

c = a * b *(a_u * b_u / c_u)/ 2 (Q. A + QB - QC)

我们通常选择单位因素与两者的力量相关,以便净计算

c =(a * b) >> k

可以执行,其中整数k = qA + QB - QC + log2 (A_U*B_U/C_U)

你如何选择Q号,单位缩放因子和整数位大小?

这些是定点系统设计中的非常重要的步骤。您需要看看的是解决问题和溢出问题。溢出涉及可以由整数数表示的最小值和最大值,而分辨率涉及最小可能的增量值。

如果我具有表示具有单元60V Q12的电压的无符号16位整数,则最小电压为0,每个计数为60V / 4096 = 14.65mV /计数,最大电压为65535 * 60V / 409V = 959.99V。

我们可以为Q12表示选择更大的缩放因子,并且它将提高每个计数的最大电压和电压。

如果选择太大的缩放因子,则不会有您需要的分辨率。如果选择太小的缩放因子,数字范围的“天花板”将太低,您将无法表示足够大的数量。

请意识到这两种数量(Q编号和单位缩放因子)对于相同的表示不是唯一的:60V Q12 = 15V Q10 = 960V Q15:在所有三种情况下,整数2608表示38.2V。因此,选择是否调用某个表示Q12或Q10或Q15或其他东西是真正的任意的,它只是强制了不同的工程单位缩放因子。

如果在不同情况下遇到问题(分辨率和溢出差),这意味着您需要使用更多数量的位来存储工程量。

我的拇指规则是,16位整数几乎总是足以存储大多数ADC读数,输出值和增益和偏移参数。 32位整数几乎总是足以实现集成商和中间计算。已经有时我需要48位甚至64位中间存储值,但除了在宽动态范围的情况下,这很少见。

如果您在14到16位之间有ADC,则可以使用16位整数来存储RAW阅读,但您可能仍希望使用32位存储来处理ADC的增益/偏移校准 - 否则略微偏离1(例如1.05或0.95)的增益,您可能会在试图将结果存储在16位数字中的低位比特中的问题 - 如果原始ADC计数增加一个,有时缩放结果增加1计数,有时达到0或2个计数。这是一个解决方案问题,Cure是使用额外的比特来最小化量化错误而不耗尽溢出。

对于PI控制器输入和输出,显而易见的选择是选择缩放因子,使整数溢出范围对应于最大输入或输出值,但有时这效果很好,有时它不会很好。

返回PI控制器在固定点

好的,准备在固定点实现PI控制器?开始:

int16 x, sat;
int32 x_integral, p_term;
int16 Kp, Ki2;
const int16 N = [controls proportional gain scaling: see discussion later]
int16 x_min16, x_max16;
const int32 nmin = -(1 << (15+N));
const int32 nmax = (1 << (15+N)) - 1;

/* ... other code skipped ... */

int16 e = u - y;
if ((sat < 0 && e < 0) || (sat > 0 && e > 0))
 ;
/* do nothing if there is saturation, and error is in the same direction;
 * if you're careful you can implement as "if (sat*e > 0)"
 */
else
 x_integral = x_integral + (int32)Ki2*e;
const int32 x_min32 = ((int32)x_min16) << 16;
const int32 x_max32 = ((int32)x_max16) << 16;
(x_integral,sat) = satlimit(x_integral, x_min32, x_max32);
p_term = limit((int32)Kp*e, nmin, nmax);
x = limit((p_term >> N) + (x_integral >> 16), x_min16, x_max16);

/* satlimit(x, min, max) does the following:
 * if x is between min and max, return (x,0)
 * if x < min, return (min, -1)
 * if x > max, return (max, +1)
 *
 * limit(x, min, max) does the following:
 * if x is between min and max, return x
 * if x < min, return min
 * if x > max, return max
 */

这里有几个微妙之处。

集成商缩放

在我上面给出的实现中,Integrator是32位值,其中积分器65536 = 2 ^ 16计数等于输出的1计数。或者换句话说,积分器的底部16位是额外的分辨率以累计随时间的增加,并且积分器的前16位是积分器的输出。

您需要更多位于Integrator状态变量中的比特,而不是常规输入/输出,以处理小时间戳的效果。对于低通滤波器状态变量,这也是如此。您想要的是最大的预期积分增益,不会导致溢出,最小的预期积分增益做一些有用的东西。

如果Ki2非常小(例如1计数),积分器需要一段时间才能累积错误,但您不会丢失任何分辨率。如果我们将积分器实现为16位值

 x_integral = x_integral + (Ki2*e) >> 16;

然后,对于积分增益的低值,小错误值刚刚消失,切勿在积分器中累积。

“您需要更多位于Integrator State变量”规则的推论,是您永远不应该以比合理更慢或更快的速率执行PI循环。如果您慢慢运行PI循环,则循环的带宽和/或稳定性将受到影响。这非常简单。但是,如果你太快运行PI循环,那么问题也是如下:整合了100,000次每秒的集成商必须集成较少的数量,而不是整合每秒1000次的集成商。执行Integrator或Filter的速率越快,状态变量所需的分辨率就越多。如果您正在执行少于预期带宽的5倍的控制循环,那太慢,如果您正在执行您的控件循环超过预期带宽的500倍,那可能太快了。

因此,处理数值问题的一个解决方案是以较慢的速率运行控制循环 - 只需确保您过滤输入,以便不符合别名问题。

整合对照的比例与比例 - 然后整合辩论,又重要

当我提出何时应用积分增益时(集成的-Tem-deal-Degressatibe)的何时应用程序时,我说有5个理由更喜欢缩放 - 然后集成。这是#5:

缩放然后集成是一种非常自然的方式来挑选定点缩放因子。选择输入误差的缩放因子和PI控制器输出的缩放因子后,它非常自然地汇集在一起​​,积分器通常只有在上面讨论的那样使用额外的16位积分器分辨率。

如果您集成了然后缩放,则在此奇怪的中间缩放因子中测量集成器,您必须处理两次分辨率和溢出:一次在集成器中,并在最终增益缩放步骤中进行一次。我发现它更繁琐地设计,因此在应用积分增益之前,这是另一个不仅仅是实现集成步骤的原因。

比例增益缩放

我用0到16之间的可变换档术语n写入这个控制循环的比例术语。在某些情况下,拾取n = 0不合适,并且在某些情况下,挑选n = 16不合适。您需要弄清楚所需的比例增益范围,并确保可以在没有溢出的情况下实现最高增益,但最小的增益具有足够的分辨率:如果最小的增益转换为固定点,则为1,以及您想要将其调整为10%,您已卡住 - 增益为0,1或2计数。当转换为固定点时,最小增益值应至少为5或10个计数。如果您需要比例增益调整范围超过3000:1(非常异常),您可能需要使用32位缩放因子和32x32位数学而不是16x16数学。

通过选择输入和输出缩放因子和N的选择来固定增益缩放因子之间的关系。作为一个示例,假设输入缩放因子是2a = 32768计数,并且输出缩放系数是14.4V = 32768计数,和n = 8。

对于10V / A的增益,您将输入1A = 16384计数,输出为10V = 22756计数。

(16384 * k)>>8 = 22756表示k = 355.56计数对应于10V / a。如果您的系统增益需要在1V / A(= 35.556计数)和100V / A(= 3555.6计数)之间,则N = 8的选择是一个很好的选择。如果您的系统增益需要在0.1V / a(= 3.5556计数)和10V / A之间,那么n = 8太小; n = 10或n = 12是更好的。如果您的系统增益需要在10V / A和100​​0V / A之间(= 35556计数),那么n = 8太大; n = 6或n = 4是更好的。

那个达到的衍生物!

让我们休息一下,然后回到衍生的术语中,这些术语被遗漏了所有这些讨论:我们一直在谈论PI控制器,但已经提到了几次PID控制器。

D术语是我很少使用的东西,因为它为误差项中的高频误差和高增益提供了低增益,这通常只是噪声。

如果您可以使用没有D术语的PI控制器,请执行此操作 - 您的系统将更简单,您不必担心它。

有些系统可以从D术语中受益。它们通常涉及长延迟或阶段滞后的系统,其中有一定数量的数量,但不能。

例如,考虑一个热控制器,在那里您有一个加热器块和温度传感器。如果加热器块大并且温度传感器安装在外部,则在传感器看到施加到加热元件的功率的任何变化之前可能需要很长时间。理想情况下,您还可以测量加热器块内部深度的温度,并使用相对快速的控制回路来调节,然后较慢的控制回路调节外部温度。但是,如果您无法添加额外的温度传感器,则需要一种方法可以注意到加热器块开始升温。如果使用PI循环,比例术语不够快,即在温度误差明显下降时,您已经向加热元件施加了很长时间,即使您突然关闭加热元件,传感器处的温度将继续加热,因为热扩散到加热块的外部。积分术语甚至较慢 - 积分术语是处理直流和低频误差;他们故意是控制循环的最慢响应的部分。因此,当传感器读数开始增加时,衍生项可以帮助您扼杀控制器的输出,但错误仍然是正的。

由于噪声内容,它通常通过促进或低通滤波器实现导数术语来实现:取消读数之间的差异,但然后滤除真正的高频内容,因此您留下了您关心的频率。在热控制回路中,除非有问题的系统真的很小,否则响应时间以秒为单位测量,因此任何高于10或20Hz的东西可能都不会有用。

只要记住,除非您需要,否则您不应该使用D术语。

好的,现在回到算术和溢出的讨论。

添加和减法溢出

让我们看看这个简单的行:

int16 e = u - y;

这里不可能有任何错误,对吗?

错误的!如果Y = 32767计数和U = -32767计数,则E = -65534计数。但这不能适用于INT16变量,只能在-32768和+32767计数之间保持值; -65534计数将别名到+2计数,这是一个不正确的计算。

我们要做的是三件事之一:

1.使用32位数学(一种痛苦)留下计算的空间,以达到其全系列+/- 65535计数

2.确保在最坏情况下,我们永远无法获得溢出(例如,如果我们绝对是正为U,而Y仅限于0-4095计数) - 这并不总是可能的

3.饱和计算:

int16 e = limit((int32)u - y, -32768, +32767);

有些处理器具有内置算术饱和度,但它往往无法从像C.的高级语言中无法访问。

对担忧的原因的其他线条如下:

x_integral = x_integral + (int32)Ki2*e;
x = limit((p_term >> N) + (x_integral >> 16), x_min16, x_max16);

这是对的 - 您需要查看任何添加或减法的计算。

只要右手侧不会导致溢出,上线(积分器更新)不是问题,这里最大的因素是x_Integral的最大值(由x_max32和x_min32确定)和最大积分增益和最大积分增益错误。您可能必须在将ki2 * e产品添加到添加之前限制,或者首先使用临时64位变量。

下线(限制比例项和积分项的总和)不是关注的关注,只要下面的中间值是32位值:

(p_term >> N) + (x_integral >> 16) 

在C中,这将是这种情况,因为P_term和X_Integral是32位值。

乘法溢出

只要您向您想要的编译器表示为16x16 = 32位乘以(或8x8 = 16或32x32 = 64,视情况而定,您永远不会溢出。 (尝试吧!与符号和无符号整数的极端检查。)不幸的是,C / C ++的算术规则是使乘法结果与其推广的输入相同,因此如此:

int16 a = ..., b = ...;
int32 c = a*b;

C的结果可能是不正确的,因为* B具有隐式类型的INT16。要在C中获取中间值来正确计算16x16 = 32,您必须将其中一个操作数推广到32位整数:

int16 a = ..., b = ...;
int32 c = (int32)a*b;

一个好的编译器将看到它并理解您想要做16x16 = 32的乘法。 Mediocre编译器将推广第二操作数B到32位,执行32x32位乘以,并占据结果的底部32位 - 这给出了正确的答案,但在库函数调用中浪费CPU周期。如果您有这种编译器,则您有几个选择。一个选择是使用更好的编译器,第二种选择是您可以访问合适的东西的编译器内在函数:

int32 c = __imul1632(a,b);

其中__imul1632是适当的内在功能,它乘以16x16乘以;我制作了这个函数名称,但系统可能有一个。当编译器看到内部函数时,它将其替换为适当的短系列装配说明,而不是实际制作库函数调用。

第三种选择是,您应该非常大声向您的编译供应商抱怨,以确保您在C中使用的机制来计算16x16 = 32乘以。

一旦完成乘法,您要么需要将其存储在32位值,或者需要将其移位到16位值:

int16 a = ..., b = ...;
int16 c = ((int32)a*b) >> 16;

此计算溢出。但越来越少于16的换档计数越来越容易溢出,并且您需要限制中间结果:

int16 a = ..., b = ...;
const int32 limit12 = (1 << (12+15)) - 1;
int16 c = limit((int32)a*b, -limit12, limit12) >> 12;

对于一个不幸的案例,这也是如此,即使是15个班次,也是如此......

溢出的其他病理病例

如果你是square -2 ^ 15 = -32768,你将得到2 ^ 30。向右移动15,您得到2 ^ 15,它不会符合签名的16位整数。 (-32768)*( - 32768)是一个不适合的计算  c = (a*b)>>15计算。如果您明确地知道一个或两个操作数不能是-32768(例如,如果一个数字是非负收益),那么您不必担心,但否则您必须在右转移之前限制结果:

int16 a = ..., b = ...;
const int32 limit15 = (1 << (15+15)) - 1;
int16 c = min32((int32)a*b, limit15) >> 15;
// min32(x,max) = minimum of the two values 

溢流的其他病理情况也涉及这种“邪恶”值为-32768。

int16 evil = -32768;
int16 good1 = -evil;
int16 good2 = abs(evil);

不幸的是,好的还是评估到-32768,即使逻辑上也应该是32768(它不适用于符号16位整数)。如果使用ABS()函数,请确保了解它如何处理-32768:如果它是如此实现,那么您有同样的问题:

#define abs(x) ((x)>=0 ? (x) : (-x))
inline int16 abs(x) { return x>=0 ? x : -x; }

如果它使用内置装配指令实现,则需要检查该指令如何处理输入。 TI C2800 DSP具有ABS指令,如果设置了溢出模式位,则表现出不同的行为;如果设置了OVM,则ABS(-32768)= +32767,但否则,ABS(-32768)= -32768。

退步

哇!我们又在哪里?我们不是在谈论PI控制器吗?让我们放大到大局,总结。

据选择是使用浮点与定点算法的选择,现在你可以理解这一点

  • 如果我们使用浮点算术,我们可以直接在我们的软件中代表工程价值,我们有几个细节来处理,它可能会花费我们的资源(一些处理器没有浮点指令,所以浮点数学必须在图书馆函数调用中处理,这些函数较慢;其他处理器确实有浮点指令,但它们通常比整数数学运行更慢)
  • 如果我们使用固定点算术,则数学真的很简单,快速迅速执行计算机,而是作为控制系统设计师我们必须做大量的咕噜声,以确保我们不会遇到解决方案或溢出错误我们的系统。

如果您确定使用定点算术,请考虑以下问题:

  • 明智地选择输入和输出的缩放因子
  • 为添加分辨率添加额外的16位,以实现积分器大小
  • 如果您有一个具有大动态范围的系统,则比例增益的缩放因子可能需要具有32位
  • 查看 全部 溢出的算术步骤,甚至简单地添加,减去和否定操作

无论如何,请注意您可以实现PI控制器,并使其正常工作 - 这可能会在您的部件上采取一些额外的工作以确保它。

祝你的下一个控制系统好运!


[]
评论 Tosbrink. 2012年12月20日
谢谢你这么综合的杰森文章。我目前正在挖掘它以进行闭环电源控制。一个拼写纠正:我认为它应该是960V Q16不是Q15在固定点讨论中。
[]
评论 埃里克 79 2014年12月7日
良好的文章!非常直接和实用。
我会尝试你的方法。
我发现一个小字母,60V Q12 = 15V Q10 = 960V Q15 ---> Q15 should be Q16.
谢谢你花时间写下这一切。
埃里克

要发布回复评论,请单击连接到每个注释的“回复”按钮。发布新的评论(不是回复评论),请在评论的顶部查看“写评论”选项卡。

注册将允许您参加所有相关网站的论坛,并为您提供所有PDF下载。

注册

我同意 使用条款 隐私政策 .

尝试我们偶尔但流行的时事通讯。非常容易取消订阅。
或登录