Fuzzy Logic Dot Net Sample






4.58/5 (27投票s)
2003年8月5日
13分钟阅读

160858

6448
C# 中的模糊逻辑库
引言
这是 Fuzzy Dot Net 系列的第二篇文章,在这篇文章中,我们将学习如何使用第一篇文章中提供的类来实现一些概念。在这篇文章中,我们将介绍 Fuzzy Dot Net 示例应用程序。该应用程序是对中央供暖系统的一种简化。之所以将其简化为中央供暖系统,是因为我想说明使用模糊逻辑的程序应该如何编写,以及“模糊性”是如何引入到程序中的。该程序在任何时候都不追求其温度表示的准确性,并且我故意让自己可以被争辩说模糊逻辑本身可以实现得比现在更好。正如我所说的,这是故意为之,我甚至会在文章中指出一些我自己故意削减程序中模糊逻辑用量的区域,以便让初学者更容易理解代码。
首先,我们将审视应用程序本身,看看它试图做什么,以及它是如何测量其所模拟的给定房间内可能发生的各种温度变化的。然后,我们将进入文章的真正重点,探讨模糊逻辑在应用程序中的使用方式,以及进一步应用模糊逻辑可以提高应用程序准确性和结果的方法。
Fuzzy Dot Net 示例应用程序
上面是 Fuzzy Dot Net 示例应用程序的图片。该应用程序模拟房间的温度,当通过持续监控时,会相应地调整温度,图像中三角形位图的中心是当前温度。由于房间内的温度不断变化,假想的加热器不断试图通过加热或冷却房间来补偿。上图显示了程序的起始设置,它给出了房间的起始温度以及我们希望将房间保持的理想温度。请注意,理想温度已被设为只读,因为加热器面板目前不够灵活,无法应对期望温度的变化。
上面的房间设置面板显示了每当调用房间定时器时损失的温度量,请注意这是累积的,以及调用房间定时器之间的时间间隔。这代表了房间总体的热量损失,例如通过墙壁,而不是通过门或窗户的任何特定热量损失。
上面的门设置面板显示了每当调用门定时器时损失的温度量,请注意这是累积的,以及调用门定时器之间的时间间隔。门设置代表了通过门从房间中损失一部分热量的想法。这可能是由于门的边缘有缝隙,或者在定时器触发时门被打开。
上面的窗户设置面板显示了当每个窗户定时器被调用时,两个窗户中每个窗户损失的温度。除了每个窗户有不同的温度损失外,两个窗户也都有自己的定时器设置。窗户设置代表了通过窗户损失的热量,可能是由于窗户连接处的密封件有故障导致边缘有缝隙,或者因为窗户被打开。有两个窗户是因为它们的想法是它们大小不同,因此产生不同的热量损失率。
上面的外部设置面板显示了损失到房间外部温度的温度量。外部温度设置应代表外部温度,并反映出房间外部温度与房间内部温度之间的温差会损失多少热量,尽管这尚未实现。定时器间隔是定时器触发之间的时间(秒)。
加热器设置代表了如果代码处于特定温度下,加热器将产生的热量单位数。这由主“更新”定时器使用,并且可以选择更改更新定时器触发的时间。这些设置将在稍后详细讨论。
运行示例应用程序
当您首次启动示例应用程序时,温度位图将停留在四十左右。这是因为加热器设置略有偏差,无法自行达到所需的最佳温度。我可以对此进行编码,但认为这会提供一些轻微的娱乐价值,让人们看看是否能让它保持在五十左右。
程序本来可以编码成锁定在某个温度,但我认为在模糊逻辑程序中这并不现实。该程序的想法是,代码会不断地尝试调整到一个因各种原因而不断变化的温度水平,尽管这只是一个模拟效果,但它使程序的行为更接近真实世界。
示例应用程序中的模糊逻辑
如前所述,模糊逻辑的理念是我们处理的不是固定数字,而是大部分是数字范围,这些范围通常用一些对我们建模的世界有意义的术语来标识。在任何给定点,我们可能知道或不知道这些数字的确切值,只知道它们属于某个预定范围。从上面的加热器设置可以看出,应用程序中使用的主要模糊逻辑值涉及房间当前的温度以及加热器对温度的响应。对于从零度到中度的给定值,加热器将以用户可增量的方式增加加热器输出的值,对于从温暖到沸腾的范围内的值,加热器将以用户可增量的方式减小加热器输出的值。
加热器的值在代码中定义为
heaterSet.Name = "Heater Set";
FuzzyNumber temp26 = new FuzzyNumber( "Freezing", 0, 10 );
FuzzyNumber temp27 = new FuzzyNumber( "Very Cold", 11, 20 );
FuzzyNumber temp28 = new FuzzyNumber( "Cold", 21, 30 );
FuzzyNumber temp29 = new FuzzyNumber( "Cool", 31, 40 );
FuzzyNumber temp30 = new FuzzyNumber( "Medium", 41, 50 );
FuzzyNumber temp31 = new FuzzyNumber( "Warm", 51, 60 );
FuzzyNumber temp32 = new FuzzyNumber( "Warmer", 61, 70 );
FuzzyNumber temp33 = new FuzzyNumber( "Quite Hot", 71, 80 );
FuzzyNumber temp34 = new FuzzyNumber( "Hot", 81, 90 );
FuzzyNumber temp35 = new FuzzyNumber( "Boiling", 91, 100 );
heaterSet[ 0 ] = temp26;
heaterSet[ 1 ] = temp27;
heaterSet[ 2 ] = temp28;
heaterSet[ 3 ] = temp29;
heaterSet[ 4 ] = temp30;
heaterSet[ 5 ] = temp31;
heaterSet[ 6 ] = temp32;
heaterSet[ 7 ] = temp33;
heaterSet[ 8 ] = temp34;
heaterSet[ 9 ] = temp35;
Fuzzy Number Set 类有一个私有变量 setValue
,可以通过 FuzzySetValue
访问器访问,并用作 Fuzzy Numbers 集合的值。这与第一篇文章中给出的演示代码形成对比,演示代码依赖于 Fuzzy Numbers 类中包含的各个值。所以在这个阶段,我们认为集合是一个单一的特定值,而不是它是一系列不同的值。
这个单一值将属于集合中包含的值之一的范围内,这些范围从零度到沸腾,如上面设置数组的代码所示。代码中使用的其他值是门和窗户等的值。下面的代码是为 doorSet
编写的,几乎与设置影响温度的所有其他项的代码相同。
roomSet.Name = "Room Set";
FuzzyNumber temp16 = new FuzzyNumber( "Warm", 0, 10 );
FuzzyNumber temp17 = new FuzzyNumber( "Cool", 11, 20 );
FuzzyNumber temp18 = new FuzzyNumber( "Cooler", 21, 30 );
FuzzyNumber temp19 = new FuzzyNumber( "Cold", 31, 40 );
FuzzyNumber temp20 = new FuzzyNumber( "Freezing", 41, 50 );
roomSet[ 0 ] = temp16;
roomSet[ 1 ] = temp17;
roomSet[ 2 ] = temp18;
roomSet[ 3 ] = temp19;
roomSet[ 4 ] = temp20;
代码设置了五个温度区域,从“温暖”开始,该给定项的值越高,房间的温度就会降低到零度以下,在默认设置下需要调用定时器多次。请记住,这些值越高,它们对房间温度的降低作用越大。
应用程序的工作方式是,当调用每个定时器时,该项的值会增加其设置面板上的数字控件中的值。一旦调用了更新定时器,代码就会检查该值(例如房间的温度)属于哪个区域。这是通过使用 Fuzzy Number Set 类中的 IsTerm
函数来实现的,该函数是:
public bool IsTerm( string strTerm )
{
for( int i=0; i<this.Count; i++ )
{
if( ( ( FuzzyNumber )FuzzyArray[ i ] ).Name == strTerm )
{
FuzzyNumber temp = ( FuzzyNumber )FuzzyArray[ i ];
if( temp.Maximum >= this.setValue &&
temp.Minimum <= this.setValue )
{
return true;
}
else
return false;
}
}
return false;
}
IsTerm
函数简单地循环遍历集合,并检查传递到函数中的术语是否用作 Fuzzy Numbers 之一的标题。如果找到的术语是集合中某个 Fuzzy Numbers 的名称,那么代码会快速检查值是否准确,然后它将返回 true。
这意味着,如果我们检查门的值是否介于我们称之为“较低”的范围之间,那么 IsTerm
函数就会以“较低”为参数被调用,如果函数返回 true,那么代码会将一个值赋给名为 nHeater
的变量。这个变量用于累积来自所有门、窗户等的数值,然后从 heaterSet
的值中减去。
应该注意的是,这就是我之前提到的关于简化代码的内容。对于这个示例代码,我选择在此阶段分配一个固定值。如果这是一个真实的应用程序,它可能不会这样工作。在现实世界中,我们将使用模糊逻辑规则,这些规则可能会被表述为“如果房间温度损失偏热,则加热器设置偏冷”。这段代码的最终结果无论哪种方式可能都相同,但我建议您参考下面的部分以更全面地解释模糊逻辑规则。
一旦 nHeater
变量从 heaterSet
值中减去,我们就检查 heaterSet
属于哪个区域。这就是加热器设置面板发挥作用的地方,所以如果加热器设置的值落在集合的“温暖”部分的范围内,那么在“如果温度是温暖的”控件中输入的值将从加热器设置值中减去。
模糊逻辑规则
模糊逻辑规则是我们开始思考代码工作方式的自然发展。规则在这种方式下使用的理念是,规则以一种字面和更自然的方式指定,而不是它们本应有的方式。我的意思是,代码中的规则是以英语语言的方式表达的,而不是伪代码。即,而不是说“如果名为 Heater Set 的集合的值在 41 到 50 之间,并且名为 Room Set 的集合的值在 11 到 20 之间,则将加热器值减少 3”。或者使用模糊逻辑规则陈述相同的内容,我们将有“如果加热器偏热且房间偏冷,则将加热器值减少 3”。
然而,我们可以进一步优化这一点,例如,如果我们创建一个名为 Heater Control 的集合,该集合旨在控制在任何特定时间添加到加热器或从中减去的值的大小。这个理论集合可能包含模糊值 Light、Medium、Large 和 Heavy,这意味着我们的规则可以读作“如果加热器偏热且房间偏冷,则 Heater Control 等于 Medium。”这在很大程度上让我们摆脱了严格考虑值的想法,而更多地从现实世界的角度思考值的范围。
上面的图表显示了一个理论上的模糊逻辑规则的处理过程。它基本上是一个标准的 if then 语句,但如果 then 语句的前提条件不成立,则 C 的值是未知的。这就引出了我要说的最后一点,那就是在创建任何使用模糊逻辑的系统时,最需要牢记的可能不是集合将包含什么,甚至它们将被命名为什么,而是您可能需要在某个时候将模糊集合中使用的值转换为输出值。在代码中下定决心时,对数字含糊不清是可以的,但在某个时候您必须对您试图回答的任何问题给出明确的答案,届时您需要能够返回有意义的项目。
问题
下载的代码有一个主要的 bug 让我非常烦恼,但我似乎无能为力。每当在 Developer Studio 2003 中打开项目时,程序顶部的图形就会被 GUI 丢失,如果您尝试添加它,环境会抱怨输入值不正确。这纯粹是胡说八道,非常令人恼火,这意味着每次打开项目时,您都必须从用户控件中删除 FuzzyGraphic
,然后将 FuzzyGraphic
添加到用户控件并将其重新定位在窗体上。然后,您必须打开窗体代码并删除添加的变量(FuzzyGraphic1
),并执行查找和替换,将 FuzzyGraphic1
替换为 FuzzyGraphic2
。我希望这只是我系统上的问题,但到目前为止,我没有理由相信它不会。
最后
对于还在注意的各位,这还不是结束。随着我实现更多模糊逻辑到系统中,我们将看到一些规则生效,还有更多内容。
历史
-
2003 年 8 月 5 日:初始发布。
-
2003 年 8 月 8 日:添加了注释和下一篇文章的链接
注意
系列中的最后一篇文章包含库的最新代码。不会进行向后兼容的尝试,我将按我自己的意愿更改库。
下一篇文章链接
参考文献
- Tom Archer ( 2001 ) C# 内幕,Microsoft Press
- Jeffery Richter (2002) Applied Microsoft .NET Framework Programming, Microsoft Press
- Charles Peltzold ( 2002 ) 使用 C# 编程 Microsoft Windows,Microsoft Press
- Robinson 等 ( 2001 ) 专业 C#,Wrox
- Bart Kosko ( 1994 ) 模糊思维,Flamingo
- Buckley & Eslami ( 2002 ) 模糊逻辑与模糊集导论,Physica-Verlag
-
Earl Cox ( 1999 ) 模糊系统手册,AP Professional