8.1 水位自动控制

汽包锅炉液位控制原理

本实验拟使用PID算法实现液位控制,如下图所示:

水位控制系统(网络图片,侵权删除)

其中有若干部件构成,分别说明如下:

1,水池Pool,底面积为1m2,初始液位为1m,水的初始容积为1m3,目标水位(targetLevel)控制在1.2m,实际液位(actualLevel)受入口管道(TubeIn)和出口管道(TubeOut)流动情况而定。

2,出口管道(TubeOu)截面积(orificeArea)默认等于1cm圆形的面积,即3.14×0.01×0.01m2。出口流速根据无局部阻力损失的伯努利方程确定,即,其中g为重力加速度,h为水池液位(= actualLevel)。

3,入口管道(TubeIn)默认最大流量为0.01m3/s,入口管道实际流量受电磁阀(Valve)的开度(valveOpenning)控制,并与开度成正比。

4,电磁阀(Valve)的开度(valveOpenning)决定了入口管道水流量(flux),开度介于0和1,等于0时入口完全关闭,开度为1时流量达到最大0.01m3/s。

5,液位传感器(LevelSensor)可以实时测量水池液位,并反馈给PID控制器。

6,PID控制器(Controller),PID控制器根据目标水位(targetLevel)和传感器测量的实际水位(actualLevel)确定电磁阀(Valve)的开度(valveOpenning),从而形成闭环控制:

其中Kp、TI、TD分别为静态比例放大系数、积分时间和微分时间,μ(t)为位移量,决定了执行机构的动作,e(t)用于表征目标设定值(如目标水位targetLevel所对应的电信号量)与实际值(如实际水位actualLevel经过传感器和信号处理最终得到的电信号量)的差值。

  • 程序实现

先写一个html界面用于绘制液位高度(这个html文档本身不包含js脚本程序,而是链接到外部脚本js文件PID.Controller.js,这个文件后期给出,注意此html文档):

1. <!DOCTYPE html>

2. <html style="height: 100%">

3. <head>

4. <meta charset="utf-8">

5. </head>

6. <body style="height: 100%;margin: 0">

7. <div id="container"style="height:100%"></div>

8. <script type="text/javascript"src="http://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script>

9. <script type="text/javascript"src="PID.Controller.js"></script>

10. </body>

11. </html>

封装几个类,闭住眼睛设想一下这几个类就是真实的设备,依次是进水水管类TubeIn、出水水管类TubeOut、阀门类Valve、液位传感器类LevelSensor以及PID控制器类Controller:

1. class TubeIn{

2. constructor(flux) {

3. this.flux = flux;

4. }

5. }

6.

7. class TubeOut{

8. constructor(orificeArea) {

9. this.orificeArea = orificeArea;

10. }

11. }

12.

13. class Valve{

14. constructor(valveOpenning) {

15. this.setValveOpenning(valveOpenning);

16. }

17. setValveOpenning(value){

18. if(value>1) value=1;

19. if(value<0) value=0;

20. this.valveOpenning = value;

21. }

22. }

23.

24. class LevelSensor{

25. static getLevel(pool){

26. return pool.actualLevel;

27. }

28. }

29.

30. class Controller{

31. static PID(target,current,KP,TI=0,TD=0){

32. var out=target-current;

33. return out*KP;

34. }

35. }

然后是我们的水池类Pool(假设它是锅炉汽包),其中进水量根据进水管的阀门开度而定,出水量根据伯努利方程由液位而定:

1. class Pool {

2. constructor(radius,iniVolume,targetLevel) {

3. this.radius = radius;

4. this.volume = iniVolume;

5. this.targetLevel = targetLevel;

6. this.actualLevel = iniVolume/Math.PI/radius/radius;

7.

8. this.tubeIn=new TubeIn(0.01);

9. this.valve=newValve(1);

10. this.tubeOut=newTubeOut(Math.PI*0.01*0.01);

11. }

12. waterFlowIn(timeStep){

13. this.volume+=this.valve.valveOpenning*this.tubeIn.flux*timeStep;

14. this.actualLevel = this.volume/Math.PI/this.radius/this.radius;

15. }

16. waterFlowOut(timeStep){

17. this.actualLevel = this.volume/Math.PI/this.radius/this.radius;

18. var velocityOut=Math.sqrt(2*9.8*this.actualLevel);

19. this.volume-=velocityOut*this.tubeOut.orificeArea*timeStep;

20. this.actualLevel = this.volume/Math.PI/this.radius/this.radius;

21. }

22. }

好了,看看随着时间液位是怎么变化的?

1. var pool=new Pool(Math.sqrt(1/Math.PI),1,1.2);

2. varstartTime=0,endTime=40,timeStep=0.1,flowTime=0;

3. variteratorNum=(endTime-startTime)/timeStep;

4. varKP=20,TI=0,TD=0;

5. var flowTimes=[],actualLevels=[];

6. for(var iter=0;iter<iteratorNum;iter++){

7. var actualLevel=LevelSensor.getLevel(pool);//传感器反馈测量的水位值

8. varmu=Controller.PID(pool.targetLevel,actualLevel,KP,TI,TD);//PID控制器输出位移量

9. pool.valve.setValveOpenning(mu);//根据位移量设定阀门开度

10. pool.waterFlowIn(timeStep);//流入水

11. pool.waterFlowOut(timeStep);//流出水

12. flowTime+=timeStep;

13. flowTimes.push(flowTime);

14. actualLevels.push(pool.actualLevel);

15. }

好了,绘制液位变化的时候到了:

1. var dom = document.getElementById("container");

2. var myChart =echarts.init(dom);

3. var app = {};

4. option= null;

5. option= {

6. xAxis:{

7. type:'category',

8. data:flowTimes

9. },

10. yAxis:{

11. type:'value',

12. min:1

13. },

14. series:[{

15. data:actualLevels,

16. type:'line'

17. }],

18. title:{

19. text:"PID控制器下的水位"

20. },

21. tooltip:{

22. trigger:'axis',

23. formatter:function (params) {

24. params= params[0];

25. return "水位/m:"+params.value+"\r\n"+"时间/s:"+params.name;

26. },

27. axisPointer:{

28. animation:false

29. }

30. }

31. };

32.

33. if (option&& typeof option === "object") {

34. myChart.setOption(option,true);

35. }

点击运行网页(由于使用了js较新的ES6特性,旧版本的浏览器可能不支持class关键字,请使用最新的浏览器,本文使用微软浏览器Microsoft Edge 42.17134.1.0),得到液位随时间变化曲线:

整个js代码(js脚本文件PID.Controller.js):

1. class TubeIn{

2. constructor(flux) {

3. this.flux = flux;

4. }

5. }

6.

7. class TubeOut{

8. constructor(orificeArea) {

9. this.orificeArea = orificeArea;

10. }

11. }

12.

13. class Valve{

14. constructor(valveOpenning) {

15. this.setValveOpenning(valveOpenning);

16. }

17. setValveOpenning(value){

18. if(value>1) value=1;

19. if(value<0) value=0;

20. this.valveOpenning = value;

21. }

22. }

23.

24. class LevelSensor{

25. static getLevel(pool){

26. return pool.actualLevel;

27. }

28. }

29.

30. class Controller{

31. static PID(target,current,KP,TI=0,TD=0){

32. var out=target-current;

33. return out*KP;

34. }

35. }

36.

37. class Pool {

38. constructor(radius,iniVolume,targetLevel) {

39. this.radius = radius;

40. this.volume = iniVolume;

41. this.targetLevel = targetLevel;

42. this.actualLevel = iniVolume/Math.PI/radius/radius;

43.

44. this.tubeIn=newTubeIn(0.01);

45. this.valve=newValve(1);

46. this.tubeOut=newTubeOut(Math.PI*0.01*0.01);

47. }

48. waterFlowIn(timeStep){

49. this.volume+=this.valve.valveOpenning*this.tubeIn.flux*timeStep;

50. this.actualLevel = this.volume/Math.PI/this.radius/this.radius;

51. }

52. waterFlowOut(timeStep){

53. this.actualLevel = this.volume/Math.PI/this.radius/this.radius;

54. var velocityOut=Math.sqrt(2*9.8*this.actualLevel);

55. this.volume-=velocityOut*this.tubeOut.orificeArea*timeStep;

56. this.actualLevel = this.volume/Math.PI/this.radius/this.radius;

57. }

58. }

59.

60. var pool=new Pool(Math.sqrt(1/Math.PI),1,1.2);

61. varstartTime=0,endTime=40,timeStep=0.1,flowTime=0;

62. variteratorNum=(endTime-startTime)/timeStep;

63. varKP=20,TI=0,TD=0;

64. varflowTimes=[],actualLevels=[];

65. for(var iter=0;iter<iteratorNum;iter++){

66. var actualLevel=LevelSensor.getLevel(pool);//传感器反馈测量的水位值

67. varmu=Controller.PID(pool.targetLevel,actualLevel,KP,TI,TD);//PID控制器输出位移量

68. pool.valve.setValveOpenning(mu);//根据位移量设定阀门开度

69. pool.waterFlowIn(timeStep);//流入水

70. pool.waterFlowOut(timeStep);//流出水

71. flowTime+=timeStep;

72. flowTimes.push(flowTime);

73. actualLevels.push(pool.actualLevel);

74. }

75.

76. var dom = document.getElementById("container");

77. var myChart =echarts.init(dom);

78. var app = {};

79. option= null;

80. option= {

81. xAxis:{

82. type:'category',

83. data:flowTimes

84. },

85. yAxis:{

86. type:'value',

87. min:1

88. },

89. series:[{

90. data:actualLevels,

91. type:'line'

92. }],

93. title:{

94. text:"PID控制器下的水位"

95. },

96. tooltip:{

97. trigger:'axis',

98. formatter:function (params) {

99. params= params[0];

100. return "水位/m:"+params.value+"\r\n"+"时间/s:"+params.name;

101. },

102. axisPointer:{

103. animation:false

104. }

105. }

106.};

107.

108.if (option && typeofoption === "object") {

109. myChart.setOption(option,true);

110.}

111.

112.console.log(actualLevels,"end")

[结语]:曲线异常光滑,与通常控制曲线不同,这是因为我们的K控制器(阀门开度)调节性能太理想了,实际上的阀门控制不可能这么光滑。另外程序之给出了P控制器的代码,读者可以改为PI或PID控制器。

原文发布于微信公众号 - 传输过程数值模拟学习笔记(SongSimStudio)

原文发表时间:2019-04-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券