在本文中: 开发人员经常将模糊逻辑与有限状态机结合使用,甚至代替有限状态机。 在本章中,您将了解为什么模糊逻辑优于传统逻辑技术。
模糊逻辑
生活中,我们经常使用“有点”、“差不多”、“几乎没有”、“接近”等形容词,模糊了界限。
传统逻辑中,要么黑,要么白,要么是0,要么是1,要么是0,要么是1,没有其他情况。 在模糊逻辑中,0和1之间还可能存在其他情况,这些情况都是灰色区域。 例如,无论你是高、矮、胖还是瘦,多高才叫高,多矮才叫矮3D角色,并没有明确的界限。 可以说是有点高,很高,或者很高,所以界限(或者临界条件)是模糊的。 0到1之间的值代表事件属于0或1的程度,即隶属度。 我们可以编写一个隶属函数来确定隶属度,或者一个概率来表示这种状态。 会员等级代表等级。 其值越大,该状态的概率越高。 反之,出现这种状态的概率越低。
1965年,加州大学伯克利分校教授Lotfi Zadeh撰写了第一篇讨论模糊集合论的论文。
模糊逻辑的两个基本原理是:
模糊逻辑的含义:让计算机以近似人类行为的方式解决问题模糊逻辑的本质:一切都与程度有关在游戏中使用模糊逻辑
模糊逻辑可以在游戏中以多种方式使用。 这里只介绍三个方面,控制、威胁评估、分类。
您可以使用模糊逻辑来控制队友或其他非玩家角色,您可以使用模糊逻辑来评估玩家所呈现的威胁,并且您可以使用模糊逻辑来区分玩家和非玩家角色。
控制
模糊逻辑可以控制游戏中单位的移动,使它们能够顺利地穿过拐点并绕过障碍物。 (利用隶属函数控制物体的转向力)
威胁评估
模糊逻辑在游戏中的另一种可能的应用涉及直接运动控制之外的决策。
在战争模拟游戏中游戏开发中的人工智能,计算机军队经常需要部署防御军队来抵御敌人的潜在威胁。 假设计算机军队知道敌军的距离和敌军的规模。 距离可以表示为近、接近、远和非常远,而规模可以表示为零星、很少、中等、大或巨大。
有了这些信息,我们就可以使用模糊逻辑让计算机评估敌军构成的威胁。 对成都的威胁可分为无威胁、低威胁、中威胁、高威胁。 在确认对成都的威胁后,计算机可以依次做出决定并部署适当的部队进行防御。
分类
假设您想根据游戏中的战斗能力对玩家角色和非玩家角色进行分类。 除了等级之外,还可以参考玩家角色的体力、命中次数、武器熟练度、护甲等级等。 最后,你将这些因素结合起来,创造出诸如弱者、毫不费力、平庸、顽强、可怕等等级制度。
例如,如果一个玩家被击中次数多,护甲一般,体力好,武器熟练度低,那么等级可能会很平庸。 模糊逻辑允许您确定这个级别。
模糊逻辑基础知识简介
模糊过程由三个基本步骤组成,如图10-1所示:
模糊过程由三个基本步骤组成,即:
模糊化:根据隶属函数从特定输入得到模糊集合的隶属度的过程推理方法:从模糊规则和输入对相关模糊集合的隶属度得到模糊结论的方法。将模糊的结论转化为具体、准确的输出过程
计算过程大致如下:
输入(收集清晰数据) ——> 模糊化(根据隶属函数,如分段函数、分布函数,从具体输入中获取隶属模糊集(特征数据)) ——> 模糊规则库+推理方法 ——> 模糊结论——>去模糊化
模糊
模糊系统的输入是明确的数字。 在模糊化的过程中,我们需要根据隶属函数将这些明确的值映射到模糊集合中的隶属度。
隶属函数
隶属函数(或译为隶属函数)将输入变量对应于模糊集中0到1之间的值,以求隶属度。 如果给定集合中的隶属度为 1,我们就说输入数据对于该集合来说绝对正确。 如果隶属度为 0,我们就说该集合绝对为假。 如果隶属度在0到1之间,则在一定范围内为真,即在一定程度上为真。 (例:有点高,即高到一定程度)
在讨论模糊逻辑的隶属函数之前,我们先来谈谈传统逻辑的隶属函数,比如布尔逻辑。 图10-2显示了布尔逻辑的隶属函数(归属函数)。
观察图10-2,我们可以看到,当输入数据小于x0时,结果为假(false),当输入数据大于x0时,结果为真(true)。 没有中间值,即传统逻辑中的黑或白、非零或1、非假或真。 例如,如果x0=170公斤,那么任何超过170公斤的人都属于超重,而任何低于170公斤的人都不是超重。 即使一个人的体重达到169公斤,他仍然被认为没有超重。 这对于现实生活中的判断来说是不合理的,但是如果隶属函数是模糊的,它可以让我们实现从假到真,或者从不超重到超重的逐步过渡。
图10-3是模糊逻辑的隶属函数图。 如下。
从图10-3可以看出,在0和1之间逐渐变化。当小于x0时,结果为假(false),即隶属度为0;当小于x0时,结果为假(false),即隶属度为0; 当大于x1时,结果为真(true),即隶属度为1。对于x0和x1之间的值,隶属度线性变化。
使用直线的点斜率方程,表示该隶属函数的方程可以写如下。
回到体重的例子。 让这个函数代表超重成员资格。 令x0等于175,x1等于195。如果某人体重175磅,根据这个隶属函数,他的隶属度为0,即他没有超重。 如果他的体重是185磅,根据计算,他的会员度是0.5,这意味着他有点超重了。
一般来说,我们关注的是输入变量根据隶属函数与模糊集的对应程度。 例如,我们想知道某人的体重是否超重、太瘦或理想。 对此,我们需要建立一个模糊集,以便根据隶属函数观察其隶属度落在哪个区间,即代表什么程度。 模糊集如图10-4所示。
有了这样一个集合,我们就可以计算出每个输入值在这三个集合中的隶属度。 如果一个人太瘦,隶属度为0,理想隶属度为0.75,如果过重,隶属度为0.15。 那么我们可以推断这个人的权重是理想的,即隶属度为75%(0.75)。
三角隶属函数也是除之前的线性隶属函数(图10-3)之外常用的隶属函数之一,如图10-5所示。
参见图10-5,该三角形隶属函数的方程如下:
另一种常用的隶属函数是梯形隶属函数,如图10-7所示。
梯形隶属函数的方程如下:
到目前为止,我们已经讨论了三种最常用的线性隶属函数:线性隶属函数(图10-3)、三角形隶属函数(图10-5)和梯形隶属函数(图10-7)。 当需要更高的精度或非线性时,有时会使用高斯曲线或S形曲线。
例 10-1 是我们讨论过的每个隶属函数的程序表示。
//例10-1:模糊逻辑中的隶属函数
//图10-3对应的直线型隶属函数
double FuzzyGrade(double value,double x0,double x1)
{
double result=0;
double x=value;
if(x<=x0)
result=0;
else if(x>=x1)
result=1;
else
result=(x/(x1-x0)) - (x0/(x1-x0));
return result;
}
//图10-5对应的三角形隶属函数
double FuzzyTriangle(double value,double x0,double x1,double x2)
{
double result=0;
double x=value;
if(x<=x0)
result=0;
else if(x==x1)
result=1;
else if( (x>x0) && (xelse
result=(-x/(x2-x1)) + (x2/(x2-x1));
return result;
}
//图10-7对应的梯形隶属函数
double FuzzyTrapezoid(double value,double x0,double x1,double x2,double x3)
{
double result=0;
double x=value;
if(x<=x0)
result=0;
else if( (x>=x1) && (x<=x2) )
result=1;
else if( (x>x0) && (xelse
result=(-x/(x3-x2)) + (x3/(x3-x2));
return result;
}
要查找给定输入值在特定集合内的隶属度,只需调用示例 10-1 中的函数之一并传递与函数形状定义相关的值和参数即可。
屏障功能
栅栏函数有时可用于修改隶属函数返回的隶属度。 隶属函数主要提供其他词汇材料,您可以将其与其他逻辑运算结合使用。 两个常用的屏障函数是VERY()和NOT_VARY(),它们的定义如下:
这里的Truth(A)是A在某个模糊集中的隶属度。 隶属函数可以有效地改变隶属函数的形状。 例如,当将隶属函数应用于线性隶属函数时,隶属函数的线性部分变为非线性。
模糊系统中不需要屏障函数。 我们可以构造隶属函数来满足我们的需求,而无需使用额外的障碍函数。 这里提到它只是因为障碍函数经常出现在模糊逻辑领域。
模糊规则
对明确的输入进行模糊化之后,接下来要做的就是构建一组规则,以某种逻辑方式组合输入数据以生成一些输出结果。
模糊公理
我们需要能够处理交集(AND)、并集(OR)和补集(NOT)。 对于模糊变量,这些逻辑运算符的定义如下:
这里的真值(A)是A在某个模糊集合中的隶属度,其值在0到1之间。真值(B)是B在某个模糊集合中的隶属度。 可以看出,OR逻辑运算符定义为操作数中的最大值,AND逻辑运算符定义为操作数中的最小值,NOT运算符为1减去操作数的隶属度。
例如,假设某对的隶属度为超重0.7,身高0.3,则前面定义的逻辑运算符的结果将如下:
超重 AND 高 = MIN(0.7,0.3)=0.3
超重 OR 高 = MAX(0.7,0.3)=0.7
NOT 超重 = 1-0.7=0.3
NOT 高 = 1-0.3=0.7
NOT(超重 AND 高)= 1-MIN(0.7,0.3)=1-0.3=0.7
如果将模糊逻辑算子写成程序,则如例10-2所示。
//例10-2:模糊逻辑运算符函数
double FuzzyAND(double A,double B)
{
return MIN(A,B);
}
double FuzzyOR(double A,double B)
{
return MAX(A,B);
}
double FuzzyNOT(double A,double B)
{
return 1.0-A;
}
模糊规则的评价运算
在传统逻辑的布尔系统中,每条规则都会被一一评估,直到其中一个为真,然后运行结论。
在模糊逻辑系统中,所有规则将同时运行,并且每个规则都会运行(因为每个规则部分正确),但是运行的强度或程度会有所不同。 每个规则的前提的逻辑运算结果将产生该规则的结论的强度。 换句话说,每个规则的强度代表了输出模糊集中的隶属度。
例如。 我们使用模糊逻辑系统来计算生物是否应该攻击玩家。 输入变量是距离、生物的生命值和玩家的等级。 各变量的隶属函数如图10-10所示。
本例中的输出动作可以是逃跑、攻击或什么都不做。 我们可以编写类似于以下语句的规则。
if(位于肉搏战距离内 AND 健康)AND NOT 坚强 then 攻击
if(NOT 位于肉搏战距离内)AND 健康 then 什么也不做
if(NOT 位于距离外 AND NOT 健康)AND (NOT 懦弱) then 逃跑
您还可以设置其他规则来处理其他可能性。 在您的游戏中,将评估所有规则并获取每个输出的成员资格。 每个输入变量都有隶属度后,您可能会得到如下输出:
攻击的隶属度为0.2
什么也不做的隶属度为0.4
逃跑的隶属度为0.7
可以发现,如示例 10-3 所示,模糊规则与传统的 if-then 规则有显着不同。
degreeAttack=MIN( MIN (degreeMelee,degreeUninjured),1.0-degreeHard );
degreeDoNothing=MIN( (1.0-degreeMelee),degreeUninjured);
degreeFlee=MIN(MIN ((1.0-degreeOutOfRange),(1.0-degreeUninjured)),(1.0-degreeWimp) );
输出的程度代表了每条规则的强度。 解释输出结果的实践是基于最高程度的行动。 在本例中,最终动作是逃跑(因为逃跑的隶属度为 0.7)。
防模糊
当你想使用精确值作为模糊系统的输出数据时,你需要去模糊化过程。
正如我们之前所说,每个规则都会得到某个输出模糊集中的隶属度。 对于前面的例子,假设你不仅想求某个有限动作(不做任何事、逃跑或攻击)的概率,还想输出结果来找出该生物应该以多快的速度采取行动。 例如,如果输出动作是逃跑,那么该生物是通过步行还是跑步来逃跑,它离开的速度有多快? 为了获得准确的值,我们必须聚合输出强度,这意味着我们需要定义一个输出隶属函数。
无为、逃跑、攻击的输出隶属函数如图10-11所示。
使用前面讨论的数值输出(攻击的隶属度为 0.2,无所作为的隶属度为 0.4,逃跑的隶属度为 0.7),我们最终得到隶属度函数游戏开发中的人工智能,如图 10-12 所示。
为了获得最终的隶属函数2d素材,每个输出集根据其规则强度(即最终的输出隶属度)进行裁剪。 然后使用并集将所有输出集组合起来。
此时,我们只有输出的隶属函数,还没有确切的数字。 我们可以使用许多方法从这样的输出模糊集中获得精确的数字。 最常用的方法之一是找到输出模糊集所占据区域的几何中心,并使用该中心的横轴坐标值作为精确的输出值。 这样就得到了所有规则之间的折衷值,即单个输出数,它实际上是所有输出隶属度的加权平均值。
要找到这个输出函数的中心,你必须使用数值积分来计算曲线所包围的面积,或者你可以将其想象为一个多边形,然后使用计算机在几何上找到中心,或者你可以使用其他手段。
不管怎样,寻找一个区域的中心会消耗大量的计算能力,尤其是在游戏中计算次数会更多。 幸运的是,有一种更简单的方法可用,即单值输出隶属函数。
单值输出隶属函数本质上是一个预先去模糊化的输出函数。 例如,我们可以为每个输出动作分配一个比率,例如-10代表逃跑,1代表什么都不做,10代表攻击。 在我们的例子中,逃逸行为最终得到的速率是-10乘以0.7(逃逸的隶属度为0.7),即-7就是逃逸率。 这时,要计算所有输出值的聚合,只需要简单的加权平均,而不需要找到中心。
一般来说,假设 μ 是输出几何的真实程度,x 是与该输出几何相关的精确单个值,最终聚合和去模糊化的输出为:
对于我们的例子(攻击的隶属度是0.2,无所事事的隶属度是0.4,逃跑的隶属度是0.7。逃跑是-10,无所事事是1,攻击是10),最终的结果如下:
输出值= [ 0.7*(-10) + (0.4)*1+(0.2)*(10) ] / (0.7+0.4+0.2)