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 constant
double e = 2.718281828459045235360287471352;
//sigmodally shaped membership function
double sigmf(double x, double a, double c){
return 1 / (1 + pow(e,-a * (x - c)));
}
//class to encapsulate line movement and basic kinematics
class 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 class
class 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 AI
class 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.