嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

PYTHON – 让“Monty 语言”进入自动化行业:第 3 部分

如果您仍在关注本系列文章,也许您已经按照第 1 部分中的介绍成功搭建出了自己的硬件部分,并且已经完成了第 2 部分中介绍的各个安装步骤。到目前为止,我们还没有编写一个字节的 Python 代码。是时候开始编写了。

我喜欢通过互动的方式进行测试和调试,因此我使用 RevPi 附带的 Python IDE“IDLE”。我们通过 VNC 连接从 RevPi 桌面上启动了此工具:

image060_b181b8662011a881a34a95d28dd06488791c7eb8.jpg

点击红树莓图标,打开“Programming(编程)”,再点击 Python 3 (IDLE)。如果您有兴趣探讨是选择 Python 2 还是 Python 3,欢迎通过 Google 搜索上网浏览相关内容,加入这场“宗教之争”。而现在,只需跟着我使用 Python 3,我保证您会对最终结果感到满意 ;-)

如果已完成上述操作,则会出现一个名为“Python Shell”的窗口:

image062_69ba40b5da89f69f61265d9b9e5901025ba4fe89.jpg

提示符“>>>”表示系统已准备好运行 Python 语句。Python 是一种解释型语言,所有内容都是在运行时编译。所有输入的内容都会得到运行。因此,如果输入语句“4+5”<enter>,就能得到系统的回复“9”:

>>> 4+5

9

>>> 

当我不确定正确的语法或结果类型时,我经常使用这种方法来运行一行代码。例如,输入 2^8 以得到 2 的 8 次方:

>>> 2^10

10

这意味着语法错误,而输入 2**8 才能得到想要的结果:

>>> 2**8

256

本文不介绍 Python 参考资料或 Python 教程。网络上有大量资料可供查阅,我也经常参考官方文件来快速寻找答案。

几乎所有的 Python 脚本都在一开始有“导入”部分。此部分告诉编译器脚本要使用哪些库。Python 广泛使用各种库。我认为,使用极少的常用语言元素,而把余下的任务留给庞大的全球社区提供的各种库,正是该语言取得成功的核心原因。这一切都归功于“集群智能”。如果您遇到了特定的问题,并通过 Google 搜索 Python 库,找到库的可能性相当大。例如 PID 控制算法,我找到了“simple-pid”等库。大多数情况下,需要先将库安装在系统上才能使用它。我使用 SSH 会话 (PuTTY) 输入壳命令“sudo pip install simple-pid”(在下面的屏幕转存图片中,系统已经安装了库):

image064_1f1b9c21d87bb052f3c6bac2fd7c93dde040f63c.jpg

顺便提一句:我还可以使用 RevPi 桌面上的终端窗口进行操作:

image066_216cfc1a947ae01fa7cd835e96eae3f704ba03e2.jpg

“pip”是一款出色的工具,它可以从 Python 框架的中央知识库中获取库,并将它安装到您的系统中。在上面,我们已经通过 Linux apt-get 命令从 KUNBUS 知识库中安装了“python3-revpimodio2”和“revpipyload”。

如果安装成功,则运行导入库的语句时将不会出现错误消息:

>>> from simple_pid import PID

>>> import revpimodio2

好了,现在让我们来编写脚本。我们通过 Python Shell 的菜单“File(文件)”->“New File(新建文件)”来打开一个新的空白编辑器窗口,并在头两行输入导入语句。您会发现,编辑器会在您输入 Python 关键词之后立即帮您用不同的颜色进行标记。

image068_a7c9285d39338b785bf546e283bc63ca3737b66e.jpg

我讨厌因系统崩溃而浪费时间,所以最好时不时地保存一下文件。要进行保存,请点击“File(文件)”->“Save(保存)”。为了便于操作,我建议将文件保存在“/home/pi”目录下,文件命名为“myplc.py”。

许多库都可以在 GitHub 上找到,此外还能在上面找到文件。在这里寻找我们的库。在这里,我们还找到了一个可以帮助我们很快理解用法的示例。我们根据示例,通过以下语句创建出 PID 对象类别的“TempController”实例:

TempController = PID(1,0.1,0.0, setpoint=250, output_limits=(0,100))

该类别的 init call 文件对函数参数进行了说明:

  • Kp=1.0 比例增益值(最好称为“控制器增益”)
  • Ki=0.1 积分增益值
  • Kd=0.0 微分增益值
  • setpoint=250 PID 将设法达到的初始设定点(本例中为 25°C 温度 *10
  • sample_time=0.1 控制器在生成新输出值之前应等待的时间(秒)。PID 在持续调用时(例如在回路中)发挥出最佳效果,但是凭借采样时间设置,每次更新的时间间隔(接近)保持不变。如果设置为“None”,则 PID 将在每次调用时计算出新的输出值。我使用的是 100 ms,因为在使用 10W 加热器的情况下,温度变化的速度非常缓慢。
  • output_limits=(0, 100) 最初使用的输出限值,是含有两个元素的可迭代对象,例如:(下限,上限)。输出永远不会低于下限,也不会高出上限。 
    rpi.io.PWM_heater1.value = int(pwm)
    rpi.io.PWM_heater2.value = int(pwm)
    ​

    任何一端的限值都可以设置为“None”,表示在对应方向上没有限制。设置输出限值还能避免积分饱和,因为积分项永远不允许超过限值范围。在本例中,我们想要的 PWM 数值范围为 0% 至 100%。

    • auto_mode=True 这是默认设置(PID 控制器已启用)
    • proportional_on_measurement=False 确定比例项是否直接在输入上计算(而非采用在误差上计算的传统方法)。使用 proportional-on-measurement 可避免某些类型的系统出现超调量的情况。我使用默认设置 = False。

如果您对 PID 控制算法不熟悉,那么可以在此页中查阅关于调节参数 Kp、Ki 和 Kd 的更多信息。我们无法预测这些调节参数的值,因而需要通过测试控制行为来设置这些参数(详见下文)。

我们还需要创建 revpimodio 类别的实例。于是我们使用 revpimodio 网页上的文件找到了它的 init-call:

rpi = revpimodio2.RevPiModIO(autorefresh=True)

为了运行 PID 控制器,我们需要循环调用三个函数:

第一个是从过程映像中获得当前温度:

Temp = rpi.io.Temp10.value

第二个是计算 PID 输出值:

pwm = TempController(Temp)

最后一个是通过写入过程映像将 PWM 输出设置为计算值。

由于过程映像中的 PWM 值是无符号整数,我们需要用函数“int()”转换 PID 输出。我使用 Python 的 while 循环结合实际参数“True”将这四行置于无限循环中。如果您对 Python 不熟悉,请注意 Python 语言不使用 {} 括号来给代码分组。代码分组是通过“空格”(即缩进)来实现的,请参见 while 循环内部的四条语句前面的空格。

由于我们使用的是一个运行多任务处理的 Linux 系统,我们不希望任务中紧张的无限循环把 CPU 卡死。所以,我使用库“time”和函数“sleep(n)”给循环设置了 0.05 秒的停顿时间。为了每隔 1 秒调试输出,我还增加了一个计数器变量,该变量在每次循环时增大,直到达到 20,同时打印出输入和输出值后再重置为 0。

完整程序如下所示:

from simple_pid import PID
import revpimodio2
import time

TempController = PID(5,0.13,10.5, sample_time=0.1, setpoint=400, output_limits=(0,100))
rpi = revpimodio2.RevPiModIO(autorefresh=True)
n=0
while True:
    Temp = rpi.io.Temp10.value
    pwm = TempController(Temp)
    rpi.io.PWM_heater1.value = int(pwm)
    rpi.io.PWM_heater2.value = int(pwm)
    n +=1
    if n==20:
        n=0
        print(Temp, int(pwm))
    time.sleep(0.05)

如果通过 <F5> 或菜单“Run(运行)”->“Run Module(运行模块)”打开脚本,就能在 Python Shell 窗口中看见循环值。按下 CTRL-C 可停止运行。

现在到了试验调节数值的时候了。它们当然取决于热源和温度传感器之间的实际反馈。我希望得到灵敏的反馈。因此我选择在传感器和灯泡之间设置较小的间距。

image069_e0eb3b963e65bbd93cae8de4bdc6b11ad8afe283.jpg

我还可以将它们放在箱子或铝箔罩这类可以储存热量的空间中。

但是在本例中,凭借 2 台 10W 加热器,加上设定温度在 40°C 左右,我可以设法调整三个调节参数以达到近乎完美的控制。为此,我首先将 Ki 和 Kd 设置为 0。Kp 先后取值 1、5、10 和 20,然后我发现在取值为 10 时,最终温度达到稳定。这表示:在设定点以下每偏差 0.1°C,控制函数将增加 10% 的 PWM 开通时间。当取值为 20 时,导致了 1°C 以上的振幅。然后,我将 Kp 取值为稳定值 10 的一半,即 5。结果最终温度非常稳定,但在设定点的基础上出现负偏移 – 温度仅上升至 39°C 左右,而非 40°C。我开始非常缓慢地增加 Ki,结果在一定程度上补偿了偏移,但同时增大了振幅。当取值 0.13 时,温度达到设定点 40°C 左右,最大振幅稳定在 1°C。增大 Kd 时,控制器在一定程度上向更高或更低的 PWM 值“注入”短脉冲。这样确实能够减小振幅。我最终将 Kd 取值为 10 左右,振幅为 +/-0.1°C(即温度测量的总体精度)。调节结果如下所示:

image071_31ff32c32b2680fa325d3ef1b6ae0657d27b72fe.pngimage073_c31651457fa97b3dfd570eb22dca1d3b3b4c3d66.png

15:57 时,控制器打开,此时环境温度为 34°C(是的,我的办公室会经历气候变化!)。15:59(2 分钟后)时,温度稳定在 40°C(设定点)。第二张图片显示出温度在设定点上下呈现非常小幅的振荡。

如果您的文本输出显示出可接受的控制循环,那么可以接着进行下一步:使用 MQTT 和 Node-Red 为加热器控制器搭建小型 GUI,以获得如以上时间进程的精确图表。而这将是第 4 部分要介绍的内容。 

Volker de Haas started electronics and computing with a KIM1 and machine language in the 70s. Then FORTRAN, PASCAL, BASIC, C, MUMPS. Developed complex digital circuits and analogue electronics for neuroscience labs (and his MD grade). Later: database engineering, C++, C#, industrial hard- and software developer (transport, automotive, automation). Designed and constructed the open-source PLC / IPC "Revolution Pi". Now offering advanced development and exceptional exhibits.
DesignSpark Electrical Logolinkedin