I feel I have a fairly good grasp of the whole purpose and reason for fuzzy logic, particularly for game AI, but I get a bit confused when it comes down to actually implementing it.

Firstly, here is a screenshot of my fuzzy inference system in matlab:

As you can see, I have 2 inputs (each with 2 membership functions), 2 rules and 1 output.

I understand the inputs well, but I don't quite see how the output values properly tie in via the rules to give a sensible output. My system should assess the current motion of the car and the distance between the car and the line to output a new car motion (change along the single axis in units per frame).

I don't think my output is doing anything useful, but I don't understand what an output actually 'does' based on the rules. Considering rules, what I thought I should be looking for was a way to assess greater or less than, but the only valid options are equal to or not equal to an input's membership function.

My application follows this structure:

//define membership functions for inputs (sigmf curves for rate of change of car and distance from line target) //called every frame, 30 frames per second update(){ //evaluate input membership function results //evaluate rules based on input results //set new rate of change of car based on output membership function }

The part I don't understand is how an output membership function has any application towards evaluating a rate of change.

My output has 2 membership functions, and my rules should dictate which one is applied. But what are the inputs to my output membership functions? I can't seem to find a good explanation online or in my course notes, I am probably very close to getting this to work nicely but my misunderstanding is probably making it seem more complicated than it should be.

Here is the full source (it isn't pluggable yet, while I test it):

[source lang=cpp]#include <iostream>#include <math.h>//for keyboard input#include "conio.h"//for Sleep()#include "Windows.h"using namespace std;//global for Euler's/Napier's constantdouble e = 2.718281828459045235360287471352;//sigmodally shaped membership functiondouble sigmf(double x, double a, double c){ return 1 / (1 + pow(e,-a * (x - c)));}//class to encapsulate line movement and basic kinematicsclass Line{ float x; float xMax; float xSpeed; float xSpeedMax; float friction; float accel;public: Line(float sX, float sXMax, float sFriction, float sXSpeedMax, float sAccel){ x = sX; xMax = sXMax; friction = sFriction; xSpeedMax = sXSpeedMax; accel = sAccel; xSpeed = 0; } void update(){ //if a key has been pressed if(_kbhit() != 0){ switch(_getch()){ // A or a case 97: case 65: xSpeed -= accel; //apply motion down the axis if(xSpeed < -xSpeedMax) xSpeed = -xSpeedMax; //cap the speed break; // D or d case 100: case 68: xSpeed += accel; //apply motion up the axis if(xSpeed > xSpeedMax) xSpeed = xSpeedMax; //cap the speed break; } } if(xSpeed > 0){ //if moving xSpeed -= friction; //apply friction if(xSpeed > 0){ //if still moving x += xSpeed; //move if(x > xMax){ //don't go out of bounds x = xMax; xSpeed = 0; } } else { xSpeed = 0; //don't let friction make it move the other way return; } } else if(xSpeed < 0){ //if moving xSpeed += friction; //apply friction if(xSpeed < 0){ //if still moving x += xSpeed; //move if(x < 0){ //don't go out of bounds x = 0; xSpeed = 0; } } else { xSpeed = 0; //don't let friction make it move the other way return; } } } void draw(){ int i; //pad with spaces until max x marker co-ord for(i = 0;i < xMax;i ++){ cout << " "; } cout << "V\n"; //draw the max x co-ord marker //pad with spaces until x co-ord for(i = 0;i < x;i ++){ cout << " "; } cout << "|\n"; //draw the line } float getX(){ return x; } float getXMax(){ return xMax; }};//semi-pluggable fuzzy inference system classclass FuzzySystem{ double distRange, currentSteerRange; //distance input evaluation double distInput(double dist){ double sigmfResult = sigmf(dist,-0.19,-28.23) * distRange; if(dist > 0) return sigmfResult; return -sigmfResult; } double distInput(float dist){ return distInput(static_cast<double>(dist)); } //rate of change input evaluation double currentSteerInput(double currentSteer){ double sigmfResult = sigmf(currentSteer,-1.02,-5.228) * currentSteerRange; if(currentSteer > 0) return sigmfResult; return -sigmfResult; } double currentSteerInput(float dist){ return currentSteerInput(static_cast<double>(dist)); } //calculates the output from the system based on input results float output(double oDistResult, double oCurrentSteerResult){ //if dist and current steer are "right" (rule) if(oDistResult < 0){ //find a new output double newSteer = sigmf(oDistResult,1.02,5.228)*10; //cout << "newSteer: " << newSteer << endl; printf("'right'\nnewSteer: %.4f\n",newSteer); return static_cast<float>(newSteer); //if dist and current steer are "left" (rule) } else if(oDistResult > 0){ //find a new output double newSteer = sigmf(oDistResult,1.02,5.228)*10; //cout << "newSteer: " << newSteer << endl; printf("'left'\nnewSteer: %.4f\n",newSteer); return static_cast<float>(-newSteer); } else return 0.0f; //no speed, close enough to the line }public: //constructor, sets up the range for inputs FuzzySystem(double sDistRange = 60, double sCurrentSteerRange = 10){ distRange = sDistRange; currentSteerRange = sCurrentSteerRange; } //returns a new speed for the car based on the outcome of fuzzy logic float resolve(double dist, double currentSteer){ cout << dist << endl; cout << currentSteer << "\n-------------------\n"; double distResult = distInput(dist); double currentSteerResult = currentSteerInput(currentSteer); cout << "distResult: " << distResult << endl << "currentSteerResult: " << currentSteerResult << "\n-------------------\n"; float out = output(distResult, currentSteerResult); return out; } float resolve(float dist, float currentSteer){ return resolve(static_cast<double>(dist),static_cast<double>(currentSteer)); }};//class to encapsulate the car and it's associated AIclass CarAI{ Line* line; //maintain a pointer to the line FuzzySystem* fuzzy; float x, xLast, xSpeed; //returns the distance to the line float distToLine(){ return x - line->getX(); } //returns the rate of change of the car's x co-ordinate float xChange(){ return x - xLast; }public: CarAI(Line* sLine){ fuzzy = new FuzzySystem; line = sLine; x = line->getX() + 2.327f; xSpeed = 0; } //called each frame to assess car AI and apply motion void update(){ //have the fuzzy inference system control the x speed xSpeed = fuzzy->resolve(distToLine(),xChange()); //cap the speed if(xSpeed < -10) xSpeed = -10; if(xSpeed > 10) xSpeed = 10; //remember previous x co-ord xLast = x; //apply motion x += xSpeed; //clamp into range if(x < 0) x = 0; if(x > line->getXMax()) x = line->getXMax(); } void draw(){ int i; //pad with spaces until x co-ord for(i = 0;i < x;i ++){ cout << " "; } cout << "#\n"; //draw the car }};int main(){ //introduction message int dots = 1; //loop while there is no keyboard input while(_kbhit() == 0){ if(dots == 1){ //introduction message cout << "AI For Games Development - coursework\n"; cout << "0801109 - Philip Robinson\n"; cout << "Racing Line FIS test application\n\n"; cout << "A and D allow the racing line (|) to be moved left and right.\n"; cout << "V marks the maximum x co-ordinate of the racing line and the " "car.\n"; cout << "The car is denoted by a # character.\n\n"; cout << "Press any key to continue"; } cout << "."; dots ++; Sleep(600); if(dots > 3){ dots = 1; //clear the screen system("cls"); } } //make the Line object Line line(11,60,0.16f,3.0f,0.32f); //make the CarAI object CarAI car(&line); //is the update loop to keep running or not bool run = true; //sleep time in ms each frame int sleepTime = 30; //frame counter long frame = 0; //enter the main loop while(run){ //clear the screen system("cls"); //increment the frame counter frame ++; //update the racing line line.update(); //update the car car.update(); //show the frame count cout << "frame: " << frame << endl; cout << "line x: " << line.getX() << endl; //draw the line line.draw(); //draw the car car.draw(); //limit the frame rate Sleep(sleepTime); } //clear the screen system("cls"); //write closing message cout << "closing app\n"; //sleep Sleep(1000); //end the process return 0;}[/source]

Can anybody direct me to further reading on the topic, or clarify how it all goes together for me? I understand that this post is a bit ambiguous, but that only reflects my own confusion on this topic; feel free to probe me for anything else you need to know to help me. I want to get this completed soon.

Thanks in advance for any help.