Introduction
Recently I was assigned a project in my Artificial Intelligence Course that required Fuzzy Logic to solve a simple problem. The idea was to have a Soccer Playing Robot that would go after the soccer ball given some simple Fuzzy Rules.
As usual, I started the project looking for some already made implementation of a fuzzy system (I know I know, but it works!). To my surprise, I found plenty of articles on the subject, but none providing a simple framework to build your own Fuzzy System.
Hence this article. The code provided here is not intended to be a complete implementation of a Fuzzy System, but rather to be a good starting point for your own needs. As a bonus, I included the soccer playing robot as a sample of using the FuzzyLogic Namespace.
Background
This is just a simple crash course introduction to what Fuzzy Systems are. It is not mathematical nor formal centered, but rather very technical.
We all use Logic in our programming day, but that logic is one in which things are true or false. Not in between. This works great for deterministic situations. Unfortunately, our mind doesn't work like that and rarely treats facts as completely true or completely false.
Fuzzy Logic aims at recreating how the mind works by eliminating the absolute true or false and giving degrees of membership. These allow you to have an idea of how much a variable is one value or the other.
Based on the research I had to do for the soccer playing robot, I drew these conclusions about what fuzzy systems are:
- Linguistic variables: These are simply variables whose values are linguistic. e.g.: for the linguistic variable "direction" the possible values could be "north", "east", "south" and "west".
- Membership functions: These functions take a crisp value (angle) as a parameter and output a number between 0 and 1. Each linguistic value has an associated Membership function. The idea behind the Membership function is to define how much a crisp value is related to a specific linguistic value. The most common Membership functions are:
- Linear,
- Triangular, and
- Trapezoidal
For instance the "north" linguistic value could have a Triangular Membership function with parameters a=0°, b=90° and c=180°. This would mean that a 45° angle would have a 0.5 Membership to the linguistic value "north".
- Fuzzy sets: These are sets whose elements are all the possible linguistic values of a specific linguistic variable, each associated with a Membership value of the same Crisp value. e.g.: the set defined by the direction linguistic variable and the crisp value 80° would be:
(direction, 80°) :== {(north, 0.9),(west,0),(south,0),(east,0.1)}
- Fuzzy Rules: Fuzzy Rules are basically if <condition> then <decision>. The condition part is, for this example, in the form of <linguistic variable 1> = <linguistic value 1> AND <linguistic variable 2> = <linguistic value 2> AND ... The decision part, in a Tagaki Sugeno Fuzzy System, is an assignment of a Crisp Value. e.g.: IF direction=north and direction=west THEN turn:=-15°.
A Fuzzy Rules Set based on the Tagaki-Sugeno lets one easily calculate a crisp output from all the fuzzy values. The Fuzzy Rules Set would have a finite number of rules, all having the same crisp variable in the decision part. The final value of the crisp variable would be a weighted average of each Rule's crisp value. The weight of each rule is determined by the multiplication of the membership degrees of each (<linguistic variable>,<linguistic value>) pair.
Using the Code
As you review the code, you'll see it is very simplistic. As it was developed with a specific purpose in mind, the classes lack plenty of would-be useful methods. However, I did strive to make the code as OO as I could so that other people could easily add their own code.
Let's get down to business. The Project defines a new FuzzyLogic namespace which has:
- Class
TagakiSugenoFuzzySystem
. This class lets you define a set of rules, all defining the same crisp variable and calculate the aggregated crisp output. - Class
TagakiSugenoFuzzyRule
. This class defines a rule in the Tagaki-Sugeno System. It is defined by an array of Linguistic values and a crisp output. - Interface
IMembershipFunction
. Defines an interface that lets you create your own membership function for VariableValue
. - Class
VariableValue
. Defines a Linguistic value and has an IMembershipFuncition
that defines a Membership
function for the specific value. - Class
Variable
. It is composed of a list of VariableValue
. - Class
FuzzySet
. This associates a crisp value with a variable and provides a method to calculate the membership of the crisp value with each VariableValue
of the Variable
.
To get an idea of how you could work with this namespace, I'll walk you through the soccer playing robot sample.
The Sample
For the sample, I'll define a Fuzzy System that steers a soccer playing robot towards the soccer ball. To do this, I define 2 linguistic variables with its associated values like this:
distance
: left
, center
, right
angle
: north
, west
, south
, east
The angle refers to where the ball is in respect to the robots heading. The distance defines if the ball is in the left side, center or right of the robot.
In the attached project, you can find the constructor of the Form1
form. This is where I define the fuzzy system to calculate how much the robots wheels must turn.
We start the definition of the system with its variables:
distancia.addValue(new VariableValue
("left", new TriangleMembershipFunction(-4000, -10, 0)));
distancia.addValue(new VariableValue
("center", new TriangleMembershipFunction(-10, 0, 10)));
distancia.addValue(new VariableValue
("right", new TriangleMembershipFunction(0, 10, 4000)));
angulo.addValue(new VariableValue
("north", new TriangleMembershipFunction(0, 90, 180)));
angulo.addValue(new VariableValue
("west", new TriangleMembershipFunction(90, 180, 270)));
angulo.addValue(new VariableValue
("south", new TriangleMembershipFunction(180, 270, 360)));
angulo.addValue(new VariableValue
("east", new InverseTrapezoidalMembershipFunction(0, 90, 270, 360)));
Next we define the Fuzzy Rules by which our system will work. Here are the first three:
system.addRule(new TagakiSugenoFuzzyRule(15, new string[] { "north", "left" }));
system.addRule(new TagakiSugenoFuzzyRule(0, new string[] { "north", "center" }));
system.addRule(new TagakiSugenoFuzzyRule(-15, new string[] { "north", "right" }));
Let's examine the first rule. It is saying that if the angle is pointing north, and the ball is on the robots left side, the wheels should turn 15°. The same logic applies for the rest of the rules.
Once the Fuzzy system is completely defined, we can start pumping out results. To do this, we define the input FuzzySet
s for the system like this:
FuzzySet setDistancia = new FuzzySet(distancia, robot.getDistancia(posPelota));
FuzzySet setAngulo = new FuzzySet(angulo, robot.getAngulo(posPelota));
FuzzySet[] sets = { setAngulo, setDistancia };
Once the sets are in place, we can feed them to the Fuzzy System to get a crisp result and implement the resulting decision from the system. Here, we'll move the robot 5 units in the direction the robot is moving after turning the amount of the Crisp Output the Fuzzy system gives.
robot.angRobot += Math.PI * system.CrispOutput(sets) / 180;
robot.posRobot.X += (float)(5 * Math.Cos(robot.angRobot));
robot.posRobot.Y += (float)(5 * Math.Sin(robot.angRobot));
Points of Interest
One serious limitation the current code implementation has is that every rule must have the exact same number of conditionals and in the exact same order. This can easily be changed, but due to time constrains was not done. If interest from the community arises, I could find the time to do it, or even better put it on SourceForge.
History
- 2008-10-07: First version