前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >8.1 水位自动控制

8.1 水位自动控制

作者头像
周星星9527
发布2019-04-26 17:24:17
1.2K0
发布2019-04-26 17:24:17
举报

汽包锅炉液位控制原理

本实验拟使用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控制器。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 传输过程数值模拟学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档